Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Document internals of logging system #2028

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions source/Concepts/About-Logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,238 @@ 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.

Log files
---------

Log output files written to a directory that defaults to ``~/.ros/log`` but can be changed through enviroment variables. The filenames follow this pattern:

``<exe>_<pid>_<milliseconds-since-epoch>.log``

These are created per operating system process.
The external lib logging using ``spdlog`` is what writes these files in the current implementation.
Comment on lines +48 to +56

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this part to https://github.com/ros2/ros2_documentation/blob/rolling/source/Tutorials/Logging-and-logger-configuration.rst?, I see there's a section about Logging directory configuration


Screen log output
-----------------

The screen output is written from the handler within ``rcutils``.
The screen output to ``stdout`` and ``stderr`` can originate from many different processes when ROS nodes are started through the launch system.
For this reason, the launch system itself consumes ``stderr`` and ``stdout`` from each process and writes to its own ``stderr`` and ``stdout`` streams to interleave the log messages from the various processes.

Logging from a ROS Node
-----------------------

When logging from within a C++ ROS Node one should use the interface defined in the ``rclcpp`` library.

Initialization of the ROS Logging system
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When a ``rclcpp::Node`` is created the global Context object is created and initialized.
As part of this process, the logging system is initialized.
One of the steps of initializing the logging system is setting the global output handler, which is a function that is called as the final step to write out a log.
The code within this function defines where the log string and data go.
``rclcpp`` defines an output handler which locks a global logging mutex and then calls ``rcl_logging_multiple_output_handler``, which is defined in ``rcl/logging.hpp``.

One implication of this is that in any ROS process based on ``rclcpp`` it is single-threaded during logging.
To combat the performance implications of this you can use throttling.
This is necessary because the logging system writes to file streams (stderr, stdout, normal files) which cannot be done from multiple threads in a sane way.
A possible technique to log from a thread you need to be lock-free would be to use internal queues to send data to another thread that then logs your data.
Comment on lines +78 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
One implication of this is that in any ROS process based on ``rclcpp`` it is single-threaded during logging.
To combat the performance implications of this you can use throttling.
This is necessary because the logging system writes to file streams (stderr, stdout, normal files) which cannot be done from multiple threads in a sane way.
A possible technique to log from a thread you need to be lock-free would be to use internal queues to send data to another thread that then logs your data.
One implication of this is that in any ROS process based on ``rclcpp`` is single-threaded during logging.
This is necessary because the logging system writes to file streams (stderr, stdout, normal files) which cannot easily be done from multiple threads.

I think this paragraph can have some explanation cut and can be combined with the above paragraph.

Maybe add the cutout segments as notes, for example.


Compiling out logging
^^^^^^^^^^^^^^^^^^^^^

To compile wihtout logging set ``RCLCPP_LOGGING_ENABLED=0`` and ``RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_NONE``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To compile wihtout logging set ``RCLCPP_LOGGING_ENABLED=0`` and ``RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_NONE``.
To compile without logging, set ``RCLCPP_LOGGING_ENABLED=0`` and ``RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_NONE``.


Will this only work with a source build of ros2 or can this be done per project that depends on ros2?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Will this only work with a source build of ros2 or can this be done per project that depends on ros2?
Will this only work with a source build of ROS 2, or can this be done per project that depends on ROS 2?

We prefer "ROS 2."


rclcpp ``Logger`` class
^^^^^^^^^^^^^^^^^^^^^^^

Stores a name string.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Stores a name string.
The ``Logger`` class stores a name string, which is used to identify the logger.

If this is created from the Node the string is the name of the node, otherwise, it is whatever string you pass into the function ``get_logger``.
Logger object is a required first argument for the ``RCLCPP_`` macros.

``RCLCPP`` macros
^^^^^^^^^^^^^^^^^

Defined by generated header in rclcpp (rclcpp/resource/logging.hpp.em).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to make the first lines of sections full sentences, or to format them differently, in a way that clearly indicates this is a definition.

This header is generated as much of the content is repeated over and over with a small difference for the combinations of level and features.
The first argument is required to be of the type ``rclcpp::Logger``.
The ``rclcpp::Logger`` is used to get the name of the logger to pass to the ``RCUTILS_`` macro it uses.
This is the interface that users of rclcpp are expected to use and implements several features on top of the ``RCUTILS_LOG_<LEVEL>[_<FEATURE(s)>]_NAMED`` logging macros.
The name argument for the ``RCUTILS`` macro that is used comes from the ``rclcpp::Logger``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The name argument for the ``RCUTILS`` macro that is used comes from the ``rclcpp::Logger``.
The name argument for the ``RCUTILS`` macro, that is used, comes from the ``rclcpp::Logger``.

