日志记录和记录器配置

概述

ROS 2 中的日志子系统旨在将日志消息传递到各种目标,包括:

  • 传递到控制台(如果已连接)

  • 记录磁盘上的文件(如果本地存储可用)

  • 传递到 ROS 2 网络上的 /rosout 主题

默认情况下,ROS 2 节点中的日志消息将传递到控制台(在 stderr 上)、记录磁盘上的文件以及 ROS 2 网络上的 /rosout 主题。

可以根据每个节点单独启用或禁用所有目标。

本文档的其余部分将介绍日志子系统背后的一些想法。

严重性级别

日志消息具有与之关联的严重性级别:按升序排列的 DEBUGINFOWARN``​​、``ERRORFATAL

记录器将仅处理严重性等于或高于为记录器选择的指定级别的日志消息。

每个节点都有一个与之关联的记录器,该记录器自动包含节点的名称和命名空间。 如果节点的名称在外部重新映射到源代码中定义以外的其他名称,它将反映在记录器名称中。 还可以创建使用特定名称的非节点记录器。

记录器名称表示层次结构。 如果名为“abc.def”的记录器的级别未设置,它将遵循其名为“abc”的父级的级别,如果该级别也未设置,则将使用默认记录器级别。 当记录器“abc”的级别发生变化时,其所有后代(例如“abc.def”、“abc.ghi.jkl”)的级别都会受到影响,除非其级别已明确设置。

API

这些是 ROS 2 日志基础设施的最终用户应该使用的 API,按客户端库划分。

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL} - output the given printf-style message every time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_ONCE - output the given printf-style message only the first time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_EXPRESSION - output the given printf-style message only if the given expression is true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_FUNCTION - output the given printf-style message only if the given function returns true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST - output the given printf-style message all but the first time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_THROTTLE - output the given printf-style message no more than the given rate in integer milliseconds

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST_THROTTLE - output the given printf-style message no more than the given rate in integer milliseconds, but skip the first

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM - output the given C++ stream-style message every time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_ONCE - output the given C++ stream-style message only the first time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_EXPRESSION - output the given C++ stream-style message only if the given expression is true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_FUNCTION - output the given C++ stream-style message only if the given function returns true

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST - output the given C++ stream-style message all but the first time this line is hit

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_THROTTLE - output the given C++ stream-style message no more than the given rate in integer milliseconds

  • RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST_THROTTLE - output the given C++ stream-style message no more than the given rate in integer milliseconds, but skip the first

Each of the above APIs takes an rclcpp::Logger object as the first argument. This can be pulled from the node API by calling node->get_logger() (recommended), or by constructing a stand-alone rclcpp::Logger object.

  • rcutils_logging_set_logger_level - Set the logging level for a particular logger name to the given severity level

  • rcutils_logging_get_logger_effective_level - Given a logger name, return the logger level (which may be unset)

配置

由于“rclcpp”和“rclpy”使用相同的底层日志记录基础结构,因此配置选项相同。

环境变量

The following environment variables control some aspects of the ROS 2 loggers. For each of the environment settings, note that this is a process-wide setting, so it applies to all nodes in that process.

  • 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} - 严重性级别。

    • {name} - 记录器的名称(可能为空)。

    • {message} - 日志消息(可能为空)。

    • {function_name} - 调用此函数的函数名称(可能为空)。

    • {file_name} - 调用此函数的文件名(可能为空)。

    • {time} - 自纪元以来的时间(以秒为单位)。

    • {time_as_nanoseconds} - 自纪元以来的时间(以纳秒为单位)。

    • {line_number} - 调用此函数的行号(可能为空)。

    如果未指定格式,则使用默认值 [{severity}] [{time}] [{name}]: {message}

节点创建

初始化 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

日志子系统设计

下图显示了日志子系统的五个主要部分及其交互方式。

ROS 2 logging architecture

rcutils

rcutils 有一个日志实现,可以根据某种格式格式化日志消息(请参阅上面的“配置”),并将这些日志消息输出到控制台。 rcutils 实现了完整的日志解决方案,但允许更高级别的组件以依赖注入模型将自身插入到日志基础结构中。 当我们讨论下面的“rcl”层时,这将变得更加明显。

请注意,这是一个 每个进程 的日志实现,因此在此级别配置的任何内容都会影响整个进程,而不仅仅是单个节点。

rcl_logging_spdlog

rcl_logging_spdlog 实现了“rcl_logging_interface”API,从而为“rcl”层提供外部日志服务。 具体来说,rcl_logging_spdlog 实现采用格式化的日志消息,并使用 spdlog 库将它们写入磁盘上的日志文件,通常在 ~/.ros/log 中(尽管这是可配置的;请参阅上面的 配置)。

rcl

rcl 中的日志子系统使用 rcutilsrcl_logging_spdlog 来提供大部分 ROS 2 日志服务。

当日志消息进入时,rcl 决定将它们发送到哪里。

日志消息可以传递到 3 个主要位置;单个节点可以启用它们的任意组合:

  • 通过 rcutils 层发送到控制台

  • 通过 rcl_logging_spdlog 层发送到磁盘

  • 通过 RMW 层发送到 ROS 2 网络上的 /rosout 主题

rclcpp

这是位于 rcl API 之上的主要 ROS 2 C++ API。 在日志记录上下文中,rclcpp 提供 RCLCPP_ 日志记录宏;请参阅上面的 API 了解完整列表。 当其中一个 RCLCPP_ 宏运行时,它会根据宏的严重性级别检查节点的当前严重性级别。 如果宏的严重性级别大于或等于节点严重性级别,则将格式化消息并输出到当前配置的所有位置。 请注意,rclcpp 使用全局互斥锁进行日志调用,因此同一进程内的所有日志记录调用最终都是单线程的。

rclpy

这是位于 rcl API 之上的主要 ROS 2 Python API。 在日志记录上下文中,“rclpy”提供了“logger.debug”样式的函数;完整列表请参见上面的“API”。 当其中一个“logger.debug”函数运行时,它会根据宏的严重性级别检查节点的当前严重性级别。 如果宏的严重性级别大于或等于节点严重性级别,则消息将被格式化并输出到当前配置的所有位置。

日志记录用法