launch
launch copied to clipboard
Support setting log-level via command-line
Feature request
Feature description
Allow setting log-level via command line, similar as documented for ros2 run, ros2 service, etc. see ros2/Tutorials/Logging-and-logger-configuration
Steps to reproduce issue
ros2 launch demo_nodes_cpp talker_listener.launch.py --ros-args --log-level debug
Expected behavior
log level is set to debug
Actual behavior
ros2: error: unrecognized arguments: --ros-args --log-level debug
@norro Could you provide more information about why this feature is needed and what it would do?
Hello, I am also interested in this feature, for my use case I'd like to sometimes launch my system at logging level WARN, and sometimes at logging level ERROR (basically I'd like to easily switch between more or less verbose options).
First of all, it's important not to mix up ROS 2 node arguments with ROS 2 cli arguments. --ros-args applies to ROS 2 nodes only. You may pass it to ros2 run solely because it parses none of it and forwards it all to the underlying process.
Having said that, currently this can be achieved by explicitly declaring a launch argument and then using that argument to set launch_ros.actions.Node arguments. Something along the lines of:
def generate_launch_description():
return launch.LaunchDescription([
launch.actions.DeclareArgument(name='log_level', default_value='info'),
launch_ros.actions.Node(
# name, namespace, executable, etc.
arguments=['--ros-args', '--log-level', LaunchConfiguration('log_level')]
)
])
should do.
It could be made implicit and applicable to all nodes, though in that case I wonder if it's worth all the plumbing instead of simply having rcl look at some environment variable to select the default logger level. Would something like that satisfy your use cases @norro @tim-fan? CC @wjwwood for valuable feedback.
That would satisfy my needs and would even provide a more fine-grained handle than I initially envisioned. As this is currently already possible, I would close this issue if @tim-fan doesn't object.
Thanks for the great suggestion, @hidmic!
Hi, thanks for the feedback.
I had considered the suggestion above of passing log-level arguments down to individual nodes. An issue with this for me is when I am including other launch files from external packages, in which case I cannot add the extra arguments. I'd say it may get a bit unwieldy in general for larger projects, if every launch file needs to pass logging levels to every node, in order to achieve this global level of control over logging.
As an alternative, the "environment variable sets default logging level" suggestion sounds good to me 👍
I have a slight preference for command line arguments over environment variables, but I think that would be ok too.
Typically in ROS 1, this kind of thing was done with a launch argument as @hidmic suggested simply because you often did not want all nodes to change log level, but instead you wanted to target a specific node or namespace to change the level. This is increasingly important with larger systems. So at some point setting an env var before ros2 launch will stop scaling, but it can be useful for now. It can also be useful for changing the log level for groups of nodes/processes within a launch file or in included launch files with less effort than adding additional command line arguments. We do need to set some precedence order between the two methods (env var vs command line) in case both are used. Another feature that could scale is using a parameter, we could have all the nodes declare a parameter for controlling the log level and it has many of the benefits of both env vars and command line arguments within a launch file, as you can override it using command line arguments or for a group of nodes with SetParameter:
https://github.com/ros2/launch_ros/blob/a8c12a95f169334e09eca2502d2c629847fa82bb/launch_ros/launch_ros/actions/set_parameter.py#L33
Alternatively we could have some special handling for --ros-args in ros2 launch, but that would need to be a more carefully thought out feature, ideally not just passing these args to all node entries in the launch file, but instead having the ability to target it to specific nodes (in the case of dynamically loaded component nodes) and/or executables.
This applies equally to name remapping and parameter overrides, which are also often done at runtime (desirable as a command line option). It would be very convenient to forward these options to all of the nodes, since these are things that are good candidates to change at runtime, and it is burdensome to have to edit the launch code to do so, even to add boilerplate to forward these options. There would be some edge cases that wouldn't make a ton of sense, especially for name remapping, which can be node specific like __node:=new_node_name. However, we could just say that the expected behavior in that case is just giving all the nodes the same name, and if that is a bug, then it is your fault for specifying that command line option.
This way, you can set log level to nodes. Try this launch file. This worked for me.
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
import launch_ros.actions
from launch.substitutions import TextSubstitution
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
return LaunchDescription([
DeclareLaunchArgument(
"Parameter_launch_argument", default_value=TextSubstitution(text=str("Parameter_launch")),
description="Parameter launch default value"
),
DeclareLaunchArgument(
"log_level",
default_value = TextSubstitution(text=str("WARN")),
description="Logging level"
),
launch_ros.actions.Node(
package="serverpublisher",
executable="talker",
name="custom_msg_publisher_to_Life_iteration_topic",
parameters=[
{"Parameter_Publisher": LaunchConfiguration('Parameter_launch_argument')}
],
output="screen",
arguments=['--ros-args', '--log-level', LaunchConfiguration('log_level')]
),
launch_ros.actions.Node(
package="serverpublisher",
executable="server",
name="custom_server_to_add_three_ints",
output="screen",
arguments=['--ros-args', '--log-level', LaunchConfiguration('log_level')]
),
launch_ros.actions.Node(
package="serverpublisher",
executable="talkerandlistner",
name="custom_msg_subscriber_to_No_Life_iteration_topic",
output="screen",
arguments=['--ros-args', '--log-level', LaunchConfiguration('log_level')]
)
])
Coming from Galactic, node logging has been introduced, which sets up log level to specific node, avoiding global log level
From the last example, it looks like this:
<...>
DeclareLaunchArgument(
"log_level",
default_value = TextSubstitution(text=str("WARN")),
description="Logging level"
),
launch_ros.actions.Node(
package="serverpublisher",
executable="talker",
name="custom_msg_publisher_to_Life_iteration_topic",
parameters=[
{"Parameter_Publisher": LaunchConfiguration('Parameter_launch_argument')}
],
output="screen",
arguments=['--ros-args', '--log-level', ['custom_msg_publisher_to_Life_iteration_topic:=', LaunchConfiguration('log_level')]]
), # change here
launch_ros.actions.Node(
package="serverpublisher",
executable="server",
name="custom_server_to_add_three_ints",
output="screen",
arguments=['--ros-args', '--log-level', ['custom_server_to_add_three_ints:=', LaunchConfiguration('log_level')]]
), # change here
Slightly upsetting, from my understanding you cannot pass the Node name making it more elegant:
arguments=['--ros-args', '--log-level', [name, ':=', LaunchConfiguration('log_level')]