1.. _module-pw_log: 2 3====== 4pw_log 5====== 6Pigweed's logging module provides facilities for applications to log 7information about the execution of their application. The module is split into 8two components: 9 101. The facade (this module) which is only a macro interface layer 112. The backend, provided elsewhere, that implements the low level logging 12 13``pw_log`` also defines a logging protobuf, helper utilities, and an RPC 14service for efficiently storing and transmitting log messages. See 15:ref:`module-pw_log-protobuf` for details. 16 17.. toctree:: 18 :hidden: 19 20 protobuf 21 22-------------- 23Usage examples 24-------------- 25Here is a typical usage example, showing setting the module name, and using the 26long-form names. 27 28.. code-block:: cpp 29 30 #define PW_LOG_MODULE_NAME "BLE" 31 32 #include "pw_log/log.h" 33 34 int main() { 35 PW_LOG_INFO("Booting..."); 36 PW_LOG_DEBUG("CPU temp: %.2f", cpu_temperature); 37 if (BootFailed()) { 38 PW_LOG_CRITICAL("Had trouble booting due to error %d", GetErrorCode()); 39 ReportErrorsAndHalt(); 40 } 41 PW_LOG_INFO("Successfully booted"); 42 } 43 44In ``.cc`` files, it is possible to dispense with the ``PW_`` part of the log 45names and go for shorter log macros. Include ``pw_log/short.h`` or 46``pw_log/shorter.h`` for shorter versions of the macros. 47 48.. code-block:: cpp 49 50 #define PW_LOG_MODULE_NAME "BLE" 51 52 #include "pw_log/shorter.h" 53 54 int main() { 55 INF("Booting..."); 56 DBG("CPU temp: %.2f", cpu_temperature); 57 if (BootFailed()) { 58 CRT("Had trouble booting due to error %d", GetErrorCode()); 59 ReportErrorsAndHalt(); 60 } 61 INF("Successfully booted"); 62 } 63 64Layer diagram example: ``stm32f429i-disc1`` 65=========================================== 66Below is an example diagram showing how the modules connect together for the 67``stm32f429i-disc1`` target, where the ``pw_log`` backend is ``pw_log_basic``. 68``pw_log_basic`` uses the ``pw_sys_io`` module to log in plaintext, which in 69turn outputs to the STM32F429 bare metal backend for ``pw_sys_io``, which is 70``pw_sys_io_baremetal_stm32f429i``. 71 72.. image:: example_layer_diagram.svg 73 74Logging macros 75============== 76These are the primary macros for logging information about the functioning of a 77system, intended to be used directly. 78 79.. cpp:function:: PW_LOG(level, flags, fmt, ...) 80 81 This is the primary mechanism for logging. 82 83 *level* - An integer level as defined by ``pw_log/levels.h``. 84 85 *flags* - Arbitrary flags the backend can leverage. The semantics of these 86 flags are not defined in the facade, but are instead meant as a general 87 mechanism for communication bits of information to the logging backend. 88 ``pw_log`` reserves 2 flag bits by default, but log backends may provide for 89 more or fewer flag bits. 90 91 Here are some ideas for what a backend might use flags for: 92 93 - Example: ``HAS_PII`` - A log has personally-identifying data 94 - Example: ``HAS_DII`` - A log has device-identifying data 95 - Example: ``RELIABLE_DELIVERY`` - Ask the backend to ensure the log is 96 delivered; this may entail blocking other logs. 97 - Example: ``BEST_EFFORT`` - Don't deliver this log if it would mean blocking 98 or dropping important-flagged logs 99 100 *fmt* - The message to log, which may contain format specifiers like ``%d`` 101 or ``%0.2f``. 102 103 Example: 104 105 .. code-block:: cpp 106 107 PW_LOG(PW_LOG_FLAGS, PW_LOG_LEVEL_INFO, "Temp is %d degrees", temp); 108 PW_LOG(UNRELIABLE_DELIVERY, PW_LOG_LEVEL_ERROR, "It didn't work!"); 109 110 .. note:: 111 112 ``PW_LOG()`` should not be used frequently; typically only when adding 113 flags to a particular message to mark PII or to indicate delivery 114 guarantees. For most cases, prefer to use the direct ``PW_LOG_INFO`` or 115 ``PW_LOG_DEBUG`` style macros, which are often implemented more efficiently 116 in the backend. 117 118 119.. cpp:function:: PW_LOG_DEBUG(fmt, ...) 120.. cpp:function:: PW_LOG_INFO(fmt, ...) 121.. cpp:function:: PW_LOG_WARN(fmt, ...) 122.. cpp:function:: PW_LOG_ERROR(fmt, ...) 123.. cpp:function:: PW_LOG_CRITICAL(fmt, ...) 124 125 Shorthand for `PW_LOG(PW_LOG_FLAGS, <level>, fmt, ...)`. 126 127-------------------- 128Module configuration 129-------------------- 130This module has configuration options that globally affect the behavior of 131pw_log via compile-time configuration of this module, see the 132:ref:`module documentation <module-structure-compile-time-configuration>` for 133more details. 134 135.. c:macro:: PW_LOG_LEVEL_DEFAULT 136 137 Controls the default value of ``PW_LOG_LEVEL``. Setting 138 ``PW_LOG_LEVEL_DEFAULT`` will change the behavior of all source files that 139 have not explicitly set ``PW_LOG_LEVEL``. Defaults to ``PW_LOG_LEVEL_DEBUG``. 140 141.. c:macro:: PW_LOG_FLAGS_DEFAULT 142 143 Controls the default value of ``PW_LOG_FLAGS``. Setting 144 ``PW_LOG_FLAGS_DEFAULT`` will change the behavior of all source files that 145 have not explicitly set ``PW_LOG_FLAGS``. Defaults to ``0``. 146 147.. c:macro:: PW_LOG_ENABLE_IF_DEFAULT 148 149 Controls the default value of ``PW_LOG_ENABLE_IF``. Setting 150 ``PW_LOG_ENABLE_IF_DEFAULT`` will change the behavior of all source files that 151 have not explicitly set ``PW_LOG_ENABLE_IF``. Defaults to 152 ``((level) >= PW_LOG_LEVEL)``. 153 154 155Per-source file configuration 156============================= 157This module defines macros that can be overridden to independently control the 158behavior of ``pw_log`` statements for each C or C++ source file. To override 159these macros, add ``#define`` statements for them before including headers. 160 161The option macro definitions must be visibile to ``pw_log/log.h`` the first time 162it is included. To handle potential transitive includes, place these 163``#defines`` before all ``#include`` statements. This should only be done in 164source files, not headers. For example: 165 166 .. code-block:: cpp 167 168 // Set the pw_log option macros here, before ALL of the #includes. 169 #define PW_LOG_MODULE_NAME "Calibration" 170 #define PW_LOG_LEVEL PW_LOG_LEVEL_WARN 171 172 #include <array> 173 #include <random> 174 175 #include "devices/hal9000.h" 176 #include "pw_log/log.h" 177 #include "pw_rpc/server.h" 178 179 int MyFunction() { 180 PW_LOG_INFO("hello???"); 181 } 182 183.. c:macro:: PW_LOG_MODULE_NAME 184 185 A string literal module name to use in logs. Log backends may attach this 186 name to log messages or use it for runtime filtering. Defaults to ``""``. The 187 ``PW_LOG_MODULE_NAME_DEFINED`` macro is set to ``1`` or ``0`` to indicate 188 whether ``PW_LOG_MODULE_NAME`` was overridden. 189 190.. c:macro:: PW_LOG_FLAGS 191 192 Log flags to use for the ``PW_LOG_<level>`` macros. Different flags may be 193 applied when using the ``PW_LOG`` macro directly. 194 195 Log backends use flags to change how they handle individual log messages. 196 Potential uses include assigning logs priority or marking them as containing 197 personal information. Defaults to ``PW_LOG_FLAGS_DEFAULT``. 198 199.. c:macro:: PW_LOG_LEVEL 200 201 Filters logs by level. Source files that define ``PW_LOG_LEVEL`` will display 202 only logs at or above the chosen level. Log statements below this level will 203 be compiled out of optimized builds. Defaults to ``PW_LOG_LEVEL_DEFAULT``. 204 205 Example: 206 207 .. code-block:: cpp 208 209 #define PW_LOG_LEVEL PW_LOG_LEVEL_INFO 210 211 #include "pw_log/log.h" 212 213 void DoSomething() { 214 PW_LOG_DEBUG("This won't be logged at all"); 215 PW_LOG_INFO("This is INFO level, and will display"); 216 PW_LOG_WARN("This is above INFO level, and will display"); 217 } 218 219.. c:macro:: PW_LOG_ENABLE_IF(level, flags) 220 221 Filters logs by an arbitrary expression based on ``level`` and ``flags``. 222 Source files that define ``PW_LOG_ENABLE_IF(level, flags)`` will display if 223 the given expression evaluates true. Defaults to 224 ``PW_LOG_ENABLE_IF_DEFAULT``. 225 226 Example: 227 228 .. code-block:: cpp 229 230 // Pigweed's log facade will call this macro to decide to log or not. In 231 // this case, it will drop logs with the PII flag set if display of PII is 232 // not enabled for the application. 233 #define PW_LOG_ENABLE_IF(level, flags) \ 234 (level >= PW_LOG_LEVEL_INFO && \ 235 !((flags & MY_PRODUCT_PII_MASK) && MY_PRODUCT_LOG_PII_ENABLED) 236 237 #include "pw_log/log.h" 238 239 // This define might be supplied by the build system. 240 #define MY_PRODUCT_LOG_PII_ENABLED false 241 242 // This is the PII mask bit selected by the application. 243 #define MY_PRODUCT_PII_MASK (1 << 5) 244 245 void DoSomethingWithSensitiveInfo() { 246 PW_LOG_DEBUG("This won't be logged at all"); 247 PW_LOG_INFO("This is INFO level, and will display"); 248 249 // In this example, this will not be logged since logging with PII 250 // is disabled by the above macros. 251 PW_LOG(PW_LOG_LEVEL_INFO, 252 MY_PRODUCT_PII_MASK, 253 "Sensitive: %d", 254 sensitive_info); 255 } 256 257.. attention:: 258 259 At this time, only compile time filtering is supported. In the future, we 260 plan to add support for runtime filtering. 261 262------------------ 263Logging attributes 264------------------ 265The logging facade in Pigweed is designed to facilitate the capture of at least 266the following attributes: 267 268- *Level* - The log level; for example, INFO, DEBUG, ERROR, etc. Typically an 269 integer 270- *Flags* - Bitset for e.g. RELIABLE_DELIVERY, or HAS_PII, or BEST_EFFORT 271- *File* - The file where the log was triggered 272- *Line* - The line number in the file where the log line occured 273- *Function* - What function the log comes from. This is expensive in binary 274 size to use! 275- *Module* - The user-defined module name for the log statement; e.g. “BLE” or 276 “BAT” 277- *Message* - The message itself; with % format arguments 278- *Arguments* - The format arguments to message 279- *Thread* - For devices running with an RTOS, capturing the thread is very 280 useful 281- *Others* - Processor security level? Maybe Thread is a good proxy for this 282 283Each backend may decide to capture different attributes to balance the tradeoff 284between call site code size, call site run time, wire format size, logging 285complexity, and more. 286 287.. _module-pw_log-circular-deps: 288 289---------------------------------------------- 290Avoiding circular dependencies with ``PW_LOG`` 291---------------------------------------------- 292Because logs are so widely used, including in low-level libraries, it is 293common for the ``pw_log`` backend to cause circular dependencies. Because of 294this, log backends may avoid declaring explicit dependencies, instead relying 295on include paths to access header files. 296 297In GN, the ``pw_log`` backend's full implementation with true dependencies is 298made available through the ``$dir_pw_log:impl`` group. When ``pw_log_BACKEND`` 299is set, ``$dir_pw_log:impl`` must be listed in the ``pw_build_LINK_DEPS`` 300variable. See :ref:`module-pw_build-link-deps`. 301 302In the ``pw_log``, the backend's full implementation is placed in the 303``$pw_log_BACKEND.impl`` target. ``$dir_pw_log:impl`` depends on this 304backend target. The ``$pw_log_BACKEND.impl`` target may be an empty group if 305the backend target can use its dependencies directly without causing circular 306dependencies. 307 308In order to break dependency cycles, the ``pw_log_BACKEND`` target may need 309to directly provide dependencies through include paths only, rather than GN 310``public_deps``. In this case, GN header checking can be disabled with 311``check_includes = false``. 312 313---------------------- 314Google Logging Adapter 315---------------------- 316Pigweed provides a minimal C++ stream-style Google Log set of adapter 317macros around PW_LOG under ``pw_log/glog_adapter.h`` for compatibility with 318non-embedded code. While it is effective for porting server code to 319microcontrollers quickly, we do not advise embedded projects use that approach 320unless absolutely necessary. 321 322Configuration 323============== 324 325.. c:macro:: PW_LOG_CFG_GLOG_BUFFER_SIZE_BYTES 326 327 The size of the stack-allocated buffer used by the Google Logging (glog) 328 macros. This only affects the glog macros provided through pw_log/glog.h. 329 330 Pigweed strongly recommends sticking to printf-style logging instead 331 of C++ stream-style Google Log logging unless absolutely necessary. The glog 332 macros are only provided for compatibility with non-embedded code. See 333 :ref:`module-pw_log-design-discussion` for more details. 334 335 Undersizing this buffer will result in truncated log messages. 336 337----------------- 338Design discussion 339----------------- 340 341.. _module-pw_log-design-discussion: 342 343Why not use C++ style stream logging operators like Google Log? 344=============================================================== 345There are multiple reasons to avoid the C++ stream logging style in embedded, 346but the biggest reason is that C++ stream logging defeats log tokenization. By 347having the string literals broken up between ``<<`` operators, tokenization 348becomes impossible with current language features. 349 350Consider this example use of Google Log: 351 352.. code-block:: cpp 353 354 LOG(INFO) << "My temperature is " << temperature << ". State: " << state; 355 356This log statement has two string literals. It might seem like one could convert 357move to tokenization: 358 359.. code-block:: cpp 360 361 LOG(INFO) << TOKEN("My temperature is ") << temperature << TOKEN(". State: ") << state; 362 363However, this doesn't work. The key problem is that the tokenization system 364needs to allocate the string in a linker section that is excluded from the 365final binary, but is in the final ELF executable (and so can be extracted). 366Since there is no way to declare a string or array in a different section in 367the middle of an experession in C++, it is not possible to tokenize an 368expression like the above. 369 370In contrast, the ``printf``-style version is a single statement with a single 371string constant, which can be expanded by the preprocessor (as part of 372``pw_tokenizer``) into a constant array in a special section. 373 374.. code-block:: cpp 375 376 // Note: LOG_INFO can be tokenized behind the macro; transparent to users. 377 PW_LOG_INFO("My temperature is %d. State: %s", temperature, state); 378 379Additionally, while Pigweed is mostly C++, it a practical reality that at times 380projects using Pigweed will need to log from third-party libraries written in 381C. Thus, we also wanted to retain C compatibility. 382 383In summary, printf-style logging is better for Pigweed's target audience 384because it: 385 386- works with tokenization 387- is C compatibile 388- has smaller call sites 389 390See also :ref:`module-pw_log_tokenized` for details on leveraging Pigweed's 391tokenizer module for logging. 392 393See also :ref:`module-pw_tokenizer` for details on Pigweed's tokenizer, 394which is useful for more than just logging. 395 396Why does the facade use header redirection instead of C functions? 397================================================================== 398Without header redirection, it is not possible to do sophisticated macro 399transforms in the backend. For example, to apply tokenization to log strings, 400the backend must define the handling macros. Additionally, compile-time 401filtering by log level or flags is not possible without header redirection. 402While it may be possible to do the filtering in the facade, that would imply 403having the same filtering implementation for all log handling, which is a 404restriction we want to avoid. 405 406Why is the module name done as a preprocessor define rather than an argument? 407============================================================================= 408APIs are a balance between power and ease of use. In the practical cases we 409have seen over the years, most translation units only need to log to one 410module, like ``"BLE"``, ``"PWR"``, ``"BAT"`` and so on. Thus, adding the 411argument to each macro call seemed like too much. On the other hand, flags are 412something that are typically added on a per-log-statement basis, and is why the 413flags are added on a per-call basis (though hidden through the high-level 414macros). 415 416-------------- 417pw_log in Java 418-------------- 419``pw_log`` provides a thin Java logging class that uses Google's `Flogger 420<https://google.github.io/flogger/>`_ API. The purpose of this wrapper is to 421support logging on platforms that do not support Flogger. The main 422implementation in ``pw_log/java/main`` simply wraps a 423``com.google.common.flogger.FluentLogger``. An implementation that logs to 424Android's ``android.util.Log`` instead is provided in 425``pw_log/java/android_main``. 426