diff --git a/launch_ros/launch_ros/actions/load_composable_nodes.py b/launch_ros/launch_ros/actions/load_composable_nodes.py index 7875a178..c881a2e7 100644 --- a/launch_ros/launch_ros/actions/load_composable_nodes.py +++ b/launch_ros/launch_ros/actions/load_composable_nodes.py @@ -40,6 +40,7 @@ from launch_ros.parameter_descriptions import ParameterFile import lifecycle_msgs.msg +import rcl_interfaces.msg from .composable_node_container import ComposableNodeContainer from .lifecycle_transition import LifecycleTransition @@ -321,7 +322,6 @@ def get_composable_node_load_request( combined_ns = make_namespace_absolute(prefix_namespace(base_ns, expanded_ns)) if combined_ns is not None: request.node_namespace = combined_ns - # request.log_level = perform_substitutions(context, node_description.log_level) remappings = [] global_remaps = context.launch_configurations.get('ros_remaps', None) if global_remaps: @@ -366,4 +366,18 @@ def get_composable_node_load_request( ) ) ] + + if composable_node_description.log_level is not None: + log_level_str = perform_substitutions( + context, composable_node_description.log_level).upper() + log_level_map = { + 'DEBUG': rcl_interfaces.msg.Log.DEBUG, + 'INFO': rcl_interfaces.msg.Log.INFO, + 'WARN': rcl_interfaces.msg.Log.WARN, + 'WARNING': rcl_interfaces.msg.Log.WARN, + 'ERROR': rcl_interfaces.msg.Log.ERROR, + 'FATAL': rcl_interfaces.msg.Log.FATAL, + } + request.log_level = log_level_map.get(log_level_str, 0) + return request diff --git a/launch_ros/launch_ros/descriptions/composable_lifecycle_node.py b/launch_ros/launch_ros/descriptions/composable_lifecycle_node.py index 479efd01..78383dc5 100644 --- a/launch_ros/launch_ros/descriptions/composable_lifecycle_node.py +++ b/launch_ros/launch_ros/descriptions/composable_lifecycle_node.py @@ -119,3 +119,8 @@ def remappings(self) -> Optional[RemapRules]: def extra_arguments(self) -> Optional[Parameters]: """Get container extra arguments YAML files or dicts with substitutions to be performed.""" return super().extra_arguments + + @property + def log_level(self) -> Optional[List[Substitution]]: + """Get log level as a sequence of substitutions to be performed.""" + return super().log_level diff --git a/launch_ros/launch_ros/descriptions/composable_node.py b/launch_ros/launch_ros/descriptions/composable_node.py index e3028ea0..e23bb513 100644 --- a/launch_ros/launch_ros/descriptions/composable_node.py +++ b/launch_ros/launch_ros/descriptions/composable_node.py @@ -45,6 +45,7 @@ def __init__( remappings: Optional[SomeRemapRules] = None, extra_arguments: Optional[SomeParameters] = None, condition: Optional[Condition] = None, + log_level: Optional[SomeSubstitutionsType] = None, ) -> None: """ Initialize a ComposableNode description. @@ -57,6 +58,7 @@ def __init__( :param remappings: list of from/to pairs for remapping names :param extra_arguments: container specific arguments to be passed to the loaded node :param condition: action will be executed if the condition evaluates to true + :param log_level: log level for the node (e.g. 'debug', 'info', 'warn', 'error', 'fatal') """ self.__package = normalize_to_list_of_substitutions(package) self.__node_plugin = normalize_to_list_of_substitutions(plugin) @@ -83,6 +85,10 @@ def __init__( self.__condition = condition + self.__log_level = None # type: Optional[List[Substitution]] + if log_level is not None: + self.__log_level = normalize_to_list_of_substitutions(log_level) + @classmethod def parse(cls, parser: Parser, entity: Entity): """Parse composable_node.""" @@ -138,6 +144,10 @@ def parse(cls, parser: Parser, entity: Entity): for extra_arg in extra_arguments: extra_arg.assert_entity_completely_parsed() + log_level = entity.get_attr('log_level', optional=True) + if log_level is not None: + kwargs['log_level'] = parser.parse_substitution(log_level) + return cls, kwargs @property @@ -178,3 +188,8 @@ def extra_arguments(self) -> Optional[Parameters]: def condition(self) -> Optional[Condition]: """Getter for condition.""" return self.__condition + + @property + def log_level(self) -> Optional[List[Substitution]]: + """Get log level as a sequence of substitutions to be performed.""" + return self.__log_level