日志与 Logger 配置
更多日志相关内容,请参阅日志页面。
在代码中使用日志语句
Section titled “在代码中使用日志语句”以下代码会从 ROS 2 节点输出一条 DEBUG 级别的日志:
C++
// printf 风格RCLCPP_DEBUG(node->get_logger(), "My log message %d", 4);
// C++ stream 风格RCLCPP_DEBUG_STREAM(node->get_logger(), "My log message " << 4);Python
node.get_logger().debug('My log message %d' % (4))注意,以上两种写法都无需手动添加换行符,日志基础设施会自动处理。
以下代码会从 ROS 2 节点输出一条 INFO 级别的日志,但仅在首次执行时输出:
C++
// printf 风格RCLCPP_INFO_ONCE(node->get_logger(), "My log message %d", 4);
// C++ stream 风格RCLCPP_INFO_STREAM_ONCE(node->get_logger(), "My log message " << 4);Python
num = 4node.get_logger().info(f'My log message {num}', once=True)跳过首次记录
Section titled “跳过首次记录”以下代码会从 ROS 2 节点输出一条 WARN 级别的日志,但会跳过首次执行:
C++
// printf 风格RCLCPP_WARN_SKIPFIRST(node->get_logger(), "My log message %d", 4);
// C++ stream 风格RCLCPP_WARN_STREAM_SKIPFIRST(node->get_logger(), "My log message " << 4);Python
num = 4node.get_logger().warning('My log message {0}'.format(num), skip_first=True)以下代码会从 ROS 2 节点输出一条 ERROR 级别的日志,但每秒最多输出一次。
用于指定消息间隔(单位毫秒)的 interval 参数应为整数类型,以便正确转换为 rcutils_duration_value_t(即 int64_t):
C++
// printf 风格RCLCPP_ERROR_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message %d", 4);
// C++ stream 风格RCLCPP_ERROR_STREAM_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message " << 4);
// 目前,使用 nanoseconds() 方法来使用现有的 rclcpp::Duration 值,参见 https://github.com/ros2/rclcpp/issues/1929RCLCPP_ERROR_STREAM_THROTTLE(node->get_logger(), *node->get_clock(), msg_interval.nanoseconds()/1000000, "My log message " << 4);Python
num = 4node.get_logger().error(f'My log message {num}', throttle_duration_sec=1)跳过首次的节流日志
Section titled “跳过首次的节流日志”以下代码会从 ROS 2 节点输出一条 DEBUG 级别的日志,每秒最多输出一次,且会跳过首次执行:
C++
// printf 风格RCLCPP_DEBUG_SKIPFIRST_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message %d", 4);
RCLCPP_DEBUG_SKIPFIRST_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message " << 4);Python
num = 4node.get_logger().debug(f'My log message {num}', skip_first=True, throttle_duration_sec=1.0)这个演示展示了各类日志调用方式,以及如何在代码内部和外部配置各 logger 的日志级别。
使用以下命令启动演示:
$ ros2 run logging_demo logging_demo_main程序运行后,你会看到不同类型的日志输出。起初只会显示 INFO 及以上级别(WARN、ERROR、FATAL)的日志。注意,第一条消息只会被记录一次——尽管该行代码在每次迭代中都会执行,这是该日志调用本身的特性决定的。
日志目录配置
Section titled “日志目录配置”日志目录通过两个环境变量配置:ROS_LOG_DIR 和 ROS_HOME。规则如下:
- 如果设置了
ROS_LOG_DIR且值不为空,则使用$ROS_LOG_DIR。 - 否则使用
$ROS_HOME/log;若ROS_HOME未设置或为空,则默认将~/.ros作为ROS_HOME。
例如,要将日志目录设置为 ~/my_logs:
Linux
$ export ROS_LOG_DIR=~/my_logs$ ros2 run logging_demo logging_demo_mainmacOS
$ export ROS_LOG_DIR=~/my_logs$ ros2 run logging_demo logging_demo_mainWindows
$ set "ROS_LOG_DIR=~/my_logs"$ ros2 run logging_demo logging_demo_main运行后,日志文件将位于 ~/my_logs/ 目录下。
你也可以设置 ROS_HOME,日志目录随之变为 $ROS_HOME/log。ROS_HOME 的设计初衷是为所有需要基础目录的功能提供统一的根路径。注意,此方式要求 ROS_LOG_DIR 未设置或为空。例如,将 ROS_HOME 设置为 ~/my_ros_home:
Linux
$ export ROS_HOME=~/my_ros_home$ ros2 run logging_demo logging_demo_mainmacOS
$ export ROS_HOME=~/my_ros_home$ ros2 run logging_demo logging_demo_mainWindows
$ set "ROS_HOME=~/my_ros_home"$ ros2 run logging_demo logging_demo_main运行后,日志文件将位于 ~/my_ros_home/log/ 目录下。
通过代码配置 Logger 级别
Section titled “通过代码配置 Logger 级别”迭代 10 次后,logger 级别会切换为 DEBUG,从而输出更多调试消息。
部分调试消息会触发一些额外的函数调用或表达式求值,这些函数或表达式此前因 DEBUG 日志未启用而被跳过。有关所用调用的详细说明,请参阅该演示的源代码;有关支持的日志调用完整列表,请参阅 rclcpp 日志文档。
从外部配置 Logger 级别
Section titled “从外部配置 Logger 级别”ROS 2 节点提供了可在运行时从外部配置日志级别的服务,默认处于禁用状态。以下代码展示了创建节点时如何启用 logger 服务。
C++
// 创建一个启用 logger 服务的节点auto node = std::make_shared<rclcpp::Node>("NodeWithLoggerService", rclcpp::NodeOptions().enable_logger_service(true));Python
# 创建一个启用 logger 服务的节点node = Node('NodeWithLoggerService', enable_logger_service=True)如果按上述配置运行某个节点,执行 ros2 service list 时会看到两个服务:
$ ros2 service list.../NodeWithLoggerService/get_logger_levels/NodeWithLoggerService/set_logger_levels...-
get_logger_levels
通过此服务获取指定 logger 的日志级别。
运行
ros2 service call获取NodeWithLoggerService和rcl的日志级别。Terminal window $ ros2 service call /NodeWithLoggerService/get_logger_levels rcl_interfaces/srv/GetLoggerLevels '{names: ["NodeWithLoggerService", "rcl"]}'requester: making request: rcl_interfaces.srv.GetLoggerLevels_Request(names=['NodeWithLoggerService', 'rcl'])response:rcl_interfaces.srv.GetLoggerLevels_Response(levels=[rcl_interfaces.msg.LoggerLevel(name='NodeWithLoggerService', level=0), rcl_interfaces.msg.LoggerLevel(name='rcl', level=0)]) -
set_logger_levels
通过此服务设置指定 logger 的日志级别。
运行
ros2 service call设置NodeWithLoggerService和rcl的日志级别。Terminal window $ ros2 service call /NodeWithLoggerService/set_logger_levels rcl_interfaces/srv/SetLoggerLevels '{levels: [{name: "NodeWithLoggerService", level: 20}, {name: "rcl", level: 10}]}'requester: making request: rcl_interfaces.srv.SetLoggerLevels_Request(levels=[rcl_interfaces.msg.LoggerLevel(name='NodeWithLoggerService', level=20), rcl_interfaces.msg.LoggerLevel(name='rcl', level=10)])response:rcl_interfaces.srv.SetLoggerLevels_Response(results=[rcl_interfaces.msg.SetLoggerLevelsResult(successful=True, reason=''), rcl_interfaces.msg.SetLoggerLevelsResult(successful=True, reason='')])
此外,还有演示代码展示了如何通过 logger 服务设置或获取日志级别。
-
rclcpp:演示代码
Terminal window $ ros2 run demo_nodes_cpp use_logger_service -
rclpy:演示代码
Terminal window $ ros2 run demo_nodes_py use_logger_service
警告:当前存在一个已知限制:
get_logger_levels和set_logger_levels服务不是线程安全的,即同一时刻只能有一个线程调用这些服务。详情请参阅 https://github.com/ros2/rcutils/issues/397
使用 logger 配置组件
Section titled “使用 logger 配置组件”响应日志配置请求的服务端已封装为组件,可直接集成到现有的 composition 系统中。例如,如果你正在使用容器运行节点,只需在容器中额外加载 logging_demo::LoggerConfig 组件,即可配置 logger。
以调试 composition::Talker 演示为例,首先像平常一样启动 talker:
终端 1:
$ ros2 run rclcpp_components component_container终端 2:
$ ros2 component load /ComponentManager composition composition::Talker然后,需要启用调试日志时,使用以下命令加载 LoggerConfig 组件:
终端 2:
$ ros2 component load /ComponentManager logging_demo logging_demo::LoggerConfig最后,通过指定空名称的 logger,将所有尚未设置的 logger 统一配置为 debug 级别。注意,已被显式设置为特定级别的 logger 不受此调用影响。
终端 2:
$ ros2 service call /config_logger logging_demo/srv/ConfigLogger "{logger_name: '', level: DEBUG}"你应该会看到进程中所有此前未设置的 logger(包括 ROS 2 核心)开始输出调试信息。
通过命令行配置 Logger 级别
Section titled “通过命令行配置 Logger 级别”从 Bouncy 版本起,ROS 2 支持通过命令行为尚未显式配置级别的 logger 设置日志级别。使用以下命令行参数重新启动演示:
$ ros2 run logging_demo logging_demo_main --ros-args --log-level debug这样会将所有未设置的 logger 的默认级别配置为 debug。你应该会看到来自演示本身以及 ROS 2 核心 logger 的调试输出。
你也可以通过命令行配置个别 logger。使用以下命令行参数重新启动演示:
$ ros2 run logging_demo logging_demo_main --ros-args --log-level logger_usage_demo:=debug控制台输出格式化
Section titled “控制台输出格式化”如果想调整输出的详细程度,可以使用 RCUTILS_CONSOLE_OUTPUT_FORMAT 环境变量自定义格式。例如,想在输出中额外显示时间戳和调用位置,可以先停止演示,设置环境变量后重新启动:
Linux
$ export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"$ ros2 run logging_demo logging_demo_mainmacOS
$ export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"$ ros2 run logging_demo logging_demo_mainWindows
$ set "RCUTILS_CONSOLE_OUTPUT_FORMAT=[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"$ ros2 run logging_demo logging_demo_main你应该会看到每条消息都会额外打印以秒为单位的时间戳,以及函数名、文件名和行号。
有关控制台输出格式的更多配置选项,请参阅 logger 控制台配置文档。
控制台输出着色
Section titled “控制台输出着色”默认情况下,日志输出到终端时会自动着色。如果想强制启用或禁用着色,可以使用 RCUTILS_COLORIZED_OUTPUT 环境变量。例如:
Linux
$ export RCUTILS_COLORIZED_OUTPUT=0 # 1 为强制启用$ ros2 run logging_demo logging_demo_mainmacOS
$ export RCUTILS_COLORIZED_OUTPUT=0 # 1 为强制启用$ ros2 run logging_demo logging_demo_mainWindows
$ set "RCUTILS_COLORIZED_OUTPUT=0" :: 1 为强制启用$ ros2 run logging_demo logging_demo_main你应该会看到 debug、warn、error 和 fatal 日志不再着色。
注意:在 Linux 和 macOS 上,强制着色意味着输出重定向到文件时,文件中会包含 ANSI 颜色转义码。在 Windows 上,着色功能依赖控制台 API,如果在不支持的环境中被强制着色,会收到着色失败的警告。由于默认行为已经会自动检测输出目标是否为控制台,通常不建议强制设置着色。
注意:如果通过
ros2 launch启动多个节点,这些节点不会直接附加到当前终端(除非设置了emulate_tty=True)。因此,要想在ros2 launch中看到着色输出,需要显式设置RCUTILS_COLORIZED_OUTPUT=1。
控制台输出的默认流
Section titled “控制台输出的默认流”在 Foxy 及更高版本中,所有调试级别的输出默认发送到 stderr。可以通过将 RCUTILS_LOGGING_USE_STDOUT 环境变量设置为 1,强制所有输出发送到 stdout。例如:
Linux
$ export RCUTILS_LOGGING_USE_STDOUT=1macOS
$ export RCUTILS_LOGGING_USE_STDOUT=1Windows
$ set "RCUTILS_LOGGING_USE_STDOUT=1"行缓冲的控制台输出
Section titled “行缓冲的控制台输出”默认情况下,所有日志输出都是无缓冲的。可以通过将 RCUTILS_LOGGING_BUFFERED_STREAM 环境变量设置为 1 来强制启用缓冲。例如:
Linux
$ export RCUTILS_LOGGING_BUFFERED_STREAM=1macOS
$ export RCUTILS_LOGGING_BUFFERED_STREAM=1Windows
$ set "RCUTILS_LOGGING_BUFFERED_STREAM=1"然后运行:
$ ros2 run logging_demo logging_demo_main设置日志文件名前缀
Section titled “设置日志文件名前缀”默认情况下,日志文件名基于可执行文件名,后跟进程 ID 和文件创建时的系统时间戳。可以使用 --log-file-name 命令行参数将日志文件名前缀更改为自定义名称:
$ ros2 run demo_nodes_cpp talker --ros-args --log-file-name filename这会将日志文件名前缀配置为 filename,而不是可执行文件名(在本例中为 talker)。