日志记录
有关可用功能的详细信息,请参阅“日志记录页面 <../../Concepts/Intermediate/About-Logging>”。
在代码中使用日志语句
基本日志记录
以下代码将在“DEBUG”严重性下从 ROS 2 节点输出日志消息:
// printf style
RCLCPP_DEBUG(node->get_logger(), "My log message %d", 4);
// C++ stream style
RCLCPP_DEBUG_STREAM(node->get_logger(), "My log message " << 4);
node.get_logger().debug('My log message %d' % (4))
请注意,在这两种情况下,都不会添加尾随换行符,因为日志记录基础结构会自动添加一个。
仅在第一次记录
以下代码将在严重性为“INFO”的 ROS 2 节点输出日志消息,但仅在第一次命中时输出:
// printf style
RCLCPP_INFO_ONCE(node->get_logger(), "My log message %d", 4);
// C++ stream style
RCLCPP_INFO_STREAM_ONCE(node->get_logger(), "My log message " << 4);
num = 4
node.get_logger().info(f'My log message {num}', once=True)
除第一次之外的所有时间都记录
以下代码将在严重程度为“WARN”时从 ROS 2 节点输出日志消息,但不会输出第一次遇到的日志消息:
// printf style
RCLCPP_WARN_SKIPFIRST(node->get_logger(), "My log message %d", 4);
// C++ stream style
RCLCPP_WARN_STREAM_SKIPFIRST(node->get_logger(), "My log message " << 4);
num = 4
node.get_logger().warning('My log message {0}'.format(num), skip_first=True)
日志记录已限制
以下代码将从 ROS 2 节点以“ERROR”严重性输出一条日志消息,但每秒不超过一次。
指定消息间隔毫秒数的间隔参数应具有整数数据类型,以便可以将其转换为“rcutils_duration_value_t”(“int64_t”):
// printf style
RCLCPP_ERROR_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message %d", 4);
// C++ stream style
RCLCPP_ERROR_STREAM_THROTTLE(node->get_logger(), *node->get_clock(), 1000, "My log message " << 4);
// For now, use the nanoseconds() method to use an existing rclcpp::Duration value, see https://github.com/ros2/rclcpp/issues/1929
RCLCPP_ERROR_STREAM_THROTTLE(node->get_logger(), *node->get_clock(), msg_interval.nanoseconds()/1000000, "My log message " << 4);
num = 4
node.get_logger().error(f'My log message {num}', throttle_duration_sec=1)
除第一次之外,日志记录均受到限制
以下代码将从 ROS 2 节点以“DEBUG”严重性输出一条日志消息,每秒不超过一次,并跳过第一次命中的情况:
// printf style
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);
num = 4
node.get_logger().debug(f'My log message {num}', skip_first=True, throttle_duration_sec=1.0)
日志记录演示
在此`演示<https://github.com/ros2/demos/tree/rolling/logging_demo>`_中,显示了不同类型的日志调用,并在本地和外部配置了不同记录器的严重性级别。
使用以下命令启动演示:
ros2 run logging_demo logging_demo_main
随着时间的推移,您将看到具有不同属性的各种日志调用的输出。 首先,您将只看到严重性为“INFO”及以上的日志调用的输出(“WARN”、“ERROR”、“FATAL”)。 请注意,第一条消息只会记录一次,尽管每次迭代都会到达该行,因为这是用于该消息的日志调用的属性。
日志目录配置
可以通过两个环境变量配置日志目录:“ROS_LOG_DIR”和“ROS_HOME”。 逻辑如下:
如果“ROS_LOG_DIR”已设置且不为空,则使用“$ROS_LOG_DIR”。
否则,使用“$ROS_HOME/log”,如果未设置或为空,则使用“~/.ros”作为“ROS_HOME”。
例如,将日志目录设置为“~/my_logs”:
export ROS_LOG_DIR=~/my_logs
ros2 run logging_demo logging_demo_main
export ROS_LOG_DIR=~/my_logs
ros2 run logging_demo logging_demo_main
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
:
export ROS_HOME=~/my_ros_home
ros2 run logging_demo logging_demo_main
export ROS_HOME=~/my_ros_home
ros2 run logging_demo logging_demo_main
set "ROS_HOME=~/my_ros_home"
ros2 run logging_demo logging_demo_main
然后,您将在 ~/my_ros_home/log/
下找到日志。
记录器级别配置:以编程方式
经过 10 次迭代后,记录器的级别将设置为 DEBUG
,这将导致记录其他消息。
其中一些调试消息会导致评估其他函数/表达式,这些函数/表达式之前被跳过,因为未启用 DEBUG
日志调用。
有关使用的调用的进一步说明,请参阅演示的 源代码,并参阅 rclcpp 日志记录文档以获取受支持的日志记录调用的完整列表。
记录器级别配置:外部
ROS 2 节点具有可用于在运行时从外部配置日志记录级别的服务。
默认情况下,这些服务是禁用的。 以下代码显示如何在创建节点时启用记录器服务。
// Create a node with logger service enabled
auto node = std::make_shared<rclcpp::Node>("NodeWithLoggerService", rclcpp::NodeOptions().enable_logger_service(true))
# Create a node with logger service enabled
node = Node('NodeWithLoggerService', enable_logger_service=True)
如果你按照上面的配置运行其中一个节点,运行“ros2 服务列表”时你会发现 2 个服务:
$ ros2 service list
...
/NodeWithLoggerService/get_logger_levels
/NodeWithLoggerService/set_logger_levels
...
get_logger_levels
使用此服务获取指定记录器名称的记录器级别。
运行“ros2 service call”以获取“NodeWithLoggerService”和“rcl”的记录器级别。
$ ros2 service call /NodeWithLoggerService/get_logger_levels rcl_interfaces/srv/GetLoggerLevels '{names: ["NodeWithLoggerService", "rcl"]}' 请求者:发出请求:rcl_interfaces.srv.GetLoggerLevels_Request(names=['NodeWithLoggerService', 'rcl'])响应: 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
使用此服务为指定的记录器名称设置记录器级别。
- 运行“ros2 service call”为“NodeWithLoggerService”和“rcl”设置记录器级别。
$ ros2 service call /NodeWithLoggerService/set_logger_levels rcl_interfaces/srv/SetLoggerLevels '{levels: [{name: "NodeWithLoggerService", level: 20}, {name: "rcl", level: 10}]}' 请求者:发出请求:rcl_interfaces.srv.SetLoggerLevels_Request(levels=[rcl_interfaces.msg.LoggerLevel(name='NodeWithLoggerService', level=20), rcl_interfaces.msg.LoggerLevel(name='rcl', level=10)])
响应: rcl_interfaces.srv.SetLoggerLevels_Response(results=[rcl_interfaces.msg.SetLoggerLevelsResult(successful=True, Reason=’’), rcl_interfaces.msg.SetLoggerLevelsResult(successful=True, Reason=’’)])
还有演示代码展示如何通过记录器服务设置或获取记录器级别。
Warning
目前存在一个限制,即 get_logger_levels
和 set_logger_levels
服务不是线程安全的。
这意味着您需要确保一次只有一个线程在调用服务。
请参阅 https://github.com/ros2/rcutils/issues/397 中的详细信息
使用记录器配置组件
响应记录器配置请求的服务器已开发为组件,因此可以将其添加到现有的基于组合的系统中。 例如,如果您使用“容器运行节点 <../Intermediate/Composition>”,则为了能够配置记录器,您只需请求它另外将“logging_demo::LoggerConfig”组件加载到容器中。
例如,如果您想调试“composition::Talker”演示,您可以正常启动 talker:
Shell 1:
ros2 run rclcpp_components component_container
Shell 2:
ros2 component load /ComponentManager composition composition::Talker
然后,当您想要启用调试日志记录时,请使用以下命令加载“LoggerConfig”组件:
Shell 2
ros2 component load /ComponentManager logging_demo logging_demo::LoggerConfig
最后,通过寻址空名记录器将所有未设置的记录器配置为调试严重性。 请注意,已专门配置为使用特定严重性的记录器不会受到此调用的影响。
Shell 2:
ros2 service call /config_logger logging_demo/srv/ConfigLogger "{logger_name: '', level: DEBUG}"
您应该会看到进程中任何先前未设置的记录器的调试输出开始出现,包括来自 ROS 2 核心的记录器。
记录器级别配置:命令行
从 Bouncy ROS 2 版本开始,可以从命令行配置未明确设置严重性的记录器的严重性级别。 重新启动演示,包括以下命令行参数:
ros2 run logging_demo logging_demo_main --ros-args --log-level debug
这会将任何未设置的记录器的默认严重性配置为调试严重性级别。 您应该会看到来自演示本身和 ROS 2 核心的记录器的调试输出。
可以从命令行配置单个记录器的严重性级别。 重新启动演示,包括以下命令行参数:
ros2 run logging_demo logging_demo_main --ros-args --log-level logger_usage_demo:=debug
控制台输出格式
如果您想要更详细或更简洁的格式,可以使用“RCUTILS_CONSOLE_OUTPUT_FORMAT”环境变量。 例如,要额外获取日志调用的时间戳和位置,请停止演示并使用环境变量设置重新启动它:
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"
ros2 run logging_demo logging_demo_main
export RCUTILS_CONSOLE_OUTPUT_FORMAT="[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"
ros2 run logging_demo logging_demo_main
set "RCUTILS_CONSOLE_OUTPUT_FORMAT=[{severity} {time}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"
ros2 run logging_demo logging_demo_main
您应该会看到时间戳(以秒为单位)以及每条消息中额外打印的函数名称、文件名和行号。
有关配置控制台记录器格式的更多信息,请参阅:ref:logger 控制台配置
控制台输出着色
默认情况下,当输出针对终端时,输出会着色。 如果您想强制启用或禁用它,可以使用``RCUTILS_COLORIZED_OUTPUT``环境变量。 例如:
export RCUTILS_COLORIZED_OUTPUT=0 # 1 for forcing it
ros2 run logging_demo logging_demo_main
export RCUTILS_COLORIZED_OUTPUT=0 # 1 for forcing it
ros2 run logging_demo logging_demo_main
set "RCUTILS_COLORIZED_OUTPUT=0" :: 1 for forcing it
ros2 run logging_demo logging_demo_main
您应该看到调试、警告、错误和严重日志现在没有彩色。
Note
在 Linux 和 MacOS 中,强制彩色输出意味着如果您将输出重定向到文件,则 ansi 转义颜色代码将出现在该文件上。 在 Windows 中,着色方法依赖于控制台 API。 如果强制执行,您将收到一条新警告,提示着色失败。 默认行为已经检查输出是否为控制台,因此不建议强制着色。
控制台输出的默认流
在 Foxy 及更高版本中,所有调试级别的输出默认都会转到 stderr。可以通过将“RCUTILS_LOGGING_USE_STDOUT”环境变量设置为“1”来强制所有输出转到 stdout。 例如:
export RCUTILS_LOGGING_USE_STDOUT=1
export RCUTILS_LOGGING_USE_STDOUT=1
set "RCUTILS_LOGGING_USE_STDOUT=1"
行缓冲控制台输出
默认情况下,所有日志输出均未缓冲。 您可以通过将“RCUTILS_LOGGING_BUFFERED_STREAM”环境变量设置为 1 来强制缓冲。 例如:
export RCUTILS_LOGGING_BUFFERED_STREAM=1
export RCUTILS_LOGGING_BUFFERED_STREAM=1
set "RCUTILS_LOGGING_BUFFERED_STREAM=1"
然后运行:
ros2 run logging_demo logging_demo_main
设置日志文件名前缀
默认情况下,日志文件名基于可执行文件名,后跟文件创建时的进程 ID 和系统时间戳。 您可以使用“–log-file-name”命令行参数将日志文件名前缀更改为您选择的前缀:
ros2 run demo_nodes_cpp talker --ros-args --log-file-name filename
这会将日志文件名前缀配置为“filename”,而不是可执行文件名(在本例中为“talker”)。