blob: 120333dd1c84b520e1c9acc43c7f1ebf2f46558b [file] [log] [blame]
Keir Mierleaf5e3582019-12-30 13:11:05 -08001.. _chapter-pw-log:
2
3.. default-domain:: cpp
4
5.. highlight:: cpp
6
7------
8pw_log
9------
10Pigweed's logging module provides facilities for applications to log
11information about the execution of their application. The module is split into
12two components:
13
141. The facade (this module) which is only a macro interface layer
152. The backend, provided elsewhere, that implements the low level logging
16
Keir Mierlebc5a2692020-05-21 16:52:25 -070017Usage examples
18--------------
19Here is a typical usage example, showing setting the module name, and using the
20long-form names.
Keir Mierleaf5e3582019-12-30 13:11:05 -080021
22.. code-block:: cpp
23
24 #define PW_LOG_MODULE_NAME "BLE"
25 #include "pw_log/log.h"
26
27 int main() {
28 PW_LOG_INFO("Booting...");
Keir Mierlebc5a2692020-05-21 16:52:25 -070029 PW_LOG_DEBUG("CPU temp: %.2f", cpu_temperature);
Keir Mierleaf5e3582019-12-30 13:11:05 -080030 if (BootFailed()) {
31 PW_LOG_CRITICAL("Had trouble booting due to error %d", GetErrorCode());
32 ReportErrorsAndHalt();
33 }
34 PW_LOG_INFO("Successfully booted");
35 }
36
Keir Mierlebc5a2692020-05-21 16:52:25 -070037In ``.cc`` files, it is possible to dispense with the ``PW_`` part of the log
38names and go for shorter log macros.
39
40.. code-block:: cpp
41
42 #define PW_LOG_MODULE_NAME "BLE"
43 #define PW_LOG_USE_ULTRA_SHORT_NAMES 1
44 #include "pw_log/log.h"
45
46 int main() {
47 INF("Booting...");
48 DBG("CPU temp: %.2f", cpu_temperature);
49 if (BootFailed()) {
50 CRT("Had trouble booting due to error %d", GetErrorCode());
51 ReportErrorsAndHalt();
52 }
53 INF("Successfully booted");
54 }
55
56Layer diagram example: ``stm32f429i-disc1``
57-------------------------------------------
58Below is an example diagram showing how the modules connect together for the
59``stm32f429i-disc1`` target, where the ``pw_log`` backend is ``pw_log_basic``.
60``pw_log_basic`` uses the ``pw_sys_io`` module to log in plaintext, which in
61turn outputs to the STM32F429 bare metal backend for ``pw_sys_io``, which is
62``pw_sys_io_baremetal_stm32f429i``.
63
64.. blockdiag::
65
66 blockdiag {
67 default_fontsize = 14;
68 orientation = portrait;
69
70 group {
71 color = "#AAAAAA";
72 label = "Microcontroller"
73
74 app [label = "App code"];
75 facade [label = "pw_log"];
76 backend [label = "pw_log_basic"];
77 sys_io [label = "pw_sys_io"];
78 sys_io_bm [label = "pw_sys_io_\nstm32f429"];
79 uart [label = "UART pins"];
80 }
81 ftdi [label = "FTDI cable"];
82 computer [label = "Minicom"];
83
84 app -> facade -> backend -> sys_io -> sys_io_bm -> uart -> ftdi -> computer;
85
86 //app -> facade [folded];
87 //backend -> sys_io [folded];
88 //uart -> ftdi [folded];
89 }
Keir Mierleaf5e3582019-12-30 13:11:05 -080090
91Logging macros
92--------------
93
Keir Mierlebc5a2692020-05-21 16:52:25 -070094These are the primary macros for logging information about the functioning of a
95system, intended to be used directly.
96
Keir Mierleaf5e3582019-12-30 13:11:05 -080097.. cpp:function:: PW_LOG(level, flags, fmt, ...)
98
99 This is the primary mechanism for logging.
100
101 *level* - An integer level as defined by ``pw_log/levels.h``.
102
103 *flags* - Arbitrary flags the backend can leverage. The semantics of these
104 flags are not defined in the facade, but are instead meant as a general
105 mechanism for communication bits of information to the logging backend.
106
107 Here are some ideas for what a backend might use flags for:
108
109 - Example: ``HAS_PII`` - A log has personally-identifying data
110 - Example: ``HAS_DII`` - A log has device-identifying data
111 - Example: ``RELIABLE_DELIVERY`` - Ask the backend to ensure the log is
112 delivered; this may entail blocking other logs.
113 - Example: ``BEST_EFFORT`` - Don't deliver this log if it would mean blocking
114 or dropping important-flagged logs
115
116 *fmt* - The message to log, which may contain format specifiers like ``%d``
117 or ``%0.2f``.
118
119 Example:
120
121 .. code-block:: cpp
122
Keir Mierlec4725d72020-06-04 12:17:38 -0700123 PW_LOG(PW_LOG_DEFAULT_FLAGS, PW_LOG_LEVEL_INFO, "Temp is %d degrees", temp);
Wyatt Heplere010bff2020-04-15 11:02:37 -0700124 PW_LOG(UNRELIABLE_DELIVERY, PW_LOG_LEVEL_ERROR, "It didn't work!");
Keir Mierleaf5e3582019-12-30 13:11:05 -0800125
126 .. note::
127
128 ``PW_LOG()`` should not be used frequently; typically only when adding
129 flags to a particular message to mark PII or to indicate delivery
Keir Mierlebc5a2692020-05-21 16:52:25 -0700130 guarantees. For most cases, prefer to use the direct ``PW_LOG_INFO`` or
131 ``PW_LOG_DEBUG`` style macros, which are often implemented more efficiently
132 in the backend.
Keir Mierleaf5e3582019-12-30 13:11:05 -0800133
134
135.. cpp:function:: PW_LOG_DEBUG(fmt, ...)
136.. cpp:function:: PW_LOG_INFO(fmt, ...)
137.. cpp:function:: PW_LOG_WARN(fmt, ...)
138.. cpp:function:: PW_LOG_ERROR(fmt, ...)
139.. cpp:function:: PW_LOG_CRITICAL(fmt, ...)
140
Keir Mierlec4725d72020-06-04 12:17:38 -0700141 Shorthand for `PW_LOG(PW_LOG_DEFAULT_FLAGS, <level>, fmt, ...)`.
142
143Filtering logs
144--------------
145
146``pw_log`` supports compile time filtering of logs through two mechanisms.
147
1481. Filter by level. Source files that define ``PW_LOG_LEVEL`` will display all
149 logs at or above the chosen level.
150
151 Example:
152
153 .. code-block:: cpp
154
155 #include "pw_log/log.h"
156
157 #define PW_LOG_LEVEL PW_LOG_LEVEL_INFO
158
159 void DoSomething() {
160 PW_LOG_DEBUG("This won't be logged at all");
161 PW_LOG_INFO("This is INFO level, and will display");
162 PW_LOG_WARN("This is above INFO level, and will display");
163 }
164
1652. Filter by arbitrary expression based on ``level`` and ``flags``. Source
166 files that define ``PW_LOG_ENABLE_IF(level, flags)`` will display if the
167 given expression returns true.
168
169 Example:
170
171 .. code-block:: cpp
172
173 #include "pw_log/log.h"
174
175 // This define might be supplied by the build system.
176 #define MY_PRODUCT_LOG_PII_ENABLED false
177
178 // This is the PII mask bit selected by the application.
179 #define MY_PRODUCT_PII_MASK (1 << 5)
180
181 // Pigweed's log facade will call this macro to decide to log or not. In
182 // this case, it will drop logs with the PII flag set if display of PII is
183 // not enabled for the application.
184 #define PW_LOG_ENABLE_IF(level, flags) \
185 (level >= PW_LOG_INFO && \
186 !((flags & MY_PRODUCT_PII_MASK) && MY_PRODUCT_LOG_PII_ENABLED)
187
188 void DoSomethingWithSensitiveInfo() {
189 PW_LOG_DEBUG("This won't be logged at all");
190 PW_LOG_INFO("This is INFO level, and will display");
191
192 // In this example, this will not be logged since logging with PII
193 // is disabled by the above macros.
194 PW_LOG(PW_LOG_LEVEL_INFO,
195 MY_PRODUCT_PII_MASK,
196 "Sensitive: %d",
197 sensitive_info);
198 }
199
200.. attention::
201
202 At this time, only compile time filtering is supported. In the future, we
203 plan to add support for runtime filtering.
Keir Mierleaf5e3582019-12-30 13:11:05 -0800204
205Logging attributes
206------------------
207
208The logging facade in Pigweed is designed to facilitate the capture of at least
209the following attributes:
210
211- *Level* - The log level; for example, INFO, DEBUG, ERROR, etc. Typically an
212 integer
213- *Flags* - Bitset for e.g. RELIABLE_DELIVERY, or HAS_PII, or BEST_EFFORT
214- *File* - The file where the log was triggered
215- *Line* - The line number in the file where the log line occured
216- *Function* - What function the log comes from. This is expensive in binary
217 size to use!
218- *Module* - The user-defined module name for the log statement; e.g. “BLE” or
219 “BAT”
220- *Message* - The message itself; with % format arguments
221- *Arguments* - The format arguments to message
222- *Thread* - For devices running with an RTOS, capturing the thread is very
223 useful
224- *Others* - Processor security level? Maybe Thread is a good proxy for this
225
226Each backend may decide to capture different attributes to balance the tradeoff
227between call site code size, call site run time, wire format size, logging
228complexity, and more.
Keir Mierlebc5a2692020-05-21 16:52:25 -0700229
230Design discussion
231-----------------
232
233Why not use C++ style stream logging operators like Google Log?
234~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235There are multiple reasons to avoid the C++ stream logging style in embedded,
236but the biggest reason is that C++ stream logging defeats log tokenization. By
237having the string literals broken up between ``<<`` operators, tokenization
238becomes impossible with current language features.
239
240Consider this example use of Google Log:
241
242.. code-block:: cpp
243
244 LOG(INFO) << "My temperature is " << temperature << ". State: " << state;
245
246This log statement has two string literals. It might seem like one could convert
247move to tokenization:
248
249.. code-block:: cpp
250
251 LOG(INFO) << TOKEN("My temperature is ") << temperature << TOKEN(". State: ") << state;
252
253However, this doesn't work. The key problem is that the tokenization system
254needs to allocate the string in a linker section that is excluded from the
255final binary, but is in the final ELF executable (and so can be extracted).
256Since there is no way to declare a string or array in a different section in
257the middle of an experession in C++, it is not possible to tokenize an
258expression like the above.
259
260In contrast, the ``printf``-style version is a single statement with a single
261string constant, which can be expanded by the preprocessor (as part of
262``pw_tokenizer``) into a constant array in a special section.
263
264.. code-block:: cpp
265
266 // Note: LOG_INFO can be tokenized behind the macro; transparent to users.
267 PW_LOG_INFO("My temperature is %d. State: %s", temperature, state);
268
269Additionally, while Pigweed is mostly C++, it a practical reality that at times
270projects using Pigweed will need to log from third-party libraries written in
271C. Thus, we also wanted to retain C compatibility.
272
273In summary, printf-style logging is better for Pigweed's target audience
274because it:
275
276- works with tokenization
277- is C compatibile
278- has smaller call sites
279
280The Pigweed authors additionally maintain a C++ stream-style embedded logging
281library for compatibility with non-embedded code. While it is effective for
282porting server code to microcontrollers quickly, we do not advise embedded
283projects use that approach unless absolutely necessary.
284
285- See also :ref:`chapter-pw-log-tokenized` for details on leveraging Pigweed's
286 tokenizer module for logging.
287- See also :ref:`chapter-pw-tokenizer` for details on Pigweed's tokenizer,
288 which is useful for more than just logging.
289
290Why does the facade use header redirection instead of C functions?
291~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
292Without header redirection, it is not possible to do sophisticated macro
293transforms in the backkend. For example, to apply tokenization to log strings,
294the backend must define the handling macros. Additionally, compile-time
295filtering by log level or flags is not possible without header redirection.
296While it may be possible to do the filtering in the facade, that would imply
297having the same filtering implementation for all log handling, which is a
298restriction we want to avoid.
299
300Why is the module name done as a preprocessor define rather than an argument?
301~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
302APIs are a balance between power and ease of use. In the practical cases we
303have seen over the years, most translation units only need to log to one
304module, like ``"BLE"``, ``"PWR"``, ``"BAT"`` and so on. Thus, adding the
305argument to each macro call seemed like too much. On the other hand, flags are
306something that are typically added on a per-log-statement basis, and is why the
307flags are added on a per-call basis (though hidden through the high-level
308macros).