日志记录和日志记录器配置

概述

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,按客户端库进行划分。

  • 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 - 仅在给定函数返回真时,输出给定的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 - 给定记录器名称,返回记录器的级别(可能未设置)

配置

由于``rclcpp``和``rclpy``使用相同的底层日志记录基础设施,配置选项是相同的。

环境变量

以下环境变量控制ROS 2日志记录器的某些方面。对于每个环境设置,请注意这是一个进程级的设置,因此适用于该进程中的所有节点。

  • ROS_LOG_DIR - 控制用于将日志消息写入磁盘的日志目录(如果启用)。如果非空,则使用此变量中指定的确切目录。如果为空,则使用``ROS_HOME``环境变量的内容构建路径,形式为``$ROS_HOME/.log``。在所有情况下,``~``字符会扩展为用户的主目录。

  • ROS_HOME - 控制用于各种ROS文件(包括日志和配置文件)的主目录。在日志记录的上下文中,此变量用于构建用于日志文件的目录路径。如果非空,则使用此变量的内容作为ROS_HOME路径。在所有情况下,``~``字符会扩展为用户的主目录。

  • RCUTILS_LOGGING_USE_STDOUT - 控制输出消息流的目标。如果未设置或为0,则使用stderr。如果为1,则使用stdout。

  • RCUTILS_LOGGING_BUFFERED_STREAM - 控制日志流(如在 RCUTILS_LOGGING_USE_STDOUT 中配置)是否应该是行缓冲或无缓冲的。如果未设置此项,则使用流的默认设置(通常为标准输出的行缓冲,标准错误输出的无缓冲)。如果设置为 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 日志架构

rcutils

rcutils``具有一个日志实现,可以根据特定格式(参见上面的``配置)格式化日志消息,并将这些日志消息输出到控制台。``rcutils``实现了一个完整的日志解决方案,但允许较高级别的组件以依赖注入模型插入到日志基础设施中。当我们谈到下面的``rcl``层时,这将变得更加明显。

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

rcl_logging_spdlog

rcl_logging_spdlog 实现了 rcl_logging_interface API,因此为 rcl 层提供外部日志记录服务。特别是,rcl_logging_spdlog 实现接受格式化的日志消息,并使用 spdlog 库将它们写入磁盘上的日志文件,通常位于 ~/.ros/log``(尽管此处可配置;请参阅上述 ``Configuration)。

rcl

rcl 中的日志记录子系统使用 rcutilsrcl_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``函数时,它会将节点的当前严重级别与宏的严重级别进行比较。如果宏的严重级别大于或等于节点的严重级别,则会对消息进行格式化,并输出到当前配置的所有位置。

记录用法