• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _module-pw_log:
2
3======
4pw_log
5======
6.. pigweed-module::
7   :name: pw_log
8
9Pigweed's logging module provides facilities for applications to log
10information about the execution of their application. The module is split into
11two components:
12
131. The facade (this module) which is only a macro interface layer
142. The backend, provided elsewhere, that implements the low level logging
15
16``pw_log`` also defines a logging protobuf, helper utilities, and an RPC
17service for efficiently storing and transmitting log messages. See
18:ref:`module-pw_log-protobuf` for details.
19
20--------------
21Usage examples
22--------------
23Here is a typical usage example, showing setting the module name, and using the
24long-form names.
25
26.. code-block:: cpp
27
28   #define PW_LOG_MODULE_NAME "BLE"
29
30   #include "pw_log/log.h"
31
32   int main() {
33     PW_LOG_INFO("Booting...");
34     PW_LOG_DEBUG("CPU temp: %.2f", cpu_temperature);
35     if (BootFailed()) {
36       PW_LOG_CRITICAL("Had trouble booting due to error %d", GetErrorCode());
37       ReportErrorsAndHalt();
38     }
39     PW_LOG_INFO("Successfully booted");
40   }
41
42In ``.cc`` files, it is possible to dispense with the ``PW_`` part of the log
43names and go for shorter log macros. Include ``pw_log/short.h`` or
44``pw_log/shorter.h`` for shorter versions of the macros.
45
46.. warning::
47   The shorter log macros collide with `Abseil's logging API
48   <https://abseil.io/docs/cpp/guides/logging>`_. Do not use them in upstream
49   Pigweed modules, or any code that may depend on Abseil.
50
51.. code-block:: cpp
52
53   #define PW_LOG_MODULE_NAME "BLE"
54
55   #include "pw_log/shorter.h"
56
57   int main() {
58     INF("Booting...");
59     DBG("CPU temp: %.2f", cpu_temperature);
60     if (BootFailed()) {
61       CRT("Had trouble booting due to error %d", GetErrorCode());
62       ReportErrorsAndHalt();
63     }
64     INF("Successfully booted");
65   }
66
67The ``pw_log`` facade also exposes a handful of macros that only apply
68specifically to tokenized logging. See :ref:`module-pw_log-tokenized-args` for
69details.
70
71Layer diagram example: ``stm32f429i-disc1``
72===========================================
73Below is an example diagram showing how the modules connect together for the
74``stm32f429i-disc1`` target, where the ``pw_log`` backend is ``pw_log_basic``.
75``pw_log_basic`` uses the ``pw_sys_io`` module to log in plaintext, which in
76turn outputs to the STM32F429 bare metal backend for ``pw_sys_io``, which is
77``pw_sys_io_baremetal_stm32f429i``.
78
79.. image:: example_layer_diagram.svg
80
81.. _module-pw_log-macros:
82
83Logging macros
84==============
85These are the primary macros for logging information about the functioning of a
86system, intended to be used directly.
87
88.. c:macro:: PW_LOG(level, module, flags, fmt, ...)
89
90   This is the primary mechanism for logging.
91
92   *level* - An integer level as defined by ``pw_log/levels.h``.
93
94   *module* - A string literal for the module name. Defaults to
95   :c:macro:`PW_LOG_MODULE_NAME`.
96
97   *flags* - Arbitrary flags the backend can leverage. The semantics of these
98   flags are not defined in the facade, but are instead meant as a general
99   mechanism for communication bits of information to the logging backend.
100   ``pw_log`` reserves 2 flag bits by default, but log backends may provide for
101   more or fewer flag bits.
102
103   Here are some ideas for what a backend might use flags for:
104
105   - Example: ``HAS_PII`` - A log has personally-identifying data
106   - Example: ``HAS_DII`` - A log has device-identifying data
107   - Example: ``RELIABLE_DELIVERY`` - Ask the backend to ensure the log is
108     delivered; this may entail blocking other logs.
109   - Example: ``BEST_EFFORT`` - Don't deliver this log if it would mean blocking
110     or dropping important-flagged logs
111
112   *fmt* - The message to log, which may contain format specifiers like ``%d``
113   or ``%0.2f``.
114
115   Example:
116
117   .. code-block:: cpp
118
119      PW_LOG(PW_LOG_LEVEL_INFO, PW_LOG_MODULE_NAME, PW_LOG_FLAGS, "Temp is %d degrees", temp);
120      PW_LOG(PW_LOG_LEVEL_ERROR, PW_LOG_MODULE_NAME, UNRELIABLE_DELIVERY, "It didn't work!");
121
122   .. note::
123
124      ``PW_LOG()`` should not be used frequently; typically only when adding
125      flags to a particular message to mark PII or to indicate delivery
126      guarantees.  For most cases, prefer to use the direct ``PW_LOG_INFO`` or
127      ``PW_LOG_DEBUG`` style macros, which are often implemented more efficiently
128      in the backend.
129
130
131.. c:macro:: PW_LOG_DEBUG(fmt, ...)
132.. c:macro:: PW_LOG_INFO(fmt, ...)
133.. c:macro:: PW_LOG_WARN(fmt, ...)
134.. c:macro:: PW_LOG_ERROR(fmt, ...)
135.. c:macro:: PW_LOG_CRITICAL(fmt, ...)
136
137   Shorthand for ``PW_LOG(<level>, PW_LOG_MODULE_NAME, PW_LOG_FLAGS, fmt, ...)``.
138
139.. c:macro:: PW_LOG_EVERY_N(level, rate, ...)
140
141   A simple rate limit logger which will simply log one out of every N logs.
142
143   *level* - An integer level as defined by ``pw_log/levels.h``.
144
145   *rate* - Rate to reduce logs to, every ``rate``-th log will complete, others
146   will be suppressed.
147
148.. c:macro:: PW_LOG_EVERY_N_DURATION(level, min_interval_between_logs, msg, ...)
149
150   This is a rate-limited form of logging, especially useful for progressive
151   or chatty logs that should be logged periodically, but not on each instance
152   of the logger being called.
153
154   *level* - An integer level as defined by ``pw_log/levels.h``.
155
156   *min_interval_between_logs* - A ``std::chrono::duration`` of the minimum time
157   between logs. Logs attempted before this time duration will be completely
158   dropped.
159   Dropped logs will be counted to add a drop count and calculated rate of the
160   logs.
161
162   *msg* - Formattable log message, as you would pass to the above ``PW_LOG``
163   macro.
164
165   .. note::
166
167      ``PW_LOG_EVERY_N`` is simpler, if you simply need to reduce uniformly
168      periodic logs by a fixed or variable factor not based explicitly on a
169      duration. Each call to the macro will incur a static ``uint32_t`` within
170      the calling context.
171
172      ``PW_LOG_EVERY_N_DURATION`` is able to suppress all logs based on a time
173      interval, suppressing logs logging faster than the desired time interval.
174      Each call to the duration macro will incur a static 16 byte object to
175      track the time interval within the calling context.
176
177   Example:
178
179   .. code-block:: cpp
180
181      // Ensure at least 500ms between transfer parameter logs.
182      chrono::SystemClock::duration rate_limit_ =
183         chrono::SystemClock::for_at_least(std::chrono::milliseconds(500));
184
185      PW_LOG_EVERY_N_DURATION(PW_LOG_LEVEL_INFO,
186                              rate_limit_,
187                              "Transfer %u sending transfer parameters!"
188                              static_cast<unsigned>(session_id_));
189
190--------------------
191Module configuration
192--------------------
193This module has configuration options that globally affect the behavior of
194pw_log via compile-time configuration of this module, see the
195:ref:`module documentation <module-structure-compile-time-configuration>` for
196more details.
197
198.. c:macro:: PW_LOG_LEVEL_DEFAULT
199
200   Controls the default value of ``PW_LOG_LEVEL``. Setting
201   ``PW_LOG_LEVEL_DEFAULT`` will change the behavior of all source files that
202   have not explicitly set ``PW_LOG_LEVEL``. Defaults to ``PW_LOG_LEVEL_DEBUG``.
203
204.. c:macro:: PW_LOG_FLAGS_DEFAULT
205
206   Controls the default value of ``PW_LOG_FLAGS``. Setting
207   ``PW_LOG_FLAGS_DEFAULT`` will change the behavior of all source files that
208   have not explicitly set ``PW_LOG_FLAGS``. Defaults to ``0``.
209
210.. c:macro:: PW_LOG_ENABLE_IF_DEFAULT
211
212   Controls the default value of ``PW_LOG_ENABLE_IF``. Setting
213   ``PW_LOG_ENABLE_IF_DEFAULT`` will change the behavior of all source files that
214   have not explicitly set ``PW_LOG_ENABLE_IF``. Defaults to
215   ``((level) >= PW_LOG_LEVEL)``.
216
217
218Per-source file configuration
219=============================
220This module defines macros that can be overridden to independently control the
221behavior of ``pw_log`` statements for each C or C++ source file. To override
222these macros, add ``#define`` statements for them before including headers.
223
224The option macro definitions must be visible to ``pw_log/log.h`` the first time
225it is included. To handle potential transitive includes, place these
226``#defines`` before all ``#include`` statements. This should only be done in
227source files, not headers. For example:
228
229.. code-block:: cpp
230
231   // Set the pw_log option macros here, before ALL of the #includes.
232   #define PW_LOG_MODULE_NAME "Calibration"
233   #define PW_LOG_LEVEL PW_LOG_LEVEL_WARN
234
235   #include <array>
236   #include <random>
237
238   #include "devices/hal9000.h"
239   #include "pw_log/log.h"
240   #include "pw_rpc/server.h"
241
242   int MyFunction() {
243     PW_LOG_INFO("hello???");
244   }
245
246.. c:macro:: PW_LOG_MODULE_NAME
247
248   A string literal module name to use in logs. Log backends may attach this
249   name to log messages or use it for runtime filtering. Defaults to ``""``. The
250   ``PW_LOG_MODULE_NAME_DEFINED`` macro is set to ``1`` or ``0`` to indicate
251   whether ``PW_LOG_MODULE_NAME`` was overridden.
252
253.. c:macro:: PW_LOG_FLAGS
254
255   Log flags to use for the ``PW_LOG_<level>`` macros. Different flags may be
256   applied when using the ``PW_LOG`` macro directly.
257
258   Log backends use flags to change how they handle individual log messages.
259   Potential uses include assigning logs priority or marking them as containing
260   personal information. Defaults to ``PW_LOG_FLAGS_DEFAULT``.
261
262.. c:macro:: PW_LOG_LEVEL
263
264   Filters logs by level. Source files that define ``PW_LOG_LEVEL`` will display
265   only logs at or above the chosen level. Log statements below this level will
266   be compiled out of optimized builds. Defaults to ``PW_LOG_LEVEL_DEFAULT``.
267
268   Example:
269
270   .. code-block:: cpp
271
272      #define PW_LOG_LEVEL PW_LOG_LEVEL_INFO
273
274      #include "pw_log/log.h"
275
276      void DoSomething() {
277        PW_LOG_DEBUG("This won't be logged at all");
278        PW_LOG_INFO("This is INFO level, and will display");
279        PW_LOG_WARN("This is above INFO level, and will display");
280      }
281
282.. c:macro:: PW_LOG_ENABLE_IF(level, flags)
283
284   Filters logs by an arbitrary expression based on ``level`` and ``flags``.
285   Source files that define ``PW_LOG_ENABLE_IF(level, flags)`` will display if
286   the given expression evaluates true. Defaults to
287   ``PW_LOG_ENABLE_IF_DEFAULT``.
288
289   Example:
290
291   .. code-block:: cpp
292
293      // Pigweed's log facade will call this macro to decide to log or not. In
294      // this case, it will drop logs with the PII flag set if display of PII is
295      // not enabled for the application.
296      #define PW_LOG_ENABLE_IF(level, flags) \
297          (level >= PW_LOG_LEVEL_INFO && \
298           !((flags & MY_PRODUCT_PII_MASK) && MY_PRODUCT_LOG_PII_ENABLED)
299
300      #include "pw_log/log.h"
301
302      // This define might be supplied by the build system.
303      #define MY_PRODUCT_LOG_PII_ENABLED false
304
305      // This is the PII mask bit selected by the application.
306      #define MY_PRODUCT_PII_MASK (1 << 5)
307
308      void DoSomethingWithSensitiveInfo() {
309        PW_LOG_DEBUG("This won't be logged at all");
310        PW_LOG_INFO("This is INFO level, and will display");
311
312        // In this example, this will not be logged since logging with PII
313        // is disabled by the above macros.
314        PW_LOG(PW_LOG_LEVEL_INFO,
315               MY_PRODUCT_PII_MASK,
316               "Sensitive: %d",
317               sensitive_info);
318      }
319
320.. attention::
321
322   At this time, only compile time filtering is supported. In the future, we
323   plan to add support for runtime filtering.
324
325------------------
326Logging attributes
327------------------
328The logging facade in Pigweed is designed to facilitate the capture of at least
329the following attributes:
330
331- *Level* - The log level; for example, INFO, DEBUG, ERROR, etc. Typically an
332  integer
333- *Flags* - Bitset for e.g. RELIABLE_DELIVERY, or HAS_PII, or BEST_EFFORT
334- *File* - The file where the log was triggered
335- *Line* - The line number in the file where the log line occured
336- *Function* - What function the log comes from. This is expensive in binary
337  size to use!
338- *Module* - The user-defined module name for the log statement; e.g. “BLE” or
339  “BAT”
340- *Message* - The message itself; with % format arguments
341- *Arguments* - The format arguments to message
342- *Thread* - For devices running with an RTOS, capturing the thread is very
343  useful
344- *Others* - Processor security level? Maybe Thread is a good proxy for this
345
346Each backend may decide to capture different attributes to balance the tradeoff
347between call site code size, call site run time, wire format size, logging
348complexity, and more.
349
350.. _module-pw_log-circular-deps:
351
352----------------------------------------------
353Avoiding circular dependencies with ``PW_LOG``
354----------------------------------------------
355Because logs are so widely used, including in low-level libraries, it is
356common for the ``pw_log`` backend to cause circular dependencies. Because of
357this, log backends may avoid declaring explicit dependencies, instead relying
358on include paths to access header files.
359
360GN
361==
362In GN, the ``pw_log`` backend's full implementation with true dependencies is
363made available through the ``$dir_pw_log:impl`` group. When ``pw_log_BACKEND``
364is set, ``$dir_pw_log:impl`` must be listed in the ``pw_build_LINK_DEPS``
365variable. See :ref:`module-pw_build-link-deps`.
366
367In the ``pw_log``, the backend's full implementation is placed in the
368``$pw_log_BACKEND.impl`` target. ``$dir_pw_log:impl`` depends on this
369backend target. The ``$pw_log_BACKEND.impl`` target may be an empty group if
370the backend target can use its dependencies directly without causing circular
371dependencies.
372
373In order to break dependency cycles, the ``pw_log_BACKEND`` target may need
374to directly provide dependencies through include paths only, rather than GN
375``public_deps``. In this case, GN header checking can be disabled with
376``check_includes = false``.
377
378.. _module-pw_log-bazel-backend_impl:
379
380Bazel
381=====
382In Bazel, log backends may avoid cyclic dependencies by placing the full
383implementation in an ``impl`` target, like ``//pw_log_tokenized:impl``. The
384``//pw_log:backend_impl`` label flag should be set to the ``impl`` target
385required by the log backend used by the platform.
386
387You must add a dependency on the ``@pigweed//pw_log:backend_impl`` target to
388any binary using ``pw_log``.
389
390See :ref:`docs-build_system-bazel_link-extra-lib` for a general discussion of
391cyclic dependencies in low-level libraries in Bazel.
392
393----------------------
394Google Logging Adapter
395----------------------
396Pigweed provides a minimal C++ stream-style Google Log set of adapter
397macros around PW_LOG under ``pw_log/glog_adapter.h`` for compatibility with
398non-embedded code. While it is effective for porting server code to
399microcontrollers quickly, we do not advise embedded projects use that approach
400unless absolutely necessary.
401
402Configuration
403==============
404
405.. c:macro:: PW_LOG_CFG_GLOG_BUFFER_SIZE_BYTES
406
407   The size of the stack-allocated buffer used by the Google Logging (glog)
408   macros. This only affects the glog macros provided through pw_log/glog.h.
409
410   Pigweed strongly recommends sticking to printf-style logging instead
411   of C++ stream-style Google Log logging unless absolutely necessary. The glog
412   macros are only provided for compatibility with non-embedded code. See
413   :ref:`module-pw_log-design-discussion` for more details.
414
415   Undersizing this buffer will result in truncated log messages.
416
417-----------------
418Design discussion
419-----------------
420
421.. _module-pw_log-design-discussion:
422
423Why not use C++ style stream logging operators like Google Log?
424===============================================================
425There are multiple reasons to avoid the C++ stream logging style in embedded,
426but the biggest reason is that C++ stream logging defeats log tokenization. By
427having the string literals broken up between ``<<`` operators, tokenization
428becomes impossible with current language features.
429
430Consider this example use of Google Log:
431
432.. code-block:: cpp
433
434   LOG(INFO) << "My temperature is " << temperature << ". State: " << state;
435
436This log statement has two string literals. It might seem like one could convert
437move to tokenization:
438
439.. code-block:: cpp
440
441   LOG(INFO) << TOKEN("My temperature is ") << temperature << TOKEN(". State: ") << state;
442
443However, this doesn't work. The key problem is that the tokenization system
444needs to allocate the string in a linker section that is excluded from the
445final binary, but is in the final ELF executable (and so can be extracted).
446Since there is no way to declare a string or array in a different section in
447the middle of an experession in C++, it is not possible to tokenize an
448expression like the above.
449
450In contrast, the ``printf``-style version is a single statement with a single
451string constant, which can be expanded by the preprocessor (as part of
452``pw_tokenizer``) into a constant array in a special section.
453
454.. code-block:: cpp
455
456   // Note: LOG_INFO can be tokenized behind the macro; transparent to users.
457   PW_LOG_INFO("My temperature is %d. State: %s", temperature, state);
458
459Additionally, while Pigweed is mostly C++, it a practical reality that at times
460projects using Pigweed will need to log from third-party libraries written in
461C. Thus, we also wanted to retain C compatibility.
462
463In summary, printf-style logging is better for Pigweed's target audience
464because it:
465
466- works with tokenization
467- is C compatibile
468- has smaller call sites
469
470See also :ref:`module-pw_log_tokenized` for details on leveraging Pigweed's
471tokenizer module for logging.
472
473See also :ref:`module-pw_tokenizer` for details on Pigweed's tokenizer,
474which is useful for more than just logging.
475
476Why does the facade use header redirection instead of C functions?
477==================================================================
478Without header redirection, it is not possible to do sophisticated macro
479transforms in the backend. For example, to apply tokenization to log strings,
480the backend must define the handling macros. Additionally, compile-time
481filtering by log level or flags is not possible without header redirection.
482While it may be possible to do the filtering in the facade, that would imply
483having the same filtering implementation for all log handling, which is a
484restriction we want to avoid.
485
486Why is the module name done as a preprocessor define rather than an argument?
487=============================================================================
488APIs are a balance between power and ease of use. In the practical cases we
489have seen over the years, most translation units only need to log to one
490module, like ``"BLE"``, ``"PWR"``, ``"BAT"`` and so on. Thus, adding the
491argument to each macro call seemed like too much. On the other hand, flags are
492something that are typically added on a per-log-statement basis, and is why the
493flags are added on a per-call basis (though hidden through the high-level
494macros).
495
496--------------
497pw_log in Java
498--------------
499``pw_log`` provides a thin Java logging class that uses Google's `Flogger
500<https://google.github.io/flogger/>`_ API. The purpose of this wrapper is to
501support logging on platforms that do not support Flogger. The main
502implementation in ``pw_log/java/main`` simply wraps a
503``com.google.common.flogger.FluentLogger``. An implementation that logs to
504Android's ``android.util.Log`` instead is provided in
505``pw_log/java/android_main``.
506
507----------------
508pw_log in Python
509----------------
510``pw_log`` provides utilities to decode ``LogEntries`` and the encapsulated
511``LogEntry`` proto messages.
512
513The ``Log`` class represents a decoded ``LogEntry`` in a human-readable and
514consumable fashion.
515
516The ``LogStreamDecoder`` offers APIs to decode ``LogEntries`` and ``LogEntry``
517while tracking and logging log drops. It requires a ``decoded_log_handler`` to
518pass decoded logs to. This class can also be customized to use an optional token
519database if the message, module and thread names are tokenized; a custom
520timestamp parser; and optional message parser for any extra message parsing.
521``pw_log`` includes examples for customizing the ``LogStreamDecoder``:
522``timestamp_parser_ns_since_boot`` parses the timestamp number from nanoseconds
523since boot to an HH:MM::SS string, ``log_decoded_log`` emits a decoded ``Log``
524to the provided logger in a format known to ``pw console``, and
525``pw_status_code_to_name`` searches the decoded log message for a matching
526pattern encapsulating the status code number and replaces it with the status
527name.
528
529Python API
530==========
531
532pw_log.log_decoder
533------------------
534.. automodule:: pw_log.log_decoder
535    :members: Log, LogStreamDecoder
536    :undoc-members:
537    :show-inheritance:
538
539.. toctree::
540   :hidden:
541   :maxdepth: 1
542
543   protobuf
544   tokenized_args
545   Backends <backends>
546