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

/rosout value

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 --ros-args

Effect

--disable-rosout-logs

Stop publishing logs to /rosout.

--disable-external-lib-logs

Disable the external backend, normally responsible for disk logs.

--disable-stdout-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 INFO threshold.

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 ROS_DOMAIN_ID, and network discovery.

Some startup messages are missing

Start the observer first, then restart the example.

No log file in the expected directory

Check ROS_LOG_DIR, ROS_HOME, directory permissions, and whether disk logging was disabled.

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