This is worded strangely. Maybe try reorganizing the sentence?


The structure of the macro is ``RCLCPP_<LEVEL>[_<FEATURE(s)>](logger, ...)``.
The possible severity levels are ``DEBUG``, ``INFO``, ``WARN``, ``ERROR``, or ``FATAL``.
The possible feature combinations are ``ONCE``, ``EXPRESSION``, ``FUNCTION``, ``SKIPFIRST``, ``THROTTLE``, ``SKIPFIRST_THROTTLE``, ``STREAM``, ``STREAM_ONCE``, ``STREAM_EXPRESSION``, ``STREAM_FUNCTION``, ``STREAM_SKIPFIRST``, ``STREAM_THROTTLE``, ``STREAM_SKIPFIRST_THROTTLE``.
Comment on lines +109 to +110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The possible severity levels are ``DEBUG``, ``INFO``, ``WARN``, ``ERROR``, or ``FATAL``.
The possible feature combinations are ``ONCE``, ``EXPRESSION``, ``FUNCTION``, ``SKIPFIRST``, ``THROTTLE``, ``SKIPFIRST_THROTTLE``, ``STREAM``, ``STREAM_ONCE``, ``STREAM_EXPRESSION``, ``STREAM_FUNCTION``, ``STREAM_SKIPFIRST``, ``STREAM_THROTTLE``, ``STREAM_SKIPFIRST_THROTTLE``.
The possible severity levels are ``DEBUG``, ``INFO``, ``WARN``, ``ERROR``, or ``FATAL``, and the possible feature combinations are ``ONCE``, ``EXPRESSION``, ``FUNCTION``, ``SKIPFIRST``, ``THROTTLE``, ``SKIPFIRST_THROTTLE``, ``STREAM``, ``STREAM_ONCE``, ``STREAM_EXPRESSION``, ``STREAM_FUNCTION``, ``STREAM_SKIPFIRST``, ``STREAM_THROTTLE``, ``STREAM_SKIPFIRST_THROTTLE``.


.. list-table:: RCLCPP logging macro FEATURES
:widths: 10 90
:header-rows: 1

* - FEATURE
- Description
* - ONCE
- All subsequent log calls except the first one are being ignored.
* - EXPRESSION
- Log calls are being ignored when the expression evaluates to false.
* - FUNCTION
- Log calls are being ignored when the function returns false.
* - SKIPFIRST
- The first log call is being ignored but all subsequent calls are being processed.
* - THROTTLE
- Log calls are being ignored if the last logged message is not longer ago than the specified duration.
* - STREAM
- Uses a ``std::stringstream`` to construct the logged string.

Logging from within ROS 2
-------------------------

Configuration of the logging system and handling of logging output is defined in ``rcl/logging.h``. Macros used by rclcpp for logging are defined in the C utility package ``rcutils``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be good to put a link to rcl/logging.h? How about for other times that a file is referenced?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there should be one sentence per line.

Suggested change
Configuration of the logging system and handling of logging output is defined in ``rcl/logging.h``. Macros used by rclcpp for logging are defined in the C utility package ``rcutils``.
Configuration of the logging system and handling of logging output is defined in ``rcl/logging.h``.
Macros used by rclcpp for logging are defined in the C utility package ``rcutils``.


rcl/logging.h
^^^^^^^^^^^^^

Configuration of the logging system can come from process arguments.
The available arguments are defined in ``rcl/arguments.h``.
See `logging demo <../Tutorials/Logging-and-logger-configuration.html#logger-level-configuration-command-line>` for example usage.

.. list-table:: Global arguments for logging
:widths: 10 20 70
:header-rows: 1

* - Argument
- Default
- Description
* - --log-level
- Unset
- The ROS flag that precedes the ROS logging level to set.
* - --log-config-file
- Unset
- The ROS flag that precedes the name of a configuration file to configure logging in external logger.
* - stdout-logs
- enabled
- The suffix of the ROS flag to enable or disable stdout logging. must be preceded with --enable- or --disable-.
* - rosout-logs
- enabled
- The suffix of the ROS flag to enable or disable rosout logging. must be preceded with --enable- or --disable-.
* - external-lib-logs
- enabled
- The suffix of the ROS flag to enable or disable external library logging. must be preceded with --enable- or --disable-.

