Skip to content

Add per node & component log level support #3091

@penww

Description

@penww

Description

This issue tracks the addition of a log_level field to NodeOptions, allowing users to set the initial log severity for a node at construction time. This enables per-component log level customization, particularly useful in component container deployments.

Motivation

In ROS 2, all nodes loaded into a component container share the same process. Prior to this change, there was no way to specify a different initial log level for individual components at load time — the only option was to set a global log level or call set_level() programmatically after construction.

This is a common need in production deployments where:

  • Different components have different verbosity requirements (e.g., a sensor driver at WARN, a planner at DEBUG).
  • Operators want to configure log levels declaratively via launch files or LoadNode service calls without modifying node source code.
  • Debugging a single component in a multi-component container should not require restarting the entire process with a global log level change.

We can see this feature has designed but not full implementation:

Expected Behavior:

  • Set component log_level with CLI:
ros2 component load /ComponentManager <PKG> <PLUGIN> --log-level debug
  • Set component log_level with launch:
def generate_launch_description():
    return LaunchDescription([ComposableNodeContainer(
        name='component_demo_container',
        namespace='',
        package='rclcpp_components',
        executable='component_container',
        composable_node_descriptions=[
            ComposableNode(
                package='<PKG>',
                plugin='<PLUGIN>',
                log_level='DEBUG',  # component log level
            ),
        ],
        arguments=['--ros-args', '--log-level', 'DEBUG'],  # container log level
    )])

Design / Implementation Considerations

NodeOptions API

A new log_level getter/setter pair is added to NodeOptions:

// Get the log level (RCUTILS_LOG_SEVERITY_* value; 0 = unset/default)
int log_level() const;

// Set the initial log level for this node
NodeOptions & log_level(int log_level);

The default value is RCUTILS_LOG_SEVERITY_UNSET (0), which preserves existing behavior — no level is applied at construction.

Node construction (node.cpp)

After the node is fully constructed, if log_level is not UNSET, the logger level is applied:

if (options.log_level() != RCUTILS_LOG_SEVERITY_UNSET) {
  node_logging_->get_logger().set_level(
    static_cast<rclcpp::Logger::Level>(options.log_level()));
}

This is intentionally applied after construction so the logger is fully initialized.

ComponentManager integration (component_manager.cpp)

When handling a LoadNode service request, the log_level field from the request is validated and forwarded to NodeOptions:

if (request->log_level != 0) {
  // Validate against known RCUTILS severity values
  options.log_level(request->log_level);
}

Invalid values (not one of DEBUG, INFO, WARN, ERROR, FATAL) throw a ComponentManagerException with a descriptive message.

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions