blob: 510f1476a5e5dd5352b31f559c37b6b33fa88968 [file]
.. _module-pw_log_tokenized:
----------------
pw_log_tokenized
----------------
.. pigweed-module::
:name: pw_log_tokenized
The ``pw_log_tokenized`` module contains utilities for tokenized logging. It
connects ``pw_log`` to ``pw_tokenizer`` and supports
:ref:`module-pw_log-tokenized-args`.
C++ backend
===========
``pw_log_tokenized`` provides two backends for ``pw_log`` that tokenize log
messages with the ``pw_tokenizer`` module. One backend includes metadata, and
the other omits it to save code size.
With metadata
-------------
The default ``//pw_log_tokenized`` backend passes the log level, 16-bit
tokenized module name, and flag bits through a metadata argument. The macro
eventually passes logs to the :cc:`pw_log_tokenized_HandleLog` function, which
must be implemented by the application.
Example implementation:
.. code-block:: cpp
extern "C" void pw_log_tokenized_HandleLog(uint32_t metadata,
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 info(metadata);
if (info.level() < current_log_level) {
return;
}
if (info.flags() & HIGH_PRIORITY_LOG != 0) {
EmitHighPriorityLog(info.module(), message, size);
} else {
EmitLowPriorityLog(info.module(), message, size);
}
}
Without metadata
----------------
The ``//pw_log_tokenized:light`` backend omits the metadata entirely to
save code size. The macro eventually passes logs to the
:cc:`pw_log_tokenized_HandleLogWithoutMetadata` function, which must be
implemented by the application.
Example implementation:
.. code-block:: cpp
extern "C" void pw_log_tokenized_HandleLogWithoutMetadata(
const uint8_t message[], size_t size) {
// Since no metadata is provided, applications might route these logs
// to a default stream without filtering.
EmitLog(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
letter.
.. code-block::
"■key1♦contents1■key2♦contents2■key3♦contents3"
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
used.
``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/hyper.cc"
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.
The format string is created by the :cc:`PW_LOG_TOKENIZED_FORMAT_STRING`
macro.
The metadata bit field
----------------------
``pw_log_tokenized`` packs runtime-accessible metadata into a 32-bit integer
which is passed as the metadata argument for ``pw_log_tokenizer``'s global
handler with metadata facade. Packing this metadata into a single word rather
than separate arguments reduces the code size significantly.
Four items are packed into the metadata argument (when used):
- 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
module.
For applications that do not need log metadata at runtime, ``pw_log_tokenized``
provides a macro that omits the metadata to save code size:
:cc:`PW_LOG_TOKENIZED_TO_GLOBAL_HANDLER` (used by the
``//pw_log_tokenized:light`` backend). If this macro is used as the
``pw_log`` backend, the log is routed to the
:cc:`pw_log_tokenized_HandleLogWithoutMetadata` function, bypassing the typical
log handler.
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.
* :cc:`PW_LOG_TOKENIZED_LEVEL_BITS`
* :cc:`PW_LOG_TOKENIZED_LINE_BITS`
* :cc:`PW_LOG_TOKENIZED_FLAG_BITS`
* :cc:`PW_LOG_TOKENIZED_MODULE_BITS`
Creating and reading Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``pw_log_tokenized`` provides :cc:`GenericMetadata
<pw::log_tokenized::GenericMetadata>` to facilitate the creation and
interpretation of packed log :cc:`Metadata <pw::log_tokenized::Metadata>`.
The following example shows that a ``Metadata`` object can be created from a
``uint32_t`` log metadata.
.. code-block:: cpp
extern "C" void pw_log_tokenized_HandleLog(uint32_t metadata,
const uint8_t message[],
size_t size_bytes) {
pw::log_tokenized::Metadata info = metadata;
// Check the log level to see if this log is a crash.
if (info.level() == PW_LOG_LEVEL_FATAL) {
HandleCrash(info,
pw::ConstByteSpan(reinterpret_cast<const std::byte*>(message),
size_bytes));
PW_UNREACHABLE;
}
// ...
}
It's also possible to get a ``uint32_t`` representation of a ``Metadata``
object:
.. code-block:: cpp
// Logs an explicitly created string token.
void LogToken(uint32_t token, int level, int line_number, int module) {
const uint32_t metadata =
log_tokenized::Metadata(level, module, PW_LOG_FLAGS, line_number).value();
std::array<std::byte, sizeof(token)> token_buffer =
pw::bytes::CopyInOrder(endian::little, token);
pw_log_tokenized_HandleLog(
metadata,
reinterpret_cast<const uint8_t*>(token_buffer.data()),
token_buffer.size());
}
The binary tokenized message may be encoded in the :ref:`prefixed Base64 format
<module-pw_tokenizer-base64-format>` with the
:cc:`pw::log_tokenized::PrefixedBase64Encode` function.
Parsing metadata fields
-----------------------
The metadata fields packed into the format string can be parsed with the
:cc:`pw::log_tokenized::ParseFields` function. This function takes a string
and a callback that is called for each key-value pair.
Build targets
-------------
The build for ``pw_log_tokenized`` provides two backend targets for the
``pw_log`` facade: ``//pw_log_tokenized`` and
``//pw_log_tokenized:light``. Both targets provide the
``pw_log_tokenized/log_tokenized.h`` header.
- ``//pw_log_tokenized`` routes logs to the ``pw_log_tokenized:handler`` facade, which must
be implemented by the user.
- ``//pw_log_tokenized:light`` routes logs to the ``pw_log_tokenized:light_handler``
facade, which must also be implemented by the user.
GCC has a bug resulting in section attributes of templated functions being
ignored. This in turn means that log tokenization cannot work for templated
functions, because the token database entries are lost at build time.
For more information see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70435.
If you are using GCC, the ``gcc_partially_tokenized`` target can be used as a
backend for the ``pw_log`` facade instead which tokenizes as much as possible
and uses the ``pw_log_string:handler`` for the rest using string logging.
Python package
==============
``pw_log_tokenized`` includes a Python package for decoding tokenized logs.
pw_log_tokenized
----------------
.. automodule:: pw_log_tokenized
:members:
:undoc-members: