Add logging API (internal to log, public to set the current log level)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 8 May 2017 17:04:03 +0000 (13:04 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:42 +0000 (12:57 -0400)
See doc/logging-guide.adoc which explains the whole logging API, how to
set and initialize the log levels, how to write logging statements, etc.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
14 files changed:
Makefile.am
configure.ac
doc/Makefile.am
doc/api/Doxyfile.in
doc/logging-guide.adoc [new file with mode: 0644]
include/Makefile.am
include/babeltrace/lib-logging-internal.h [new file with mode: 0644]
include/babeltrace/logging-internal.h [new file with mode: 0644]
include/babeltrace/logging.h [new file with mode: 0644]
lib/Makefile.am
lib/logging.c [new file with mode: 0644]
logging/LICENSE [new file with mode: 0644]
logging/Makefile.am [new file with mode: 0644]
logging/log.c [new file with mode: 0644]

index 6e90e481cabf841283d3064287020ed67c4630cd..63613f885912907a775b99f1d6069dbcfc6ab206 100644 (file)
@@ -5,7 +5,8 @@ ACLOCAL_AMFLAGS = -I m4
 SUBDIRS =                      \
        include                 \
        common                  \
-       compat
+       compat                  \
+       logging
 
 if WITH_PYTHON_PLUGINS
 SUBDIRS += python-plugin-provider
index b12fe3d2944755396fb86644f0a8d4a38728a1ec..b31f7acc16e628972d735dda7f517f3e9d3b80b3 100644 (file)
@@ -400,6 +400,14 @@ AM_CONDITIONAL([BUILT_IN_PYTHON_PLUGIN_SUPPORT], [test "x$built_in_python_plugin
 
 PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.0.0])
 
+# Logging
+AC_ARG_VAR([BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level for Babeltrace program, library, and plugins (VERBOSE, DEBUG, INFO, WARN, ERROR (default), FATAL, or NONE)])
+AS_IF([test "x$BABELTRACE_MINIMAL_LOG_LEVEL" = "x"], [BABELTRACE_MINIMAL_LOG_LEVEL=DEBUG])
+AS_IF([test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "VERBOSE" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "DEBUG" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "INFO" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "WARN" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "ERROR" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "FATAL" && test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "NONE"], [
+       AC_MSG_ERROR([Invalid BABELTRACE_MINIMAL_LOG_LEVEL value ($BABELTRACE_MINIMAL_LOG_LEVEL): use VERBOSE, DEBUG, INFO, WARN, ERROR, FATAL, or NONE.])
+])
+AC_DEFINE_UNQUOTED([BT_LOG_LEVEL], [BT_LOG_$BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level])
+
 LIBS="$LIBS $GMODULE_LIBS"
 PACKAGE_CFLAGS="$GMODULE_CFLAGS -Wall -Wformat"
 AC_SUBST(PACKAGE_CFLAGS)
@@ -473,6 +481,7 @@ AC_CONFIG_FILES([
        lib/ctf-ir/Makefile
        lib/ctf-writer/Makefile
        include/Makefile
+       logging/Makefile
        bindings/Makefile
        bindings/python/Makefile
        bindings/python/babeltrace/Makefile
@@ -566,6 +575,9 @@ done
 ]
 PPRINT_PROP_STRING([Target architecture], $target_arch)
 
+# Minimal log level
+PPRINT_PROP_STRING([Minimal log level], $BABELTRACE_MINIMAL_LOG_LEVEL)
+
 # API doc
 test "x$enable_api_doc" = "xyes" && value=1 || value=0
 PPRINT_PROP_BOOL([HTML API documentation], $value)
index 6ee9c2335ed9f76dae1ea4c68a20b67956238552..274f9b8b20b508436e0d71be0b3f33497b78de4a 100644 (file)
@@ -6,6 +6,11 @@ endif
 
 dist_man_MANS = babeltrace.1 babeltrace-log.1
 
-dist_doc_DATA = API.txt lttng-live.txt ref-counting.md
+dist_doc_DATA = API.txt lttng-live.txt ref-counting.md logging-guide.adoc
 
 EXTRA_DIST = development.txt
+
+logging-guide.html: logging-guide.adoc
+       asciidoc --verbose -a source-highlighter=pygments logging-guide.adoc
+
+CLEANFILES = logging-guide.html
index 7cff9c7987c8a1a09d7ed904dbec2b034fa87c30..adb2a564afc2257dee346e5817d79524793cae65 100644 (file)
@@ -146,6 +146,7 @@ INPUT                  = "@top_srcdir@/include/babeltrace/ctf-ir" \
                          "@top_srcdir@/include/babeltrace/plugin" \
                          "@top_srcdir@/include/babeltrace/ref.h" \
                          "@top_srcdir@/include/babeltrace/values.h" \
+                         "@top_srcdir@/include/babeltrace/logging.h" \
                          "@srcdir@/dox/main-page.dox" \
                          "@srcdir@/dox/includes-build.dox" \
                          "@srcdir@/dox/write-plugin.dox" \
diff --git a/doc/logging-guide.adoc b/doc/logging-guide.adoc
new file mode 100644 (file)
index 0000000..921188e
--- /dev/null
@@ -0,0 +1,685 @@
+= Babeltrace logging guide
+Philippe Proulx
+7 May 2017
+:toc:
+:toclevels: 5
+
+This guide explains to the Babeltrace developer how to insert logging
+statements in Babeltrace's CLI, library, and plugins.
+
+
+== Motive
+
+Logging is a great instrument for a developer to be able to collect
+information about a running software.
+
+Babeltrace is a complex software with many layers. When a Babeltrace
+graph fails to run, what caused the failure? It could be caused by any
+component, any notification iterator, and any deeply nested validation
+of a CTR IR object, for example. With the appropriate logging statements
+manually placed in the source code, we can find the cause of a bug
+faster.
+
+While <<level,care must be taken>> when placing _INFO_ to _FATAL_
+logging statements, you should deliberately instrument your Babeltrace
+module with _DEBUG_ and _VERBOSE_ logging statements to help future
+you and other developers understand what's happening at run-time.
+
+
+== API
+
+The Babeltrace logging API is internal: it is not exposed to the users
+of the library, only to their developers. The only thing that a library
+user can control is the current log level of the library with
+`bt_logging_set_global_level()` and the initial library's log level with
+the `BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable.
+
+This API is based on https://github.com/wonder-mice/zf_log[zf_log], a
+lightweight, yet featureful, MIT-licensed core logging library for C and
+$$C++$$. The zf_log source files were modified to have the `BT_` and
+`bt_` prefixes, and other small changes.
+
+The logging functions are implemented in the logging convenience
+library (`logging` directory).
+
+
+=== Headers
+
+The logging API headers are:
+
+`<babeltrace/logging.h>`::
+    Public header which a library user can use to control and read
+    libbabeltrace's current log level.
+
+`<babeltrace/logging-internal.h>`::
+    Internal, generic logging API which you can use in any Babeltrace
+    subproject. This is the translation of `zf_log.h`.
+
+`<babeltrace/lib-logging-internal.h>`::
+    Specific internal header to use within the library. This header
+    defines `BT_LOG_OUTPUT_LEVEL` to a custom, library-wide hidden
+    symbol which is the library's current log level before including
+    `<babeltrace/logging-internal.h>`.
+
+Do not include `<babeltrace/logging-internal.h>` or
+`<babeltrace/lib-logging-internal.h>` in a header which contains logging
+statements: this header could be included in source files which define a
+different <<tag,tag>>, for example.
+
+
+=== Log levels ===
+
+The API offers the following log levels:
+
+* _VERBOSE_
+* _DEBUG_
+* _INFO_
+* _WARN_
+* _ERROR_
+* _FATAL_
+
+See <<level,how to decide which one to use>> below.
+
+There are two important log level variables:
+
+[[build-time-log-level]]Build-time, minimal log level::
+    The minimal log level, or build-time log level, is set at build
+    time and determines the minimal log level which can be executed.
+    This applies to all the subprojects and modules (CLI, library,
+    plugins, etc.).
++
+All the logging statements with a level below this level are **not built
+at all**. All the logging statements with a level equal to or greater
+than this level _can_ be executed, depending on the run-time log level
+(see below).
++
+You can set this level at configuration time with the
+`BABELTRACE_MINIMAL_LOG_LEVEL` environment variable, for example:
++
+--
+----
+$ BABELTRACE_MINIMAL_LOG_LEVEL=WARN ./configure
+----
+--
++
+The default build-time log level is `DEBUG`. For optimal performance,
+set it to `NONE`, which effectively disables all logging in all the
+Babeltrace subprojects.
++
+The library's public API provides `bt_logging_get_minimal_level()` to
+get the configured minimal log level.
+
+[[run-time-log-level]]Run-time, dynamic log level::
+    The dynamic log level is set at run-time and determines the current,
+    active log level. All the logging statements with a level below this
+    level are not executed, but they evaluate the condition. All the
+    logging statements with a level equal to or greater than this level
+    are executed, provided that their level is also enabled at build
+    time (see above).
++
+In `zf_log`, there is a concept of a global run-time log level which
+uses the `_bt_log_global_output_lvl` symbol. In practice, we never use
+this symbol, and always make sure that `BT_LOG_OUTPUT_LEVEL` is defined
+to a module-wise or subproject-wise hidden symbol before including
+`<babeltrace/logging-internal.h>`. In the library,
+`<babeltrace/lib-logging-internal.h>` does this job: just include
+this header which defines `BT_LOG_OUTPUT_LEVEL` to the appropriate
+symbol before it includes `<babeltrace/logging-internal.h>`. In plugins,
+for example, there is one log level per component class, which makes
+log filtering easier during execution.
++
+In libbabeltrace, the user can set the current run-time log level with
+the `bt_logging_set_global_level()` function, for example:
++
+--
+[source,c]
+----
+bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO);
+----
+--
++
+The library's initial run-time log level is defined by the
+`BABELTRACE_LOGGING_GLOBAL_LEVEL` environment variable (`VERBOSE`, `DEBUG`,
+`INFO`, `WARN`, `ERROR`, `FATAL`, or `NONE`), or set to _NONE_ if this
+environment variable is undefined.
++
+Other subprojects have their own way of setting their run-time log
+level. For example, the CLI uses the `BABELTRACE_CLI_LOG_LEVEL`
+environment variable, and the `text.pretty` sink component class
+initializes its log level thanks to the
+`BABELTRACE_PLUGIN_TEXT_PRETTY_SINK_LOG_LEVEL` environment variable
+(also _NONE_ by default).
++
+Make sure that there is a documented way to initialize or modify the
+log level of your subproject or module, and that it's set to _NONE_
+by default.
+
+
+=== Logging statement macros
+
+The Babeltrace logging statement macros work just like `printf()` and
+contain their log level in their name:
+
+`BT_LOGV("format string", ...)`::
+    Standard verbose logging statement.
+
+`BT_LOGD("format string", ...)`::
+    Standard debug logging statement.
+
+`BT_LOGI("format string", ...)`::
+    Standard info logging statement.
+
+`BT_LOGW("format string", ...)`::
+    Standard warning logging statement.
+
+`BT_LOGE("format string", ...)`::
+    Standard error logging statement.
+
+`BT_LOGF("format string", ...)`::
+    Standard fatal logging statement.
+
+`BT_LOGV_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory verbose logging statement.
+
+`BT_LOGD_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory debug logging statement.
+
+`BT_LOGI_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory info logging statement.
+
+`BT_LOGW_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory warning logging statement.
+
+`BT_LOGE_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory error logging statement.
+
+`BT_LOGF_MEM(data_ptr, data_size, "format string", ...)`::
+    Memory fatal logging statement.
+
+`BT_LOGV_STR("preformatted string")`::
+    Preformatted string verbose logging statement.
+
+`BT_LOGD_STR("preformatted string")`::
+    Preformatted string debug logging statement.
+
+`BT_LOGI_STR("preformatted string")`::
+    Preformatted string info logging statement.
+
+`BT_LOGW_STR("preformatted string")`::
+    Preformatted string warning logging statement.
+
+`BT_LOGE_STR("preformatted string")`::
+    Preformatted string error logging statement.
+
+`BT_LOGF_STR("preformatted string")`::
+    Preformatted string fatal logging statement.
+
+
+=== Conditional logging
+
+`BT_LOG_IF(cond, statement)`::
+    Execute `statement` only if `cond` is true.
++
+Example:
++
+--
+[source,c]
+----
+BT_LOG_IF(i < count / 2, BT_LOGD("Log this: i=%d", i));
+----
+--
+
+To check the <<build-time-log-level,build-time log level>>:
+
+[source,c]
+----
+#if BT_LOG_ENABLED_DEBUG
+...
+#endif
+----
+
+This tests if the _DEBUG_ level was enabled at build-time. This
+means that the current, dynamic log level _could_ be _DEBUG_, but it
+could also be higher. The rule of thumb is to use only logging
+statements at the same level in a `BT_LOG_ENABLED_*` conditional block.
+
+The available definitions for build-time conditions are:
+
+* `BT_LOG_ENABLED_VERBOSE`
+* `BT_LOG_ENABLED_DEBUG`
+* `BT_LOG_ENABLED_INFO`
+* `BT_LOG_ENABLED_WARN`
+* `BT_LOG_ENABLED_ERROR`
+* `BT_LOG_ENABLED_FATAL`
+
+To check the current, <<run-time-log-level,run-time log level>>:
+
+[source,c]
+----
+if (BT_LOG_ON_DEBUG) {
+    ...
+}
+----
+
+This tests if the _DEBUG_ log level is dynamically turned on
+(implies that it's also enabled at build-time). This check could have a
+noticeable impact on performance.
+
+The available definitions for run-time conditions are:
+
+* `BT_LOG_ON_VERBOSE`
+* `BT_LOG_ON_DEBUG`
+* `BT_LOG_ON_INFO`
+* `BT_LOG_ON_WARN`
+* `BT_LOG_ON_ERROR`
+* `BT_LOG_ON_FATAL`
+
+Those macros check the subproject-specific or module-specific log level
+symbol (defined by `BT_LOG_OUTPUT_LEVEL`).
+
+Never, ever write code which would be executed only to compute the
+fields of a logging statement outside a conditional logging scope,
+for example:
+
+[source,c]
+----
+int number = get_number_of_event_classes_with_property_x(...);
+BT_LOGD("Bla bla: number=%d", number);
+----
+
+Do this instead:
+
+[source,c]
+----
+if (BT_LOG_ON_DEBUG) {
+    int number = get_number_of_event_classes_with_property_x(...);
+    BT_LOGD("Bla bla: number=%d", number);
+}
+----
+
+Or even this:
+
+[source,c]
+----
+BT_LOGD("Bla bla: number=%d", get_number_of_event_classes_with_property_x(...));
+----
+
+
+[[tag]]
+== Tag
+
+Before including `<babeltrace/logging-internal.h>` (or
+`<babeltrace/lib-logging-internal.h>`) in your C source file, define
+`BT_LOG_TAG` to a name which represents your module. The tag name _must_
+be only uppercase letters/digits and the hyphen (`-`) character.
+
+For example:
+
+[source,c]
+----
+#define BT_LOG_TAG "EVENT-CLASS"
+#include <babeltrace/logging-internal.h>
+----
+
+A tag is conceptually similar to a logger name.
+
+
+=== Babeltrace tags
+
+==== CTF IR (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Attributes |`ATTRS`
+|Clock class and values |`CLOCK-CLASS`
+|Event class |`EVENT-CLASS`
+|Event |`EVENT`
+|Field path |`FIELD-PATH`
+|Field types |`FIELD-TYPES`
+|Fields |`FIELDS`
+|Packet |`PACKET`
+|Resolver |`RESOLVE`
+|Stream class |`STREAM-CLASS`
+|Stream |`STREAM`
+|Trace |`TRACE`
+|Validation |`VALIDATION`
+|Visitor |`VISITOR`
+|===
+
+
+==== CTF writer (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Clock |`CTF-WRITER-CLOCK`
+|CTF writer |`CTF-WRITER`
+|Serialization |`CTF-WRITER-SER`
+|===
+
+
+==== Graph (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Clock class priority map |`CC-PRIO-MAP`
+|Component (common) |`COMP`
+|Component class |`COMP-CLASS`
+|Connection |`CONNECTION`
+|Filter component |`COMP-FILTER`
+|Graph |`GRAPH`
+|Notification iterator |`NOTIF-ITER`
+|Port |`PORT`
+|Sink component |`COMP-SINK`
+|Source component |`COMP-SOURCE`
+|===
+
+==== Notifications (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Event notification |`NOTIF-EVENT`
+|Inacitivity notification |`NOTIF-INACTIVITY`
+|Notification |`NOTIF`
+|Packet notification |`NOTIF-PACKET`
+|Stream notification |`NOTIF-STREAM`
+|===
+
+
+==== Plugin (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Plugin |`PLUGIN`
+|Python plugin provider |`PLUGIN-PY`
+|Shared object plugin provider |`PLUGIN-SO`
+|===
+
+
+==== Values (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Values |`VALUES`
+|===
+
+
+==== Reference counting (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Reference counting |`REF`
+|===
+
+
+==== Common (library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Common |`COMMON`
+|===
+
+
+==== CLI
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|CLI (main) |`CLI`
+|CLI configuration (common) |`CLI-CFG`
+|CLI configuration from CLI arguments |`CLI-CFG-ARGS`
+|CLI connection configuration from CLI arguments |`CLI-CFG-ARGS-CONNECT`
+|===
+
+
+==== libctfcopytrace (plugin convenience library)
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Clock fields |`LIBCTFCOPYTRACE-CLOCK-FIELDS`
+|libctfcopytrace |`LIBCTFCOPYTRACE`
+|===
+
+
+==== `ctf` plugin
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Plugin (main) |`PLUGIN-CTF`
+|Common: BTR |`PLUGIN-CTF-BTR`
+|Common: CTF IR generation metadata visitor |`PLUGIN-CTF-METADATA-IR-VISITOR`
+|Common: Metadata decoder |`PLUGIN-CTF-METADATA-DECODER`
+|Common: Metadata lexer |`PLUGIN-CTF-METADATA-LEXER`
+|Common: Metadata parser |`PLUGIN-CTF-METADATA-PARSER`
+|Common: Notification iterator |`PLUGIN-CTF-NOTIF-ITER`
+|`fs` sink (main) |`PLUGIN-CTF-FS-SINK`
+|`fs` sink: write |`PLUGIN-CTF-FS-SINK-WRITE`
+|`fs` source (main) |`PLUGIN-CTF-FS-SRC`
+|`fs` source: data stream |`PLUGIN-CTF-FS-SRC-DS`
+|`fs` source: file |`PLUGIN-CTF-FS-SRC-FILE`
+|`fs` source: metadata |`PLUGIN-CTF-FS-SRC-METADATA`
+|`lttng-live` source (main) |`PLUGIN-CTF-LTTNG-LIVE`
+|`lttng-live` source: data stream |`PLUGIN-CTF-LTTNG-LIVE-DS`
+|`lttng-live` source: metadata |`PLUGIN-CTF-LTTNG-LIVE-METADATA`
+|`lttng-live` source: viewer connection |`PLUGIN-CTF-LTTNG-LIVE-VIEWER`
+|===
+
+
+==== `lttng-utils` plugin
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Plugin (main) |`PLUGIN-LTTNG-UTILS`
+|`debug-info` filter (main) |`PLUGIN-LTTNG-UTILS-DBG-INFO`
+|`debug-info` filter: binary info |`PLUGIN-LTTNG-UTILS-DBG-INFO-BIN-INFO`
+|`debug-info` filter: copy |`PLUGIN-LTTNG-UTILS-DBG-INFO-COPY`
+|`debug-info` filter: CRC32 |`PLUGIN-LTTNG-UTILS-DBG-INFO-CRC32`
+|`debug-info` filter: DWARF |`PLUGIN-LTTNG-UTILS-DBG-INFO-DWARF`
+|===
+
+
+==== `text` plugin
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Plugin (main) |`PLUGIN-TEXT`
+|`pretty` filter (main) |`PLUGIN-TEXT-PRETTY`
+|`pretty` filter: print |`PLUGIN-TEXT-PRETTY-PRINT`
+|===
+
+
+==== `utils` plugin
+
+[options="header,autowidth"]
+|===
+|Subsystem/object |Tag name
+
+|Plugin (main) |`PLUGIN-UTILS`
+|`dummy` sink (main) |`PLUGIN-UTILS-DUMMY`
+|`muxer` filter (main) |`PLUGIN-UTILS-MUXER`
+|`trimmer` filter (main) |`PLUGIN-UTILS-TRIMMER`
+|`trimmer` filter: copy |`PLUGIN-UTILS-TRIMMER-COPY`
+|`trimmer` filter: iterator |`PLUGIN-UTILS-TRIMMER-ITER`
+|===
+
+
+[[level]]
+== Log level
+
+Choosing the appropriate level for your logging statement is very
+important.
+
+[options="header,autowidth",cols="default,default,asciidoc,default"]
+|===
+|Log level |Description |Use cases |Impact on performance
+
+|_FATAL_
+|The program, library, or plugin cannot continue to work in this
+condition: it must be terminated immediately.
+
+An assertion is usually an indicator of where you should put a
+_FATAL_-level logging statement. In Babeltrace, however, memory
+allocation errors are usually propagated back to the caller, so they
+belong to the _ERROR_ log level.
+|
+* Unexpected return values from system calls.
+|Almost none: should be executed in production.
+
+|_ERROR_
+|An important error which is somewhat not fatal, that is, the program,
+library, or plugin can continue to work after this, but you judge that
+it should be reported to the user.
+
+Usually, the program cannot recover from such an error, but it can at
+least exit cleanly.
+|
+* Memory allocation errors.
+* Failed to perform an operation which should work considering the
+  implementation and the satisfied preconditions. For example, the
+  failure to create an empty object (no parameters): most probably
+  failed internally because of an allocation error.
+|Almost none: should be executed in production.
+
+|_WARN_
+|A logic error which still allows the execution to continue.
+|
+* Unexpected values for function parameters.
+* Other user-induced errors (the user does not honor a function's
+  preconditions).
++
+For example, the caller tries to set a property of a frozen stream
+class.
+|Almost none: can be executed in production.
+
+|_INFO_
+|Any useful information which a non-developer user would understand.
+|
+* Successful loading of a plugin (with name, version, etc.).
+* Successful connection to or disconnection from another system.
+* An optional subsystem cannot be loaded.
+|Very little: can be executed in production if
+_INFO_ level information is desired.
+
+|_DEBUG_
+|Something that only Babeltrace developers would be interested into.
+|
+* High-level function entry/exit.
+* Object creation, destruction, copying, and freezing.
+* The result of some computation/validation.
+|Noticeable, but not as much as the _VERBOSE_ level: not executed in
+production.
+
+|_VERBOSE_
+|Low-level debugging context information. More appropriate for tracing
+in general.
+|
+* Reference count change.
+* Status of each iteration of a loop.
+* State machine's state change.
+* Data structure lookup/modification.
+* List of ELF sections found in a plugin.
+* Get or set an object's property.
+* Object comparison's intermediate results.
+|Huge: not executed in production.
+|===
+
+
+== Message
+
+Follow those rules when you write a logging statement's message:
+
+* Use an english sentence which starts with a capital letter. Start the
+  sentence with the appropriate verb tense depending on the context. For
+  example:
++
+--
+** _Creating ..._
+** _Created ..._ or _Successfully created ..._
+--
++
+For warning and error messages, you can start the message with _Cannot_
+followed by a verb if it's appropriate.
+
+* Do not include the log level in the message itself. For example,
+  do not start the message with _Error while_ or _Warning:_.
+
+* Do not put newlines, tabs, or other special characters in the
+  message, unless you want to log a string with such characters. Note
+  that multiline log messages can be hard to parse, analyze, and filter,
+  however.
+
+* **If there are fields that your logging statement must record**,
+  follow the message with `:` followed by a space, then with the list of
+  fields (more about this below). If there are no fields, end the
+  sentence with a period.
+
+The statement's fields _must_ be a comma-separated list of
++__name__=__value__+ tokens. Keep +__name__+ as simple as possible
+(lowercase if possible). If +__value__+ is a string, put it between
+double quotes.
+
+Example:
+
+    "Cannot add event class to stream class: stream-class-addr=%p, "
+    "stream-class-name=\"%s\", stream-class-id=%" PRId64
+    "event-class-addr=%p, event-class-name=\"%s\", event-class-id=%" PRId64
+
+By following a standard format for the statement fields, it is easier
+to use tools like https://www.elastic.co/products/logstash[Logstash]
+to split fields and analyze logs.
+
+Prefer the following suffixes in field names:
+
+[options="header,autowidth"]
+|===
+|Field name suffix |Description |Format specifier
+
+|`-addr` |Memory address |`%p`
+|`-fd` |File descriptor |`%d`
+|`-fp` |File stream (`FILE *`) |`%p`
+|`-id` |Object's ID |`%" PRId64 "` or `%" PRIu64 "`
+|`-name` |Object's name |`\"%s\"`
+|`-ref-cnt` |Object's reference count |`%u`
+|===
+
+
+== Output
+
+The log is printed to the standard error stream. A log line contains the
+time, the process and thread IDs, the log level, the tag, the source's
+function name, file name and line number, and the message.
+
+Example:
+
+    05-11 00:58:03.691 23402 23402 D VALUES bt_value_destroy@values.c:498 Destroying value: addr=0xb9c3eb0
+
+You can easily filter the log with `grep` or `ag`. For example, to
+keep only the _WARN_-level log messages that the `VALUES` module
+generates:
+
+    $ export BABELTRACE_LOGGING_GLOBAL_LEVEL=VERBOSE
+    $ ./test_ctf_writer_complete 2>&1 | ag 'W VALUES'
index ad1817ce4f8b3a50b91eb2fe254aa532a14b5272..581478203615d04c87187e0b23e8137886f2990a 100644 (file)
@@ -1,7 +1,8 @@
 babeltraceinclude_HEADERS = \
        babeltrace/babeltrace.h \
        babeltrace/values.h \
-       babeltrace/ref.h
+       babeltrace/ref.h \
+       babeltrace/logging.h
 
 babeltracectfinclude_HEADERS = \
        babeltrace/ctf/events.h
@@ -35,7 +36,6 @@ babeltraceplugininclude_HEADERS = \
 
 babeltracegraphinclude_HEADERS = \
        babeltrace/graph/clock-class-priority-map.h \
-       babeltrace/graph/clock-class-priority-map-internal.h \
        babeltrace/graph/component-class-filter.h \
        babeltrace/graph/component-class-sink.h \
        babeltrace/graph/component-class-source.h \
@@ -69,57 +69,60 @@ noinst_HEADERS = \
        babeltrace/babeltrace-internal.h \
        babeltrace/bitfield-internal.h \
        babeltrace/common-internal.h \
+       babeltrace/compat/dirent-internal.h \
+       babeltrace/compat/fcntl-internal.h \
+       babeltrace/compat/glib-internal.h \
+       babeltrace/compat/limits-internal.h \
+       babeltrace/compat/memstream-internal.h \
+       babeltrace/compat/mman-internal.h \
+       babeltrace/compat/send-internal.h \
+       babeltrace/compat/stdio-internal.h \
+       babeltrace/compat/stdlib-internal.h \
+       babeltrace/compat/string-internal.h \
+       babeltrace/compat/utc-internal.h \
+       babeltrace/compat/uuid-internal.h \
        babeltrace/compiler-internal.h \
-       babeltrace/prio-heap-internal.h \
-       babeltrace/ref-internal.h \
-       babeltrace/object-internal.h \
-       babeltrace/ctf-writer/writer-internal.h \
-       babeltrace/ctf-writer/serialize-internal.h \
        babeltrace/ctf-ir/attributes-internal.h \
-       babeltrace/ctf-ir/field-types-internal.h \
-       babeltrace/ctf-ir/fields-internal.h \
-       babeltrace/ctf-ir/event-internal.h \
+       babeltrace/ctf-ir/clock-class-internal.h \
        babeltrace/ctf-ir/event-class-internal.h \
+       babeltrace/ctf-ir/event-internal.h \
        babeltrace/ctf-ir/field-path-internal.h \
-       babeltrace/ctf-ir/clock-class-internal.h \
+       babeltrace/ctf-ir/field-types-internal.h \
+       babeltrace/ctf-ir/fields-internal.h \
+       babeltrace/ctf-ir/packet-internal.h \
        babeltrace/ctf-ir/resolve-internal.h \
        babeltrace/ctf-ir/stream-class-internal.h \
        babeltrace/ctf-ir/stream-internal.h \
-       babeltrace/ctf-ir/packet-internal.h \
        babeltrace/ctf-ir/trace-internal.h \
        babeltrace/ctf-ir/validation-internal.h \
        babeltrace/ctf-ir/visitor-internal.h \
        babeltrace/ctf-writer/clock-internal.h \
        babeltrace/ctf-writer/functor-internal.h \
-       babeltrace/compat/uuid-internal.h \
-       babeltrace/compat/memstream-internal.h \
-       babeltrace/compat/string-internal.h \
-       babeltrace/compat/utc-internal.h \
-       babeltrace/compat/limits-internal.h \
-       babeltrace/compat/glib-internal.h \
-       babeltrace/compat/send-internal.h \
-       babeltrace/compat/fcntl-internal.h \
-       babeltrace/compat/stdlib-internal.h \
-       babeltrace/compat/dirent-internal.h \
-       babeltrace/compat/stdio-internal.h \
-       babeltrace/compat/mman-internal.h \
+       babeltrace/ctf-writer/serialize-internal.h \
+       babeltrace/ctf-writer/writer-internal.h \
        babeltrace/endian-internal.h \
-       babeltrace/mmap-align-internal.h \
-       babeltrace/plugin/plugin-internal.h \
-       babeltrace/plugin/plugin-so-internal.h \
+       babeltrace/graph/clock-class-priority-map-internal.h \
        babeltrace/graph/component-class-internal.h \
-       babeltrace/graph/connection-internal.h \
-       babeltrace/graph/port-internal.h \
-       babeltrace/graph/component-internal.h \
-       babeltrace/graph/graph-internal.h \
        babeltrace/graph/component-filter-internal.h \
+       babeltrace/graph/component-internal.h \
        babeltrace/graph/component-sink-internal.h \
        babeltrace/graph/component-source-internal.h \
+       babeltrace/graph/connection-internal.h \
+       babeltrace/graph/graph-internal.h \
        babeltrace/graph/notification-eot-internal.h \
        babeltrace/graph/notification-event-internal.h \
+       babeltrace/graph/notification-heap-internal.h \
        babeltrace/graph/notification-inactivity-internal.h \
-       babeltrace/graph/notification-iterator-internal.h \
        babeltrace/graph/notification-internal.h \
+       babeltrace/graph/notification-iterator-internal.h \
        babeltrace/graph/notification-packet-internal.h \
        babeltrace/graph/notification-stream-internal.h \
-       babeltrace/graph/notification-heap-internal.h
+       babeltrace/graph/port-internal.h \
+       babeltrace/lib-logging-internal.h \
+       babeltrace/logging-internal.h \
+       babeltrace/mmap-align-internal.h \
+       babeltrace/object-internal.h \
+       babeltrace/plugin/plugin-internal.h \
+       babeltrace/plugin/plugin-so-internal.h \
+       babeltrace/prio-heap-internal.h \
+       babeltrace/ref-internal.h
diff --git a/include/babeltrace/lib-logging-internal.h b/include/babeltrace/lib-logging-internal.h
new file mode 100644 (file)
index 0000000..a692cd5
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef BABELTRACE_LIB_LOGGING_INTERNAL_H
+#define BABELTRACE_LIB_LOGGING_INTERNAL_H
+
+/*
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+
+#define BT_LOG_OUTPUT_LEVEL bt_lib_log_level
+
+#include <babeltrace/logging-internal.h>
+
+BT_HIDDEN
+int bt_lib_log_level;
+
+#endif /* BABELTRACE_LIB_LOGGING_INTERNAL_H */
diff --git a/include/babeltrace/logging-internal.h b/include/babeltrace/logging-internal.h
new file mode 100644 (file)
index 0000000..5cad0c3
--- /dev/null
@@ -0,0 +1,960 @@
+/*
+ * This is zf_log.h, modified with Babeltrace prefixes.
+ * See <https://github.com/wonder-mice/zf_log/>.
+ * See logging/LICENSE in the Babeltrace source tree.
+ */
+
+#pragma once
+
+#ifndef BABELTRACE_LOGGING_INTERNAL_H
+#define BABELTRACE_LOGGING_INTERNAL_H
+
+#include <babeltrace/logging.h>
+
+/* To detect incompatible changes you can define BT_LOG_VERSION_REQUIRED to be
+ * the current value of BT_LOG_VERSION before including this file (or via
+ * compiler command line):
+ *
+ *   #define BT_LOG_VERSION_REQUIRED 4
+ *   #include <babeltrace/log-internal.h>
+ *
+ * Compilation will fail when included file has different version.
+ */
+#define BT_LOG_VERSION 4
+#if defined(BT_LOG_VERSION_REQUIRED)
+       #if BT_LOG_VERSION_REQUIRED != BT_LOG_VERSION
+               #error different bt_log version required
+       #endif
+#endif
+
+/* Log level guideline:
+ * - BT_LOG_FATAL - happened something impossible and absolutely unexpected.
+ *   Process can't continue and must be terminated.
+ *   Example: division by zero, unexpected modifications from other thread.
+ * - BT_LOG_ERROR - happened something possible, but highly unexpected. The
+ *   process is able to recover and continue execution.
+ *   Example: out of memory (could also be FATAL if not handled properly).
+ * - BT_LOG_WARN - happened something that *usually* should not happen and
+ *   significantly changes application behavior for some period of time.
+ *   Example: configuration file not found, auth error.
+ * - BT_LOG_INFO - happened significant life cycle event or major state
+ *   transition.
+ *   Example: app started, user logged in.
+ * - BT_LOG_DEBUG - minimal set of events that could help to reconstruct the
+ *   execution path. Usually disabled in release builds.
+ * - BT_LOG_VERBOSE - all other events. Usually disabled in release builds.
+ *
+ * *Ideally*, log file of debugged, well tested, production ready application
+ * should be empty or very small. Choosing a right log level is as important as
+ * providing short and self descriptive log message.
+ */
+#define BT_LOG_VERBOSE BT_LOGGING_LEVEL_VERBOSE
+#define BT_LOG_DEBUG   BT_LOGGING_LEVEL_DEBUG
+#define BT_LOG_INFO    BT_LOGGING_LEVEL_INFO
+#define BT_LOG_WARN    BT_LOGGING_LEVEL_WARN
+#define BT_LOG_ERROR   BT_LOGGING_LEVEL_ERROR
+#define BT_LOG_FATAL   BT_LOGGING_LEVEL_FATAL
+#define BT_LOG_NONE    BT_LOGGING_LEVEL_NONE
+
+/* "Current" log level is a compile time check and has no runtime overhead. Log
+ * level that is below current log level it said to be "disabled". Otherwise,
+ * it's "enabled". Log messages that are disabled has no runtime overhead - they
+ * are converted to no-op by preprocessor and then eliminated by compiler.
+ * Current log level is configured per compilation module (.c/.cpp/.m file) by
+ * defining BT_LOG_DEF_LEVEL or BT_LOG_LEVEL. BT_LOG_LEVEL has higer priority
+ * and when defined overrides value provided by BT_LOG_DEF_LEVEL.
+ *
+ * Common practice is to define default current log level with BT_LOG_DEF_LEVEL
+ * in build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
+ * project or target:
+ *
+ *   CC_ARGS := -DBT_LOG_DEF_LEVEL=BT_LOG_INFO
+ *
+ * And when necessary to override it with BT_LOG_LEVEL in .c/.cpp/.m files
+ * before including bt_log.h:
+ *
+ *   #define BT_LOG_LEVEL BT_LOG_VERBOSE
+ *   #include <babeltrace/log-internal.h>
+ *
+ * If both BT_LOG_DEF_LEVEL and BT_LOG_LEVEL are undefined, then BT_LOG_INFO
+ * will be used for release builds (NDEBUG is defined) and BT_LOG_DEBUG
+ * otherwise (NDEBUG is not defined).
+ */
+#if defined(BT_LOG_LEVEL)
+       #define _BT_LOG_LEVEL BT_LOG_LEVEL
+#elif defined(BT_LOG_DEF_LEVEL)
+       #define _BT_LOG_LEVEL BT_LOG_DEF_LEVEL
+#else
+       #ifdef NDEBUG
+               #define _BT_LOG_LEVEL BT_LOG_INFO
+       #else
+               #define _BT_LOG_LEVEL BT_LOG_DEBUG
+       #endif
+#endif
+
+/* "Output" log level is a runtime check. When log level is below output log
+ * level it said to be "turned off" (or just "off" for short). Otherwise it's
+ * "turned on" (or just "on"). Log levels that were "disabled" (see
+ * BT_LOG_LEVEL and BT_LOG_DEF_LEVEL) can't be "turned on", but "enabled" log
+ * levels could be "turned off". Only messages with log level which is
+ * "turned on" will reach output facility. All other messages will be ignored
+ * (and their arguments will not be evaluated). Output log level is a global
+ * property and configured per process using bt_log_set_output_level() function
+ * which can be called at any time.
+ *
+ * Though in some cases it could be useful to configure output log level per
+ * compilation module or per library. There are two ways to achieve that:
+ * - Define BT_LOG_OUTPUT_LEVEL to expresion that evaluates to desired output
+ *   log level.
+ * - Copy bt_log.h and bt_log.c files into your library and build it with
+ *   BT_LOG_LIBRARY_PREFIX defined to library specific prefix. See
+ *   BT_LOG_LIBRARY_PREFIX for more details.
+ *
+ * When defined, BT_LOG_OUTPUT_LEVEL must evaluate to integral value that
+ * corresponds to desired output log level. Use it only when compilation module
+ * is required to have output log level which is different from global output
+ * log level set by bt_log_set_output_level() function. For other cases,
+ * consider defining BT_LOG_LEVEL or using bt_log_set_output_level() function.
+ *
+ * Example:
+ *
+ *   #define BT_LOG_OUTPUT_LEVEL g_module_log_level
+ *   #include <babeltrace/log-internal.h>
+ *   static int g_module_log_level = BT_LOG_INFO;
+ *   static void foo() {
+ *       BT_LOGI("Will check g_module_log_level for output log level");
+ *   }
+ *   void debug_log(bool on) {
+ *       g_module_log_level = on? BT_LOG_DEBUG: BT_LOG_INFO;
+ *   }
+ *
+ * Note on performance. This expression will be evaluated each time message is
+ * logged (except when message log level is "disabled" - see BT_LOG_LEVEL for
+ * details). Keep this expression as simple as possible, otherwise it will not
+ * only add runtime overhead, but also will increase size of call site (which
+ * will result in larger executable). The prefered way is to use integer
+ * variable (as in example above). If structure must be used, log_level field
+ * must be the first field in this structure:
+ *
+ *   #define BT_LOG_OUTPUT_LEVEL (g_config.log_level)
+ *   #include <babeltrace/log-internal.h>
+ *   struct config {
+ *       int log_level;
+ *       unsigned other_field;
+ *       [...]
+ *   };
+ *   static config g_config = {BT_LOG_INFO, 0, ...};
+ *
+ * This allows compiler to generate more compact load instruction (no need to
+ * specify offset since it's zero). Calling a function to get output log level
+ * is generaly a bad idea, since it will increase call site size and runtime
+ * overhead even further.
+ */
+#if defined(BT_LOG_OUTPUT_LEVEL)
+       #define _BT_LOG_OUTPUT_LEVEL BT_LOG_OUTPUT_LEVEL
+#else
+       #define _BT_LOG_OUTPUT_LEVEL _bt_log_global_output_lvl
+#endif
+
+/* "Tag" is a compound string that could be associated with a log message. It
+ * consists of tag prefix and tag (both are optional).
+ *
+ * Tag prefix is a global property and configured per process using
+ * bt_log_set_tag_prefix() function. Tag prefix identifies context in which
+ * component or module is running (e.g. process name). For example, the same
+ * library could be used in both client and server processes that work on the
+ * same machine. Tag prefix could be used to easily distinguish between them.
+ * For more details about tag prefix see bt_log_set_tag_prefix() function. Tag
+ * prefix
+ *
+ * Tag identifies component or module. It is configured per compilation module
+ * (.c/.cpp/.m file) by defining BT_LOG_TAG or BT_LOG_DEF_TAG. BT_LOG_TAG has
+ * higer priority and when defined overrides value provided by BT_LOG_DEF_TAG.
+ * When defined, value must evaluate to (const char *), so for strings double
+ * quotes must be used.
+ *
+ * Default tag could be defined with BT_LOG_DEF_TAG in build script (e.g.
+ * Makefile, CMakeLists.txt, gyp, etc.) for the entire project or target:
+ *
+ *   CC_ARGS := -DBT_LOG_DEF_TAG=\"MISC\"
+ *
+ * And when necessary could be overriden with BT_LOG_TAG in .c/.cpp/.m files
+ * before including bt_log.h:
+ *
+ *   #define BT_LOG_TAG "MAIN"
+ *   #include <babeltrace/log-internal.h>
+ *
+ * If both BT_LOG_DEF_TAG and BT_LOG_TAG are undefined no tag will be added to
+ * the log message (tag prefix still could be added though).
+ *
+ * Output example:
+ *
+ *   04-29 22:43:20.244 40059  1299 I hello.MAIN Number of arguments: 1
+ *                                    |     |
+ *                                    |     +- tag (e.g. module)
+ *                                    +- tag prefix (e.g. process name)
+ */
+#if defined(BT_LOG_TAG)
+       #define _BT_LOG_TAG BT_LOG_TAG
+#elif defined(BT_LOG_DEF_TAG)
+       #define _BT_LOG_TAG BT_LOG_DEF_TAG
+#else
+       #define _BT_LOG_TAG 0
+#endif
+
+/* Source location is part of a log line that describes location (function or
+ * method name, file name and line number, e.g. "runloop@main.cpp:68") of a
+ * log statement that produced it.
+ * Source location formats are:
+ * - BT_LOG_SRCLOC_NONE - don't add source location to log line.
+ * - BT_LOG_SRCLOC_SHORT - add source location in short form (file and line
+ *   number, e.g. "@main.cpp:68").
+ * - BT_LOG_SRCLOC_LONG - add source location in long form (function or method
+ *   name, file and line number, e.g. "runloop@main.cpp:68").
+ */
+#define BT_LOG_SRCLOC_NONE  0
+#define BT_LOG_SRCLOC_SHORT 1
+#define BT_LOG_SRCLOC_LONG  2
+
+/* Source location format is configured per compilation module (.c/.cpp/.m
+ * file) by defining BT_LOG_DEF_SRCLOC or BT_LOG_SRCLOC. BT_LOG_SRCLOC has
+ * higer priority and when defined overrides value provided by
+ * BT_LOG_DEF_SRCLOC.
+ *
+ * Common practice is to define default format with BT_LOG_DEF_SRCLOC in
+ * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
+ * project or target:
+ *
+ *   CC_ARGS := -DBT_LOG_DEF_SRCLOC=BT_LOG_SRCLOC_LONG
+ *
+ * And when necessary to override it with BT_LOG_SRCLOC in .c/.cpp/.m files
+ * before including bt_log.h:
+ *
+ *   #define BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE
+ *   #include <babeltrace/log-internal.h>
+ *
+ * If both BT_LOG_DEF_SRCLOC and BT_LOG_SRCLOC are undefined, then
+ * BT_LOG_SRCLOC_NONE will be used for release builds (NDEBUG is defined) and
+ * BT_LOG_SRCLOC_LONG otherwise (NDEBUG is not defined).
+ */
+#if defined(BT_LOG_SRCLOC)
+       #define _BT_LOG_SRCLOC BT_LOG_SRCLOC
+#elif defined(BT_LOG_DEF_SRCLOC)
+       #define _BT_LOG_SRCLOC BT_LOG_DEF_SRCLOC
+#else
+       #ifdef NDEBUG
+               #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_NONE
+       #else
+               #define _BT_LOG_SRCLOC BT_LOG_SRCLOC_LONG
+       #endif
+#endif
+#if BT_LOG_SRCLOC_LONG == _BT_LOG_SRCLOC
+       #define _BT_LOG_SRCLOC_FUNCTION _BT_LOG_FUNCTION
+#else
+       #define _BT_LOG_SRCLOC_FUNCTION 0
+#endif
+
+/* Censoring provides conditional logging of secret information, also known as
+ * Personally Identifiable Information (PII) or Sensitive Personal Information
+ * (SPI). Censoring can be either enabled (BT_LOG_CENSORED) or disabled
+ * (BT_LOG_UNCENSORED). When censoring is enabled, log statements marked as
+ * "secrets" will be ignored and will have zero overhead (arguments also will
+ * not be evaluated).
+ */
+#define BT_LOG_CENSORED   1
+#define BT_LOG_UNCENSORED 0
+
+/* Censoring is configured per compilation module (.c/.cpp/.m file) by defining
+ * BT_LOG_DEF_CENSORING or BT_LOG_CENSORING. BT_LOG_CENSORING has higer priority
+ * and when defined overrides value provided by BT_LOG_DEF_CENSORING.
+ *
+ * Common practice is to define default censoring with BT_LOG_DEF_CENSORING in
+ * build script (e.g. Makefile, CMakeLists.txt, gyp, etc.) for the entire
+ * project or target:
+ *
+ *   CC_ARGS := -DBT_LOG_DEF_CENSORING=BT_LOG_CENSORED
+ *
+ * And when necessary to override it with BT_LOG_CENSORING in .c/.cpp/.m files
+ * before including bt_log.h (consider doing it only for debug purposes and be
+ * very careful not to push such temporary changes to source control):
+ *
+ *   #define BT_LOG_CENSORING BT_LOG_UNCENSORED
+ *   #include <babeltrace/log-internal.h>
+ *
+ * If both BT_LOG_DEF_CENSORING and BT_LOG_CENSORING are undefined, then
+ * BT_LOG_CENSORED will be used for release builds (NDEBUG is defined) and
+ * BT_LOG_UNCENSORED otherwise (NDEBUG is not defined).
+ */
+#if defined(BT_LOG_CENSORING)
+       #define _BT_LOG_CENSORING BT_LOG_CENSORING
+#elif defined(BT_LOG_DEF_CENSORING)
+       #define _BT_LOG_CENSORING BT_LOG_DEF_CENSORING
+#else
+       #ifdef NDEBUG
+               #define _BT_LOG_CENSORING BT_LOG_CENSORED
+       #else
+               #define _BT_LOG_CENSORING BT_LOG_UNCENSORED
+       #endif
+#endif
+
+/* Check censoring at compile time. Evaluates to true when censoring is disabled
+ * (i.e. when secrets will be logged). For example:
+ *
+ *   #if BT_LOG_SECRETS
+ *       char ssn[16];
+ *       getSocialSecurityNumber(ssn);
+ *       BT_LOGI("Customer ssn: %s", ssn);
+ *   #endif
+ *
+ * See BT_LOG_SECRET() macro for a more convenient way of guarding single log
+ * statement.
+ */
+#define BT_LOG_SECRETS (BT_LOG_UNCENSORED == _BT_LOG_CENSORING)
+
+/* Static (compile-time) initialization support allows to configure logging
+ * before entering main() function. This mostly useful in C++ where functions
+ * and methods could be called during initialization of global objects. Those
+ * functions and methods could record log messages too and for that reason
+ * static initialization of logging configuration is customizable.
+ *
+ * Macros below allow to specify values to use for initial configuration:
+ * - BT_LOG_EXTERN_TAG_PREFIX - tag prefix (default: none)
+ * - BT_LOG_EXTERN_GLOBAL_FORMAT - global format options (default: see
+ *   BT_LOG_MEM_WIDTH in bt_log.c)
+ * - BT_LOG_EXTERN_GLOBAL_OUTPUT - global output facility (default: stderr or
+ *   platform specific, see BT_LOG_USE_XXX macros in bt_log.c)
+ * - BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL - global output log level (default: 0 -
+ *   all levals are "turned on")
+ *
+ * For example, in log_config.c:
+ *
+ *   #include <babeltrace/log-internal.h>
+ *   BT_LOG_DEFINE_TAG_PREFIX = "MyApp";
+ *   BT_LOG_DEFINE_GLOBAL_FORMAT = {CUSTOM_MEM_WIDTH};
+ *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback, 0};
+ *   BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_INFO;
+ *
+ * However, to use any of those macros bt_log library must be compiled with
+ * following macros defined:
+ * - to use BT_LOG_DEFINE_TAG_PREFIX define BT_LOG_EXTERN_TAG_PREFIX
+ * - to use BT_LOG_DEFINE_GLOBAL_FORMAT define BT_LOG_EXTERN_GLOBAL_FORMAT
+ * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT define BT_LOG_EXTERN_GLOBAL_OUTPUT
+ * - to use BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL define
+ *   BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+ *
+ * When bt_log library compiled with one of BT_LOG_EXTERN_XXX macros defined,
+ * corresponding BT_LOG_DEFINE_XXX macro MUST be used exactly once somewhere.
+ * Otherwise build will fail with link error (undefined symbol).
+ */
+#define BT_LOG_DEFINE_TAG_PREFIX const char *_bt_log_tag_prefix
+#define BT_LOG_DEFINE_GLOBAL_FORMAT bt_log_format _bt_log_global_format
+#define BT_LOG_DEFINE_GLOBAL_OUTPUT bt_log_output _bt_log_global_output
+#define BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL int _bt_log_global_output_lvl
+
+/* Pointer to global format options. Direct modification is not allowed. Use
+ * bt_log_set_mem_width() instead. Could be used to initialize bt_log_spec
+ * structure:
+ *
+ *   const bt_log_output g_output = {BT_LOG_PUT_STD, output_callback, 0};
+ *   const bt_log_spec g_spec = {BT_LOG_GLOBAL_FORMAT, &g_output};
+ *   BT_LOGI_AUX(&g_spec, "Hello");
+ */
+#define BT_LOG_GLOBAL_FORMAT ((const bt_log_format *)&_bt_log_global_format)
+
+/* Pointer to global output variable. Direct modification is not allowed. Use
+ * bt_log_set_output_v() or bt_log_set_output_p() instead. Could be used to
+ * initialize bt_log_spec structure:
+ *
+ *   const bt_log_format g_format = {40};
+ *   const bt_log_spec g_spec = {g_format, BT_LOG_GLOBAL_OUTPUT};
+ *   BT_LOGI_AUX(&g_spec, "Hello");
+ */
+#define BT_LOG_GLOBAL_OUTPUT ((const bt_log_output *)&_bt_log_global_output)
+
+/* When defined, all library symbols produced by linker will be prefixed with
+ * provided value. That allows to use bt_log library privately in another
+ * libraries without exposing bt_log symbols in their original form (to avoid
+ * possible conflicts with other libraries / components that also could use
+ * bt_log for logging). Value must be without quotes, for example:
+ *
+ *   CC_ARGS := -DBT_LOG_LIBRARY_PREFIX=my_lib_
+ *
+ * Note, that in this mode BT_LOG_LIBRARY_PREFIX must be defined when building
+ * bt_log library AND it also must be defined to the same value when building
+ * a library that uses it. For example, consider fictional KittyHttp library
+ * that wants to use bt_log for logging. First approach that could be taken is
+ * to add bt_log.h and bt_log.c to the KittyHttp's source code tree directly.
+ * In that case it will be enough just to define BT_LOG_LIBRARY_PREFIX in
+ * KittyHttp's build script:
+ *
+ *   // KittyHttp/CMakeLists.txt
+ *   target_compile_definitions(KittyHttp PRIVATE
+ *                              "BT_LOG_LIBRARY_PREFIX=KittyHttp_")
+ *
+ * If KittyHttp doesn't want to include bt_log source code in its source tree
+ * and wants to build bt_log as a separate library than bt_log library must be
+ * built with BT_LOG_LIBRARY_PREFIX defined to KittyHttp_ AND KittyHttp library
+ * itself also needs to define BT_LOG_LIBRARY_PREFIX to KittyHttp_. It can do
+ * so either in its build script, as in example above, or by providing a
+ * wrapper header that KittyHttp library will need to use instead of bt_log.h:
+ *
+ *   // KittyHttpLogging.h
+ *   #define BT_LOG_LIBRARY_PREFIX KittyHttp_
+ *   #include <babeltrace/log-internal.h>
+ *
+ * Regardless of the method chosen, the end result is that bt_log symbols will
+ * be prefixed with "KittyHttp_", so if a user of KittyHttp (say DogeBrowser)
+ * also uses bt_log for logging, they will not interferer with each other. Both
+ * will have their own log level, output facility, format options etc.
+ */
+#ifdef BT_LOG_LIBRARY_PREFIX
+       #define _BT_LOG_DECOR__(prefix, name) prefix ## name
+       #define _BT_LOG_DECOR_(prefix, name) _BT_LOG_DECOR__(prefix, name)
+       #define _BT_LOG_DECOR(name) _BT_LOG_DECOR_(BT_LOG_LIBRARY_PREFIX, name)
+
+       #define bt_log_set_tag_prefix _BT_LOG_DECOR(bt_log_set_tag_prefix)
+       #define bt_log_set_mem_width _BT_LOG_DECOR(bt_log_set_mem_width)
+       #define bt_log_set_output_level _BT_LOG_DECOR(bt_log_set_output_level)
+       #define bt_log_set_output_v _BT_LOG_DECOR(bt_log_set_output_v)
+       #define bt_log_set_output_p _BT_LOG_DECOR(bt_log_set_output_p)
+       #define bt_log_out_stderr_callback _BT_LOG_DECOR(bt_log_out_stderr_callback)
+       #define _bt_log_tag_prefix _BT_LOG_DECOR(_bt_log_tag_prefix)
+       #define _bt_log_global_format _BT_LOG_DECOR(_bt_log_global_format)
+       #define _bt_log_global_output _BT_LOG_DECOR(_bt_log_global_output)
+       #define _bt_log_global_output_lvl _BT_LOG_DECOR(_bt_log_global_output_lvl)
+       #define _bt_log_write_d _BT_LOG_DECOR(_bt_log_write_d)
+       #define _bt_log_write_aux_d _BT_LOG_DECOR(_bt_log_write_aux_d)
+       #define _bt_log_write _BT_LOG_DECOR(_bt_log_write)
+       #define _bt_log_write_aux _BT_LOG_DECOR(_bt_log_write_aux)
+       #define _bt_log_write_mem_d _BT_LOG_DECOR(_bt_log_write_mem_d)
+       #define _bt_log_write_mem_aux_d _BT_LOG_DECOR(_bt_log_write_mem_aux_d)
+       #define _bt_log_write_mem _BT_LOG_DECOR(_bt_log_write_mem)
+       #define _bt_log_write_mem_aux _BT_LOG_DECOR(_bt_log_write_mem_aux)
+       #define _bt_log_stderr_spec _BT_LOG_DECOR(_bt_log_stderr_spec)
+#endif
+
+#if defined(__printflike)
+       #define _BT_LOG_PRINTFLIKE(a, b) __printflike(a, b)
+#else
+       #define _BT_LOG_PRINTFLIKE(a, b)
+#endif
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__GNUC__)
+       #define _BT_LOG_FUNCTION __FUNCTION__
+#else
+       #define _BT_LOG_FUNCTION __func__
+#endif
+
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+       #define _BT_LOG_INLINE __inline
+       #define _BT_LOG_IF(cond) \
+               __pragma(warning(push)) \
+               __pragma(warning(disable:4127)) \
+               if(cond) \
+               __pragma(warning(pop))
+       #define _BT_LOG_WHILE(cond) \
+               __pragma(warning(push)) \
+               __pragma(warning(disable:4127)) \
+               while(cond) \
+               __pragma(warning(pop))
+#else
+       #define _BT_LOG_INLINE inline
+       #define _BT_LOG_IF(cond) if(cond)
+       #define _BT_LOG_WHILE(cond) while(cond)
+#endif
+#define _BT_LOG_NEVER _BT_LOG_IF(0)
+#define _BT_LOG_ONCE _BT_LOG_WHILE(0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Set tag prefix. Prefix will be separated from the tag with dot ('.').
+ * Use 0 or empty string to disable (default). Common use is to set it to
+ * the process (or build target) name (e.g. to separate client and server
+ * processes). Function will NOT copy provided prefix string, but will store the
+ * pointer. Hence specified prefix string must remain valid. See
+ * BT_LOG_DEFINE_TAG_PREFIX for a way to set it before entering main() function.
+ * See BT_LOG_TAG for more information about tag and tag prefix.
+ */
+void bt_log_set_tag_prefix(const char *const prefix);
+
+/* Set number of bytes per log line in memory (ASCII-HEX) output. Example:
+ *
+ *   I hello.MAIN 4c6f72656d20697073756d20646f6c6f  Lorem ipsum dolo
+ *                |<-          w bytes         ->|  |<-  w chars ->|
+ *
+ * See BT_LOGF_MEM and BT_LOGF_MEM_AUX for more details.
+ */
+void bt_log_set_mem_width(const unsigned w);
+
+/* Set "output" log level. See BT_LOG_LEVEL and BT_LOG_OUTPUT_LEVEL for more
+ * info about log levels.
+ */
+void bt_log_set_output_level(const int lvl);
+
+/* Put mask is a set of flags that define what fields will be added to each
+ * log message. Default value is BT_LOG_PUT_STD and other flags could be used to
+ * alter its behavior. See bt_log_set_output_v() for more details.
+ *
+ * Note about BT_LOG_PUT_SRC: it will be added only in debug builds (NDEBUG is
+ * not defined).
+ */
+enum
+{
+       BT_LOG_PUT_CTX = 1 << 0, /* context (time, pid, tid, log level) */
+       BT_LOG_PUT_TAG = 1 << 1, /* tag (including tag prefix) */
+       BT_LOG_PUT_SRC = 1 << 2, /* source location (file, line, function) */
+       BT_LOG_PUT_MSG = 1 << 3, /* message text (formatted string) */
+       BT_LOG_PUT_STD = 0xffff, /* everything (default) */
+};
+
+typedef struct bt_log_message
+{
+       int lvl; /* Log level of the message */
+       const char *tag; /* Associated tag (without tag prefix) */
+       char *buf; /* Buffer start */
+       char *e; /* Buffer end (last position where EOL with 0 could be written) */
+       char *p; /* Buffer content end (append position) */
+       char *tag_b; /* Prefixed tag start */
+       char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */
+       char *msg_b; /* Message start (expanded format string) */
+}
+bt_log_message;
+
+/* Type of output callback function. It will be called for each log line allowed
+ * by both "current" and "output" log levels ("enabled" and "turned on").
+ * Callback function is allowed to modify content of the buffers pointed by the
+ * msg, but it's not allowed to modify any of msg fields. Buffer pointed by msg
+ * is UTF-8 encoded (no BOM mark).
+ */
+typedef void (*bt_log_output_cb)(const bt_log_message *msg, void *arg);
+
+/* Format options. For more details see bt_log_set_mem_width().
+ */
+typedef struct bt_log_format
+{
+       unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */
+}
+bt_log_format;
+
+/* Output facility.
+ */
+typedef struct bt_log_output
+{
+       unsigned mask; /* What to put into log line buffer (see BT_LOG_PUT_XXX) */
+       void *arg; /* User provided output callback argument */
+       bt_log_output_cb callback; /* Output callback function */
+}
+bt_log_output;
+
+/* Set output callback function.
+ *
+ * Mask allows to control what information will be added to the log line buffer
+ * before callback function is invoked. Default mask value is BT_LOG_PUT_STD.
+ */
+void bt_log_set_output_v(const unsigned mask, void *const arg,
+                                                const bt_log_output_cb callback);
+static _BT_LOG_INLINE void bt_log_set_output_p(const bt_log_output *const output)
+{
+       bt_log_set_output_v(output->mask, output->arg, output->callback);
+}
+
+/* Used with _AUX macros and allows to override global format and output
+ * facility. Use BT_LOG_GLOBAL_FORMAT and BT_LOG_GLOBAL_OUTPUT for values from
+ * global configuration. Example:
+ *
+ *   static const bt_log_output module_output = {
+ *       BT_LOG_PUT_STD, 0, custom_output_callback
+ *   };
+ *   static const bt_log_spec module_spec = {
+ *       BT_LOG_GLOBAL_FORMAT, &module_output
+ *   };
+ *   BT_LOGI_AUX(&module_spec, "Position: %ix%i", x, y);
+ *
+ * See BT_LOGF_AUX and BT_LOGF_MEM_AUX for details.
+ */
+typedef struct bt_log_spec
+{
+       const bt_log_format *format;
+       const bt_log_output *output;
+}
+bt_log_spec;
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Execute log statement if condition is true. Example:
+ *
+ *   BT_LOG_IF(1 < 2, BT_LOGI("Log this"));
+ *   BT_LOG_IF(1 > 2, BT_LOGI("Don't log this"));
+ *
+ * Keep in mind though, that if condition can't be evaluated at compile time,
+ * then it will be evaluated at run time. This will increase exectuable size
+ * and can have noticeable performance overhead. Try to limit conditions to
+ * expressions that can be evaluated at compile time.
+ */
+#define BT_LOG_IF(cond, f) do { _BT_LOG_IF((cond)) { f; } } _BT_LOG_ONCE
+
+/* Mark log statement as "secret". Log statements that are marked as secrets
+ * will NOT be executed when censoring is enabled (see BT_LOG_CENSORED).
+ * Example:
+ *
+ *   BT_LOG_SECRET(BT_LOGI("Credit card: %s", credit_card));
+ *   BT_LOG_SECRET(BT_LOGD_MEM(cipher, cipher_sz, "Cipher bytes:"));
+ */
+#define BT_LOG_SECRET(f) BT_LOG_IF(BT_LOG_SECRETS, f)
+
+/* Check "current" log level at compile time (ignoring "output" log level).
+ * Evaluates to true when specified log level is enabled. For example:
+ *
+ *   #if BT_LOG_ENABLED_DEBUG
+ *       const char *const g_enum_strings[] = {
+ *           "enum_value_0", "enum_value_1", "enum_value_2"
+ *       };
+ *   #endif
+ *   // ...
+ *   #if BT_LOG_ENABLED_DEBUG
+ *       BT_LOGD("enum value: %s", g_enum_strings[v]);
+ *   #endif
+ *
+ * See BT_LOG_LEVEL for details.
+ */
+#define BT_LOG_ENABLED(lvl)     ((lvl) >= _BT_LOG_LEVEL)
+#define BT_LOG_ENABLED_VERBOSE  BT_LOG_ENABLED(BT_LOG_VERBOSE)
+#define BT_LOG_ENABLED_DEBUG    BT_LOG_ENABLED(BT_LOG_DEBUG)
+#define BT_LOG_ENABLED_INFO     BT_LOG_ENABLED(BT_LOG_INFO)
+#define BT_LOG_ENABLED_WARN     BT_LOG_ENABLED(BT_LOG_WARN)
+#define BT_LOG_ENABLED_ERROR    BT_LOG_ENABLED(BT_LOG_ERROR)
+#define BT_LOG_ENABLED_FATAL    BT_LOG_ENABLED(BT_LOG_FATAL)
+
+/* Check "output" log level at run time (taking into account "current" log
+ * level as well). Evaluates to true when specified log level is turned on AND
+ * enabled. For example:
+ *
+ *   if (BT_LOG_ON_DEBUG)
+ *   {
+ *       char hash[65];
+ *       sha256(data_ptr, data_sz, hash);
+ *       BT_LOGD("data: len=%u, sha256=%s", data_sz, hash);
+ *   }
+ *
+ * See BT_LOG_OUTPUT_LEVEL for details.
+ */
+#define BT_LOG_ON(lvl) \
+               (BT_LOG_ENABLED((lvl)) && (lvl) >= _BT_LOG_OUTPUT_LEVEL)
+#define BT_LOG_ON_VERBOSE   BT_LOG_ON(BT_LOG_VERBOSE)
+#define BT_LOG_ON_DEBUG     BT_LOG_ON(BT_LOG_DEBUG)
+#define BT_LOG_ON_INFO      BT_LOG_ON(BT_LOG_INFO)
+#define BT_LOG_ON_WARN      BT_LOG_ON(BT_LOG_WARN)
+#define BT_LOG_ON_ERROR     BT_LOG_ON(BT_LOG_ERROR)
+#define BT_LOG_ON_FATAL     BT_LOG_ON(BT_LOG_FATAL)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char *_bt_log_tag_prefix;
+extern bt_log_format _bt_log_global_format;
+extern bt_log_output _bt_log_global_output;
+extern int _bt_log_global_output_lvl;
+extern const bt_log_spec _bt_log_stderr_spec;
+
+BT_HIDDEN
+void _bt_log_write_d(
+               const char *const func, const char *const file, const unsigned line,
+               const int lvl, const char *const tag,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
+
+BT_HIDDEN
+void _bt_log_write_aux_d(
+               const char *const func, const char *const file, const unsigned line,
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(7, 8);
+
+BT_HIDDEN
+void _bt_log_write(
+               const int lvl, const char *const tag,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(3, 4);
+
+BT_HIDDEN
+void _bt_log_write_aux(
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(4, 5);
+
+BT_HIDDEN
+void _bt_log_write_mem_d(
+               const char *const func, const char *const file, const unsigned line,
+               const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(8, 9);
+
+BT_HIDDEN
+void _bt_log_write_mem_aux_d(
+               const char *const func, const char *const file, const unsigned line,
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(9, 10);
+
+BT_HIDDEN
+void _bt_log_write_mem(
+               const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(5, 6);
+
+BT_HIDDEN
+void _bt_log_write_mem_aux(
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Message logging macros:
+ * - BT_LOGV("format string", args, ...)
+ * - BT_LOGD("format string", args, ...)
+ * - BT_LOGI("format string", args, ...)
+ * - BT_LOGW("format string", args, ...)
+ * - BT_LOGE("format string", args, ...)
+ * - BT_LOGF("format string", args, ...)
+ *
+ * Memory logging macros:
+ * - BT_LOGV_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGD_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGI_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGW_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGE_MEM(data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGF_MEM(data_ptr, data_sz, "format string", args, ...)
+ *
+ * Auxiliary logging macros:
+ * - BT_LOGV_AUX(&log_instance, "format string", args, ...)
+ * - BT_LOGD_AUX(&log_instance, "format string", args, ...)
+ * - BT_LOGI_AUX(&log_instance, "format string", args, ...)
+ * - BT_LOGW_AUX(&log_instance, "format string", args, ...)
+ * - BT_LOGE_AUX(&log_instance, "format string", args, ...)
+ * - BT_LOGF_AUX(&log_instance, "format string", args, ...)
+ *
+ * Auxiliary memory logging macros:
+ * - BT_LOGV_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGD_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGI_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGW_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGE_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOGF_MEM_AUX(&log_instance, data_ptr, data_sz, "format string", args, ...)
+ *
+ * Preformatted string logging macros:
+ * - BT_LOGV_STR("preformatted string");
+ * - BT_LOGD_STR("preformatted string");
+ * - BT_LOGI_STR("preformatted string");
+ * - BT_LOGW_STR("preformatted string");
+ * - BT_LOGE_STR("preformatted string");
+ * - BT_LOGF_STR("preformatted string");
+ *
+ * Explicit log level and tag macros:
+ * - BT_LOG_WRITE(level, tag, "format string", args, ...)
+ * - BT_LOG_WRITE_MEM(level, tag, data_ptr, data_sz, "format string", args, ...)
+ * - BT_LOG_WRITE_AUX(&log_instance, level, tag, "format string", args, ...)
+ * - BT_LOG_WRITE_MEM_AUX(&log_instance, level, tag, data_ptr, data_sz,
+ *                        "format string", args, ...)
+ *
+ * Format string follows printf() conventions. Both data_ptr and data_sz could
+ * be 0. Tag can be 0 as well. Most compilers will verify that type of arguments
+ * match format specifiers in format string.
+ *
+ * Library assuming UTF-8 encoding for all strings (char *), including format
+ * string itself.
+ */
+#if BT_LOG_SRCLOC_NONE == _BT_LOG_SRCLOC
+       #define BT_LOG_WRITE(lvl, tag, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write(lvl, tag, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_aux(log, lvl, tag, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_mem_aux(log, lvl, tag, d, d_sz, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+#else
+       #define BT_LOG_WRITE(lvl, tag, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
+                                                       lvl, tag, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_MEM(lvl, tag, d, d_sz, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_mem_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
+                                                       lvl, tag, d, d_sz, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_AUX(log, lvl, tag, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
+                                                       log, lvl, tag, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+       #define BT_LOG_WRITE_MEM_AUX(log, lvl, tag, d, d_sz, ...) \
+                       do { \
+                               if (BT_LOG_ON(lvl)) \
+                                       _bt_log_write_mem_aux_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, \
+                                                       log, lvl, tag, d, d_sz, __VA_ARGS__); \
+                       } _BT_LOG_ONCE
+#endif
+
+static _BT_LOG_INLINE void _bt_log_unused(const int dummy, ...) {(void)dummy;}
+
+#define _BT_LOG_UNUSED(...) \
+               do { _BT_LOG_NEVER _bt_log_unused(0, __VA_ARGS__); } _BT_LOG_ONCE
+
+#if BT_LOG_ENABLED_VERBOSE
+       #define BT_LOGV(...) \
+                       BT_LOG_WRITE(BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGV_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_VERBOSE, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGV_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGV_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(log, BT_LOG_VERBOSE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGV(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGV_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGV_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGV_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if BT_LOG_ENABLED_DEBUG
+       #define BT_LOGD(...) \
+                       BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGD_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGD_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGD_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_DEBUG, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGD(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGD_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGD_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGD_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if BT_LOG_ENABLED_INFO
+       #define BT_LOGI(...) \
+                       BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGI_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGI_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGI_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_INFO, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGI(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGI_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGI_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGI_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if BT_LOG_ENABLED_WARN
+       #define BT_LOGW(...) \
+                       BT_LOG_WRITE(BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGW_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGW_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGW_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_WARN, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGW(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGW_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGW_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGW_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if BT_LOG_ENABLED_ERROR
+       #define BT_LOGE(...) \
+                       BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGE_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGE_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGE_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_ERROR, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGE(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGE_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGE_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGE_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#if BT_LOG_ENABLED_FATAL
+       #define BT_LOGF(...) \
+                       BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGF_AUX(log, ...) \
+                       BT_LOG_WRITE_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
+       #define BT_LOGF_MEM(d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+       #define BT_LOGF_MEM_AUX(log, d, d_sz, ...) \
+                       BT_LOG_WRITE_MEM_AUX(log, BT_LOG_FATAL, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+#else
+       #define BT_LOGF(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGF_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGF_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
+       #define BT_LOGF_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
+#endif
+
+#define BT_LOGV_STR(s) BT_LOGV("%s", (s))
+#define BT_LOGD_STR(s) BT_LOGD("%s", (s))
+#define BT_LOGI_STR(s) BT_LOGI("%s", (s))
+#define BT_LOGW_STR(s) BT_LOGW("%s", (s))
+#define BT_LOGE_STR(s) BT_LOGE("%s", (s))
+#define BT_LOGF_STR(s) BT_LOGF("%s", (s))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Output to standard error stream. Library uses it by default, though in few
+ * cases it could be necessary to specify it explicitly. For example, when
+ * bt_log library is compiled with BT_LOG_EXTERN_GLOBAL_OUTPUT, application must
+ * define and initialize global output variable:
+ *
+ *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR};
+ *
+ * Another example is when using custom output, stderr could be used as a
+ * fallback when custom output facility failed to initialize:
+ *
+ *   bt_log_set_output_v(BT_LOG_OUT_STDERR);
+ */
+enum { BT_LOG_OUT_STDERR_MASK = BT_LOG_PUT_STD };
+void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg);
+#define BT_LOG_OUT_STDERR BT_LOG_OUT_STDERR_MASK, 0, bt_log_out_stderr_callback
+
+/* Predefined spec for stderr. Uses global format options (BT_LOG_GLOBAL_FORMAT)
+ * and BT_LOG_OUT_STDERR. Could be used to force output to stderr for a
+ * particular message. Example:
+ *
+ *   f = fopen("foo.log", "w");
+ *   if (!f)
+ *       BT_LOGE_AUX(BT_LOG_STDERR, "Failed to open log file");
+ */
+#define BT_LOG_STDERR (&_bt_log_stderr_spec)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/babeltrace/logging.h b/include/babeltrace/logging.h
new file mode 100644 (file)
index 0000000..0e1d663
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef BABELTRACE_LOGGING_H
+#define BABELTRACE_LOGGING_H
+
+/*
+ * Babeltrace - Logging
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <babeltrace/ref.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+@defgroup logging Logging
+@ingroup apiref
+@brief Logging.
+
+@code
+#include <babeltrace/logging.h>
+@endcode
+
+The functions in this module control the Babeltrace library's logging
+behaviour.
+
+You can set the current global log level of the library with
+bt_logging_set_global_level(). Note that, if the level you set is below
+the minimal logging level (configured at build time, which you can get
+with bt_logging_get_minimal_level()), the logging statement between the
+current global log level and the minimal log level are not executed.
+
+@file
+@brief Logging functions.
+@sa logging
+
+@addtogroup logging
+@{
+*/
+
+/**
+@brief Log levels.
+*/
+enum bt_logging_level {
+       /// Additional, low-level debugging context information.
+       BT_LOGGING_LEVEL_VERBOSE        = 1,
+
+       /**
+       Debugging information, only useful when searching for the
+       cause of a bug.
+       */
+       BT_LOGGING_LEVEL_DEBUG          = 2,
+
+       /**
+       Non-debugging information and failure to load optional
+       subsystems.
+       */
+       BT_LOGGING_LEVEL_INFO           = 3,
+
+       /**
+       Errors caused by a bad usage of the library, that is, a
+       non-observance of the documented function preconditions.
+
+       The library's and object's states remain consistent when a
+       warning is issued.
+       */
+       BT_LOGGING_LEVEL_WARN           = 4,
+
+       /**
+       An important error from which the library cannot recover, but
+       the executed stack of functions can still return cleanly.
+       */
+       BT_LOGGING_LEVEL_ERROR          = 5,
+
+       /**
+       The library cannot continue to work in this condition: it must
+       terminate immediately, without even returning to the user's
+       execution.
+       */
+       BT_LOGGING_LEVEL_FATAL          = 6,
+
+       /// Logging is disabled.
+       BT_LOGGING_LEVEL_NONE           = 0xff,
+};
+
+/**
+@brief Returns the minimal log level of the Babeltrace library.
+
+The minimal log level is defined at the library's build time. Any
+logging statement with a level below the minimal log level is not
+compiled. This means that it is useless, although possible, to set the
+global log level with bt_logging_set_global_level() below this level.
+
+@returns       Minimal, build time log level.
+
+@sa bt_logging_get_global_level(): Returns the current global log level.
+*/
+extern enum bt_logging_level bt_logging_get_minimal_level(void);
+
+/**
+@brief Returns the current global log level of the Babeltrace library.
+
+@returns       Current global log level.
+
+@sa bt_logging_set_global_level(): Sets the current global log level.
+@sa bt_logging_get_minimal_level(): Returns the minimal log level.
+*/
+extern enum bt_logging_level bt_logging_get_global_level(void);
+
+/**
+@brief Sets the current global log level of the Babeltrace library
+       to \p log_level.
+
+If \p log_level is below what bt_logging_get_minimal_level() returns,
+the logging statements with a level between \p log_level and the minimal
+log level cannot be executed.
+
+@param[in] log_level   Library's new global log level.
+
+@sa bt_logging_get_global_level(): Returns the global log level.
+*/
+extern void bt_logging_set_global_level(enum bt_logging_level log_level);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BABELTRACE_LOGGING_H */
index 068d858e639ea96565beb6f26e16c87fe5e9000c..416d703c26438244e50b1ba5ce0a5e7db3fdc41c 100644 (file)
@@ -4,7 +4,7 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
 
 lib_LTLIBRARIES = libbabeltrace.la
 
-libbabeltrace_la_SOURCES = babeltrace.c values.c ref.c
+libbabeltrace_la_SOURCES = babeltrace.c values.c ref.c logging.c
 libbabeltrace_la_LDFLAGS = -version-info $(BABELTRACE_LIBRARY_VERSION)
 
 libbabeltrace_la_LIBADD = \
@@ -13,6 +13,7 @@ libbabeltrace_la_LIBADD = \
        graph/libgraph.la \
        plugin/libplugin.la \
        $(top_builddir)/common/libbabeltrace-common.la \
+       $(top_builddir)/logging/libbabeltrace-logging.la \
        ctf-ir/libctf-ir.la \
        ctf-writer/libctf-writer.la
 
diff --git a/lib/logging.c b/lib/logging.c
new file mode 100644 (file)
index 0000000..4272677
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <babeltrace/logging.h>
+#include <babeltrace/lib-logging-internal.h>
+
+BT_HIDDEN
+int bt_lib_log_level = BT_LOG_NONE;
+
+enum bt_logging_level bt_logging_get_minimal_level(void)
+{
+       return BT_LOG_LEVEL;
+}
+
+enum bt_logging_level bt_logging_get_global_level(void)
+{
+       return bt_lib_log_level;
+}
+
+void bt_logging_set_global_level(enum bt_logging_level log_level)
+{
+       bt_lib_log_level = log_level;
+}
+
+static
+void __attribute__((constructor)) bt_logging_ctor(void)
+{
+       enum bt_logging_level log_level = BT_LOG_NONE;
+       const char *log_level_env = getenv("BABELTRACE_LOGGING_GLOBAL_LEVEL");
+
+       if (!log_level_env) {
+               goto set_level;
+       }
+
+       if (strcmp(log_level_env, "VERBOSE") == 0) {
+               log_level = BT_LOGGING_LEVEL_VERBOSE;
+       } else if (strcmp(log_level_env, "DEBUG") == 0) {
+               log_level = BT_LOGGING_LEVEL_DEBUG;
+       } else if (strcmp(log_level_env, "INFO") == 0) {
+               log_level = BT_LOGGING_LEVEL_INFO;
+       } else if (strcmp(log_level_env, "WARN") == 0) {
+               log_level = BT_LOGGING_LEVEL_WARN;
+       } else if (strcmp(log_level_env, "ERROR") == 0) {
+               log_level = BT_LOGGING_LEVEL_ERROR;
+       } else if (strcmp(log_level_env, "FATAL") == 0) {
+               log_level = BT_LOGGING_LEVEL_FATAL;
+       }
+
+set_level:
+       bt_logging_set_global_level(log_level);
+}
diff --git a/logging/LICENSE b/logging/LICENSE
new file mode 100644 (file)
index 0000000..5569c1d
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 wonder-mice
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/logging/Makefile.am b/logging/Makefile.am
new file mode 100644 (file)
index 0000000..ef9077d
--- /dev/null
@@ -0,0 +1,5 @@
+AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libbabeltrace-logging.la
+
+libbabeltrace_logging_la_SOURCES = log.c
diff --git a/logging/log.c b/logging/log.c
new file mode 100644 (file)
index 0000000..6d09e56
--- /dev/null
@@ -0,0 +1,1376 @@
+/*
+ * This is zf_log.c, modified with Babeltrace prefixes.
+ * See <https://github.com/wonder-mice/zf_log/>.
+ * See LICENSE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+
+/* When defined, Android log (android/log.h) will be used by default instead of
+ * stderr (ignored on non-Android platforms). Date, time, pid and tid (context)
+ * will be provided by Android log. Android log features will be used to output
+ * log level and tag.
+ */
+#ifdef BT_LOG_USE_ANDROID_LOG
+       #undef BT_LOG_USE_ANDROID_LOG
+       #if defined(__ANDROID__)
+               #define BT_LOG_USE_ANDROID_LOG 1
+       #else
+               #define BT_LOG_USE_ANDROID_LOG 0
+       #endif
+#else
+       #define BT_LOG_USE_ANDROID_LOG 0
+#endif
+/* When defined, NSLog (uses Apple System Log) will be used instead of stderr
+ * (ignored on non-Apple platforms). Date, time, pid and tid (context) will be
+ * provided by NSLog. Curiously, doesn't use NSLog() directly, but piggybacks on
+ * non-public CFLog() function. Both use Apple System Log internally, but it's
+ * easier to call CFLog() from C than NSLog(). Current implementation doesn't
+ * support "%@" format specifier.
+ */
+#ifdef BT_LOG_USE_NSLOG
+       #undef BT_LOG_USE_NSLOG
+       #if defined(__APPLE__) && defined(__MACH__)
+               #define BT_LOG_USE_NSLOG 1
+       #else
+               #define BT_LOG_USE_NSLOG 0
+       #endif
+#else
+       #define BT_LOG_USE_NSLOG 0
+#endif
+/* When defined, OutputDebugString() will be used instead of stderr (ignored on
+ * non-Windows platforms). Uses OutputDebugStringA() variant and feeds it with
+ * UTF-8 data.
+ */
+#ifdef BT_LOG_USE_DEBUGSTRING
+       #undef BT_LOG_USE_DEBUGSTRING
+       #if defined(_WIN32) || defined(_WIN64)
+               #define BT_LOG_USE_DEBUGSTRING 1
+       #else
+               #define BT_LOG_USE_DEBUGSTRING 0
+       #endif
+#else
+       #define BT_LOG_USE_DEBUGSTRING 0
+#endif
+/* When defined, bt_log library will not contain definition of tag prefix
+ * variable. In that case it must be defined elsewhere using
+ * BT_LOG_DEFINE_TAG_PREFIX macro, for example:
+ *
+ *   BT_LOG_DEFINE_TAG_PREFIX = "ProcessName";
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef BT_LOG_EXTERN_TAG_PREFIX
+       #undef BT_LOG_EXTERN_TAG_PREFIX
+       #define BT_LOG_EXTERN_TAG_PREFIX 1
+#else
+       #define BT_LOG_EXTERN_TAG_PREFIX 0
+#endif
+/* When defined, bt_log library will not contain definition of global format
+ * variable. In that case it must be defined elsewhere using
+ * BT_LOG_DEFINE_GLOBAL_FORMAT macro, for example:
+ *
+ *   BT_LOG_DEFINE_GLOBAL_FORMAT = {MEM_WIDTH};
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef BT_LOG_EXTERN_GLOBAL_FORMAT
+       #undef BT_LOG_EXTERN_GLOBAL_FORMAT
+       #define BT_LOG_EXTERN_GLOBAL_FORMAT 1
+#else
+       #define BT_LOG_EXTERN_GLOBAL_FORMAT 0
+#endif
+/* When defined, bt_log library will not contain definition of global output
+ * variable. In that case it must be defined elsewhere using
+ * BT_LOG_DEFINE_GLOBAL_OUTPUT macro, for example:
+ *
+ *   BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_PUT_STD, custom_output_callback};
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT
+       #undef BT_LOG_EXTERN_GLOBAL_OUTPUT
+       #define BT_LOG_EXTERN_GLOBAL_OUTPUT 1
+#else
+       #define BT_LOG_EXTERN_GLOBAL_OUTPUT 0
+#endif
+/* When defined, bt_log library will not contain definition of global output
+ * level variable. In that case it must be defined elsewhere using
+ * BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL macro, for example:
+ *
+ *   BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = BT_LOG_WARN;
+ *
+ * This allows to specify custom value for static initialization and avoid
+ * overhead of setting this value in runtime.
+ */
+#ifdef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+       #undef BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+       #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 1
+#else
+       #define BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL 0
+#endif
+/* When defined, implementation will prefer smaller code size over speed.
+ * Very rough estimate is that code will be up to 2x smaller and up to 2x
+ * slower. Disabled by default.
+ */
+#ifdef BT_LOG_OPTIMIZE_SIZE
+       #undef BT_LOG_OPTIMIZE_SIZE
+       #define BT_LOG_OPTIMIZE_SIZE 1
+#else
+       #define BT_LOG_OPTIMIZE_SIZE 0
+#endif
+/* Size of the log line buffer. The buffer is allocated on stack. It limits
+ * maximum length of a log line.
+ */
+#ifndef BT_LOG_BUF_SZ
+       #define BT_LOG_BUF_SZ 512
+#endif
+/* Default number of bytes in one line of memory output. For large values
+ * BT_LOG_BUF_SZ also must be increased.
+ */
+#ifndef BT_LOG_MEM_WIDTH
+       #define BT_LOG_MEM_WIDTH 32
+#endif
+/* String to put in the end of each log line (can be empty). Its value used by
+ * stderr output callback. Its size used as a default value for BT_LOG_EOL_SZ.
+ */
+#ifndef BT_LOG_EOL
+       #define BT_LOG_EOL "\n"
+#endif
+/* Default delimiter that separates parts of log message. Can NOT contain '%'
+ * or '\0'.
+ *
+ * Log message format specifications can override (or ignore) this value. For
+ * more details see BT_LOG_MESSAGE_CTX_FORMAT, BT_LOG_MESSAGE_SRC_FORMAT and
+ * BT_LOG_MESSAGE_TAG_FORMAT.
+ */
+#ifndef BT_LOG_DEF_DELIMITER
+       #define BT_LOG_DEF_DELIMITER " "
+#endif
+/* Specifies log message context format. Log message context includes date,
+ * time, process id, thread id and message's log level. Custom information can
+ * be added as well. Supported fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND,
+ * MILLISECOND, PID, TID, LEVEL, S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * Must be defined as a tuple, for example:
+ *
+ *   #define BT_LOG_MESSAGE_CTX_FORMAT (YEAR, S("."), MONTH, S("."), DAY, S(" > "))
+ *
+ * In that case, resulting log message will be:
+ *
+ *   2016.12.22 > TAG function@filename.c:line Message text
+ *
+ * Note, that tag, source location and message text are not impacted by
+ * this setting. See BT_LOG_MESSAGE_TAG_FORMAT and BT_LOG_MESSAGE_SRC_FORMAT.
+ *
+ * If message context must be visually separated from the rest of the message,
+ * it must be reflected in context format (notice trailing S(" > ") in the
+ * example above).
+ *
+ * S(str) adds constant string str. String can NOT contain '%' or '\0'.
+ *
+ * F_INIT(statements) adds initialization statement(s) that will be evaluated
+ * once for each log message. All statements are evaluated in specified order.
+ * Several F_INIT() fields can be used in every log message format
+ * specification. Fields, like F_UINT(width, value), are allowed to use results
+ * of initialization statements. If statement introduces variables (or other
+ * names, like structures) they must be prefixed with "f_". Statements  must be
+ * enclosed into additional "()". Example:
+ *
+ *   #define BT_LOG_MESSAGE_CTX_FORMAT \
+ *       (F_INIT(( struct rusage f_ru; getrusage(RUSAGE_SELF, &f_ru); )), \
+ *        YEAR, S("."), MONTH, S("."), DAY, S(" "), \
+ *        F_UINT(5, f_ru.ru_nsignals), \
+ *        S(" "))
+ *
+ * F_UINT(width, value) adds unsigned integer value extended with up to width
+ * spaces (for alignment purposes). Value can be any expression that evaluates
+ * to unsigned integer. If expression contains non-standard functions, they
+ * must be declared with F_INIT(). Example:
+ *
+ *   #define BT_LOG_MESSAGE_CTX_FORMAT \
+ *        (YEAR, S("."), MONTH, S("."), DAY, S(" "), \
+ *        F_INIT(( unsigned tickcount(); )), \
+ *        F_UINT(5, tickcount()), \
+ *        S(" "))
+ *
+ * Other log message format specifications follow same rules, but have a
+ * different set of supported fields.
+ */
+#ifndef BT_LOG_MESSAGE_CTX_FORMAT
+       #define BT_LOG_MESSAGE_CTX_FORMAT \
+               (MONTH, S("-"), DAY, S(BT_LOG_DEF_DELIMITER), \
+                HOUR, S(":"), MINUTE, S(":"), SECOND, S("."), MILLISECOND, S(BT_LOG_DEF_DELIMITER), \
+                PID, S(BT_LOG_DEF_DELIMITER), TID, S(BT_LOG_DEF_DELIMITER), \
+                LEVEL, S(BT_LOG_DEF_DELIMITER))
+#endif
+/* Example:
+ */
+/* Specifies log message tag format. It includes tag prefix and tag. Custom
+ * information can be added as well. Supported fields:
+ * TAG(prefix_delimiter, tag_delimiter), S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * TAG(prefix_delimiter, tag_delimiter) adds following string to log message:
+ *
+ *   PREFIX<prefix_delimiter>TAG<tag_delimiter>
+ *
+ * Prefix delimiter will be used only when prefix is not empty. Tag delimiter
+ * will be used only when prefixed tag is not empty. Example:
+ *
+ *   #define BT_LOG_TAG_FORMAT (S("["), TAG(".", ""), S("] "))
+ *
+ * See BT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#ifndef BT_LOG_MESSAGE_TAG_FORMAT
+       #define BT_LOG_MESSAGE_TAG_FORMAT \
+               (TAG(".", BT_LOG_DEF_DELIMITER))
+#endif
+/* Specifies log message source location format. It includes function name,
+ * file name and file line. Custom information can be added as well. Supported
+ * fields: FUNCTION, FILENAME, FILELINE, S(str), F_INIT(statements),
+ * F_UINT(width, value).
+ *
+ * See BT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#ifndef BT_LOG_MESSAGE_SRC_FORMAT
+       #define BT_LOG_MESSAGE_SRC_FORMAT \
+               (FUNCTION, S("@"), FILENAME, S(":"), FILELINE, S(BT_LOG_DEF_DELIMITER))
+#endif
+/* Fields that can be used in log message format specifications (see above).
+ * Mentioning them here explicitly, so we know that nobody else defined them
+ * before us. See BT_LOG_MESSAGE_CTX_FORMAT for details.
+ */
+#define YEAR YEAR
+#define MONTH MONTH
+#define DAY DAY
+#define MINUTE MINUTE
+#define SECOND SECOND
+#define MILLISECOND MILLISECOND
+#define PID PID
+#define TID TID
+#define LEVEL LEVEL
+#define TAG(prefix_delim, tag_delim) TAG(prefix_delim, tag_delim)
+#define FUNCTION FUNCTION
+#define FILENAME FILENAME
+#define FILELINE FILELINE
+#define S(str) S(str)
+#define F_INIT(statements) F_INIT(statements)
+#define F_UINT(width, value) F_UINT(width, value)
+/* Number of bytes to reserve for EOL in the log line buffer (must be >0).
+ * Must be larger than or equal to length of BT_LOG_EOL with terminating null.
+ */
+#ifndef BT_LOG_EOL_SZ
+       #define BT_LOG_EOL_SZ sizeof(BT_LOG_EOL)
+#endif
+/* Compile instrumented version of the library to facilitate unit testing.
+ */
+#ifndef BT_LOG_INSTRUMENTED
+       #define BT_LOG_INSTRUMENTED 0
+#endif
+
+#if defined(__linux__)
+       #if !defined(__ANDROID__) && !defined(_GNU_SOURCE)
+               #define _GNU_SOURCE
+       #endif
+#endif
+#if defined(__MINGW32__)
+       #ifdef __STRICT_ANSI__
+               #undef __STRICT_ANSI__
+       #endif
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <babeltrace/logging-internal.h>
+#include <babeltrace/logging.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+       #include <windows.h>
+#else
+       #include <unistd.h>
+       #include <sys/time.h>
+       #if defined(__linux__)
+               #include <linux/limits.h>
+       #else
+               #include <sys/syslimits.h>
+       #endif
+#endif
+
+#if defined(__linux__)
+       #include <sys/prctl.h>
+       #include <sys/types.h>
+       #if !defined(__ANDROID__)
+               #include <sys/syscall.h>
+       #endif
+#endif
+#if defined(__MACH__)
+       #include <pthread.h>
+#endif
+
+#define INLINE _BT_LOG_INLINE
+#define VAR_UNUSED(var) (void)var
+#define RETVAL_UNUSED(expr) do { while(expr) break; } while(0)
+#define STATIC_ASSERT(name, cond) \
+       typedef char assert_##name[(cond)? 1: -1]
+#define ASSERT_UNREACHABLE(why) assert(!sizeof(why))
+#ifndef _countof
+       #define _countof(xs) (sizeof(xs) / sizeof((xs)[0]))
+#endif
+
+#if BT_LOG_INSTRUMENTED
+       #define INSTRUMENTED_CONST
+#else
+       #define INSTRUMENTED_CONST const
+#endif
+
+#define _PP_PASTE_2(a, b) a ## b
+#define _PP_CONCAT_2(a, b) _PP_PASTE_2(a, b)
+
+#define _PP_PASTE_3(a, b, c) a ## b ## c
+#define _PP_CONCAT_3(a, b, c) _PP_PASTE_3(a, b, c)
+
+/* Microsoft C preprocessor is a piece of shit. This moron treats __VA_ARGS__
+ * as a single token and requires additional expansion to realize that it's
+ * actually a list. If not for it, there would be no need in this extra
+ * expansion.
+ */
+#define _PP_ID(x) x
+#define _PP_NARGS_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,...) _24
+#define _PP_NARGS(...) _PP_ID(_PP_NARGS_N(__VA_ARGS__,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
+
+/* There is a more efficient way to implement this, but it requires
+ * working C preprocessor. Unfortunately, Microsoft Visual Studio doesn't
+ * have one.
+ */
+#define _PP_HEAD__(x, ...) x
+#define _PP_HEAD_(...) _PP_ID(_PP_HEAD__(__VA_ARGS__, ~))
+#define _PP_HEAD(xs) _PP_HEAD_ xs
+#define _PP_TAIL_(x, ...) (__VA_ARGS__)
+#define _PP_TAIL(xs) _PP_TAIL_ xs
+#define _PP_UNTUPLE_(...) __VA_ARGS__
+#define _PP_UNTUPLE(xs) _PP_UNTUPLE_ xs
+
+/* Apply function macro to each element in tuple. Output is not
+ * enforced to be a tuple.
+ */
+#define _PP_MAP_1(f, xs) f(_PP_HEAD(xs))
+#define _PP_MAP_2(f, xs) f(_PP_HEAD(xs)) _PP_MAP_1(f, _PP_TAIL(xs))
+#define _PP_MAP_3(f, xs) f(_PP_HEAD(xs)) _PP_MAP_2(f, _PP_TAIL(xs))
+#define _PP_MAP_4(f, xs) f(_PP_HEAD(xs)) _PP_MAP_3(f, _PP_TAIL(xs))
+#define _PP_MAP_5(f, xs) f(_PP_HEAD(xs)) _PP_MAP_4(f, _PP_TAIL(xs))
+#define _PP_MAP_6(f, xs) f(_PP_HEAD(xs)) _PP_MAP_5(f, _PP_TAIL(xs))
+#define _PP_MAP_7(f, xs) f(_PP_HEAD(xs)) _PP_MAP_6(f, _PP_TAIL(xs))
+#define _PP_MAP_8(f, xs) f(_PP_HEAD(xs)) _PP_MAP_7(f, _PP_TAIL(xs))
+#define _PP_MAP_9(f, xs) f(_PP_HEAD(xs)) _PP_MAP_8(f, _PP_TAIL(xs))
+#define _PP_MAP_10(f, xs) f(_PP_HEAD(xs)) _PP_MAP_9(f, _PP_TAIL(xs))
+#define _PP_MAP_11(f, xs) f(_PP_HEAD(xs)) _PP_MAP_10(f, _PP_TAIL(xs))
+#define _PP_MAP_12(f, xs) f(_PP_HEAD(xs)) _PP_MAP_11(f, _PP_TAIL(xs))
+#define _PP_MAP_13(f, xs) f(_PP_HEAD(xs)) _PP_MAP_12(f, _PP_TAIL(xs))
+#define _PP_MAP_14(f, xs) f(_PP_HEAD(xs)) _PP_MAP_13(f, _PP_TAIL(xs))
+#define _PP_MAP_15(f, xs) f(_PP_HEAD(xs)) _PP_MAP_14(f, _PP_TAIL(xs))
+#define _PP_MAP_16(f, xs) f(_PP_HEAD(xs)) _PP_MAP_15(f, _PP_TAIL(xs))
+#define _PP_MAP_17(f, xs) f(_PP_HEAD(xs)) _PP_MAP_16(f, _PP_TAIL(xs))
+#define _PP_MAP_18(f, xs) f(_PP_HEAD(xs)) _PP_MAP_17(f, _PP_TAIL(xs))
+#define _PP_MAP_19(f, xs) f(_PP_HEAD(xs)) _PP_MAP_18(f, _PP_TAIL(xs))
+#define _PP_MAP_20(f, xs) f(_PP_HEAD(xs)) _PP_MAP_19(f, _PP_TAIL(xs))
+#define _PP_MAP_21(f, xs) f(_PP_HEAD(xs)) _PP_MAP_20(f, _PP_TAIL(xs))
+#define _PP_MAP_22(f, xs) f(_PP_HEAD(xs)) _PP_MAP_21(f, _PP_TAIL(xs))
+#define _PP_MAP_23(f, xs) f(_PP_HEAD(xs)) _PP_MAP_22(f, _PP_TAIL(xs))
+#define _PP_MAP_24(f, xs) f(_PP_HEAD(xs)) _PP_MAP_23(f, _PP_TAIL(xs))
+#define _PP_MAP(f, xs) _PP_CONCAT_2(_PP_MAP_, _PP_NARGS xs) (f, xs)
+
+/* Apply function macro to each element in tuple in reverse order.
+ * Output is not enforced to be a tuple.
+ */
+#define _PP_RMAP_1(f, xs) f(_PP_HEAD(xs))
+#define _PP_RMAP_2(f, xs) _PP_RMAP_1(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_3(f, xs) _PP_RMAP_2(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_4(f, xs) _PP_RMAP_3(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_5(f, xs) _PP_RMAP_4(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_6(f, xs) _PP_RMAP_5(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_7(f, xs) _PP_RMAP_6(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_8(f, xs) _PP_RMAP_7(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_9(f, xs) _PP_RMAP_8(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_10(f, xs) _PP_RMAP_9(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_11(f, xs) _PP_RMAP_10(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_12(f, xs) _PP_RMAP_11(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_13(f, xs) _PP_RMAP_12(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_14(f, xs) _PP_RMAP_13(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_15(f, xs) _PP_RMAP_14(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_16(f, xs) _PP_RMAP_15(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_17(f, xs) _PP_RMAP_16(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_18(f, xs) _PP_RMAP_17(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_19(f, xs) _PP_RMAP_18(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_20(f, xs) _PP_RMAP_19(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_21(f, xs) _PP_RMAP_20(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_22(f, xs) _PP_RMAP_21(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_23(f, xs) _PP_RMAP_22(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP_24(f, xs) _PP_RMAP_23(f, _PP_TAIL(xs)) f(_PP_HEAD(xs))
+#define _PP_RMAP(f, xs) _PP_CONCAT_2(_PP_RMAP_, _PP_NARGS xs) (f, xs)
+
+/* Used to implement _BT_LOG_MESSAGE_FORMAT_CONTAINS() macro. All possible
+ * fields must be mentioned here. Not counting F_INIT() here because it's
+ * somewhat special and is handled spearatly (at least for now).
+ */
+#define _BT_LOG_MESSAGE_FORMAT_MASK__             (0<<0)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__YEAR         (1<<1)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__MONTH        (1<<2)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__DAY          (1<<3)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__HOUR         (1<<4)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__MINUTE       (1<<5)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__SECOND       (1<<6)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__MILLISECOND  (1<<7)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__PID          (1<<8)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__TID          (1<<9)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__LEVEL        (1<<10)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__TAG(ps, ts)  (1<<11)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__FUNCTION     (1<<12)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__FILENAME     (1<<13)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__FILELINE     (1<<14)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__S(s)         (1<<15)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__F_INIT(expr) (0<<16)
+#define _BT_LOG_MESSAGE_FORMAT_MASK__F_UINT(w, v) (1<<17)
+#define _BT_LOG_MESSAGE_FORMAT_MASK(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_MASK_, _, field)
+
+/* Logical "or" of masks of fields used in specified format specification.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_FIELDS(format) \
+       (0 _PP_MAP(| _BT_LOG_MESSAGE_FORMAT_MASK, format))
+
+/* Expands to expressions that evaluates to true if field is used in
+ * specified format specification. Example:
+ *
+ *   #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(F_UINT, BT_LOG_MESSAGE_CTX_FORMAT)
+ *       ...
+ *   #endif
+ */
+#define _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, format) \
+       (_BT_LOG_MESSAGE_FORMAT_MASK(field) & _BT_LOG_MESSAGE_FORMAT_FIELDS(format))
+
+/* Same, but checks all supported format specifications.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_FIELD_USED(field) \
+       (_BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_TAG_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(field, BT_LOG_MESSAGE_SRC_FORMAT))
+
+#define _BT_LOG_MESSAGE_FORMAT_DATETIME_USED \
+       (_BT_LOG_MESSAGE_FORMAT_CONTAINS(YEAR, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MONTH, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(DAY, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(HOUR, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MINUTE, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(SECOND, BT_LOG_MESSAGE_CTX_FORMAT) || \
+        _BT_LOG_MESSAGE_FORMAT_CONTAINS(MILLISECOND, BT_LOG_MESSAGE_CTX_FORMAT))
+
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+       #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */
+       #define memccpy _memccpy
+#endif
+
+#if (defined(_MSC_VER) && !defined(__INTEL_COMPILER)) || defined(__MINGW64__)
+       #define vsnprintf(s, sz, fmt, va) fake_vsnprintf(s, sz, fmt, va)
+       static int fake_vsnprintf(char *s, size_t sz, const char *fmt, va_list ap)
+       {
+               const int n = vsnprintf_s(s, sz, _TRUNCATE, fmt, ap);
+               return 0 < n? n: (int)sz + 1; /* no need in _vscprintf() for now */
+       }
+       #if BT_LOG_OPTIMIZE_SIZE
+       #define snprintf(s, sz, ...) fake_snprintf(s, sz, __VA_ARGS__)
+       static int fake_snprintf(char *s, size_t sz, const char *fmt, ...)
+       {
+               va_list va;
+               va_start(va, fmt);
+               const int n = fake_vsnprintf(s, sz, fmt, va);
+               va_end(va);
+               return n;
+       }
+       #endif
+#endif
+
+typedef void (*time_cb)(struct tm *const tm, unsigned *const usec);
+typedef void (*pid_cb)(int *const pid, int *const tid);
+typedef void (*buffer_cb)(bt_log_message *msg, char *buf);
+
+typedef struct src_location
+{
+       const char *const func;
+       const char *const file;
+       const unsigned line;
+}
+src_location;
+
+typedef struct mem_block
+{
+       const void *const d;
+       const unsigned d_sz;
+}
+mem_block;
+
+static void time_callback(struct tm *const tm, unsigned *const usec);
+static void pid_callback(int *const pid, int *const tid);
+static void buffer_callback(bt_log_message *msg, char *buf);
+
+STATIC_ASSERT(eol_fits_eol_sz, sizeof(BT_LOG_EOL) <= BT_LOG_EOL_SZ);
+STATIC_ASSERT(eol_sz_greater_than_zero, 0 < BT_LOG_EOL_SZ);
+STATIC_ASSERT(eol_sz_less_than_buf_sz, BT_LOG_EOL_SZ < BT_LOG_BUF_SZ);
+#if !defined(_WIN32) && !defined(_WIN64)
+       STATIC_ASSERT(buf_sz_less_than_pipe_buf, BT_LOG_BUF_SZ <= PIPE_BUF);
+#endif
+static const char c_hex[] = "0123456789abcdef";
+
+static INSTRUMENTED_CONST unsigned g_buf_sz = BT_LOG_BUF_SZ - BT_LOG_EOL_SZ;
+static INSTRUMENTED_CONST time_cb g_time_cb = time_callback;
+static INSTRUMENTED_CONST pid_cb g_pid_cb = pid_callback;
+static INSTRUMENTED_CONST buffer_cb g_buffer_cb = buffer_callback;
+
+#if BT_LOG_USE_ANDROID_LOG
+       #include <android/log.h>
+
+       static INLINE int android_lvl(const int lvl)
+       {
+               switch (lvl)
+               {
+               case BT_LOG_VERBOSE:
+                       return ANDROID_LOG_VERBOSE;
+               case BT_LOG_DEBUG:
+                       return ANDROID_LOG_DEBUG;
+               case BT_LOG_INFO:
+                       return ANDROID_LOG_INFO;
+               case BT_LOG_WARN:
+                       return ANDROID_LOG_WARN;
+               case BT_LOG_ERROR:
+                       return ANDROID_LOG_ERROR;
+               case BT_LOG_FATAL:
+                       return ANDROID_LOG_FATAL;
+               default:
+                       ASSERT_UNREACHABLE("Bad log level");
+                       return ANDROID_LOG_UNKNOWN;
+               }
+       }
+
+       static void out_android_callback(const bt_log_message *const msg, void *arg)
+       {
+               VAR_UNUSED(arg);
+               *msg->p = 0;
+               const char *tag = msg->p;
+               if (msg->tag_e != msg->tag_b)
+               {
+                       tag = msg->tag_b;
+                       *msg->tag_e = 0;
+               }
+               __android_log_print(android_lvl(msg->lvl), tag, "%s", msg->msg_b);
+       }
+
+       enum { OUT_ANDROID_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
+       #define OUT_ANDROID OUT_ANDROID_MASK, 0, out_android_callback
+#endif
+
+#if BT_LOG_USE_NSLOG
+       #include <CoreFoundation/CoreFoundation.h>
+       CF_EXPORT void CFLog(int32_t level, CFStringRef format, ...);
+
+       static INLINE int apple_lvl(const int lvl)
+       {
+               switch (lvl)
+               {
+               case BT_LOG_VERBOSE:
+                       return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
+               case BT_LOG_DEBUG:
+                       return 7; /* ASL_LEVEL_DEBUG / kCFLogLevelDebug */;
+               case BT_LOG_INFO:
+                       return 6; /* ASL_LEVEL_INFO / kCFLogLevelInfo */;
+               case BT_LOG_WARN:
+                       return 4; /* ASL_LEVEL_WARNING / kCFLogLevelWarning */;
+               case BT_LOG_ERROR:
+                       return 3; /* ASL_LEVEL_ERR / kCFLogLevelError */;
+               case BT_LOG_FATAL:
+                       return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
+               default:
+                       ASSERT_UNREACHABLE("Bad log level");
+                       return 0; /* ASL_LEVEL_EMERG / kCFLogLevelEmergency */;
+               }
+       }
+
+       static void out_nslog_callback(const bt_log_message *const msg, void *arg)
+       {
+               VAR_UNUSED(arg);
+               *msg->p = 0;
+               CFLog(apple_lvl(msg->lvl), CFSTR("%s"), msg->tag_b);
+       }
+
+       enum { OUT_NSLOG_MASK = BT_LOG_PUT_STD & ~BT_LOG_PUT_CTX };
+       #define OUT_NSLOG OUT_NSLOG_MASK, 0, out_nslog_callback
+#endif
+
+#if BT_LOG_USE_DEBUGSTRING
+       #include <windows.h>
+
+       static void out_debugstring_callback(const bt_log_message *const msg, void *arg)
+       {
+               VAR_UNUSED(arg);
+               msg->p[0] = '\n';
+               msg->p[1] = '\0';
+               OutputDebugStringA(msg->buf);
+       }
+
+       enum { OUT_DEBUGSTRING_MASK = BT_LOG_PUT_STD };
+       #define OUT_DEBUGSTRING OUT_DEBUGSTRING_MASK, 0, out_debugstring_callback
+#endif
+
+BT_HIDDEN
+void bt_log_out_stderr_callback(const bt_log_message *const msg, void *arg)
+{
+       VAR_UNUSED(arg);
+       const size_t eol_len = sizeof(BT_LOG_EOL) - 1;
+       memcpy(msg->p, BT_LOG_EOL, eol_len);
+#if defined(_WIN32) || defined(_WIN64)
+       /* WriteFile() is atomic for local files opened with FILE_APPEND_DATA and
+          without FILE_WRITE_DATA */
+       WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg->buf,
+                         (DWORD)(msg->p - msg->buf + eol_len), 0, 0);
+#else
+       /* write() is atomic for buffers less than or equal to PIPE_BUF. */
+       RETVAL_UNUSED(write(STDERR_FILENO, msg->buf,
+                                               (size_t)(msg->p - msg->buf) + eol_len));
+#endif
+}
+
+static const bt_log_output out_stderr = {BT_LOG_OUT_STDERR};
+
+#if !BT_LOG_EXTERN_TAG_PREFIX
+       BT_LOG_DEFINE_TAG_PREFIX = 0;
+#endif
+
+#if !BT_LOG_EXTERN_GLOBAL_FORMAT
+       BT_LOG_DEFINE_GLOBAL_FORMAT = {BT_LOG_MEM_WIDTH};
+#endif
+
+#if !BT_LOG_EXTERN_GLOBAL_OUTPUT
+       #if BT_LOG_USE_ANDROID_LOG
+               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_ANDROID};
+       #elif BT_LOG_USE_NSLOG
+               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_NSLOG};
+       #elif BT_LOG_USE_DEBUGSTRING
+               BT_LOG_DEFINE_GLOBAL_OUTPUT = {OUT_DEBUGSTRING};
+       #else
+               BT_LOG_DEFINE_GLOBAL_OUTPUT = {BT_LOG_OUT_STDERR};
+       #endif
+#endif
+
+#if !BT_LOG_EXTERN_GLOBAL_OUTPUT_LEVEL
+       BT_LOG_DEFINE_GLOBAL_OUTPUT_LEVEL = 0;
+#endif
+
+const bt_log_spec _bt_log_stderr_spec =
+{
+       BT_LOG_GLOBAL_FORMAT,
+       &out_stderr,
+};
+
+static const bt_log_spec global_spec =
+{
+       BT_LOG_GLOBAL_FORMAT,
+       BT_LOG_GLOBAL_OUTPUT,
+};
+
+#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(LEVEL, BT_LOG_MESSAGE_CTX_FORMAT)
+static char lvl_char(const int lvl)
+{
+       switch (lvl)
+       {
+       case BT_LOG_VERBOSE:
+               return 'V';
+       case BT_LOG_DEBUG:
+               return 'D';
+       case BT_LOG_INFO:
+               return 'I';
+       case BT_LOG_WARN:
+               return 'W';
+       case BT_LOG_ERROR:
+               return 'E';
+       case BT_LOG_FATAL:
+               return 'F';
+       default:
+               ASSERT_UNREACHABLE("Bad log level");
+               return '?';
+       }
+}
+#endif
+
+#define GCCVER_LESS(MAJOR, MINOR, PATCH) \
+       (__GNUC__ < MAJOR || \
+               (__GNUC__ == MAJOR && (__GNUC_MINOR__ < MINOR || \
+                       (__GNUC_MINOR__ == MINOR && __GNUC_PATCHLEVEL__ < PATCH))))
+
+#if !defined(__clang__) && defined(__GNUC__) && GCCVER_LESS(4,7,0)
+       #define __atomic_load_n(vp, model) __sync_fetch_and_add(vp, 0)
+       #define __atomic_fetch_add(vp, n, model) __sync_fetch_and_add(vp, n)
+       #define __atomic_sub_fetch(vp, n, model) __sync_sub_and_fetch(vp, n)
+       #define __atomic_or_fetch(vp, n, model) __sync_or_and_fetch(vp, n)
+       #define __atomic_and_fetch(vp, n, model) __sync_and_and_fetch(vp, n)
+       /* Note: will not store old value of *vp in *ep (non-standard behaviour) */
+       #define __atomic_compare_exchange_n(vp, ep, d, weak, smodel, fmodel) \
+               __sync_bool_compare_and_swap(vp, *(ep), d)
+#endif
+
+#if !BT_LOG_OPTIMIZE_SIZE && !defined(_WIN32) && !defined(_WIN64)
+#define TCACHE
+#define TCACHE_STALE (0x40000000)
+#define TCACHE_FLUID (0x40000000 | 0x80000000)
+static unsigned g_tcache_mode = TCACHE_STALE;
+static struct timeval g_tcache_tv = {0, 0};
+static struct tm g_tcache_tm = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static INLINE int tcache_get(const struct timeval *const tv, struct tm *const tm)
+{
+       unsigned mode;
+       mode = __atomic_load_n(&g_tcache_mode, __ATOMIC_RELAXED);
+       if (0 == (mode & TCACHE_FLUID))
+       {
+               mode = __atomic_fetch_add(&g_tcache_mode, 1, __ATOMIC_ACQUIRE);
+               if (0 == (mode & TCACHE_FLUID))
+               {
+                       if (g_tcache_tv.tv_sec == tv->tv_sec)
+                       {
+                               *tm = g_tcache_tm;
+                               __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
+                               return !0;
+                       }
+                       __atomic_or_fetch(&g_tcache_mode, TCACHE_STALE, __ATOMIC_RELAXED);
+               }
+               __atomic_sub_fetch(&g_tcache_mode, 1, __ATOMIC_RELEASE);
+       }
+       return 0;
+}
+
+static INLINE void tcache_set(const struct timeval *const tv, struct tm *const tm)
+{
+       unsigned stale = TCACHE_STALE;
+       if (__atomic_compare_exchange_n(&g_tcache_mode, &stale, TCACHE_FLUID,
+                                                                       0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
+       {
+               g_tcache_tv = *tv;
+               g_tcache_tm = *tm;
+               __atomic_and_fetch(&g_tcache_mode, ~TCACHE_FLUID, __ATOMIC_RELEASE);
+       }
+}
+#endif
+
+static void time_callback(struct tm *const tm, unsigned *const msec)
+{
+#if !_BT_LOG_MESSAGE_FORMAT_DATETIME_USED
+       VAR_UNUSED(tm);
+       VAR_UNUSED(msec);
+#else
+       #if defined(_WIN32) || defined(_WIN64)
+       SYSTEMTIME st;
+       GetLocalTime(&st);
+       tm->tm_year = st.wYear;
+       tm->tm_mon = st.wMonth;
+       tm->tm_mday = st.wDay;
+       tm->tm_wday = st.wDayOfWeek;
+       tm->tm_hour = st.wHour;
+       tm->tm_min = st.wMinute;
+       tm->tm_sec = st.wSecond;
+       *msec = st.wMilliseconds;
+       #else
+       struct timeval tv;
+       gettimeofday(&tv, 0);
+               #ifndef TCACHE
+               localtime_r(&tv.tv_sec, tm);
+               #else
+               if (!tcache_get(&tv, tm))
+               {
+                       localtime_r(&tv.tv_sec, tm);
+                       tcache_set(&tv, tm);
+               }
+               #endif
+       *msec = (unsigned)tv.tv_usec / 1000;
+       #endif
+#endif
+}
+
+static void pid_callback(int *const pid, int *const tid)
+{
+#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT)
+       VAR_UNUSED(pid);
+#else
+       #if defined(_WIN32) || defined(_WIN64)
+       *pid = GetCurrentProcessId();
+       #else
+       *pid = getpid();
+       #endif
+#endif
+
+#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
+       VAR_UNUSED(tid);
+#else
+       #if defined(_WIN32) || defined(_WIN64)
+       *tid = GetCurrentThreadId();
+       #elif defined(__ANDROID__)
+       *tid = gettid();
+       #elif defined(__linux__)
+       *tid = syscall(SYS_gettid);
+       #elif defined(__MACH__)
+       *tid = (int)pthread_mach_thread_np(pthread_self());
+       #else
+               #define Platform not supported
+       #endif
+#endif
+}
+
+static void buffer_callback(bt_log_message *msg, char *buf)
+{
+       msg->e = (msg->p = msg->buf = buf) + g_buf_sz;
+}
+
+#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT)
+static const char *funcname(const char *func)
+{
+       return func? func: "";
+}
+#endif
+
+#if _BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT)
+static const char *filename(const char *file)
+{
+       const char *f = file;
+       for (const char *p = file; 0 != *p; ++p)
+       {
+               if ('/' == *p || '\\' == *p)
+               {
+                       f = p + 1;
+               }
+       }
+       return f;
+}
+#endif
+
+static INLINE size_t nprintf_size(bt_log_message *const msg)
+{
+       // *nprintf() always puts 0 in the end when input buffer is not empty. This
+       // 0 is not desired because its presence sets (ctx->p) to (ctx->e - 1) which
+       // leaves space for one more character. Some put_xxx() functions don't use
+       // *nprintf() and could use that last character. In that case log line will
+       // have multiple (two) half-written parts which is confusing. To workaround
+       // that we allow *nprintf() to write its 0 in the eol area (which is always
+       // not empty).
+       return (size_t)(msg->e - msg->p + 1);
+}
+
+static INLINE void put_nprintf(bt_log_message *const msg, const int n)
+{
+       if (0 < n)
+       {
+               msg->p = n < msg->e - msg->p? msg->p + n: msg->e;
+       }
+}
+
+static INLINE char *put_padding_r(const unsigned w, const char wc,
+                                                                 char *p, char *e)
+{
+       for (char *const b = e - w; b < p; *--p = wc) {}
+       return p;
+}
+
+static char *put_integer_r(unsigned v, const int sign,
+                                                  const unsigned w, const char wc, char *const e)
+{
+       static const char _signs[] = {'-', '0', '+'};
+       static const char *const signs = _signs + 1;
+       char *p = e;
+       do { *--p = '0' + v % 10; } while (0 != (v /= 10));
+       if (0 == sign) return put_padding_r(w, wc, p, e);
+       if ('0' != wc)
+       {
+               *--p = signs[sign];
+               return put_padding_r(w, wc, p, e);
+       }
+       p = put_padding_r(w, wc, p, e + 1);
+       *--p = signs[sign];
+       return p;
+}
+
+static INLINE char *put_uint_r(const unsigned v, const unsigned w, const char wc,
+                                                          char *const e)
+{
+       return put_integer_r(v, 0, w, wc, e);
+}
+
+static INLINE char *put_int_r(const int v, const unsigned w, const char wc,
+                                                         char *const e)
+{
+       return 0 <= v? put_integer_r((unsigned)v, 0, w, wc, e)
+                                : put_integer_r((unsigned)-v, -1, w, wc, e);
+}
+
+static INLINE char *put_stringn(const char *const s_p, const char *const s_e,
+                                                               char *const p, char *const e)
+{
+       const ptrdiff_t m = e - p;
+       ptrdiff_t n = s_e - s_p;
+       if (n > m)
+       {
+               n = m;
+       }
+       memcpy(p, s_p, n);
+       return p + n;
+}
+
+static INLINE char *put_string(const char *s, char *p, char *const e)
+{
+       const ptrdiff_t n = e - p;
+       char *const c = (char *)memccpy(p, s, '\0', n);
+       return 0 != c? c - 1: e;
+}
+
+static INLINE char *put_uint(unsigned v, const unsigned w, const char wc,
+                                                        char *const p, char *const e)
+{
+       char buf[16];
+       char *const se = buf + _countof(buf);
+       char *sp = put_uint_r(v, w, wc, se);
+       return put_stringn(sp, se, p, e);
+}
+
+#define PUT_CSTR_R(p, STR) \
+       do { \
+               for (unsigned i = sizeof(STR) - 1; 0 < i--;) { \
+                       *--(p) = (STR)[i]; \
+               } \
+       } _BT_LOG_ONCE
+
+#define PUT_CSTR_CHECKED(p, e, STR) \
+       do { \
+               for (unsigned i = 0; (e) > (p) && (sizeof(STR) - 1) > i; ++i) { \
+                       *(p)++ = (STR)[i]; \
+               } \
+       } _BT_LOG_ONCE
+
+/* F_INIT field support.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_INIT__
+#define _BT_LOG_MESSAGE_FORMAT_INIT__YEAR
+#define _BT_LOG_MESSAGE_FORMAT_INIT__MONTH
+#define _BT_LOG_MESSAGE_FORMAT_INIT__DAY
+#define _BT_LOG_MESSAGE_FORMAT_INIT__HOUR
+#define _BT_LOG_MESSAGE_FORMAT_INIT__MINUTE
+#define _BT_LOG_MESSAGE_FORMAT_INIT__SECOND
+#define _BT_LOG_MESSAGE_FORMAT_INIT__MILLISECOND
+#define _BT_LOG_MESSAGE_FORMAT_INIT__PID
+#define _BT_LOG_MESSAGE_FORMAT_INIT__TID
+#define _BT_LOG_MESSAGE_FORMAT_INIT__LEVEL
+#define _BT_LOG_MESSAGE_FORMAT_INIT__TAG(ps, ts)
+#define _BT_LOG_MESSAGE_FORMAT_INIT__FUNCTION
+#define _BT_LOG_MESSAGE_FORMAT_INIT__FILENAME
+#define _BT_LOG_MESSAGE_FORMAT_INIT__FILELINE
+#define _BT_LOG_MESSAGE_FORMAT_INIT__S(s)
+#define _BT_LOG_MESSAGE_FORMAT_INIT__F_INIT(expr) _PP_UNTUPLE(expr);
+#define _BT_LOG_MESSAGE_FORMAT_INIT__F_UINT(w, v)
+#define _BT_LOG_MESSAGE_FORMAT_INIT(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_INIT_, _, field)
+
+/* Implements generation of printf-like format string for log message
+ * format specification.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__             ""
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__YEAR         "%04u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MONTH        "%02u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__DAY          "%02u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__HOUR         "%02u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MINUTE       "%02u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__SECOND       "%02u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__MILLISECOND  "%03u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__PID          "%5i"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TID          "%5i"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__LEVEL        "%c"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__TAG          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FUNCTION     "%s"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILENAME     "%s"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__FILELINE     "%u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__S(s)         s
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_INIT(expr) ""
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT__F_UINT(w, v) "%" #w "u"
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_FMT(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT_, _, field)
+
+/* Implements generation of printf-like format parameters for log message
+ * format specification.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__YEAR         ,(unsigned)(tm.tm_year + 1900)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MONTH        ,(unsigned)(tm.tm_mon + 1)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__DAY          ,(unsigned)tm.tm_mday
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__HOUR         ,(unsigned)tm.tm_hour
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MINUTE       ,(unsigned)tm.tm_min
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__SECOND       ,(unsigned)tm.tm_sec
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__MILLISECOND  ,(unsigned)msec
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__PID          ,pid
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TID          ,tid
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__LEVEL        ,(char)lvl_char(msg->lvl)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__TAG          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FUNCTION     ,funcname(src->func)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILENAME     ,filename(src->file)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__FILELINE     ,src->line
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__S(s)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_INIT(expr)
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL__F_UINT(w, v) ,v
+#define _BT_LOG_MESSAGE_FORMAT_PRINTF_VAL(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL_, _, field)
+
+/* Implements generation of put_xxx_t statements for log message specification.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__YEAR         p = put_uint_r(tm.tm_year + 1900, 4, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MONTH        p = put_uint_r((unsigned)tm.tm_mon + 1, 2, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__DAY          p = put_uint_r((unsigned)tm.tm_mday, 2, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__HOUR         p = put_uint_r((unsigned)tm.tm_hour, 2, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MINUTE       p = put_uint_r((unsigned)tm.tm_min, 2, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__SECOND       p = put_uint_r((unsigned)tm.tm_sec, 2, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__MILLISECOND  p = put_uint_r(msec, 3, '0', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__PID          p = put_int_r(pid, 5, ' ', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TID          p = put_int_r(tid, 5, ' ', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__LEVEL        *--p = lvl_char(msg->lvl);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__TAG          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FUNCTION     UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILENAME     UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__FILELINE     UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__S(s)         PUT_CSTR_R(p, s);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_INIT(expr)
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R__F_UINT(w, v) p = put_uint_r(v, w, ' ', p);
+#define _BT_LOG_MESSAGE_FORMAT_PUT_R(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_R_, _, field)
+
+static void put_ctx(bt_log_message *const msg)
+{
+       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_CTX_FORMAT)
+#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_CTX_FORMAT)
+       VAR_UNUSED(msg);
+#else
+       #if _BT_LOG_MESSAGE_FORMAT_DATETIME_USED
+       struct tm tm;
+       unsigned msec;
+       g_time_cb(&tm, &msec);
+       #endif
+       #if _BT_LOG_MESSAGE_FORMAT_CONTAINS(PID, BT_LOG_MESSAGE_CTX_FORMAT) || \
+               _BT_LOG_MESSAGE_FORMAT_CONTAINS(TID, BT_LOG_MESSAGE_CTX_FORMAT)
+       int pid, tid;
+       g_pid_cb(&pid, &tid);
+       #endif
+
+       #if BT_LOG_OPTIMIZE_SIZE
+       int n;
+       n = snprintf(msg->p, nprintf_size(msg),
+                                _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_CTX_FORMAT)
+                 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_CTX_FORMAT));
+       put_nprintf(msg, n);
+       #else
+       char buf[64];
+       char *const e = buf + sizeof(buf);
+       char *p = e;
+       _PP_RMAP(_BT_LOG_MESSAGE_FORMAT_PUT_R, BT_LOG_MESSAGE_CTX_FORMAT)
+       msg->p = put_stringn(p, e, msg->p, msg->e);
+       #endif
+#endif
+}
+
+#define PUT_TAG(msg, tag, prefix_delim, tag_delim) \
+       do { \
+               const char *ch; \
+               msg->tag_b = msg->p; \
+               if (0 != (ch = _bt_log_tag_prefix)) { \
+                       for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
+               } \
+               if (0 != (ch = tag) && 0 != tag[0]) { \
+                       if (msg->tag_b != msg->p) { \
+                               PUT_CSTR_CHECKED(msg->p, msg->e, prefix_delim); \
+                       } \
+                       for (;msg->e != msg->p && 0 != (*msg->p = *ch); ++msg->p, ++ch) {} \
+               } \
+               msg->tag_e = msg->p; \
+               if (msg->tag_b != msg->p) { \
+                       PUT_CSTR_CHECKED(msg->p, msg->e, tag_delim); \
+               } \
+       } _BT_LOG_ONCE
+
+/* Implements simple put statements for log message specification.
+ */
+#define _BT_LOG_MESSAGE_FORMAT_PUT__
+#define _BT_LOG_MESSAGE_FORMAT_PUT__YEAR         UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__MONTH        UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__DAY          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__HOUR         UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__MINUTE       UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__SECOND       UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__MILLISECOND  UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__PID          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__TID          UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__LEVEL        UNDEFINED
+#define _BT_LOG_MESSAGE_FORMAT_PUT__TAG(pd, td)  PUT_TAG(msg, tag, pd, td);
+#define _BT_LOG_MESSAGE_FORMAT_PUT__FUNCTION     msg->p = put_string(funcname(src->func), msg->p, msg->e);
+#define _BT_LOG_MESSAGE_FORMAT_PUT__FILENAME     msg->p = put_string(filename(src->file), msg->p, msg->e);
+#define _BT_LOG_MESSAGE_FORMAT_PUT__FILELINE     msg->p = put_uint(src->line, 0, '\0', msg->p, msg->e);
+#define _BT_LOG_MESSAGE_FORMAT_PUT__S(s)         PUT_CSTR_CHECKED(msg->p, msg->e, s);
+#define _BT_LOG_MESSAGE_FORMAT_PUT__F_INIT(expr)
+#define _BT_LOG_MESSAGE_FORMAT_PUT__F_UINT(w, v) msg->p = put_uint(v, w, ' ', msg->p, msg->e);
+#define _BT_LOG_MESSAGE_FORMAT_PUT(field) \
+       _PP_CONCAT_3(_BT_LOG_MESSAGE_FORMAT_PUT_, _, field)
+
+static void put_tag(bt_log_message *const msg, const char *const tag)
+{
+       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_TAG_FORMAT)
+#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT)
+       VAR_UNUSED(tag);
+#endif
+#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_TAG_FORMAT)
+       VAR_UNUSED(msg);
+#else
+       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_TAG_FORMAT)
+#endif
+}
+
+static void put_src(bt_log_message *const msg, const src_location *const src)
+{
+       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_INIT, BT_LOG_MESSAGE_SRC_FORMAT)
+#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FUNCTION, BT_LOG_MESSAGE_SRC_FORMAT) && \
+       !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILENAME, BT_LOG_MESSAGE_SRC_FORMAT) && \
+       !_BT_LOG_MESSAGE_FORMAT_CONTAINS(FILELINE, BT_LOG_MESSAGE_SRC_FORMAT)
+       VAR_UNUSED(src);
+#endif
+#if !_BT_LOG_MESSAGE_FORMAT_FIELDS(BT_LOG_MESSAGE_SRC_FORMAT)
+       VAR_UNUSED(msg);
+#else
+       #if BT_LOG_OPTIMIZE_SIZE
+       int n;
+       n = snprintf(msg->p, nprintf_size(msg),
+                                _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_FMT, BT_LOG_MESSAGE_SRC_FORMAT)
+                 _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PRINTF_VAL, BT_LOG_MESSAGE_SRC_FORMAT));
+       put_nprintf(msg, n);
+       #else
+       _PP_MAP(_BT_LOG_MESSAGE_FORMAT_PUT, BT_LOG_MESSAGE_SRC_FORMAT)
+       #endif
+#endif
+}
+
+static void put_msg(bt_log_message *const msg,
+                                       const char *const fmt, va_list va)
+{
+       int n;
+       msg->msg_b = msg->p;
+       n = vsnprintf(msg->p, nprintf_size(msg), fmt, va);
+       put_nprintf(msg, n);
+}
+
+static void output_mem(const bt_log_spec *log, bt_log_message *const msg,
+                                          const mem_block *const mem)
+{
+       if (0 == mem->d || 0 == mem->d_sz)
+       {
+               return;
+       }
+       const unsigned char *mem_p = (const unsigned char *)mem->d;
+       const unsigned char *const mem_e = mem_p + mem->d_sz;
+       const unsigned char *mem_cut;
+       const ptrdiff_t mem_width = (ptrdiff_t)log->format->mem_width;
+       char *const hex_b = msg->msg_b;
+       char *const ascii_b = hex_b + 2 * mem_width + 2;
+       char *const ascii_e = ascii_b + mem_width;
+       if (msg->e < ascii_e)
+       {
+               return;
+       }
+       while (mem_p != mem_e)
+       {
+               char *hex = hex_b;
+               char *ascii = ascii_b;
+               for (mem_cut = mem_width < mem_e - mem_p? mem_p + mem_width: mem_e;
+                        mem_cut != mem_p; ++mem_p)
+               {
+                       const unsigned char ch = *mem_p;
+                       *hex++ = c_hex[(0xf0 & ch) >> 4];
+                       *hex++ = c_hex[(0x0f & ch)];
+                       *ascii++ = isprint(ch)? (char)ch: '?';
+               }
+               while (hex != ascii_b)
+               {
+                       *hex++ = ' ';
+               }
+               msg->p = ascii;
+               log->output->callback(msg, log->output->arg);
+       }
+}
+
+BT_HIDDEN
+void bt_log_set_tag_prefix(const char *const prefix)
+{
+       _bt_log_tag_prefix = prefix;
+}
+
+BT_HIDDEN
+void bt_log_set_mem_width(const unsigned w)
+{
+       _bt_log_global_format.mem_width = w;
+}
+
+BT_HIDDEN
+void bt_log_set_output_level(const int lvl)
+{
+       _bt_log_global_output_lvl = lvl;
+}
+
+BT_HIDDEN
+void bt_log_set_output_v(const unsigned mask, void *const arg,
+                                                const bt_log_output_cb callback)
+{
+       _bt_log_global_output.mask = mask;
+       _bt_log_global_output.arg = arg;
+       _bt_log_global_output.callback = callback;
+}
+
+static void _bt_log_write_imp(
+               const bt_log_spec *log,
+               const src_location *const src, const mem_block *const mem,
+               const int lvl, const char *const tag, const char *const fmt, va_list va)
+{
+       bt_log_message msg;
+       char buf[BT_LOG_BUF_SZ];
+       const unsigned mask = log->output->mask;
+       msg.lvl = lvl;
+       msg.tag = tag;
+       g_buffer_cb(&msg, buf);
+       if (BT_LOG_PUT_CTX & mask)
+       {
+               put_ctx(&msg);
+       }
+       if (BT_LOG_PUT_TAG & mask)
+       {
+               put_tag(&msg, tag);
+       }
+       if (0 != src && BT_LOG_PUT_SRC & mask)
+       {
+               put_src(&msg, src);
+       }
+       if (BT_LOG_PUT_MSG & mask)
+       {
+               put_msg(&msg, fmt, va);
+       }
+       log->output->callback(&msg, log->output->arg);
+       if (0 != mem && BT_LOG_PUT_MSG & mask)
+       {
+               output_mem(log, &msg, mem);
+       }
+}
+
+BT_HIDDEN
+void _bt_log_write_d(
+               const char *const func, const char *const file, const unsigned line,
+               const int lvl, const char *const tag,
+               const char *const fmt, ...)
+{
+       const src_location src = {func, file, line};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(&global_spec, &src, 0, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_aux_d(
+               const char *const func, const char *const file, const unsigned line,
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const char *const fmt, ...)
+{
+       const src_location src = {func, file, line};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(log, &src, 0, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write(const int lvl, const char *const tag,
+                                  const char *const fmt, ...)
+{
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(&global_spec, 0, 0, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_aux(
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const char *const fmt, ...)
+{
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(log, 0, 0, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_mem_d(
+               const char *const func, const char *const file, const unsigned line,
+               const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...)
+{
+       const src_location src = {func, file, line};
+       const mem_block mem = {d, d_sz};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(&global_spec, &src, &mem, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_mem_aux_d(
+               const char *const func, const char *const file, const unsigned line,
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...)
+{
+       const src_location src = {func, file, line};
+       const mem_block mem = {d, d_sz};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(log, &src, &mem, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_mem(const int lvl, const char *const tag,
+                                          const void *const d, const unsigned d_sz,
+                                          const char *const fmt, ...)
+{
+       const mem_block mem = {d, d_sz};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(&global_spec, 0, &mem, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+BT_HIDDEN
+void _bt_log_write_mem_aux(
+               const bt_log_spec *const log, const int lvl, const char *const tag,
+               const void *const d, const unsigned d_sz,
+               const char *const fmt, ...)
+{
+       const mem_block mem = {d, d_sz};
+       va_list va;
+       va_start(va, fmt);
+       _bt_log_write_imp(log, 0, &mem, lvl, tag, fmt, va);
+       va_end(va);
+}
This page took 0.057251 seconds and 4 git commands to generate.