ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

I'll propose a more pythonic way to merge a separate logging into roslogging. Feedback welcome!

  1. Pros
  2. Cons/Todos
  3. How To

1. Pros

  • Just use the standard python logging module, featuring formatting, namespacing hierarchy, log levels etc. pp.
  • If the library is used without modifying its logging like above, the logs are written to console in the given formatting
  • If only the forwarding code above is executed, the logs are written to console using ROS (so the formatting matches the rest of the program)
  • If additionally the wrapping/user code calls rospy.init_node(..), the logs are automatically published on /rosout
  • The logging namespace hierarchy is viewable in the logger level GUIs and for each namespace a level can be set, affecting console and rosout output as usual.

2. Cons/Todos

  • Logs issued right after init_node without any delay might not be published. I don't know where that buffer is missing, but have never experienced that with the default rospy logging (rospy.log*) so it could be fixable.

3. How To

In the ROS-agnostic library add: (named rgoap in this case, the file being rgoap/__init__.py):

## logging

import logging
_logger = logging.getLogger('rgoap')
"""The logger used in this library"""
_logger.setLevel(logging.INFO)

# add default console output
_loghandler = logging.StreamHandler()
_loghandler.setLevel(logging.INFO)
_loghandler.setFormatter(logging.Formatter('[%(levelname)s] %(message)s'))
_logger.addHandler(_loghandler)

def remove_default_loghandler():
    """Call this to mute this library or to prevent duplicate messages
    when adding another log handler to the logger named 'rgoap'."""
    _logger.removeHandler(_loghandler)

In the library's code files just log via:

import logging
_logger = logging.getLogger('rgoap') # you can also use dotted namespaces like 'rgoap.planner'!

def my_func():
    _logger.info("Hello %s", "World")

In the ROS-wrapper for that library add: (for example in rgoap_ros/__init__.py)

import rgoap
import rospy

### set up rgoap-ros interface

## forward rgoap's logging to ROS
import logging

# ..for console output
import rosgraph.roslogging as _rl
logging.getLogger('rgoap').addHandler(_rl.RosStreamHandler())

# ..for network output (/rosout)
import rospy.impl.rosout as _ro
logging.getLogger('rgoap').addHandler(_ro.RosOutHandler())

# remove the default console output
rgoap.remove_default_loghandler()