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