blob: 91b0732c3d3e3a5ade2d608b6b9a3cf7e20f988d [file] [log] [blame]
.. _module-pw_log_tokenized:
The ``pw_log_tokenized`` module contains utilities for tokenized logging. It
connects ``pw_log`` to ``pw_tokenizer``.
C++ backend
``pw_log_tokenized`` provides a backend for ``pw_log`` that tokenizes log
messages with the ``pw_tokenizer`` module. By default, log messages are
tokenized with the ``PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD`` macro.
The log level, 16-bit tokenized module name, and flags bits are passed through
the payload argument. The macro eventually passes logs to the
``pw_tokenizer_HandleEncodedMessageWithPayload`` function, which must be
implemented by the application.
Example implementation:
.. code-block:: cpp
extern "C" void pw_tokenizer_HandleEncodedMessageWithPayload(
pw_tokenizer_Payload payload, const uint8_t message[], size_t size) {
// The metadata object provides the log level, module token, and flags.
// These values can be recorded and used for runtime filtering.
pw::log_tokenized::Metadata metadata(payload);
if (metadata.level() < current_log_level) {
if (metadata.flags() & HIGH_PRIORITY_LOG != 0) {
EmitHighPriorityLog(metadata.module(), message, size);
} else {
EmitLowPriorityLog(metadata.module(), message, size);
See the documentation for :ref:`module-pw_tokenizer` for further details.
Metadata in the format string
With tokenized logging, the log format string is converted to a 32-bit token.
Regardless of how long the format string is, it's always represented by a 32-bit
token. Because of this, metadata can be packed into the tokenized string with
no cost.
``pw_log_tokenized`` uses a simple key-value format to encode metadata in a
format string. Each field starts with the ``■`` (U+25A0 "Black Square")
character, followed by the key name, the ``♦`` (U+2666 "Black Diamond Suit")
character, and then the value. The string is encoded as UTF-8. Key names are
comprised of alphanumeric ASCII characters and underscore and start with a
.. code-block::
This format makes the message easily machine parseable and human readable. It is
extremely unlikely to conflict with log message contents due to the characters
``pw_log_tokenized`` uses three fields: ``msg``, ``module``, and ``file``.
Implementations may add other fields, but they will be ignored by the
``pw_log_tokenized`` tooling.
.. code-block::
"■msg♦Hyperdrive %d set to %f■module♦engine■file♦propulsion/"
Using key-value pairs allows placing the fields in any order.
``pw_log_tokenized`` places the message first. This is prefered when tokenizing
C code because the tokenizer only hashes a fixed number of characters. If the
file were first, the long path might take most of the hashed characters,
increasing the odds of a collision with other strings in that file. In C++, all
characters in the string are hashed, so the order is not important.
Metadata in the tokenizer payload argument
``pw_log_tokenized`` packs runtime-accessible metadata into a 32-bit integer
which is passed as the "payload" argument for ``pw_log_tokenizer``'s global
handler with payload facade. Packing this metadata into a single word rather
than separate arguments reduces the code size significantly.
Four items are packed into the payload argument:
- Log level -- Used for runtime log filtering by level.
- Line number -- Used to track where a log message originated.
- Log flags -- Implementation-defined log flags.
- Tokenized :c:macro:`PW_LOG_MODULE_NAME` -- Used for runtime log filtering by
Configuring metadata bit fields
The number of bits to use for each metadata field is configurable through macros
in ``pw_log/config.h``. The field widths must sum to 32 bits. A field with zero
bits allocated is excluded from the log metadata.
Bits to allocate for the log level. Defaults to :c:macro:`PW_LOG_LEVEL_BITS`
Bits to allocate for the line number. Defaults to 11 (up to line 2047). If the
line number is too large to be represented by this field, line is reported as
Including the line number can slightly increase code size. Without the line
number, the log metadata argument is the same for all logs with the same level
and flags. With the line number, each metadata value is unique and must be
encoded as a separate word in the binary. Systems with extreme space
constraints may exclude line numbers by setting this macro to 0.
It is possible to include line numbers in tokenized log format strings, but
that is discouraged because line numbers change whenever a file is edited.
Passing the line number with the metadata is a lightweight way to include it.
Bits to use for implementation-defined flags. Defaults to 2.
Bits to use for the tokenized version of :c:macro:`PW_LOG_MODULE_NAME`.
Defaults to 16, which gives a ~1% probability of a collision with 37 module
Creating and reading Metadata payloads
A C++ class is provided to facilitate the creation and interpretation of packed
log metadata payloads. The ``GenericMetadata`` class allows flags, log level,
line number, and a module identifier to be packed into bit fields of
configurable size. The ``Metadata`` class simplifies the bit field width
templatization of ``GenericMetadata`` by pulling from this module's
configuration options. In most cases, it's recommended to use ``Metadata`` to
create or read metadata payloads.
A ``Metadata`` object can be created from a ``pw_tokenizer_Payload`` allowing
various peices of metadata to be read from the payload as seen below:
.. code-block:: cpp
extern "C" void pw_tokenizer_HandleEncodedMessageWithPayload(
pw_tokenizer_Payload payload,
const uint8_t message[],
size_t size_bytes) {
pw::log_tokenized::Metadata metadata = payload;
// Check the log level to see if this log is a crash.
if (metadata.level() == PW_LOG_LEVEL_FATAL) {
HandleCrash(metadata, pw::ConstByteSpan(
reinterpret_cast<const std::byte*>(message), size_bytes));
// ...
It's also possible to construct a ``pw_tokenizer_Payload`` using the
``Metadata`` class:
.. code-block:: cpp
// Logs an explicitly created string token.
void LogToken(uint32_t token, int level, int line_number, int module) {
const pw_tokenizer_Payload payload =
level, module, PW_LOG_FLAGS, line_number)
std::array<std::byte, sizeof(token)> token_buffer =
pw::bytes::CopyInOrder(endian::little, token);
reinterpret_cast<const uint8_t*>(,
Using a custom macro
Applications may use their own macro instead of
``PW_LOG_TOKENIZED_ENCODE_MESSAGE`` config macro. This macro should take
.. c:macro:: PW_LOG_TOKENIZED_ENCODE_MESSAGE(log_metadata, message, ...)
:param log_metadata:
Packed metadata for the log message. See the Metadata_ class for how to
unpack the details.
:type log_metadata: pw_tokenizer_Payload
:param message: The log message format string (untokenized)
:type message: :c:texpr:`const char*`
.. _Metadata:;l=113
For instructions on how to implement a custom tokenization macro, see
Build targets
The GN build for ``pw_log_tokenized`` has two targets: ``pw_log_tokenized`` and
``log_backend``. The ``pw_log_tokenized`` target provides the
``pw_log_tokenized/log_tokenized.h`` header. The ``log_backend`` target
implements the backend for the ``pw_log`` facade. ``pw_log_tokenized`` invokes
the ``pw_tokenizer:global_handler_with_payload`` facade, which must be
implemented by the user of ``pw_log_tokenized``.
Python package
``pw_log_tokenized`` includes a Python package for decoding tokenized logs.
.. automodule:: pw_log_tokenized