Post

ROS Log Command Line Interface

ROS Log Command Line Interface

Abstract

This REP proposes a new ros2log command-line package for ROS that provides a unified, user-friendly interface for runtime logging configuration and inspection. The ros2 log command will enable users to list, get, set, and monitor logging levels for ROS nodes, improving the debugging and diagnostic capabilities of ROS systems. This proposal consolidates and standardizes logging-related operations that are currently scattered across different tools and manual service calls.

Motivation

Currently, logging configuration in ROS from the command line is limited and inconsistent. Users must resort to verbose and error-prone manual service calls to configure logging at runtime. For example, to set a logger level, users must type:

1
ros2 service call /NodeWithLoggerService/set_logger_levels rcl_interfaces/srv/SetLoggerLevels '{levels: [{name: "NodeWithLoggerService", level: 20}, {name: "rcl", level: 10}]}'

This approach has several drawbacks:

  • Poor User Experience: The command is verbose, complex, and requires knowledge of service interfaces and message structures.
  • Error-Prone: Manual construction of service request messages is prone to syntax errors.
  • Low Discoverability: Users may not be aware that logging services exist or how to use them.
  • Inconsistent Interface: Logging operations do not follow the established pattern of having dedicated command groups like ros2 topic, ros2 service, ros2 lifecycle, etc.
  • Limited Functionality: There is no built-in way to list available loggers, monitor logs in real-time, or easily inspect logging configuration.

As ROS logging is crucial for debugging and production diagnostics, a dedicated, ergonomic CLI is necessary to improve developer productivity and system maintainability. A unified ros2 log command would provide:

  • Consistent CLI user experience for logging across ROS
  • Reduced likelihood of errors through simplified syntax
  • Improved discoverability via ros2 log --help
  • Enhanced production debugging and runtime introspection capabilities
  • Alignment with existing ROS CLI patterns

Specification

This REP introduces a new top-level command group ros2 log with the following subcommands:

Command Overview

SubcommandDescription
ros2 log listList nodes that support runtime log configuration
ros2 log levelsShow all valid log level values
ros2 log get <node>Retrieve the current log levels of a node
ros2 log set <node> <level>Set the log level of a node
ros2 log watch [options]Monitor and display logs in real-time
ros2 log describe <node>Display logger metadata for a node

Detailed Command Specifications

ros2 log list

Lists all nodes in the ROS graph that have the logger services enabled (i.e., nodes with enable_logger_service set to true). You can see more information about Logger level configuration: externally.

Usage:

1
ros2 log list

Output Example:

1
2
3
/my_node
/another_node
/third_node

ros2 log levels

Displays all valid log severity levels supported by ROS.

Usage:

1
ros2 log levels

Output Example:

1
2
3
4
5
6
UNSET   : The logger level is not set explicitly and will use the default or inherited level.
DEBUG   : Debug is for pedantic information, which is useful when debugging issues.
INFO    : Info is the standard informational level and is used to report expected information.
WARN    : Warning is for information that may potentially cause issues or possibly unexpected behavior.
ERROR   : Error is for information that this node cannot resolve.
FATAL   : Information about a impending node shutdown.

Note:

The ros2 log commands accept these human-readable log level names (UNSET, DEBUG, INFO, WARN, ERROR, FATAL) as arguments, eliminating the need to use numeric integer values (0, 10, 20, 30, 40, 50) that are required when calling the underlying services directly. This improves usability and reduces errors compared to the manual service call approach.

ros2 log get <node>

Retrieves and displays the current log levels for the loggers in the specified node.

Usage:

1
2
ros2 log get <node_name> [<logger_name>]
ros2 log get --all

Arguments:

  • <node_name>: The fully qualified name of the node (e.g., /my_node)
  • <logger_name>: (Optional) The name of a specific logger to query. If not specified, all loggers in the node will be displayed.

Options:

  • --all, -a: Get log levels for all nodes with logger services enabled

Output Example (all loggers in a specific node):

1
2
3
4
5
6
7
Node: /my_node
  Logger: my_node
    Level: INFO
  Logger: my_node.vision
    Level: WARN
  Logger: my_node.perception
    Level: ERROR

Output Example (specific logger):

1
2
3
Node: /my_node
  Logger: my_node.speech_recognizer
    Level: WARN

Output Example (all nodes with –all option):

1
2
3
4
5
6
7
8
9
10
11
12
13
Node: /my_node
  Logger: my_node
    Level: INFO
  Logger: my_node.vision
    Level: WARN

Node: /another_node
  Logger: another_node
    Level: DEBUG

Node: /third_node
  Logger: third_node
    Level: ERROR

Error Handling:

If the node does not have the enable_logger_service option enabled, the command will display:

1
2
Error: Logger service not available for node '/my_node'.
The 'enable_logger_service' node option must be enabled for this node.

If the specified logger name does not exist in the node, the command will display:

1
Error: Logger 'nonexistent_logger' not found in node '/my_node'.

When using --all option, nodes without logger services enabled will be skipped silently, and only nodes with enabled logger services will be queried and displayed.

