Skip to content
Advertisement

Logging with Python, ROS, and C++

I have a codebase of Python and C++ code, including heavy use of ROS. Logging is done throughout the Python code with both system logger and rospy logging — contrived example:

import logging
import rospy

logging.basicConfig(level=logging.INFO)
LOG = logging.getLogger(__name__)

def run():
    rospy.loginfo("This is a ROS log message")
    LOG.info("And now from Python")

if __name__ == '__main__':
    runt()

As for C++ code we need to add logging, probably with glog but I’m open to other options.

Is there a way to integrate the various loggers into one module? Ideally a user could do something like my_logger = AwesomeLogger(level='info', output='my_logs.txt') and then AwesomeLogger behind the scenes sets up the Python and C++ loggers, and combines all log outputs (including from ROS) into clean console messages output text file.

Note we target support for ubuntu 16.04, ROS-kinetic, C++11, Python 2.7*

*If a solution provides rational for moving to Python 3.6 you get bonus points!

UPDATE If I load a dict config from yaml (as described in this post on logging best practices I can specify handlers and loggers for ROS. But with the yaml below I get duplicated rospy log messages to the console, one in the standard rospy log format and the other in my specified format. Why??

version: 1
disable_existing_loggers: True

formatters:
    my_std:
        format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        datefmt: "%Y/%m/%d %H:%M:%S"

handlers:
    console:
        class: logging.StreamHandler
        formatter: my_std
        level: DEBUG
        stream: ext://sys.stdout
    info_file_handler:
        class: logging.handlers.RotatingFileHandler
        level: INFO
        formatter: my_std
        filename: info.log
        maxBytes: 10485760 # 10MB
        backupCount: 20
        encoding: utf8
    rosconsole:
        class: rosgraph.roslogging.RosStreamHandler
        level: DEBUG
        formatter: my_std
        colorize: True

loggers:
    my_module:
        level: INFO
        handlers: [console]
        propagate: no
    rosout:
        level: INFO
        handlers: [rosconsole]
        propagate: yes
        qualname: rosout

root:
    level: INFO
    handlers: [console, info_file_handler, rosconsole]

Advertisement

Answer

This config yaml does the trick:

version: 1
disable_existing_loggers: True

formatters:
    my_std:
        format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        datefmt: "%Y/%m/%d %H:%M:%S"

handlers:
    console:
        class: logging.StreamHandler
        formatter: my_std
        level: DEBUG
        stream: ext://sys.stdout
    info_file_handler:
        class: logging.handlers.RotatingFileHandler
        level: INFO
        formatter: my_std
        filename: info.log
        maxBytes: 10485760 # 10MB
        backupCount: 20
        encoding: utf8

loggers:
    __main__:
        level: DEBUG
        handlers: [console]
        propagate: no
    rosout:
        level: INFO
        propagate: yes
        qualname: rosout

root:
    level: INFO
    handlers: [console, info_file_handler]

And to load it,

import logging
import yaml

if os.path.exists(config_path):
    with open(config_path, 'rt') as f:
        config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement