From 62ce48d6999664b93568ef5a375a0fe1e212e570 Mon Sep 17 00:00:00 2001 From: Chris Lalancette Date: Fri, 23 Sep 2022 08:48:38 -0400 Subject: [PATCH] Significantly clean up and expand the documentation about logging. (#3025) Explain a lot more about which APIs to use, which configuration options are available, and the design of the subsystem. Signed-off-by: Chris Lalancette (cherry picked from commit c23fff0b96447b5ca1b9b888a8b664cf3c373c19) --- source/Concepts/About-Logging.rst | 165 ++++++++++++++++-- .../images/ros2_logging_architecture.png | Bin 0 -> 48956 bytes .../Logging-and-logger-configuration.rst | 134 +++++++++++++- 3 files changed, 281 insertions(+), 18 deletions(-) create mode 100644 source/Concepts/images/ros2_logging_architecture.png diff --git a/source/Concepts/About-Logging.rst b/source/Concepts/About-Logging.rst index 6e2836edbc..ebce565413 100644 --- a/source/Concepts/About-Logging.rst +++ b/source/Concepts/About-Logging.rst @@ -13,31 +13,25 @@ About logging and logger configuration Overview -------- -The logging functionality currently supported is: +The logging subsystem in ROS 2 aims to deliver logging messages to a variety of targets, including: +* To the console (if one is attached) +* To log files on disk (if local storage is available) +* To the ``/rosout`` topic on the ROS 2 network -* Client libraries (``rclcpp`` and ``rclpy``) using a common logging library to provide: +By default, log messages in ROS 2 nodes will go out to the console (on stderr), to log files on disk, and to the ``/rosout`` topic on the ROS 2 network. +All of the targets can be individually enabled or disabled on a per-node basis. - * Log calls with a variety of filters. - * Hierarchy of loggers. - * Loggers associated with nodes that automatically use the node's name and namespace. +The rest of this document will go over some of the ideas behind the logging subsystem. -* Console output. - - * File output and functionality akin to `rosout `__ for remote consumption of messages is forthcoming. - -* Programmatic configuration of logger levels. - - * Launch-time configuration of the default logger level is supported; config files and external configuration at run-time is forthcoming. - -Logger concepts ---------------- +Severity level +-------------- Log messages have a severity level associated with them: ``DEBUG``, ``INFO``, ``WARN``, ``ERROR`` or ``FATAL``, in ascending order. A logger will only process log messages with severity at or higher than a specified level chosen for the logger. -Each node (in ``rclcpp`` and ``rclpy``) has a logger associated with it that automatically includes the node's name and namespace. +Each node has a logger associated with it that automatically includes the node's name and namespace. If the node's name is externally remapped to something other than what is defined in the source code, it will be reflected in the logger name. Non-node loggers can also be created that use a specific name. @@ -45,6 +39,144 @@ Logger names represent a hierarchy. If the level of a logger named "abc.def" is unset, it will defer to the level of its parent named "abc", and if that level is also unset, the default logger level will be used. When the level of logger "abc" is changed, all of its descendants (e.g. "abc.def", "abc.ghi.jkl") will have their level impacted unless their level has been explicitly set. +APIs +---- + +These are the APIs that end users of the ROS 2 logging infrastructure should use, split up by client library. + +.. tabs:: + + .. group-tab:: C++ + + * ``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 + * ``RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_SKIPFIRST_THROTTLE`` - output the given printf-style message no more than the given rate, 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 + * ``RCLCPP_{DEBUG,INFO,WARN,ERROR,FATAL}_STREAM_SKIPFIRST_THROTTLE`` - output the given C++ stream-style message no more than the given rate, 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) + + .. group-tab:: Python + + * ``logger.{debug,info,warning,error,fatal}`` - output the given Python string to the logging infrastructure. The calls accept the following keyword args to control behavior: + + * ``throttle_duration_sec`` - if not None, the duration of the throttle interval + * ``skip_first`` - if True, output the message all but the first time this line is hit + * ``once`` - if True, only output the message the first time this line is hit + + * ``rclpy.logging.set_logger_level`` - Set the logging level for a particular logger name to the given severity level + * ``rclpy.logging.get_logger_effective_level`` - Given a logger name, return the logger level (which may be unset) + +Configuration +------------- + +Since ``rclcpp`` and ``rclpy`` use the same underlying logging infrastructure, the configuration options are the same. + +Environment variables +^^^^^^^^^^^^^^^^^^^^^ + +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`` - Control the logging directory that is used for writing logging messages to disk (if that is enabled). If non-empty, use the exact directory as specified in this variable. If empty, use the contents of the ``ROS_HOME`` environment variable to construct a path of the form ``$ROS_HOME/.log``. In all cases, the ``~`` character is expanded to the user's HOME directory. +* ``ROS_HOME`` - Control the home directory that is used for various ROS files, including logging and config files. In the context of logging, this variable is used to construct a path to a directory for log files. If non-empty, use the contents of this variable for the ROS_HOME path. In all cases, the ``~`` character is expanded to the users's HOME directory. +* ``RCUTILS_LOGGING_USE_STDOUT`` - Control what stream output messages go to. If this is unset or 0, use stderr. If this is 1, use stdout. +* ``RCUTILS_LOGGING_BUFFERED_STREAM`` - Control whether the logging stream (as configured in ``RCUTILS_LOGGING_USE_STDOUT``) should be line buffered or unbuffered. If this is unset, use the default of the stream (generally line buffered for stdout, and unbuffered for stderr). If this is 0, force the stream to be unbuffered. If this is 1, force the stream to be line buffered. +* ``RCUTILS_COLORIZED_OUTPUT`` - Control whether colors are used when outputting messages. If unset, automatically determine based on the platform and whether the console is a TTY. If 0, force disable using colors for output. If 1, force enable using colors for output. +* ``RCUTILS_CONSOLE_OUTPUT_FORMAT`` - Control the fields that are output for each log message. The available fields are: + + * ``{severity}`` - The severity level. + * ``{name}`` - The name of the logger (may be empty). + * ``{message}`` - The log message (may be empty). + * ``{function_name}`` - The function name this was called from (may be empty). + * ``{file_name}`` - The file name this was called from (may be empty). + * ``{time}`` - The time in seconds since the epoch. + * ``{time_as_nanoseconds}`` - The time in nanoseconds since the epoch. + * ``{line_number}`` - The line number this was called from (may be empty). + + If no format is given, a default of ``[{severity}] [{time}] [{name}]: {message}`` is used. + + +Node creation +^^^^^^^^^^^^^ + +When initializing a ROS 2 node, it is possible to control some aspects of the behavior via node options. +Since these are per-node options, they can be set differently for different nodes even when the nodes are composed into a single process. + +* ``log_levels`` - The log level to use for a component within this particular node. This can be set with the following: ``ros2 run demo_nodes_cpp talker --ros-args --log-level talker:=DEBUG`` +* ``external_log_config_file`` - The external file to use to configure the backend logger. If it is NULL, the default configuration will be used. Note that the format of this file is backend-specific (and is currently unimplemented for the default backend logger of spdlog). This can be set with the following: ``ros2 run demo_nodes_cpp talker --ros-args --log-config-file log-config.txt`` +* ``log_stdout_disabled`` - Whether to disable writing log messages to the console. This can be done with the following: ``ros2 run demo_nodes_cpp talker --ros-args --disable-stdout-logs`` +* ``log_rosout_disabled`` - Whether to disable writing log messages out to ``/rosout``. This can significantly save on network bandwidth, but external observers will not be able to monitor logging. This can be done with the following: ``ros2 run demo_nodes_cpp talker --ros-args --disable-rosout-logs`` +* ``log_ext_lib_disabled`` - Whether to completely disable the use of an external logger. This may be faster in some cases, but means that logs will not be written to disk. This can be done with the following: ``ros2 run demo_nodes_cpp talker --ros-args --disable-external-lib-logs`` + +Logging subsystem design +------------------------ + +The image below shows the five main pieces to the logging subsystem and how they interact. + +.. figure:: images/ros2_logging_architecture.png + :alt: ROS 2 logging architecture + :width: 550px + :align: center + +rcutils +^^^^^^^ + +``rcutils`` has a logging implementation that can format log messages according to a certain format (see ``Configuration`` above), and output those log messages to a console. +``rcutils`` implements a complete logging solution, but allows higher-level components to insert themselves into the logging infrastructure in a dependency-injection model. +This will become more evident when we talk about the ``rcl`` layer below. + +Note that this is a *per-process* logging implementation, so anything that is configured at this level will affect the entire process, not just individual nodes. + +rcl_logging_spdlog +^^^^^^^^^^^^^^^^^^ + +``rcl_logging_spdlog`` implements the ``rcl_logging_interface`` API, and thus provides external logging services to the ``rcl`` layer. +In particular, the ``rcl_logging_spdlog`` implementation takes formatted log messages and writes them out to log files on disk using the ``spdlog`` library, typically within ``~/.ros/log`` (though this is configurable; see ``Configuration`` above). + +rcl +^^^ + +The logging subsystem in ``rcl`` uses ``rcutils`` and ``rcl_logging_spdlog`` to provide the bulk of the ROS 2 logging services. +When log messages come in, ``rcl`` decides where to send them. +There are 3 main places that logging messages can be delivered; an individual node may have any combination of them enabled: + +* To the console via the ``rcutils`` layer +* To disk via the ``rcl_logging_spdlog`` layer +* To the ``/rosout`` topic on the ROS 2 network via the RMW layer + +rclcpp +^^^^^^ + +This is the main ROS 2 C++ API which sits atop the ``rcl`` API. +In the context of logging, ``rclcpp`` provides the ``RCLCPP_`` logging macros; see ``APIs`` above for a complete list. +When one of the ``RCLCPP_`` macros runs, it checks the current severity level of the node against the severity level of the macro. +If the severity level of the macro is greater than or equal to the node severity level, the message will be formatted and output to all of the places that are currently configured. +Note that ``rclcpp`` uses a global mutex for log calls, so all logging calls within the same process end up being single-threaded. + + +rclpy +^^^^^ + +This is the main ROS 2 Python API which sits atop the ``rcl`` API. +In the context of logging, ``rclpy`` provides the ``logger.debug``-style functions; see ``APIs`` above for a complete list. +When one of the ``logger.debug`` functions runs, it checks the current severity level of the node against the severity level of the macro. +If the severity level of the macro is greater than or equal to the node severity level, the message will be formatted and output to all of the places that are currently configured. + + Logging usage ------------- @@ -52,6 +184,7 @@ Logging usage .. group-tab:: C++ + * See the `rclcpp logging demo `_ for some simple examples. * See the :doc:`logging demo <../Tutorials/Demos/Logging-and-logger-configuration>` for example usage. * See the `rclcpp documentation `__ for an extensive list of functionality. diff --git a/source/Concepts/images/ros2_logging_architecture.png b/source/Concepts/images/ros2_logging_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..649ad89b85695f2f29312e2be1998112e6108b84 GIT binary patch literal 48956 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 literal 0 HcmV?d00001 diff --git a/source/Tutorials/Demos/Logging-and-logger-configuration.rst b/source/Tutorials/Demos/Logging-and-logger-configuration.rst index adeef06e53..975eb1171c 100644 --- a/source/Tutorials/Demos/Logging-and-logger-configuration.rst +++ b/source/Tutorials/Demos/Logging-and-logger-configuration.rst @@ -3,11 +3,141 @@ Logging-and-logger-configuration Tutorials/Logging-and-logger-configuration -Configuring loggers -=================== +Logging +======= + +.. contents:: Table of Contents + :depth: 2 + :local: See `the logging page <../../Concepts/About-Logging>` for details on available functionality. +Using log statements in code +---------------------------- + +Basic logging +^^^^^^^^^^^^^ + +The following code will output a log message from a ROS 2 node at ``DEBUG`` severity: + +.. tabs:: + + .. group-tab:: C++ + + .. code-block:: C++ + + // 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); + + .. group-tab:: Python + + .. code-block:: python + + node.get_logger().debug('My log message %d' % (4)) + +Note that in both cases, no trailing newline is added, as the logging infrastructure will automatically add one. + +Logging only the first time +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following code will output a log message from a ROS 2 node at ``INFO`` severity, but only the first time it is hit: + +.. tabs:: + + .. group-tab:: C++ + + .. code-block:: C++ + + // 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); + + .. group-tab:: Python + + .. code-block:: python + + num = 4 + node.get_logger().info(f'My log message {num}', once=True) + +Logging all but the first time +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following code will output a log message from a ROS 2 node at ``WARN`` severity, but not the very first time it is hit: + +.. tabs:: + + .. group-tab:: C++ + + .. code-block:: C++ + + // 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); + + .. group-tab:: Python + + .. code-block:: python + + num = 4 + node.get_logger().warning('My log message {0}'.format(num), skip_first=True) + +Logging throttled +^^^^^^^^^^^^^^^^^ + +The following code will output a log message from a ROS 2 node at ``ERROR`` severity, but no more than once per second: + +.. tabs:: + + .. group-tab:: C++ + + .. code-block:: C++ + + // 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_lock(), 1000, "My log message " << 4); + + .. group-tab:: Python + + .. code-block:: python + + num = 4 + node.get_logger().error(f'My log message {num}', throttle_duration_sec=1) + +Logging throttled all but the first time +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following code will output a log message from a ROS 2 node at ``DEBUG`` severity, no more than once per second, skipping the very first time it is hit: + +.. tabs:: + + .. group-tab:: C++ + + .. code-block:: C++ + + // 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); + + .. group-tab:: Python + + .. code-block:: python + + num = 4 + node.get_logger().debug(f'My log message {num}', skip_first=True, throttle_duration_sec=1.0) + +Logging demo +------------ + In this demo, different types of log calls are shown and the severity level of different loggers is configured locally and externally. Start the demo with: