Update logging guide: more about the WARN level
[babeltrace.git] / doc / logging-guide.adoc
CommitLineData
beb0fb75
PP
1= Babeltrace logging guide
2Philippe Proulx
37 May 2017
4:toc:
5:toclevels: 5
6
7This guide explains to the Babeltrace developer how to insert logging
8statements in Babeltrace's CLI, library, and plugins.
9
10
11== Motive
12
13Logging is a great instrument for a developer to be able to collect
14information about a running software.
15
16Babeltrace is a complex software with many layers. When a Babeltrace
17graph fails to run, what caused the failure? It could be caused by any
18component, any notification iterator, and any deeply nested validation
19of a CTR IR object, for example. With the appropriate logging statements
20manually placed in the source code, we can find the cause of a bug
21faster.
22
23While <<level,care must be taken>> when placing _INFO_ to _FATAL_
24logging statements, you should deliberately instrument your Babeltrace
25module with _DEBUG_ and _VERBOSE_ logging statements to help future
26you and other developers understand what's happening at run-time.
27
28
29== API
30
31The Babeltrace logging API is internal: it is not exposed to the users
32of the library, only to their developers. The only thing that a library
33user can control is the current log level of the library with
34`bt_logging_set_global_level()` and the initial library's log level with
35the `BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable.
36
37This API is based on https://github.com/wonder-mice/zf_log[zf_log], a
38lightweight, yet featureful, MIT-licensed core logging library for C and
39$$C++$$. The zf_log source files were modified to have the `BT_` and
40`bt_` prefixes, and other small changes.
41
42The logging functions are implemented in the logging convenience
43library (`logging` directory).
44
45
46=== Headers
47
48The logging API headers are:
49
50`<babeltrace/logging.h>`::
51 Public header which a library user can use to control and read
52 libbabeltrace's current log level.
53
54`<babeltrace/logging-internal.h>`::
55 Internal, generic logging API which you can use in any Babeltrace
56 subproject. This is the translation of `zf_log.h`.
57
58`<babeltrace/lib-logging-internal.h>`::
59 Specific internal header to use within the library. This header
60 defines `BT_LOG_OUTPUT_LEVEL` to a custom, library-wide hidden
61 symbol which is the library's current log level before including
62 `<babeltrace/logging-internal.h>`.
63
64Do not include `<babeltrace/logging-internal.h>` or
65`<babeltrace/lib-logging-internal.h>` in a header which contains logging
66statements: this header could be included in source files which define a
67different <<tag,tag>>, for example.
68
69
b4b9064d 70
beb0fb75
PP
71=== Log levels ===
72
73The API offers the following log levels:
74
75* _VERBOSE_
76* _DEBUG_
77* _INFO_
78* _WARN_
79* _ERROR_
80* _FATAL_
81
82See <<level,how to decide which one to use>> below.
83
84There are two important log level variables:
85
86[[build-time-log-level]]Build-time, minimal log level::
87 The minimal log level, or build-time log level, is set at build
88 time and determines the minimal log level which can be executed.
89 This applies to all the subprojects and modules (CLI, library,
90 plugins, etc.).
91+
92All the logging statements with a level below this level are **not built
93at all**. All the logging statements with a level equal to or greater
94than this level _can_ be executed, depending on the run-time log level
95(see below).
96+
97You can set this level at configuration time with the
98`BABELTRACE_MINIMAL_LOG_LEVEL` environment variable, for example:
99+
100--
101----
102$ BABELTRACE_MINIMAL_LOG_LEVEL=WARN ./configure
103----
104--
105+
106The default build-time log level is `DEBUG`. For optimal performance,
107set it to `NONE`, which effectively disables all logging in all the
108Babeltrace subprojects.
109+
110The library's public API provides `bt_logging_get_minimal_level()` to
111get the configured minimal log level.
112
113[[run-time-log-level]]Run-time, dynamic log level::
114 The dynamic log level is set at run-time and determines the current,
115 active log level. All the logging statements with a level below this
116 level are not executed, but they evaluate the condition. All the
117 logging statements with a level equal to or greater than this level
118 are executed, provided that their level is also enabled at build
119 time (see above).
120+
121In `zf_log`, there is a concept of a global run-time log level which
122uses the `_bt_log_global_output_lvl` symbol. In practice, we never use
123this symbol, and always make sure that `BT_LOG_OUTPUT_LEVEL` is defined
124to a module-wise or subproject-wise hidden symbol before including
125`<babeltrace/logging-internal.h>`. In the library,
126`<babeltrace/lib-logging-internal.h>` does this job: just include
127this header which defines `BT_LOG_OUTPUT_LEVEL` to the appropriate
128symbol before it includes `<babeltrace/logging-internal.h>`. In plugins,
129for example, there is one log level per component class, which makes
130log filtering easier during execution.
131+
132In libbabeltrace, the user can set the current run-time log level with
133the `bt_logging_set_global_level()` function, for example:
134+
135--
136[source,c]
137----
138bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO);
139----
140--
141+
142The library's initial run-time log level is defined by the
143`BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable (`VERBOSE`, `DEBUG`,
144`INFO`, `WARN`, `ERROR`, `FATAL`, or `NONE`), or set to _NONE_ if this
145environment variable is undefined.
146+
147Other subprojects have their own way of setting their run-time log
148level. For example, the CLI uses the `BABELTRACE_CLI_LOG_LEVEL`
149environment variable, and the `text.pretty` sink component class
150initializes its log level thanks to the
151`BABELTRACE_PLUGIN_TEXT_PRETTY_SINK_LOG_LEVEL` environment variable
152(also _NONE_ by default).
153+
154Make sure that there is a documented way to initialize or modify the
155log level of your subproject or module, and that it's set to _NONE_
156by default.
157
158
159=== Logging statement macros
160
161The Babeltrace logging statement macros work just like `printf()` and
162contain their log level in their name:
163
164`BT_LOGV("format string", ...)`::
165 Standard verbose logging statement.
166
167`BT_LOGD("format string", ...)`::
168 Standard debug logging statement.
169
170`BT_LOGI("format string", ...)`::
171 Standard info logging statement.
172
173`BT_LOGW("format string", ...)`::
174 Standard warning logging statement.
175
176`BT_LOGE("format string", ...)`::
177 Standard error logging statement.
178
179`BT_LOGF("format string", ...)`::
180 Standard fatal logging statement.
181
182`BT_LOGV_MEM(data_ptr, data_size, "format string", ...)`::
183 Memory verbose logging statement.
184
185`BT_LOGD_MEM(data_ptr, data_size, "format string", ...)`::
186 Memory debug logging statement.
187
188`BT_LOGI_MEM(data_ptr, data_size, "format string", ...)`::
189 Memory info logging statement.
190
191`BT_LOGW_MEM(data_ptr, data_size, "format string", ...)`::
192 Memory warning logging statement.
193
194`BT_LOGE_MEM(data_ptr, data_size, "format string", ...)`::
195 Memory error logging statement.
196
197`BT_LOGF_MEM(data_ptr, data_size, "format string", ...)`::
198 Memory fatal logging statement.
199
200`BT_LOGV_STR("preformatted string")`::
201 Preformatted string verbose logging statement.
202
203`BT_LOGD_STR("preformatted string")`::
204 Preformatted string debug logging statement.
205
206`BT_LOGI_STR("preformatted string")`::
207 Preformatted string info logging statement.
208
209`BT_LOGW_STR("preformatted string")`::
210 Preformatted string warning logging statement.
211
212`BT_LOGE_STR("preformatted string")`::
213 Preformatted string error logging statement.
214
215`BT_LOGF_STR("preformatted string")`::
216 Preformatted string fatal logging statement.
217
218
219=== Conditional logging
220
221`BT_LOG_IF(cond, statement)`::
222 Execute `statement` only if `cond` is true.
223+
224Example:
225+
226--
227[source,c]
228----
229BT_LOG_IF(i < count / 2, BT_LOGD("Log this: i=%d", i));
230----
231--
232
233To check the <<build-time-log-level,build-time log level>>:
234
235[source,c]
236----
237#if BT_LOG_ENABLED_DEBUG
238...
239#endif
240----
241
242This tests if the _DEBUG_ level was enabled at build-time. This
243means that the current, dynamic log level _could_ be _DEBUG_, but it
244could also be higher. The rule of thumb is to use only logging
245statements at the same level in a `BT_LOG_ENABLED_*` conditional block.
246
247The available definitions for build-time conditions are:
248
249* `BT_LOG_ENABLED_VERBOSE`
250* `BT_LOG_ENABLED_DEBUG`
251* `BT_LOG_ENABLED_INFO`
252* `BT_LOG_ENABLED_WARN`
253* `BT_LOG_ENABLED_ERROR`
254* `BT_LOG_ENABLED_FATAL`
255
256To check the current, <<run-time-log-level,run-time log level>>:
257
258[source,c]
259----
260if (BT_LOG_ON_DEBUG) {
261 ...
262}
263----
264
265This tests if the _DEBUG_ log level is dynamically turned on
266(implies that it's also enabled at build-time). This check could have a
267noticeable impact on performance.
268
269The available definitions for run-time conditions are:
270
271* `BT_LOG_ON_VERBOSE`
272* `BT_LOG_ON_DEBUG`
273* `BT_LOG_ON_INFO`
274* `BT_LOG_ON_WARN`
275* `BT_LOG_ON_ERROR`
276* `BT_LOG_ON_FATAL`
277
278Those macros check the subproject-specific or module-specific log level
279symbol (defined by `BT_LOG_OUTPUT_LEVEL`).
280
281Never, ever write code which would be executed only to compute the
282fields of a logging statement outside a conditional logging scope,
283for example:
284
285[source,c]
286----
287int number = get_number_of_event_classes_with_property_x(...);
288BT_LOGD("Bla bla: number=%d", number);
289----
290
291Do this instead:
292
293[source,c]
294----
295if (BT_LOG_ON_DEBUG) {
296 int number = get_number_of_event_classes_with_property_x(...);
297 BT_LOGD("Bla bla: number=%d", number);
298}
299----
300
301Or even this:
302
303[source,c]
304----
305BT_LOGD("Bla bla: number=%d", get_number_of_event_classes_with_property_x(...));
306----
307
308
309[[tag]]
310== Tag
311
312Before including `<babeltrace/logging-internal.h>` (or
313`<babeltrace/lib-logging-internal.h>`) in your C source file, define
314`BT_LOG_TAG` to a name which represents your module. The tag name _must_
315be only uppercase letters/digits and the hyphen (`-`) character.
316
317For example:
318
319[source,c]
320----
321#define BT_LOG_TAG "EVENT-CLASS"
322#include <babeltrace/logging-internal.h>
323----
324
325A tag is conceptually similar to a logger name.
326
327
328=== Babeltrace tags
329
330==== CTF IR (library)
331
332[options="header,autowidth"]
333|===
334|Subsystem/object |Tag name
335
336|Attributes |`ATTRS`
337|Clock class and values |`CLOCK-CLASS`
338|Event class |`EVENT-CLASS`
339|Event |`EVENT`
340|Field path |`FIELD-PATH`
341|Field types |`FIELD-TYPES`
342|Fields |`FIELDS`
343|Packet |`PACKET`
344|Resolver |`RESOLVE`
345|Stream class |`STREAM-CLASS`
346|Stream |`STREAM`
347|Trace |`TRACE`
348|Validation |`VALIDATION`
349|Visitor |`VISITOR`
350|===
351
352
353==== CTF writer (library)
354
355[options="header,autowidth"]
356|===
357|Subsystem/object |Tag name
358
359|Clock |`CTF-WRITER-CLOCK`
360|CTF writer |`CTF-WRITER`
361|Serialization |`CTF-WRITER-SER`
362|===
363
364
365==== Graph (library)
366
367[options="header,autowidth"]
368|===
369|Subsystem/object |Tag name
370
371|Clock class priority map |`CC-PRIO-MAP`
372|Component (common) |`COMP`
373|Component class |`COMP-CLASS`
374|Connection |`CONNECTION`
375|Filter component |`COMP-FILTER`
376|Graph |`GRAPH`
377|Notification iterator |`NOTIF-ITER`
378|Port |`PORT`
379|Sink component |`COMP-SINK`
380|Source component |`COMP-SOURCE`
381|===
382
383==== Notifications (library)
384
385[options="header,autowidth"]
386|===
387|Subsystem/object |Tag name
388
389|Event notification |`NOTIF-EVENT`
390|Inacitivity notification |`NOTIF-INACTIVITY`
391|Notification |`NOTIF`
392|Packet notification |`NOTIF-PACKET`
393|Stream notification |`NOTIF-STREAM`
394|===
395
396
397==== Plugin (library)
398
399[options="header,autowidth"]
400|===
401|Subsystem/object |Tag name
402
403|Plugin |`PLUGIN`
404|Python plugin provider |`PLUGIN-PY`
405|Shared object plugin provider |`PLUGIN-SO`
406|===
407
408
409==== Values (library)
410
411[options="header,autowidth"]
412|===
413|Subsystem/object |Tag name
414
415|Values |`VALUES`
416|===
417
418
419==== Reference counting (library)
420
421[options="header,autowidth"]
422|===
423|Subsystem/object |Tag name
424
425|Reference counting |`REF`
426|===
427
428
429==== Common (library)
430
431[options="header,autowidth"]
432|===
433|Subsystem/object |Tag name
434
435|Common |`COMMON`
436|===
437
438
439==== CLI
440
441[options="header,autowidth"]
442|===
443|Subsystem/object |Tag name
444
445|CLI (main) |`CLI`
446|CLI configuration (common) |`CLI-CFG`
447|CLI configuration from CLI arguments |`CLI-CFG-ARGS`
448|CLI connection configuration from CLI arguments |`CLI-CFG-ARGS-CONNECT`
449|===
450
451
452==== libctfcopytrace (plugin convenience library)
453
454[options="header,autowidth"]
455|===
456|Subsystem/object |Tag name
457
458|Clock fields |`LIBCTFCOPYTRACE-CLOCK-FIELDS`
459|libctfcopytrace |`LIBCTFCOPYTRACE`
460|===
461
462
463==== `ctf` plugin
464
465[options="header,autowidth"]
466|===
467|Subsystem/object |Tag name
468
469|Plugin (main) |`PLUGIN-CTF`
470|Common: BTR |`PLUGIN-CTF-BTR`
471|Common: CTF IR generation metadata visitor |`PLUGIN-CTF-METADATA-IR-VISITOR`
472|Common: Metadata decoder |`PLUGIN-CTF-METADATA-DECODER`
473|Common: Metadata lexer |`PLUGIN-CTF-METADATA-LEXER`
474|Common: Metadata parser |`PLUGIN-CTF-METADATA-PARSER`
475|Common: Notification iterator |`PLUGIN-CTF-NOTIF-ITER`
476|`fs` sink (main) |`PLUGIN-CTF-FS-SINK`
477|`fs` sink: write |`PLUGIN-CTF-FS-SINK-WRITE`
478|`fs` source (main) |`PLUGIN-CTF-FS-SRC`
479|`fs` source: data stream |`PLUGIN-CTF-FS-SRC-DS`
480|`fs` source: file |`PLUGIN-CTF-FS-SRC-FILE`
481|`fs` source: metadata |`PLUGIN-CTF-FS-SRC-METADATA`
482|`lttng-live` source (main) |`PLUGIN-CTF-LTTNG-LIVE`
483|`lttng-live` source: data stream |`PLUGIN-CTF-LTTNG-LIVE-DS`
484|`lttng-live` source: metadata |`PLUGIN-CTF-LTTNG-LIVE-METADATA`
485|`lttng-live` source: viewer connection |`PLUGIN-CTF-LTTNG-LIVE-VIEWER`
486|===
487
488
489==== `lttng-utils` plugin
490
491[options="header,autowidth"]
492|===
493|Subsystem/object |Tag name
494
495|Plugin (main) |`PLUGIN-LTTNG-UTILS`
496|`debug-info` filter (main) |`PLUGIN-LTTNG-UTILS-DBG-INFO`
497|`debug-info` filter: binary info |`PLUGIN-LTTNG-UTILS-DBG-INFO-BIN-INFO`
498|`debug-info` filter: copy |`PLUGIN-LTTNG-UTILS-DBG-INFO-COPY`
499|`debug-info` filter: CRC32 |`PLUGIN-LTTNG-UTILS-DBG-INFO-CRC32`
500|`debug-info` filter: DWARF |`PLUGIN-LTTNG-UTILS-DBG-INFO-DWARF`
501|===
502
503
504==== `text` plugin
505
506[options="header,autowidth"]
507|===
508|Subsystem/object |Tag name
509
510|Plugin (main) |`PLUGIN-TEXT`
511|`pretty` filter (main) |`PLUGIN-TEXT-PRETTY`
512|`pretty` filter: print |`PLUGIN-TEXT-PRETTY-PRINT`
513|===
514
515
516==== `utils` plugin
517
518[options="header,autowidth"]
519|===
520|Subsystem/object |Tag name
521
522|Plugin (main) |`PLUGIN-UTILS`
523|`dummy` sink (main) |`PLUGIN-UTILS-DUMMY`
524|`muxer` filter (main) |`PLUGIN-UTILS-MUXER`
525|`trimmer` filter (main) |`PLUGIN-UTILS-TRIMMER`
526|`trimmer` filter: copy |`PLUGIN-UTILS-TRIMMER-COPY`
527|`trimmer` filter: iterator |`PLUGIN-UTILS-TRIMMER-ITER`
528|===
529
530
531[[level]]
b4b9064d 532== Choose a log level
beb0fb75
PP
533
534Choosing the appropriate level for your logging statement is very
535important.
536
537[options="header,autowidth",cols="default,default,asciidoc,default"]
538|===
539|Log level |Description |Use cases |Impact on performance
540
541|_FATAL_
542|The program, library, or plugin cannot continue to work in this
543condition: it must be terminated immediately.
544
b4b9064d
PP
545A _FATAL_-level logging statement should always be followed by
546`abort()` or `assert(false)`.
beb0fb75
PP
547|
548* Unexpected return values from system calls.
b4b9064d
PP
549* Logic error in internal code, for example an unknown value in a
550 `switch` statement which should only deal with .
beb0fb75
PP
551|Almost none: should be executed in production.
552
553|_ERROR_
554|An important error which is somewhat not fatal, that is, the program,
555library, or plugin can continue to work after this, but you judge that
556it should be reported to the user.
557
558Usually, the program cannot recover from such an error, but it can at
559least exit cleanly.
560|
561* Memory allocation errors.
562* Failed to perform an operation which should work considering the
563 implementation and the satisfied preconditions. For example, the
564 failure to create an empty object (no parameters): most probably
565 failed internally because of an allocation error.
566|Almost none: should be executed in production.
567
568|_WARN_
569|A logic error which still allows the execution to continue.
6c1b33a8
PP
570
571_WARN_-level logging statements are for any error or weird action that
572is directly or indirectly caused by the user. For example, not having
573enough memory is beyond user control.
beb0fb75 574|
6c1b33a8
PP
575* Not honoring a public function's preconditions (NULL parameters,
576 index out of bounds, etc.).
577* Adding an invalid event class to a stream class which is already
578 part of a trace.
beb0fb75
PP
579+
580For example, the caller tries to set a property of a frozen stream
581class.
582|Almost none: can be executed in production.
583
584|_INFO_
585|Any useful information which a non-developer user would understand.
586|
587* Successful loading of a plugin (with name, version, etc.).
588* Successful connection to or disconnection from another system.
589* An optional subsystem cannot be loaded.
590|Very little: can be executed in production if
591_INFO_ level information is desired.
592
593|_DEBUG_
594|Something that only Babeltrace developers would be interested into.
595|
596* High-level function entry/exit.
597* Object creation, destruction, copying, and freezing.
598* The result of some computation/validation.
599|Noticeable, but not as much as the _VERBOSE_ level: not executed in
600production.
601
602|_VERBOSE_
603|Low-level debugging context information. More appropriate for tracing
604in general.
605|
606* Reference count change.
607* Status of each iteration of a loop.
608* State machine's state change.
609* Data structure lookup/modification.
610* List of ELF sections found in a plugin.
611* Get or set an object's property.
612* Object comparison's intermediate results.
613|Huge: not executed in production.
614|===
615
b4b9064d
PP
616Make sure not to use a _WARN_ (or higher) log level when the condition
617leading to the logging statement can occur under normal circumstances.
618For example, a public function to get some object or property from an
619object by name or key that fails to find the value is not a warning: the
620user could legitimately use this function to check if the name/key
621exists in the object. In this case, use the _VERBOSE_ level (or do not
622log at all). If a numeric index is out of bounds, however, this
623qualifies for a _WARN_ level: such API functions have documented
624preconditions that the index must be in bounds (the user can always
625check with a count or size function).
626
beb0fb75 627
b4b9064d
PP
628[[message]]
629== Write an appropriate message
beb0fb75
PP
630
631Follow those rules when you write a logging statement's message:
632
633* Use an english sentence which starts with a capital letter. Start the
634 sentence with the appropriate verb tense depending on the context. For
635 example:
636+
637--
b4b9064d
PP
638** Beginning of operation (present continuous): _Creating ..._,
639 _Copying ..._, _Serializing ..._, _Freezing ..._, _Destroying ..._
640** End of operation (simple past): _Created ..._, _Successfully created ..._,
641 _Failed to create ..._, _Set ..._ (simple past of _to set_ which is
642 also _set_)
beb0fb75
PP
643--
644+
645For warning and error messages, you can start the message with _Cannot_
b4b9064d 646or _Failed to_ followed by a verb if it's appropriate.
beb0fb75
PP
647
648* Do not include the log level in the message itself. For example,
649 do not start the message with _Error while_ or _Warning:_.
650
651* Do not put newlines, tabs, or other special characters in the
652 message, unless you want to log a string with such characters. Note
653 that multiline log messages can be hard to parse, analyze, and filter,
b4b9064d
PP
654 however, so prefer multiple `BT_LOG*()` statements over a single
655 statement with newlines.
beb0fb75
PP
656
657* **If there are fields that your logging statement must record**,
658 follow the message with `:` followed by a space, then with the list of
659 fields (more about this below). If there are no fields, end the
660 sentence with a period.
661
662The statement's fields _must_ be a comma-separated list of
663+__name__=__value__+ tokens. Keep +__name__+ as simple as possible
b4b9064d
PP
664(lowercase if possible). If +__value__+ is a non-alphanumeric string,
665put it between double quotes. Always use the `PRId64` and `PRIu64`
666specifiers when logging `int64_t` and `uint64_t` values.
beb0fb75
PP
667
668Example:
669
670 "Cannot add event class to stream class: stream-class-addr=%p, "
b4b9064d 671 "stream-class-name=\"%s\", stream-class-id=%" PRId64 ", "
beb0fb75
PP
672 "event-class-addr=%p, event-class-name=\"%s\", event-class-id=%" PRId64
673
674By following a standard format for the statement fields, it is easier
675to use tools like https://www.elastic.co/products/logstash[Logstash]
676to split fields and analyze logs.
677
678Prefer the following suffixes in field names:
679
680[options="header,autowidth"]
681|===
682|Field name suffix |Description |Format specifier
683
684|`-addr` |Memory address |`%p`
685|`-fd` |File descriptor |`%d`
686|`-fp` |File stream (`FILE *`) |`%p`
687|`-id` |Object's ID |`%" PRId64 "` or `%" PRIu64 "`
688|`-name` |Object's name |`\"%s\"`
beb0fb75
PP
689|===
690
691
692== Output
693
694The log is printed to the standard error stream. A log line contains the
b4b9064d
PP
695time, the process and thread IDs, the <<level,log level>>, the <<tag,tag>>,
696the source's function name, file name and line number, and the
697<<message,message>>.
beb0fb75
PP
698
699Example:
700
701 05-11 00:58:03.691 23402 23402 D VALUES bt_value_destroy@values.c:498 Destroying value: addr=0xb9c3eb0
702
703You can easily filter the log with `grep` or `ag`. For example, to
704keep only the _WARN_-level log messages that the `VALUES` module
705generates:
706
707 $ export BABELTRACE_LOGGING_GLOBAL_LEVEL=VERBOSE
708 $ ./test_ctf_writer_complete 2>&1 | ag 'W VALUES'
This page took 0.048775 seconds and 4 git commands to generate.