``rcl_logging_multiple_output_handler`` is a function that is called by the logging system that then, in turn, calls logging handlers for each of the three types of outputs (depending on if they are enabled): stdout, rosout, and external-lib.

stdout logging
^^^^^^^^^^^^^^

This is defined by the function ``rcutils_logging_console_output_handler`` and handles sending logs to stdout and stderr.

rosout logging
^^^^^^^^^^^^^^

Publishes a ``rcl_interfaces::Log`` message to the topic /node-name/rosout.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Publishes a ``rcl_interfaces::Log`` message to the topic /node-name/rosout.
Publishes a ``rcl_interfaces::Log`` message to the topic ``/node-name/rosout``.


external-lib logging
^^^^^^^^^^^^^^^^^^^^

This feature uses an external logging library to consume the output of the logging system using the package ``rcl_logging``.
There is currently only one external library interface defined and that is ``rcl_logging_spdlog``.
The external-lib implementation is set during the CMake configure step of the package ``rcl``.
It can be overridden with the variable ``RCL_LOGGING_IMPLEMENTATION``.
The ``rcl_logging`` README says there is a ``rcl_logging_log4cxx`` implementation, however that does not appear to exist yet.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was supported previously and got removed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe another PR can be created updating the rcl_logging README, or at least an issue opened?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ``rcl_logging`` README says there is a ``rcl_logging_log4cxx`` implementation, however that does not appear to exist yet.


rcl_logging_spdlog configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The current implementation of ``rcl_logging_spdlog`` cannot be configured with a config file.

spdlog logging
^^^^^^^^^^^^^^

This is what writes the log files.

RCUTILS internals
-----------------

``rcutils`` contains macros and functions that define some of the non-ros low-level logging behavior in C.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
``rcutils`` contains macros and functions that define some of the non-ros low-level logging behavior in C.
``rcutils`` contains macros and functions that define some of the non-ROS low-level logging behavior in C.


rcutils/logging.h
^^^^^^^^^^^^^^^^^

This header defines various internal functions used by the logging system that can be configured via environment variables.

RCUTILS_CONSOLE_OUTPUT_FORMAT enviroment variable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``RCUTILS_CONSOLE_OUTPUT_FORMAT`` environment variable can be used to set the output format of messages logged to the console.

.. list-table:: RCUTILS_CONSOLE_OUTPUT_FORMAT tokens
:widths: 10 90
:header-rows: 1

* - Token
- Description
* - file_name
- the full file name of the caller including the path
* - function_name
- the function name of the caller
* - line_number
- the line number of the caller
* - message
- the message string after it has been formatted
* - name
- the full logger name
* - severity
- the name of the severity level, e.g. `INFO`
* - time
- the timestamp of log message in floating point seconds
* - time_as_nanoseconds
- the timestamp of log message in integer nanoseconds

The format string can use these tokens by referencing them in curly brackets,
e.g. ``"[{severity}] [{name}]: {message} ({function_name}() at {file_name}:{line_number})"``.
Any number of tokens can be used.
The limit of the format string is 2048 characters.

RCUTILS_COLORIZED_OUTPUT environment variable
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``RCUTILS_COLORIZED_OUTPUT`` environment variable allows configuring if colors are used or not.

.. list-table:: RCUTILS_COLORIZED_OUTPUT values
:widths: 10 90
:header-rows: 1

* - Value
- Description
* - 1
- Force using colours.
* - 0
- Don't use colours.

If it is unset, colors are used depending on if the target stream is a terminal or not.
See `isatty` documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add a link here.


rcutils/logging_macros.h
^^^^^^^^^^^^^^^^^^^^^^^^

This is a generated header file as much of the content is repeated over and over for the various combinations.
The structure of the macros is ``RCUTILS_LOG_<LEVEL>[_FEATURE(s)][_NAMED]``.
The named variants are the ones used by rclcpp and the name comes from the Logger object.
The levels are the same as in rclcpp.
The possible feature combinations are ``ONCE``, ``EXPRESSION``, ``FUNCTION``, ``SKIPFIRST``, ``THROTTLE``, ``SKIPFIRST_THROTTLE``.
These features are implemented with a set of even more general macros.
Normal users of ros2 should not need to understand how this implementation works or use this interface.

Logging from a Python ROS Node
------------------------------

The Python interfaces are built on top of ``rclcpp`` and therefore share the same functionality.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely not true; rclcpp and rclpy are siblings. It may be true that rclpy depends on rcl, though I'm not sure of that; Python has robust logging facilities of its own, we may be using those instead.


Logging from Python launch file
-------------------------------

TODO

Logging usage
-------------

Expand Down