ros2 log set <node> <level>

Sets the log level for the logger(s) in the node.

Usage:

1
2
ros2 log set <node_name> <level> [<logger_name>]
ros2 log set --all <level>

Arguments:

  • <node_name>: The fully qualified name of the node (e.g., /my_node)
  • <level>: The log level to set (UNSET, DEBUG, INFO, WARN, ERROR, FATAL)
  • <logger_name>: (Optional) The name of a specific logger to set. If not specified, the level will be applied to all loggers in the node.

Options:

  • --all, -a: Set the log level for all loggers in all nodes with logger services enabled

Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Set the log level for all loggers in a node
ros2 log set /my_node DEBUG

# Set a specific logger's level
ros2 log set /my_node WARN my_node.vision

# Set another specific logger under the node
ros2 log set /my_node INFO my_node.perception

# Set all loggers in all nodes to ERROR level
ros2 log set --all ERROR

# Set all loggers in all nodes to DEBUG level (useful for system-wide debugging)
ros2 log set -a DEBUG

Error Handling:

If the service is not available, the command will provide a clear error message as described in the get command. If the specified logger name does not exist in the node, the command will display:

1
Error: Logger 'nonexistent_logger' not found in node '/my_node'.

When using --all option, nodes without logger services enabled will be skipped silently, and the command will set the specified log level for all loggers in all nodes with enabled logger services.

ros2 log watch [options]

Monitors and displays log messages in real-time by subscribing to the /rosout topic. This command provides filtering capabilities to display only relevant log messages.

Usage:

1
ros2 log watch [options]

Options:(T.B.D)

  • --level <level>: Show only logs at or above the specified severity level
  • --logger <logger_name>: Filter logs by logger name
  • --regex <pattern>: Filter log messages matching the specified regular expression pattern
  • --no-color: Disable colorized output (default: false)
  • --no-timestamp: Disable timestamp (default: false)
  • --function-detail: Output function name, file, and line number (default: false)

Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Watch all logs from /rosout topic
ros2 log watch

# Watch logs from a specific logger
ros2 log watch --logger my_node.perception

# Watch only ERROR and FATAL logs
ros2 log watch --level ERROR

# Watch logs containing specific text pattern
ros2 log watch --regex "sensor.*timeout"

# Combine filters: ERROR logs from specific logger matching pattern
ros2 log watch --level ERROR --logger my_node.vision --regex "camera"

Implementation Notes:

The watch command should leverage Content Filtered Topics when the underlying RMW implementation supports it, to reduce network traffic and improve performance by filtering messages at the publisher side. When Content Filtered Topics are not supported by the RMW implementation, ros2 log watch will perform client-side filtering to ensure consistent behavior across all platforms. The --regex option applies pattern matching to the message content field of the log message, allowing users to filter based on specific keywords, error codes, or message patterns.

ros2 log describe <node>

Displays comprehensive metadata about the loggers available in a node, including logger names, current levels, and effective configuration.

Usage:

1
ros2 log describe <node_name>

Arguments:

  • <node_name>: The fully qualified name of the node

Output Example:

1
2
3
4
5
6
7
8
9
10
Node: /my_node
Logger Service: Enabled

Available Loggers:
  - my_node (INFO)
  - my_node.vision (WARN)
  - my_node.perception (ERROR)
  - my_node.speech_recognizer (INFO)

Total Loggers: 4

Service Requirements

All commands that interact with node logger configuration (get, set, describe) require that the target node has the enable_logger_service node option set to true.

The logger services are:

  • <node_name>/get_logger_levels (rcl_interfaces/srv/GetLoggerLevels)
  • <node_name>/set_logger_levels (rcl_interfaces/srv/SetLoggerLevels)

By default, these services are disabled in nodes. Developers must explicitly enable them when creating nodes, see more details for Logger level configuration: externally.

Rationale

Why a New Top-Level Command Group?

Several design alternatives were considered:

  1. Adding to ros2 node command: Initially proposed in this approach was rejected because:
    • It creates inconsistency where setting is done via ros2 node log but getting requires ros2 service
    • Logging is a cross-cutting concern that applies to multiple loggers, not just the node itself
    • The ros2 node namespace is already focused on node lifecycle and introspection
  2. Using ros2 service directly: While this is technically possible, it is:
    • Too verbose and error-prone for common operations
    • Not discoverable for users unfamiliar with the service interface
    • Inconsistent with the pattern established by ros2 lifecycle, ros2 topic, etc.
  3. New ros2 log command group (chosen approach):
    • Follows the established pattern in ROS CLI (similar to ros2 lifecycle, ros2 topic, etc.)
    • Provides a dedicated namespace for all logging-related operations
    • Offers better discoverability and user experience
    • Allows for future expansion of logging features

Logger Service Requirement

The requirement that nodes must enable enable_logger_service is maintained from the existing ROS design.

This is a deliberate choice to:

  • Reduce overhead for nodes that do not need runtime log reconfiguration
  • Allow nodes to opt-in to providing runtime logging control
  • Maintain backward compatibility with existing systems

Clear error messages guide users when services are not available, helping them understand how to enable the functionality.

Content Filtered Topics for Watch

The watch command is designed to support Content Filtered Topics (CFT) when available in the RMW implementation.

This provides:

  • Reduced network bandwidth by filtering at the publisher side
  • Better performance when monitoring large-scale systems
  • Graceful fallback to client-side filtering when CFT is not available

Backwards Compatibility

This REP introduces new functionality and does not modify or remove any existing interfaces.

It is fully backward compatible:

  • Existing ros2 service commands continue to work as before
  • Nodes that do not enable enable_logger_service are unaffected
  • No changes to the ROS logging infrastructure or APIs are required
  • The new commands are purely additive to the ROS CLI

Users who currently use manual service calls can continue to do so, though the new ros2 log commands provide a more ergonomic alternative.

How to Teach This

For New Users

New users learning ROS will benefit from the intuitive command structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Discover logging commands
ros2 log --help

# See what log levels are available
ros2 log levels

# Find nodes with logging support
ros2 log list

# Check current log level
ros2 log get /my_node

# Change log level for debugging
ros2 log set /my_node DEBUG

Documentation Updates

The following documentation should be updated:

  1. ROS Tutorials: Update the “Logging and logger configuration” tutorial to demonstrate ros2 log commands
  2. CLI Reference: Add comprehensive documentation for all ros2 log subcommands
  3. Node Development Guide: Mention the need to enable enable_logger_service when runtime log configuration is desired
  4. Migration Guide: Provide examples showing how to transition from service calls to ros2 log commands

Implementation

Testing Requirements

The implementation must include:

  • Unit tests for each verb
  • Integration tests with actual nodes
  • Tests for error conditions (service not enabled, invalid arguments, etc.)
  • Tests for filtering options in watch command

API Extensions

To support the ros2 log list and ros2 log describe commands effectively, the following enhancements are needed in the ROS logging infrastructure. The existing logger services (get_logger_levels and set_logger_levels) are necessary but not sufficient for the full ros2 log specification.

The following API extensions should be proposed and implemented in the rcl and rcutils repositories:

A new function in rcutils/logging.h to query available logger names from the logger registry maintained in rcutils:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * Get all logger names matching a base logger name pattern.
 *
 * \param[in] base_logger_name The base logger name to search for.
 *   If NULL, returns all loggers in the system.
 *   If non-NULL (e.g., "my_node"), returns:
 *   - Exact match: "my_node"
 *   - Descendants: "my_node.vision", "my_node.control", "my_node.control.pid", etc.
 * \param[in] allocator Allocator to use for the output string array
 * \param[out] logger_names Output string array containing matching logger names
 * \return RCUTILS_RET_OK if successful
 */
rcutils_ret_t rcutils_logging_get_logger_names(
  const char * base_logger_name,
  rcutils_allocator_t allocator,
  rcutils_string_array_t * logger_names);

Use Cases:

  • ros2 log describe /my_node: Calls with base_logger_name = "my_node" to find all loggers under that node
  • ros2 log get /my_node: Retrieves all logger names, then queries their levels
  • Debugging tools: Calls with base_logger_name = NULL to get all system loggers

This enhancement would allow ros2 log get to discover and retrieve all loggers in a single service call.

System Logger Attribution

System loggers (such as rcl, rcutils, rclcpp, etc.) are explicitly excluded from this REP’s scope for the following reasons:

Why System Loggers Are Not Handled:

  1. Shared Process Space: System loggers exist in the process space and are shared among all nodes in that process, including composable nodes.
  2. No Clear Ownership: These loggers are not under the control of any specific node, making it impossible to definitively attribute them to a single node.
  3. Composable Node Complexity: In deployments using composable nodes, multiple nodes coexist in the same process, all sharing the same system logger instances.
  4. Global Scope: Changes to system logger levels affect the entire process, not just individual nodes, making node-specific logger services inappropriate for managing them.

Implications for ros2 log Commands:

  • ros2 log get <node_name>: Will return only loggers that belong to or are created by the specified node (e.g., node_name, node_name.subsystem), excluding system loggers like rcl, rcutils, rclcpp.
  • ros2 log set <node_name> <level>: Will only affect loggers owned by the node, not system-level loggers.
  • ros2 log describe <node_name>: Will list only the node’s own loggers, not system loggers.

Rationale:

Associating system loggers with specific nodes would be misleading and could cause confusion:

  • Users might expect that setting a system logger level via one node would only affect that node, when in reality it affects all nodes in the process.
  • In composable node scenarios, the same system logger would appear in multiple nodes’ logger lists, creating ambiguity about which “instance” is being modified.
  • The ownership model breaks down: if Node A and Node B both use rcutils, which node “owns” its log level?

This separation maintains clear semantics and prevents user confusion about the scope of logger changes.

License

This document is marked CC0 1.0 Universal. To view a copy of this mark, visit https://creativecommons.org/publicdomain/zero/1.0/.

This post is licensed under CC BY 4.0 by the author.