目录
概述
严重程度
API 接口
配置
环境变量
节点创建
日志子系统设计
rcutils
rcl_logging_spdlog
rcl
rclcpp
rclpy
记录使用情况
概述
ROS 2 中的日志子系统旨在将日志消息传递到各种目标,包括:
到控制台(如果已连接)
将文件记录到磁盘(如果本地存储可用)
到 ROS 2 网络上的
/rosout
主题
默认情况下,ROS 2 节点中的日志消息将输出到控制台(在 stderr 上)、磁盘上的日志文件以及 ROS 2 网络上的 /rosout
主题。所有目标都可以在每个节点的基础上单独启用或禁用。
本文档的其余部分将介绍日志子系统背后的一些想法。
严重级别
日志消息具有与之关联的严重性级别: DEBUG
、 INFO
、 WARN
、 ERROR
或 FATAL
,按升序排列。
记录器将只处理严重性等于或高于为记录器选择的指定级别的日志消息。
每个节点都有一个与之关联的记录器,该记录器会自动包含节点的名称和命名空间。如果节点的名称被外部重新映射为源代码中定义的名称以外的其他名称,则会反映在记录器名称中。还可以创建使用特定名称的非节点记录器。
记录器名称表示一个层次结构。如果名为“abc.def”的记录器的级别未设置,它将推迟到其父级“abc”的级别,如果该级别也未设置,将使用默认记录器级别。当更改记录器“abc”的级别时,所有后代(例如“abc.def”,“abc.ghi.jkl”)的级别将受到影响,除非它们的级别已被明确设置。
API 接口
这些是 ROS 2 日志基础设施的最终用户应该使用的 API,按客户端库分开。
C++:
RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}
- 每次命中此行时输出给定的 printf 样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_ONCE
- 仅在第一次命中此行时输出给定的 printf 样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_EXPRESSION
- 仅在给定表达式为真时输出给定的 printf 样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_FUNCTION
- 仅当给定函数返回 true 时才输出给定的 printf 样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST
- 输出给定的 printf 样式消息,除了第一次命中此行以外的所有时间RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_THROTTLE
- 以整数毫秒为单位输出给定的 printf 样式消息,不超过给定的速率RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST_THROTTLE
- 以整数毫秒为单位输出给定的 printf 样式消息,但跳过第一个RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM
- 每次命中此行时输出给定的 C++流式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_ONCE
- 仅在第一次命中此行时输出给定的 C++流样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_EXPRESSION
- 仅当给定表达式为真时输出给定的 C++ 流样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_FUNCTION
- 仅当给定函数返回 true 时输出给定的 C++ 流样式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST
- 输出给定的 C++ 流式消息,除了第一次命中该行以外RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_THROTTLE
- 以整数毫秒为单位输出给定速率的 C++流式消息RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST_THROTTLE
- 以整数毫秒为单位输出给定的 C++流式消息,但跳过第一个
上述每个 API 都将 rclcpp::Logger
对象作为第一个参数。这可以通过调用 node->get_logger()
(推荐)从节点 API 中提取,或者通过构造一个独立的 rclcpp::Logger
对象来实现。
rcutils_logging_set_logger_level
- 将特定记录器名称的日志级别设置为给定的严重级别rcutils_logging_get_logger_effective_level
- 给定一个记录器名称,返回记录器级别(可能未设置)
Python:
logger.{debug,info,warning,error,fatal}
- 将给定的 Python 字符串输出到日志基础设施。调用接受以下关键字参数来控制行为:throttle_duration_sec
- 如果不是 None,则节流间隔的持续时间(以浮点秒为单位)skip_first
- 如果为 True,则每次命中此行时输出消息,但第一次除外once
- 如果为真,则仅在第一次命中此行时输出消息
rclpy.logging.set_logger_level
- 将特定记录器名称的日志级别设置为给定的严重级别rclpy.logging.get_logger_effective_level
- 给定一个记录器名称,返回记录器级别(可能未设置)
配置
由于 rclcpp
和 rclpy
使用相同的底层日志记录基础设施,因此配置选项相同。
环境变量
以下环境变量控制 ROS 2 记录器的某些方面。对于每个环境设置,请注意这是一个进程范围的设置,因此它适用于该进程中的所有节点。
ROS_LOG_DIR
- 控制用于将日志消息写入磁盘的日志目录(如果启用)。如果非空,则使用此变量中指定的确切目录。如果为空,则使用ROS_HOME
环境变量的内容构造$ROS_HOME/.log
形式的路径。在所有情况下,~
字符将扩展到用户的 HOME 目录。ROS_HOME
- 控制用于各种 ROS 文件的主目录,包括日志记录和配置文件。在日志记录的上下文中,该变量用于构建日志文件目录的路径。如果非空,则使用该变量的内容作为 ROS_HOME 路径。在所有情况下,~
字符将扩展到用户的 HOME 目录。RCUTILS_LOGGING_USE_STDOUT
- 控制流输出消息的去向。如果未设置或为 0,则使用 stderr。如果为 1,则使用 stdout。RCUTILS_LOGGING_BUFFERED_STREAM
- 控制日志流(在RCUTILS_LOGGING_USE_STDOUT
中配置)是否应为行缓冲或非缓冲。如果未设置此项,请使用流的默认值(通常为 stdout 的行缓冲和 stderr 的非缓冲)。如果此值为 0,则强制流为非缓冲。如果此值为 1,则强制流为行缓冲。RCUTILS_COLORIZED_OUTPUT
- 控制输出消息时是否使用颜色。如果未设置,则根据平台和控制台是否为 TTY 自动确定。如果为 0,则强制禁用输出颜色。如果为 1,则强制启用输出颜色。RCUTILS_CONSOLE_OUTPUT_FORMAT
- 控制每个日志消息输出的字段。可用的字段有:如果没有给出格式,则使用默认的
[{severity}] [{time}] [{name}]: {message}
。{severity}
- 严重程度。{name}
- 记录器的名称(可能为空)。{message}
- 日志消息(可能为空)。{function_name}
- 此函数被调用时的名称(可能为空)。{file_name}
- 此文件名是从中调用的(可能为空)。{time}
- 自纪元以来的时间(以秒为单位)。{time_as_nanoseconds}
- 自纪元以来的时间(纳秒)。{date_time_with_ms}
- ISO 格式的时间,例如2024-06-11 09:29:19.304
{line_number}
- 此被调用的行号(可能为空)。
RCUTILS_CONSOLE_OUTPUT_FORMAT
还支持以下转义字符语法。
转义字符语法 | 字符表示 |
---|---|
| 警报 |
| 退格键 |
| 新行 |
| 回车 |
| 水平制表符 |
节点创建
在初始化 ROS 2 节点时,可以通过节点选项控制行为的某些方面。由于这些是每个节点的选项,即使节点组合成一个单一的进程,它们也可以为不同的节点设置不同的选项。
log_levels
- 用于此特定节点内组件的日志级别。可以使用以下命令进行设置:ros2 run demo_nodes_cpp talker --ros-args --log-level talker:=DEBUG
external_log_config_file
- 用于配置后端记录器的外部文件。如果为 NULL,将使用默认配置。请注意,此文件的格式是特定于后端的(目前尚未为 spdlog 的默认后端记录器实现)。可以通过以下方式设置:ros2 run demo_nodes_cpp talker --ros-args --log-config-file log-config.txt
log_stdout_disabled
- 是否禁用将日志消息写入控制台。可以通过以下方式完成:ros2 run demo_nodes_cpp talker --ros-args --disable-stdout-logs
log_rosout_disabled
- 是否禁用将日志消息写出到/rosout
。这可以显著节省网络带宽,但外部观察者将无法监控日志记录。可以通过以下方式完成:ros2 run demo_nodes_cpp talker --ros-args --disable-rosout-logs
log_ext_lib_disabled
- 是否完全禁用外部记录器的使用。这在某些情况下可能更快,但意味着日志不会写入磁盘。可以通过以下方式完成:ros2 run demo_nodes_cpp talker --ros-args --disable-external-lib-logs
日志子系统设计
下图显示了日志子系统的五个主要部分及其交互方式。
rcutils
rcutils
具有一个日志实现,可以根据某种格式格式化日志消息(见上文 Configuration
),并将这些日志消息输出到控制台。 rcutils
实现了一个完整的日志解决方案,但允许更高级别的组件以依赖注入模型插入到日志基础设施中。当我们在下面讨论 rcl
层时,这一点将变得更加明显。
请注意,这是一个每个进程的日志记录实现,因此在此级别配置的任何内容都将影响整个进程,而不仅仅是单个节点。
rcl_logging_spdlog
rcl_logging_spdlog
实现了 rcl_logging_interface
API,从而为 rcl
层提供外部日志服务。特别是, rcl_logging_spdlog
实现获取格式化的日志消息,并使用 spdlog
库将它们写入磁盘上的日志文件,通常在 ~/.ros/log
内(尽管这是可配置的;请参阅上面的 Configuration
)。
rcl
rcl
中的日志子系统使用 rcutils
和 rcl_logging_spdlog
提供大部分 ROS 2 日志服务。当日志消息进入时, rcl
决定将它们发送到哪里。日志消息可以传递到三个主要位置;单个节点可以启用它们的任意组合:
通过
rcutils
层到控制台通过
rcl_logging_spdlog
层到磁盘通过 RMW 层到 ROS 2 网络上的
/rosout
主题
rclcpp
这是主要的 ROS 2 C++ API,它位于 rcl
API 之上。在日志记录的上下文中, rclcpp
提供了 RCLCPP_
日志宏;请参阅上面的 APIs
以获取完整列表。当其中一个 RCLCPP_
宏运行时,它会根据宏的严重级别检查节点的当前严重级别。如果宏的严重级别大于或等于节点的严重级别,则消息将被格式化并输出到当前配置的所有位置。请注意, rclcpp
使用全局互斥锁进行日志调用,因此同一进程中的所有日志调用最终都是单线程的。
rclpy
这是主要的 ROS 2 Python API,它位于 rcl
API 之上。在日志记录的上下文中, rclpy
提供了 logger.debug
风格的函数;请参阅上面的 APIs
以获取完整列表。当运行其中一个 logger.debug
函数时,它会根据宏的严重性级别检查节点的当前严重性级别。如果宏的严重性级别大于或等于节点的严重性级别,则消息将被格式化并输出到当前配置的所有位置。
记录使用情况
C++:
请参阅 rclcpp 日志演示 https://github.com/ros2/demos/tree/jazzy/logging_demo 以获取一些简单的示例。
请参阅日志演示 https://docs.ros.org/en/jazzy/Tutorials/Demos/Logging-and-logger-configuration.html 以了解示例用法。
Python:
查看 rclpy 示例 https://github.com/ros2/examples/blob/jazzy/rclpy/services/minimal_client/examples_rclpy_minimal_client/client.py 以了解节点日志记录器的用法。
查看 rclpy 测试 https://github.com/ros2/rclpy/blob/jazzy/rclpy/test/test_logging.py 以了解关键字参数(例如 skip_first、once)的用法。