Porting of logging directives

I started porting a ros-melodic/kinetic package to ROS2 crystal.
I noticed that the logging mechanism has changed and that many of the useful macros that were there (*_STREAM, *_THROTTLE) are now gone.

Are there any plans / tasks on porting them to ROS 2? If not what’s the reasoning behind this choice?
Also can you think of an way to log without expliciltly specifying the logger object? Alternatively is there any way of acquiring the logger of the currently runnning node?

Hello @bergercookie,

It looks like there were some plans to include throttling capabilities as an additional enhancement to the C++ macros (see: https://github.com/ros2/ros2/issues/425). I don’t believe anyone is actively working on it.

If you’re using the rclcpp logging macros you will have to provide the logger object. However, you can create an arbitrary logger with any name that you’d like that isn’t tied to a node. The only way that you could get around this would be to directly use the rcutils logging macros, which take in a logger name as a string instead of as a C++ object. I’m curious as to what your use case is for not wanting to use the logger object though.

If you have a handle to the Node then you can pretty easily get the logger for that node by using the get_logger() function. In ROS2 the logging APIs do not make any assumptions about a single or any nodes actually running in the process. So you could create a logger and be logging before you event start up any nodes. Additionally, if you have multiple nodes running in the process you can select which node will publish the logs to /rosout by using the specific logger for that node.

1 Like

Hi @nburek

However, you can create an arbitrary logger with any name that you’d like that isn’t tied to a node

That’s sounds like a sensible alternative

I’m curious as to what your use case is for not wanting to use the logger object though.

What if you have a piece of code (e.g., freestanding function) that does some general computation, and doesn’t need to access parameters / topics / services etc.

Still, there might be a need to log some output there, e.g., for debugging reasons. Passing a logger object for this purpose seems kind of an overkill IMHO. I liked it when I could just LOG_WARN directly and anywhere without caring about that extra argument.

Also notice that passing the logger as an extra function parameter practically pollutes the function interface, since it doesn’t really need the logger for its basic functionality.

For example a conversion function with a signature of `bool convertPose(const geometry_msgs::Pose& p_in, CustomType& p2) conveys precisely what its about. Adding a logger reference in the parameters and you basically just add unnecessary noise to the interface.

In ROS2 the logging APIs do not make any assumptions about a single or any nodes actually running in the process. So you could create a logger and be logging before you event start up any nodes. Additionally, if you have multiple nodes running in the process you can select which node will publish the logs to /rosout by using the specific logger for that node.

That’s good to hear!
I’ll take a closer look and consider using them this way then

If you want to discuss an effort to add the missing operators, like stream, throttle (throttle is there btw), etc…, that’s fine, but please move it to an issue on GitHub as soon as you can. For the questions about why and how, that’s better left to answers.ros.org.

Make sure you consider Support - ROS Wiki when posting something like this. :slight_smile:

What you didn’t realize is that there was an implicit first argument there which was the node singleton from ROS 1. As previously discussed, ROS 2 doesn’t have that, so to emulate that macro from ROS 1 you must provide the missing first argument. We cannot guess it for you.

We intentionally require the logger name because otherwise you aren’t forced to consider what it will look like on the console. If you really want to have no logger name, then you can use "", but I don’t recommend that.

Also, you can store logger objects statically and globally, so you can just do something like static const rclcpp::Logger g_logger = rclcpp::get_logger("whatever"); in your cpp file, and then everywhere you want to log you can do RCLCPP_WARN(g_logger, "...");.

But most people don’t have which function or file a log message came from enabled when logging. Imagine you had several nodes, all in the same process, which each call this function, but only one of them produces a log message. How do you know which one it came from?

In ROS 1, the node which produced the warning was implicitly included in calls to things like ROS_WARN, so that wasn’t a problem.

In ROS 2, since nodes are not globally accessible and because there isn’t just one per process, you need to tell us which node it’s a part of when you make a logging call. You can either pass a node through you API’s or an instance of the rclcpp::Logger object (just a struct wrapping a std::string). As @nburek mentioned you can get a logger from a node which includes its full name (include a namespace) with node->get_logger().

1 Like

Actually, maybe throttle is not there in rclcpp, it is in rcutils (http://docs.ros2.org/crystal/api/rcutils/logging__macros_8h.html#af8c643f129b7e4fa679075a9508efa9f), but maybe it was delayed in rclcpp due to needing support for ROS Time (is it thottled based on wall time or ROS time?).

Looks like it:

But there’s no issue for it, but that would be a great contribution :slight_smile: