ROS 2 Logging
1. Introduction
When a robot does not behave as expected, logs help explain what its nodes are doing: starting up, receiving data, waiting for a transform, or reporting an error. A node’s logger identifies the source and importance of each message.
For example, Turtlesim logs information when it starts and spawns a turtle.
ROS logs can reach the terminal, files on disk, and other ROS tools through
the /rosout topic. Ordinary printf, print, or println! output does
not automatically become a ROS log message.
In this exercise, run Turtlesim and keyboard teleoperation and observe their existing output. No source-code changes or new packages are needed.
2. Severity levels
Level |
|
Example use |
|---|---|---|
DEBUG |
10 |
Inspect a computed value during debugging. |
INFO |
20 |
Report successful startup or a state change. |
WARN |
30 |
Report an unexpected condition that permits continued operation. |
ERROR |
40 |
Report a failed operation. |
FATAL |
50 |
Report a condition that prevents useful operation. |
The default threshold is INFO.
A threshold of WARN permits WARN, ERROR, and FATAL, while suppressing DEBUG and INFO.
A FATAL log call alone does not shut down a node; application code decides how to handle the failure.
See the ROS 2 logging concepts for severity filtering and logger configuration.
3. Start Turtlesim and teleop
In the first terminal, source ROS 2 and start the simulator:
source /opt/ros/jazzy/setup.bash
ros2 run turtlesim turtlesim_node
The Turtlesim window opens. Look at the startup log messages in this terminal: they include a severity, timestamp, logger name, and message.
In a second terminal, start keyboard teleoperation:
source /opt/ros/jazzy/setup.bash
ros2 run turtlesim turtle_teleop_key
Keep the second terminal focused and use the arrow keys to move the turtle. Watch the simulator terminal for log messages. Moving the turtle does not necessarily produce a message for every key press. The keyboard instructions printed by teleop are ordinary terminal output, not necessarily ROS logs.
4. Change the logging threshold
Stop only the simulator with Ctrl+C, then restart it with a warning threshold:
ros2 run turtlesim turtlesim_node --ros-args --log-level turtlesim:=WARN
The startup INFO messages are now suppressed. Warnings and errors can still
appear when those conditions occur. Teleop can stay running while you restart
the simulator.
Here, turtlesim is the logger name, while turtlesim_node is the executable.
Restart without the extra arguments to restore the default INFO threshold:
ros2 run turtlesim turtlesim_node
5. Observe /rosout
In a third terminal, start an observer, then restart Turtlesim with its default logging threshold to observe the startup messages:
source /opt/ros/jazzy/setup.bash
ros2 topic echo /rosout
The topic carries rcl_interfaces/msg/Log messages. Inspect the definition:
ros2 interface show rcl_interfaces/msg/Log
Each message includes stamp, level, name, msg, and source-location
fields file, function, and line. Available source-location details
depend on the client library.
Stop the observer with Ctrl+C and try each filter separately.
Show only the message text from Turtlesim:
ros2 topic echo /rosout --filter "m.name == 'turtlesim'" --field msg
Show warnings and more severe messages from any logger:
ros2 topic echo /rosout --filter "m.level >= 30"
These filters select messages received by the observer. They cannot recover
debug messages suppressed by the producing node’s threshold. Start the
observer before the node to capture startup messages; /rosout is not a
complete historical log archive.
6. Use rqt Console
Start the graphical log viewer in another sourced terminal:
ros2 run rqt_console rqt_console
Alternatively, run rqt and choose Plugins -> Logging -> Console.
If the package is missing on Ubuntu with Jazzy, install ros-jazzy-rqt-console.
Restart Turtlesim and locate the turtlesim logger in the received messages.
Use severity and text filters to narrow the view. Compare an INFO run with
a WARN run: the viewer only receives messages the node actually emits.
7. Find log files and control destinations
With the default logging backend, files normally appear under ~/.ros/log.
Set ROS_LOG_DIR before starting the node to choose an explicit directory:
export ROS_LOG_DIR="$HOME/turtlesim_logs"
Restart Turtlesim from that terminal, stop it with Ctrl+C, and inspect
that directory:
ls -lt "$ROS_LOG_DIR"
Filenames and launch-created subdirectories vary. Open the newest relevant
file and find the simulator’s startup messages from an INFO run.
Restore the default afterward:
unset ROS_LOG_DIR
The following ROS arguments selectively disable destinations:
Argument after |
Effect |
|---|---|
|
Stop publishing logs to |
|
Disable the external backend, normally responsible for disk logs. |
|
Disable console logs, despite the default stream being stderr. |
For example, restart the simulator with:
ros2 run turtlesim turtlesim_node --ros-args --disable-rosout-logs
Terminal logs should continue, but new messages from this node should no
longer arrive in the /rosout observer.
8. Stop the example
Press Ctrl+C in the teleop and simulator terminals. Stop the
/rosout observer and close rqt Console if you opened them.
9. Troubleshooting
Symptom |
Check |
|---|---|
No startup messages |
Restart Turtlesim with the default |
Terminal output but no rosout messages |
Use ROS logging calls, keep the node alive, and check that rosout is enabled. |
No messages from another terminal or computer |
Check environment setup, matching |
Some startup messages are missing |
Start the observer first, then restart the example. |
No log file in the expected directory |
Check |
10. How it works internally
Client libraries expose language-specific logging calls. Underneath, the ROS logging infrastructure filters messages and routes enabled output to
the console, the external file backend, and /rosout through the middleware.
How logs are published to /rosout
There is a per-node rosout publisher when rosout logging is enabled.
The client library manages its initialization and cleanup with the node’s lifecycle.
/rosout is the network output destination; console and disk logging are separate destinations.
RCLCPP
Node initialization performs approximately the following steps. This is simplified pseudocode, not code to add to your application:
rcl_node_init(node, ...);
if (
rcl_logging_rosout_enabled() &&
node_options.enable_rosout
) {
rcl_logging_rosout_init_publisher_for_node(node);
}
The call to
rcl_logging_rosout_init_publisher_for_node
in NodeBase initializes the publisher for /rosout. With a DDS-based
middleware, this creates the underlying DDS publisher through the ROS
middleware layer.
During node destruction, NodeBase calls
rcl_logging_rosout_fini_publisher_for_node
to clean up that rosout publisher before finalizing the node.
RCLPY
RCLPY follows the same explicit lifecycle in its underlying C++ binding:
initialize the RCL node, initialize its rosout publisher when enabled, and finalize that publisher during node destruction.
Python application code uses self.get_logger().
Publisher lifecycle and message flow
Node creation (rclcpp / rclpy)
|
+-- rcl_node_init()
|
+-- if rosout is enabled:
rcl_logging_rosout_init_publisher_for_node()
Regular logging calls (rclcpp / rclpy / rclrs)
|
+-- severity filtering and output routing
|
+-- console
+-- disk via the external logging backend
+-- /rosout via the node's publisher, when enabled
Node destruction (rclcpp / rclpy)
|
+-- clean up rosout publisher:
| rcl_logging_rosout_fini_publisher_for_node()
|
+-- rcl_node_fini()
Creating the publisher and emitting a log message are separate operations.
The publisher is prepared during node initialization and used by subsequent logging calls.
Application code should use the node’s logger rather than manually creating a /rosout publisher or calling these internal functions.
11. Further reading
rclrs 0.7 logging macros and examples. Warning: rclrs 0.7 does not publish /rosout, however next release should.