logging: strip down and clean `log.h` and `log.c`
authorSimon Marchi <simon.marchi@efficios.com>
Mon, 11 Dec 2023 20:04:49 +0000 (15:04 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Thu, 14 Dec 2023 15:57:04 +0000 (10:57 -0500)
`log.h` slowly became a mix of the original zf_log [1] file and our own
changes over that.

I find that when I need to make changes to those files, I must consider
lots of features that we just never used and probably never will.
Amongst them are:

* Portability with MSVC and Android.

* Default values (`BT_LOG_DEF_*`).

* Tag prefix.

* Custom source location format.

* Censoring.

* All the global vs. local state, for example the global output level,
  all the BT_LOG*_AUX() macros.

* Library prefix (we just hardcoded `BT_` and `bt_` long ago).

* Mask of log message parts.

* Custom output callback.

Moreover, some of those features add execution time. For example,
logging always goes through vsnprintf(), even if you want to log a
simple string without any sophisticated formatting. Also, the actual
write() call is done through a callback because the output is
configurable. So is stuff like time formatting. We just don't need that.

This patch strips down those two files to make future changes easier. In
fact, it's pretty much a rewrite, but honors the current API, except for
the BT_LOG_WRITE*() functions (renamed, see below) and for
`BT_MINIMAL_LOG_LEVEL` which I renamed to `BT_LOG_MINIMAL_LEVEL` to keep
the same prefix.

This patch also moves everything which doesn't depend on user
definitions to `log-api.h`, and renames `log.c` to `log-api.c` for
consistency.

The user definitions are `BT_LOG_OUTPUT_LEVEL` and `BT_LOG_TAG`, which
you normally use as such:

    #define BT_LOG_OUTPUT_LEVEL my_level
    #define BT_LOG_TAG          "MY-TAG"
    #include "logging/log.h"

This gives access to BT_LOGI() and such which rely on those.

I think it's cleaner to separate the independent stuff. For example, not
that it's a true use case, but you could want to use the `log.h` macros
as well as another API which relies on `logging/log-api.h` at the same
time:

    #include "my-logger.hpp"

    #define BT_LOG_OUTPUT_LEVEL my_level
    #define BT_LOG_TAG          "MY-TAG"
    #include "logging/log.h"

Then an instance of some logger class in `my-logger.hpp` may have its
own independent output level and tag. Without this patch, you would need
to know that `my-logger.hpp` includes `logging/log.h` itself and
therefore make sure to define `BT_LOG_OUTPUT_LEVEL` and `BT_LOG_TAG`
before you include it:

    #define BT_LOG_OUTPUT_LEVEL my_level
    #define BT_LOG_TAG          "MY-TAG"
    #include "my-logger.hpp"

This works when you know it, but I believe it's less straightforward.

Now, the `log-api.h` interface is much simpler. The only mandatory
definition is `BT_LOG_MINIMAL_LEVEL`.

bt_log_write():
    Logs a message (no formatting).

bt_log_write_printf():
    Formats a message and logs it.

bt_log_write_mem():
    Logs a message (no formatting) and dumps a memory block.

bt_log_write_mem_printf():
    Formats a message, logs it, and dumps a memory block.

bt_log_write_errno():
    Logs an initial message, the string of the current `errno`, and
    a message (no formatting).

bt_log_write_errno_printf():
    Logs an initial message, the string of the current `errno`, and
    a formatted message.

All the functions above accept a file name, a function name, a line
number, a log level, and a tag. They log unconditionally, meaning they
don't check any current (run-time) log level. Use BT_LOG_ON_CUR_LVL() or
BT_LOG_ON() for that (unchanged).

All the BT_LOG_WRITE*() macros expand to calling the functions above. I
didn't change the BT_LOGT*(), BT_LOGD*(), BT_LOGI*(), BT_LOGW*(),
BT_LOGE*(), and BT_LOGF*() ones, but the BT_LOG_WRITE*() ones now have
`PRINTF` in their name when there's formatting (they didn't before):

* BT_LOG_WRITE_CUR_LVL()
* BT_LOG_WRITE()
* BT_LOG_WRITE_PRINTF_CUR_LVL()
* BT_LOG_WRITE_PRINTF()
* BT_LOG_WRITE_MEM_CUR_LVL()
* BT_LOG_WRITE_MEM()
* BT_LOG_WRITE_MEM_PRINTF_CUR_LVL()
* BT_LOG_WRITE_MEM_PRINTF()
* BT_LOG_WRITE_ERRNO_CUR_LVL()
* BT_LOG_WRITE_ERRNO()
* BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL()
* BT_LOG_WRITE_ERRNO_PRINTF()

Everything else is meant to work as before.

`log-api.c` is considerably simpler (than the previous `log.c`),
implementing exactly what we need in the project and no more. The only
output format is the current colored one (not configurable) and the only
destination is the standard error stream.

I did rewrite the memory block printing while being here. It used to
look like this:

    554889e5534883ec4864488b042528000000488945e831c04883ec086a17488d  UH??SH??HdH??%(???H?E?1?H???j?H?
    0520c900005068800000004c8d0dceffffff4c8d05f3c80000b903000000ba22  ? ???Ph????L??????L????????????"
    000000488d05e7c800004889c6488d05ecc800004889c7b800000000e8a6bb00  ???H??????H??H??????H???????????
    004883c420488d45d0be000000004889c7e88a090000488d45d04889c7e8e715  ?H?? H?E??????H???????H?E?H?????

and now looks like this:

    55 48 89 e5 53 48 83 ec 48 64 48 8b 04 25 28 00 | UH..SH..HdH..%(.
    00 00 48 89 45 e8 31 c0 48 83 ec 08 6a 17 48 8d | ..H.E.1.H...j.H.
    05 50 b9 00 00 50 6a 64 4c 8d 0d d1 ff ff ff 4c | .P...PjdL......L
    8d 05 26 b9 00 00 b9 03 00 00 00 ba 22 00 00 00 | ..&........."...
    48 8d 05 1a b9 00 00 48 89 c6 48 8d 05 15 b9 00 | H......H..H.....
    00 48 89 c7 b8 00 00 00 00 e8 98 ac 00 00 48 83 | .H............H.
    c4 20 48 8d                                     | . H.

I find that my version is more readable. The number of bytes/line is
fixed (16, like the default of `od` and `hexdump`).

I made the formatted date/time string cache much more simple, simply
using a TLS variable. Now it's just:

    struct date_time_cache {
        uint64_t s;
        uint32_t ms;
        char str[128];
    };

    static __thread struct date_time_cache date_time_cache = {0};

We're already using a TLS variable for the message buffer.

`log.c` used to call WriteFile(), GetLocalTime(), and
GetCurrentProcessId(), but I believe we have access to some MinGW magic
making it possible to call write(), gettimeofday(), and getpid(). The
TID part remains very platform-specific.

If we ever need new logging features, we can easily implement them, or
cherry-pick them from zf_log if they exist.

Project files are adapted here and there, mostly to match the few
renamed functions and macros.

[1]: https://github.com/wonder-mice/zf_log

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: Icee002ccad33be0c610aeabfb39c4f8b1fe5ccb2
Reviewed-on: https://review.lttng.org/c/babeltrace/+/11398
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Simon Marchi <simon.marchi@efficios.com>
CI-Build: Simon Marchi <simon.marchi@efficios.com>

32 files changed:
configure.ac
src/Makefile.am
src/autodisc/autodisc.c
src/bindings/python/bt2/bt2/native_bt_autodisc.i.h
src/bindings/python/bt2/bt2/native_bt_component_class.i.h
src/bindings/python/bt2/bt2/native_bt_error.i.h
src/cli/babeltrace2.c
src/cli/logging.cpp
src/cli/logging.h
src/common/common.h
src/compat/mman.c
src/compat/socket.h
src/ctf-writer/assert-pre.h
src/lib/assert-cond-base.h
src/lib/assert-cond.c
src/lib/lib-logging.c
src/lib/logging.c
src/lib/logging.h
src/logging/comp-logging.h
src/logging/log-api.c [new file with mode: 0644]
src/logging/log-api.h [new file with mode: 0644]
src/logging/log.c [deleted file]
src/logging/log.h
src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp
src/plugins/ctf/common/metadata/decoder.cpp
src/plugins/ctf/common/metadata/logging.cpp
src/plugins/ctf/fs-src/fs.cpp
src/plugins/ctf/fs-src/query.cpp
src/plugins/ctf/lttng-live/viewer-connection.cpp
src/python-plugin-provider/python-plugin-provider.c
src/string-format/format-error.c
tests/plugins/flt.lttng-utils.debug-info/test-bin-info.c

index a15ce6e665d10c31e9bc5eb3ab77069f1150dcbc..03ac73b8977071dbb4f6c6b7e1bdca17e60094ce 100644 (file)
@@ -380,7 +380,7 @@ AS_IF([test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "TRACE" && \
        test "$BABELTRACE_MINIMAL_LOG_LEVEL" != "INFO"],
   [AC_MSG_ERROR([Invalid BABELTRACE_MINIMAL_LOG_LEVEL value ($BABELTRACE_MINIMAL_LOG_LEVEL): use TRACE, DEBUG, or INFO.])]
 )
-AC_DEFINE_UNQUOTED([BT_MINIMAL_LOG_LEVEL], [BT_LOG_$BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level])
+AC_DEFINE_UNQUOTED([BT_LOG_MINIMAL_LEVEL], [__BT_LOGGING_LEVEL_$BABELTRACE_MINIMAL_LOG_LEVEL], [Minimal log level])
 
 # BABELTRACE_DEV_MODE:
 AC_ARG_VAR([BABELTRACE_DEV_MODE], [Set to 1 to enable the Babeltrace developer mode (enables run-time checks for plugin developers)])
index e323856bb324a2b791eba450f2b87579c56d1868..46b8633010f3f0a2b9c72a20a5415e3b9c605358 100644 (file)
@@ -168,7 +168,8 @@ fd_cache_libfd_cache_la_SOURCES = \
 
 logging_liblogging_la_SOURCES = \
        logging/comp-logging.h \
-       logging/log.c \
+       logging/log-api.c \
+       logging/log-api.h \
        logging/log.h
 
 param_parse_libparam_parse_la_SOURCES = \
index 0ae53817866924f16e74b0da9b471ccb715f1c01..ed822ec70afb3509399aefedae8cd22d7a209975 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 #define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
-#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_OUTPUT_LEVEL ((enum bt_log_level) log_level)
 #include "logging/log.h"
 
 #include <stdbool.h>
@@ -14,9 +14,9 @@
 #include "common/common.h"
 
 #define BT_AUTODISC_LOG_AND_APPEND(_lvl, _fmt, ...)                            \
-       do {                                                            \
-               BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);    \
-               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
+       do {                                                                    \
+               BT_LOG_WRITE_PRINTF(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);     \
+               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(       \
                        "Source auto-discovery", _fmt, ##__VA_ARGS__);          \
        } while (0)
 
index d6a4d24bbe1ae0d3f089ee6639ac8ca081111d43..9b12e8c368a333199fcf7be6aff13694584b667e 100644 (file)
@@ -86,7 +86,7 @@ bt_value *bt_bt2_auto_discover_source_components(const bt_value *inputs,
                plugins,
                plugin_count,
                NULL,
-               bt_python_bindings_bt2_log_level,
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
                &auto_disc,
                NULL);
        if (status != 0) {
index 8e284c0401f8281d66bfe94ecfb718cc76b3c059..628d58679b90f54349fa207e9ed25d2e641e3ec2 100644 (file)
@@ -298,7 +298,8 @@ component_class_get_supported_mip_versions(
 
        py_cls = lookup_cc_ptr_to_py_cls(component_class);
        if (!py_cls) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Cannot find Python class associated to native component class: "
                        "comp-cls-addr=%p", component_class);
                goto error;
@@ -307,7 +308,8 @@ component_class_get_supported_mip_versions(
        py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params),
                SWIGTYPE_p_bt_value, 0);
        if (!py_params_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
@@ -324,7 +326,8 @@ component_class_get_supported_mip_versions(
                py_params_ptr, init_method_data ? init_method_data : Py_None,
                (int) log_level);
        if (!py_range_set_addr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Failed to call Python class's _bt_get_supported_mip_versions_from_native() method: "
                        "py-cls-addr=%p", py_cls);
                status = py_exc_to_status_component_class_clear(self_component_class,
@@ -355,7 +358,8 @@ component_class_get_supported_mip_versions(
                        bt_integer_range_unsigned_get_lower(range),
                        bt_integer_range_unsigned_get_upper(range));
                if (add_range_status) {
-                       BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+                       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                               (enum bt_log_level) log_level, BT_LOG_TAG,
                                "Failed to add range to supported MIP versions range set.");
                        goto error;
                }
@@ -939,7 +943,8 @@ bt_component_class_query_method_status component_class_query(
 
        py_cls = lookup_cc_ptr_to_py_cls(component_class);
        if (!py_cls) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Cannot find Python class associated to native component class: "
                        "comp-cls-addr=%p", component_class);
                goto error;
@@ -948,7 +953,8 @@ bt_component_class_query_method_status component_class_query(
        py_params_ptr = SWIG_NewPointerObj(SWIG_as_voidptr(params),
                SWIGTYPE_p_bt_value, 0);
        if (!py_params_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
@@ -957,14 +963,16 @@ bt_component_class_query_method_status component_class_query(
                SWIG_as_voidptr(priv_query_executor),
                SWIGTYPE_p_bt_private_query_executor, 0);
        if (!py_priv_query_exec_ptr) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        BT_FMT_SWIG_ALLOC_FAILED);
                goto error;
        }
 
        py_object = SWIG_FromCharPtr(object);
        if (!py_object) {
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR,
+                       (enum bt_log_level) log_level, BT_LOG_TAG,
                        "Failed to create a Python string.");
                goto error;
        }
@@ -985,8 +993,9 @@ bt_component_class_query_method_status component_class_query(
                        log_level);
                if (status < 0) {
 #define BT_FMT "Failed to call Python class's _bt_query_from_native() method: py-cls-addr=%p"
-                       BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, log_level, BT_LOG_TAG,
-                               BT_FMT, py_cls);
+                       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING,
+                               (enum bt_log_level) log_level,
+                               BT_LOG_TAG, BT_FMT, py_cls);
                        BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT_CLASS(
                                self_component_class, BT_FMT, py_cls);
 #undef BT_FMT
index 053941517829a807e59f6a5a9260b5a49a85c101..78eeb28ae5e9d39a8c1aecb724e0544bf8e5e61e 100644 (file)
@@ -13,7 +13,8 @@ PyObject *bt_bt2_format_bt_error_cause(const bt_error_cause *error_cause)
        PyObject *py_error_cause_str = NULL;
 
        error_cause_str = format_bt_error_cause(error_cause, 80,
-               bt_python_bindings_bt2_log_level, BT_COMMON_COLOR_WHEN_NEVER);
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
+               BT_COMMON_COLOR_WHEN_NEVER);
        BT_ASSERT(error_cause_str);
 
        py_error_cause_str = PyString_FromString(error_cause_str);
@@ -30,7 +31,8 @@ PyObject *bt_bt2_format_bt_error(const bt_error *error)
        PyObject *py_error_str = NULL;
 
        error_str = format_bt_error(error, 80,
-               bt_python_bindings_bt2_log_level, BT_COMMON_COLOR_WHEN_NEVER);
+               (bt_logging_level) bt_python_bindings_bt2_log_level,
+               BT_COMMON_COLOR_WHEN_NEVER);
        BT_ASSERT(error_str);
 
        py_error_str = PyString_FromString(error_str);
index 6b60ff6fca50d55b765f733552eca72b92455b88..fbe38a87d93c2d82a5ab63163372b3f1de9e9a75 100644 (file)
@@ -1813,7 +1813,7 @@ bt_get_greatest_operative_mip_version_status get_greatest_operative_mip_version(
        }
 
        status = bt_get_greatest_operative_mip_version(comp_descr_set,
-               bt_cli_log_level, mip_version);
+               (bt_logging_level) bt_cli_log_level, mip_version);
 
 end:
        bt_component_descriptor_set_put_ref(comp_descr_set);
@@ -2565,7 +2565,7 @@ void warn_command_name_and_directory_clash(struct bt_config *cfg)
 
        if (g_file_test(cfg->command_name,
                        G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
-               _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__,
+               bt_log_write_printf(__FILE__, __func__, __LINE__,
                                BT_LOG_WARNING, BT_LOG_TAG,
                                "The `%s` command was executed. "
                                "If you meant to convert a trace located in "
@@ -2608,8 +2608,8 @@ void print_error_causes(void)
         */
        fputc('\n',  stderr);
 
-       error_str = format_bt_error(error, columns, bt_cli_log_level,
-               BT_COMMON_COLOR_WHEN_AUTO);
+       error_str = format_bt_error(error, columns,
+               (bt_logging_level) bt_cli_log_level, BT_COMMON_COLOR_WHEN_AUTO);
        BT_ASSERT(error_str);
 
        fprintf(stderr, "%s\n", error_str);
index 329cb70a0cc32aa4eb47ecafda2b0622b1a50e9a..36d59bf1fca83000fab11da1fff66cfc408c3f38 100644 (file)
@@ -5,6 +5,6 @@
  */
 
 #define BT_LOG_OUTPUT_LEVEL bt_cli_log_level
-#include "logging/log.h"
+#include "logging/log-api.h"
 
 BT_LOG_INIT_LOG_LEVEL(bt_cli_log_level, "BABELTRACE_CLI_LOG_LEVEL");
index 59c936190001ca9f2b571aac3f37b4f603ca91f1..967b2382824167b931235efc0cc9631495c0576c 100644 (file)
@@ -14,7 +14,7 @@ BT_LOG_LEVEL_EXTERN_SYMBOL(bt_cli_log_level);
 
 #define BT_CLI_LOG_AND_APPEND(_lvl, _fmt, ...)                         \
        do {                                                            \
-               BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);    \
+               BT_LOG_WRITE_PRINTF(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__); \
                (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
                        "Babeltrace CLI", _fmt, ##__VA_ARGS__);         \
        } while (0)
index 1da6b3c263907fc0d288cf25fe88c7a4fa731c7b..bf3a83887b3fea8b9cea8a54d5b92a72af806151 100644 (file)
@@ -413,8 +413,8 @@ ssize_t bt_common_read(int fd, void *buf, size_t count,
                                /* retry operation */
                                continue;
                        } else {
-#ifdef BT_LOG_WRITE_ERRNO_CUR_LVL
-                               BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_ERROR,
+#ifdef BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL
+                               BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_ERROR,
                                        log_level, BT_LOG_TAG,
                                        "Error while reading", ": fd=%d", fd);
 #endif
index 79ff759901bebb5240676ee3db8486e9c4406b4f..251e87354d1a40e1908c522d4031d7095b1eed47 100644 (file)
@@ -285,7 +285,7 @@ size_t bt_mmap_get_offset_align_size(int log_level)
        SYSTEM_INFO sysinfo;
 
        GetNativeSystemInfo(&sysinfo);
-       BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, log_level, BT_LOG_TAG,
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_DEBUG, log_level, BT_LOG_TAG,
                "Allocator granularity is %lu.",
                sysinfo.dwAllocationGranularity);
 
index 7452990ff0850c018995d44d271ed9f53bca9ad6..4715677e0a043d71b8c86d073106eb9764ae16b0 100644 (file)
@@ -36,8 +36,8 @@ int bt_socket_init(int log_level BT_SOCKET_LOG_LEVEL_UNUSED_ATTR)
 
        ret = WSAStartup(verreq, &wsa);
        if (ret != 0) {
-#ifdef BT_LOG_WRITE_CUR_LVL
-               BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
+#ifdef BT_LOG_WRITE_PRINTF_CUR_LVL
+               BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR, log_level, BT_LOG_TAG,
                                "Winsock init failed with error: %d", ret);
 #endif
                goto end;
index 3d41037c6a6640a41a3b229c1516658726d9d318..38e3ac2c98644e96d875ed0efab7b728cf97eb53 100644 (file)
@@ -58,7 +58,7 @@
  */
 # define BT_CTF_ASSERT_PRE_MSG(_fmt, ...)                              \
        do {                                                            \
-               _bt_log_write_d(_BT_LOG_SRCLOC_FUNCTION, __FILE__,      \
+               bt_log_write_printf(__FILE__, __func__,                 \
                        __LINE__, BT_LOG_FATAL, BT_LOG_TAG, (_fmt),     \
                        ##__VA_ARGS__);                                 \
        } while (0)
index 2ba8538b00db9c73a2196285462d0f6742b31a68..dd624d9cd43e0f3cde9eb79b566d26219a8878ce 100644 (file)
@@ -58,7 +58,7 @@
  */
 #define BT_ASSERT_COND_MSG(_fmt, ...)                                  \
        do {                                                            \
-               bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__,           \
+               bt_lib_log(__FILE__, __func__,                          \
                        __LINE__, BT_LOG_FATAL, BT_LOG_TAG,             \
                        (_fmt), ##__VA_ARGS__);                         \
        } while (0)
index fff351822cca241ba54b4a653ac19901ea28a597..b995920074c12513191801027142c1e56e538060 100644 (file)
@@ -69,7 +69,7 @@ void bt_lib_assert_cond_failed(const char *cond_type, const char *func,
        BT_ASSERT_COND_MSG("------------------------------------------------------------------------");
        BT_ASSERT_COND_MSG("Error is:");
        va_start(args, fmt);
-       bt_lib_log_v(_BT_LOG_SRCLOC_FUNCTION, __FILE__, __LINE__, BT_LOG_FATAL,
+       bt_lib_log_v(__FILE__, __func__, __LINE__, BT_LOG_FATAL,
                BT_LOG_TAG, fmt, &args);
        va_end(args);
        BT_ASSERT_COND_MSG("Aborting...");
index 50552854175622cafadcec410859052c2c475f48..b1cab494abdd556c2b07dacfd224bdea5032ac32 100644 (file)
@@ -1479,13 +1479,13 @@ update_fmt:
  * exposed, but not part of the public ABI.
  */
 BT_EXPORT
-void bt_lib_log_v(const char *func, const char *file, unsigned line,
+void bt_lib_log_v(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, va_list *args)
 {
        BT_ASSERT(fmt);
        bt_common_custom_vsnprintf(lib_logging_buf, LIB_LOGGING_BUF_SIZE, '!',
                handle_conversion_specifier_bt, NULL, fmt, args);
-       _bt_log_write_d(func, file, line, lvl, tag, "%s", lib_logging_buf);
+       bt_log_write(file, func, line, lvl, tag, lib_logging_buf);
 }
 
 /*
@@ -1495,14 +1495,14 @@ void bt_lib_log_v(const char *func, const char *file, unsigned line,
  * exposed, but not part of the public ABI.
  */
 BT_EXPORT
-void bt_lib_log(const char *func, const char *file, unsigned line,
+void bt_lib_log(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, ...)
 {
        va_list args;
 
        BT_ASSERT(fmt);
        va_start(args, fmt);
-       bt_lib_log_v(func, file, line, lvl, tag, fmt, &args);
+       bt_lib_log_v(file, func, line, lvl, tag, fmt, &args);
        va_end(args);
 }
 
@@ -1528,8 +1528,7 @@ void bt_lib_maybe_log_and_append_cause(const char *func, const char *file,
 
        /* Log conditionally, but always append the error cause */
        if (BT_LOG_ON(lvl)) {
-               _bt_log_write_d(func, file, line, lvl, tag, "%s",
-                       lib_logging_buf);
+               bt_log_write(file, func, line, lvl, tag, lib_logging_buf);
        }
 
        status = bt_current_thread_error_append_cause_from_unknown(
index 05b43ab0f570baa87ed09f0b06bf6958e2c53cf8..0b341265c661be5955ba560c95faa8bc4f92339b 100644 (file)
@@ -28,7 +28,7 @@ int bt_lib_log_level = BT_LOG_NONE;
 BT_EXPORT
 enum bt_logging_level bt_logging_get_minimal_level(void)
 {
-       return BT_MINIMAL_LOG_LEVEL;
+       return BT_LOG_MINIMAL_LEVEL;
 }
 
 BT_EXPORT
@@ -50,7 +50,7 @@ void __attribute__((constructor)) bt_logging_ctor(void)
                bt_version_get_development_stage() : "";
 
        bt_logging_set_global_level(
-               bt_log_get_level_from_env("LIBBABELTRACE2_INIT_LOG_LEVEL"));
+               (int) bt_log_get_level_from_env("LIBBABELTRACE2_INIT_LOG_LEVEL"));
        BT_LOGI("Babeltrace %u.%u.%u%s library loaded: "
                "major=%u, minor=%u, patch=%u, extra=\"%s\"",
                bt_version_get_major(), bt_version_get_minor(),
index 7a5aafc8b3d7eab96a9f53e9d695687d7e2d2752..88c9fef9146a90500c2fa73edfceb57ec5da7eaf 100644 (file)
@@ -26,7 +26,7 @@ int bt_lib_log_level;
 #define BT_LIB_LOG(_lvl, _fmt, ...)                                    \
        do {                                                            \
                if (BT_LOG_ON(_lvl)) {                                  \
-                       bt_lib_log(_BT_LOG_SRCLOC_FUNCTION, __FILE__,   \
+                       bt_lib_log(__FILE__, __func__,                  \
                                __LINE__, _lvl, _BT_LOG_TAG,            \
                                (_fmt), ##__VA_ARGS__);                 \
                }                                                       \
@@ -49,16 +49,16 @@ int bt_lib_log_level;
  * Use one of the BT_LIB_LOG*() macros above instead of calling this
  * function directly.
  */
-void bt_lib_log(const char *func, const char *file, unsigned line,
+void bt_lib_log(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, ...);
 
-void bt_lib_log_v(const char *func, const char *file, unsigned line,
+void bt_lib_log_v(const char *file, const char *func, unsigned line,
                int lvl, const char *tag, const char *fmt, va_list *args);
 
 #define BT_LIB_LOG_AND_APPEND(_lvl, _fmt, ...)                         \
        do {                                                            \
                bt_lib_maybe_log_and_append_cause(                      \
-                       _BT_LOG_SRCLOC_FUNCTION, __FILE__,              \
+                       __func__, __FILE__,                             \
                        __LINE__, _lvl, _BT_LOG_TAG,                    \
                        (_fmt), ##__VA_ARGS__);                         \
        } while (0)
index d4ce06086498021790db6ff971baace0036997d0..00bce0473a5ac2ea5025bb30d6e4f7eebd0b2354 100644 (file)
@@ -20,7 +20,9 @@
 
 /* Logs with level `_lvl` for self component `_self_comp` */
 #define BT_COMP_LOG(_lvl, _self_comp, _fmt, ...)                       \
-       BT_LOG_WRITE((_lvl), BT_LOG_TAG, _BT_COMP_LOG_COMP_PREFIX _fmt, \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
                                bt_self_component_as_component(_self_comp)) : \
 
 /* Logs with level `_lvl` for self component class `_self_comp_class` */
 #define BT_COMP_CLASS_LOG(_lvl, _self_comp_class, _fmt, ...)           \
-       BT_LOG_WRITE((_lvl), BT_LOG_TAG, _BT_COMP_LOG_COMP_PREFIX _fmt, \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                bt_component_class_get_name(                            \
                        bt_self_component_class_as_component_class(     \
                                _self_comp_class)), ##__VA_ARGS__)
 
 #define BT_COMP_LOG_CUR_LVL(_lvl, _cur_lvl, _self_comp, _fmt, ...)     \
-       BT_LOG_WRITE_CUR_LVL((_lvl), (_cur_lvl), BT_LOG_TAG,            \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),         \
+               (enum bt_log_level) (_cur_lvl), BT_LOG_TAG,             \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -44,7 +49,9 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_ERRNO(_lvl, _self_comp, _msg, _fmt, ...)           \
-       BT_LOG_WRITE_ERRNO((_lvl), BT_LOG_TAG, _msg,                    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),   \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL),              \
+               BT_LOG_TAG, _msg,                                       \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -53,7 +60,8 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_ERRNO_CUR_LVL(_lvl, _cur_lvl, _self_comp, _msg, _fmt, ...) \
-       BT_LOG_WRITE_ERRNO_CUR_LVL((_lvl), (_cur_lvl), BT_LOG_TAG, _msg, \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),   \
+               (enum bt_log_level) (_cur_lvl), BT_LOG_TAG, _msg,       \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
@@ -62,7 +70,9 @@
                ##__VA_ARGS__)
 
 #define BT_COMP_LOG_MEM(_lvl, _self_comp, _data_ptr, _data_sz, _fmt, ...) \
-       BT_LOG_WRITE_MEM((_lvl), BT_LOG_TAG, (_data_ptr), (_data_sz),   \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),     \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG,  \
+               (_data_ptr), (_data_sz),                                \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                          \
                (_self_comp) ?                                          \
                        bt_component_get_name(                          \
 
 /* Logs error and errno string from component class context. */
 #define BT_COMP_CLASS_LOG_ERRNO(_lvl, _self_comp_class, _msg, _fmt, ...)               \
-       BT_LOG_WRITE_ERRNO((_lvl), BT_LOG_TAG, _msg,                                    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((enum bt_log_level) (_lvl),                   \
+               (enum bt_log_level) (BT_LOG_OUTPUT_LEVEL), BT_LOG_TAG, _msg,            \
                _BT_COMP_LOG_COMP_PREFIX _fmt,                                          \
                bt_component_class_get_name(                                            \
                        bt_self_component_class_as_component_class(_self_comp_class))   \
diff --git a/src/logging/log-api.c b/src/logging/log-api.c
new file mode 100644 (file)
index 0000000..55729bd
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * This is very inspired by zf_log.c (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined(_WIN32) || defined(_WIN64)
+# include <windows.h>
+#endif
+
+#ifdef __linux__
+# include <sys/prctl.h>
+# include <sys/syscall.h>
+#endif
+
+#ifdef __MACH__
+# include <pthread.h>
+#endif
+
+#ifdef __GNU__
+# include <mach.h>
+#endif
+
+#include "common/assert.h"
+#include "common/common.h"
+#include "common/macros.h"
+#include "compat/time.h"
+
+#include "log-api.h"
+
+#ifdef __CYGWIN__
+extern unsigned long pthread_getsequence_np(pthread_t *);
+#endif
+
+/*
+ * Thread-local logging message buffer to put the next message and write
+ * it with a single system call.
+ */
+static __thread char msg_buf[4 * 4096];
+
+/*
+ * Returns the number of available bytes in `msg_buf` considering the
+ * writing position `at`.
+ */
+static inline
+size_t avail_msg_buf_bytes(char *at)
+{
+       return msg_buf + sizeof(msg_buf) - at;
+}
+
+/*
+ * Appends the character `ch` to `msg_buf`.
+ */
+static inline
+void append_char_to_msg_buf(char ** const at, const char ch)
+{
+       **at = ch;
+       (*at)++;
+}
+
+/*
+ * Appends a space character to `msg_buf`.
+ */
+static inline
+void append_sp_to_msg_buf(char ** const at)
+{
+       append_char_to_msg_buf(at, ' ');
+}
+
+/*
+ * Appends the null-terminated string `str` to `msg_buf`.
+ */
+static inline
+void append_str_to_msg_buf(char ** const at, const char * const str)
+{
+       const size_t len = strlen(str);
+
+       memcpy(*at, str, len);
+       *at += len;
+}
+
+/*
+ * Formats the unsigned integer `val` with sprintf() using `fmt` and
+ * appends the resulting string to `msg_buf`.
+ */
+static inline
+void append_uint_to_msg_buf(char ** const at, const char * const fmt,
+               const unsigned int val)
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+       const int written_len = sprintf(*at, fmt, val);
+#pragma GCC diagnostic pop
+
+       BT_ASSERT_DBG(written_len > 0);
+       *at += (size_t) written_len;
+}
+
+/*
+ * Thread-local cache of seconds and milliseconds to formatted date/time
+ * string (null terminated).
+ *
+ * This is often useful because many log messages may happen during the
+ * same millisecond.
+ *
+ * Does not need any kind of protection since it's TLS.
+ */
+struct date_time_cache {
+       uint64_t s;
+       uint32_t ms;
+       char str[128];
+};
+
+static __thread struct date_time_cache date_time_cache = {0};
+
+static
+const char *date_time_cache_get(const struct timeval tv)
+{
+       const uint64_t s = (uint64_t) tv.tv_sec;
+       const uint32_t ms = (uint32_t) (tv.tv_usec / 1000);
+
+       if (date_time_cache.s != s || date_time_cache.ms != ms) {
+               /* Add to cache now */
+               struct tm tm;
+               const time_t time_s = (time_t) tv.tv_sec;
+
+               date_time_cache.s = (uint64_t) tv.tv_sec;
+               date_time_cache.ms = (uint32_t) (tv.tv_usec / 1000);
+               (void) bt_localtime_r(&time_s, &tm);
+               (void) sprintf(date_time_cache.str, "%02u-%02u %02u:%02u:%02u.%03u",
+                       tm.tm_mon + 1, tm.tm_mday,
+                       tm.tm_hour, tm.tm_min, tm.tm_sec, date_time_cache.ms);
+       }
+
+       return date_time_cache.str;
+}
+
+/*
+ * Appends a formatted date/time for `tv` to `msg_buf`.
+ */
+static inline
+void append_date_time_to_msg_buf(char ** const at, const struct timeval tv)
+{
+       const char *str = date_time_cache_get(tv);
+
+       append_str_to_msg_buf(at, str);
+}
+
+/*
+ * Appends the PID and TID to `msg_buf`.
+ */
+static inline
+void append_pid_tid_to_msg_buf(char ** const at)
+{
+       const unsigned int pid = (unsigned int) getpid();
+       unsigned int tid;
+
+       /* Look at this beautiful portability */
+#if defined(_WIN32) || defined(_WIN64)
+       tid = (unsigned int) GetCurrentThreadId();
+#elif defined(__CYGWIN__)
+       {
+               pthread_t thr = pthread_self();
+
+               tid = (unsigned int) pthread_getsequence_np(&thr);
+       }
+#elif defined(__sun__)
+       tid = (unsigned int) pthread_self();
+#elif defined(__linux__)
+       tid = (unsigned int) syscall(SYS_gettid);
+#elif defined(__APPLE__) && defined(__MACH__)
+       tid = (unsigned int) pthread_mach_thread_np(pthread_self());
+#elif defined(__GNU__)
+       {
+               mach_port_t mach_port = mach_thread_self();
+
+               mach_port_deallocate(mach_task_self(), mach_port);
+               tid = (unsigned int) mach_port;
+       }
+#else
+# error "Platform not supported"
+#endif
+
+       /* Append them now */
+       append_uint_to_msg_buf(at, "%u", pid);
+       append_sp_to_msg_buf(at);
+       append_uint_to_msg_buf(at, "%u", tid);
+}
+
+/*
+ * Writes the initial part of the log message to `msg_buf`, without the
+ * message and without resetting the terminal color).
+ */
+static
+void common_write_init(char ** const at, const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag)
+{
+       const char *color_p = "";
+       struct timeval tv;
+
+       /* Get time immediately */
+       gettimeofday(&tv, 0);
+
+       /* Write the terminal color code to use, if any */
+       switch (lvl) {
+       case BT_LOG_INFO:
+               color_p = bt_common_color_fg_blue();
+               break;
+       case BT_LOG_WARNING:
+               color_p = bt_common_color_fg_yellow();
+               break;
+       case BT_LOG_ERROR:
+       case BT_LOG_FATAL:
+               color_p = bt_common_color_fg_red();
+               break;
+       default:
+               break;
+       }
+
+       append_str_to_msg_buf(at, color_p);
+
+       /* Write date/time */
+       append_date_time_to_msg_buf(at, tv);
+       append_sp_to_msg_buf(at);
+
+       /* Write PID/TID */
+       append_pid_tid_to_msg_buf(at);
+       append_sp_to_msg_buf(at);
+
+       /* Write log level letter */
+       append_char_to_msg_buf(at, bt_log_get_letter_from_level(lvl));
+       append_sp_to_msg_buf(at);
+
+       /* Write tag */
+       if (tag) {
+               append_str_to_msg_buf(at, tag);
+               append_sp_to_msg_buf(at);
+       }
+
+       /* Write source location */
+       append_str_to_msg_buf(at, func_name);
+       append_char_to_msg_buf(at, '@');
+       append_str_to_msg_buf(at, file_name);
+       append_uint_to_msg_buf(at, ":%u", line_no);
+       append_sp_to_msg_buf(at);
+}
+
+/*
+ * Writes the final part of the log message to `msg_buf` (resets the
+ * terminal color and appends a newline), and then writes the whole log
+ * message to the standard error.
+ */
+static
+void common_write_fini(char ** const at)
+{
+       append_str_to_msg_buf(at, bt_common_color_reset());
+       append_char_to_msg_buf(at, '\n');
+       (void) write(STDERR_FILENO, msg_buf, *at - msg_buf);
+}
+
+void bt_log_write(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const char * const msg)
+{
+       char *at = msg_buf;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+       append_str_to_msg_buf(&at, msg);
+       common_write_fini(&at);
+}
+
+_BT_LOG_PRINTFLIKE(6, 0)
+static
+void write_vprintf(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const char * const fmt, va_list args)
+{
+       char *at = msg_buf;
+       int written_len;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+       written_len = vsnprintf(at, avail_msg_buf_bytes(at) - 16,
+               fmt, args);
+       if (written_len > 0) {
+               at += (size_t) written_len;
+       }
+
+       common_write_fini(&at);
+}
+
+void bt_log_write_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       write_vprintf(file_name, func_name, line_no, lvl, tag, fmt, va);
+       va_end(va);
+}
+
+/*
+ * Writes the initial part of the log message to `msg_buf`, including
+ * the initial message and `errno` string, without the message and
+ * without resetting the terminal color).
+ */
+static
+void common_write_errno_init(char ** const at, const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg)
+{
+       BT_ASSERT_DBG(errno != 0);
+       const char * const errno_msg = g_strerror(errno);
+
+       common_write_init(at, file_name, func_name, line_no, lvl, tag);
+       append_str_to_msg_buf(at, init_msg);
+       append_char_to_msg_buf(at, ':');
+       append_sp_to_msg_buf(at);
+       append_str_to_msg_buf(at, errno_msg);
+}
+
+void bt_log_write_errno(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg, const char * const msg)
+{
+       char *at = msg_buf;
+
+       common_write_errno_init(&at, file_name, func_name, line_no, lvl,
+               tag, init_msg);
+       append_str_to_msg_buf(&at, msg);
+       common_write_fini(&at);
+}
+
+void bt_log_write_errno_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const char * const init_msg, const char * const fmt, ...)
+{
+       char *at = msg_buf;
+       int written_len;
+       va_list va;
+
+       common_write_errno_init(&at, file_name, func_name, line_no, lvl,
+               tag, init_msg);
+       va_start(va, fmt);
+       written_len = vsnprintf(at, avail_msg_buf_bytes(at) - 16, fmt, va);
+       va_end(va);
+       if (written_len > 0) {
+               at += (size_t) written_len;
+       }
+
+       common_write_fini(&at);
+}
+
+/*
+ * Logs `mem_len` bytes of `mem_data` on a single line.
+ */
+static
+void write_mem_line(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const uint8_t * const mem_data,
+               const size_t mem_len, const size_t max_mem_line_len)
+{
+       static const char * const hex_chars = "0123456789abcdef";
+       char *at = msg_buf;
+       size_t i;
+
+       common_write_init(&at, file_name, func_name, line_no, lvl, tag);
+
+       /* Write hexadecimal representation */
+       for (i = 0; i < mem_len; i++) {
+               const uint8_t byte = mem_data[i];
+
+               /* Write nibble */
+               append_char_to_msg_buf(&at, hex_chars[byte >> 4]);
+               append_char_to_msg_buf(&at, hex_chars[byte & 0xf]);
+
+               /* Add a space */
+               append_sp_to_msg_buf(&at);
+       }
+
+       /* Insert spaces to align the following ASCII representation */
+       for (i = 0; i < max_mem_line_len - mem_len; i++) {
+               append_sp_to_msg_buf(&at);
+               append_sp_to_msg_buf(&at);
+               append_sp_to_msg_buf(&at);
+       }
+
+       /* Insert a vertical line between the representations */
+       append_str_to_msg_buf(&at, "| ");
+
+       /* Write the ASCII representation */
+       for (i = 0; i < mem_len; i++) {
+               const uint8_t byte = mem_data[i];
+
+               if (isprint(byte)) {
+                       append_char_to_msg_buf(&at, (char) byte);
+               } else {
+                       /* Non-printable character */
+                       append_char_to_msg_buf(&at, '.');
+               }
+       }
+
+       common_write_fini(&at);
+}
+
+/*
+ * Logs `mem_len` bytes of `mem_data` on one or more lines.
+ */
+static
+void write_mem_lines(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const uint8_t * const mem_data,
+               const size_t mem_len)
+{
+       const uint8_t *mem_at = mem_data;
+       size_t rem_mem_len = mem_len;
+
+       if (!mem_data || mem_len == 0) {
+               /* Nothing to write */
+               goto end;
+       }
+
+       while (rem_mem_len > 0) {
+               static const size_t max_mem_line_len = 16;
+
+               /* Number of bytes to write on this line */
+               const size_t mem_line_len = rem_mem_len > max_mem_line_len ?
+                       max_mem_line_len : rem_mem_len;
+
+               /* Log those bytes */
+               write_mem_line(file_name, func_name, line_no, lvl, tag,
+                       mem_at, mem_line_len, max_mem_line_len);
+
+               /* Adjust for next iteration */
+               rem_mem_len -= mem_line_len;
+               mem_at += mem_line_len;
+       }
+
+end:
+       return;
+}
+
+void bt_log_write_mem(const char * const file_name, const char * const func_name,
+               const unsigned int line_no, const enum bt_log_level lvl,
+               const char * const tag, const void * const mem_data,
+               const size_t mem_len, const char * const msg)
+{
+       bt_log_write(file_name, func_name, line_no, lvl, tag, msg);
+       write_mem_lines(file_name, func_name, line_no, lvl, tag,
+               mem_data, mem_len);
+}
+
+void bt_log_write_mem_printf(const char * const file_name,
+               const char * const func_name, const unsigned int line_no,
+               const enum bt_log_level lvl, const char * const tag,
+               const void * const mem_data, const size_t mem_len,
+               const char * const fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       write_vprintf(file_name, func_name, line_no, lvl, tag, fmt, va);
+       va_end(va);
+
+       write_mem_lines(file_name, func_name, line_no, lvl, tag,
+               mem_data, mem_len);
+}
diff --git a/src/logging/log-api.h b/src/logging/log-api.h
new file mode 100644 (file)
index 0000000..5754f8f
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
+ *
+ * This is very inspired by zf_log.h (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
+ */
+
+#ifndef BABELTRACE_LOGGING_LOG_API_H
+#define BABELTRACE_LOGGING_LOG_API_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <babeltrace2/babeltrace.h>
+
+/* Access private `__BT_LOGGING_LEVEL_*` macros. */
+#define __BT_IN_BABELTRACE_H
+#include <babeltrace2/logging-defs.h>
+#undef __BT_IN_BABELTRACE_H
+
+#include "common/macros.h"
+#include "common/assert.h"
+
+/* Log levels */
+enum bt_log_level {
+       BT_LOG_TRACE    = __BT_LOGGING_LEVEL_TRACE,
+       BT_LOG_DEBUG    = __BT_LOGGING_LEVEL_DEBUG,
+       BT_LOG_INFO     = __BT_LOGGING_LEVEL_INFO,
+       BT_LOG_WARNING  = __BT_LOGGING_LEVEL_WARNING,
+       BT_LOG_ERROR    = __BT_LOGGING_LEVEL_ERROR,
+       BT_LOG_FATAL    = __BT_LOGGING_LEVEL_FATAL,
+       BT_LOG_NONE     = __BT_LOGGING_LEVEL_NONE,
+};
+
+/*
+ * `BT_LOG_MINIMAL_LEVEL` (constant integer, mandatory): minimal log
+ * level to completely disable (not build) logging with levels that are
+ * more verbose.
+ */
+#ifndef BT_LOG_MINIMAL_LEVEL
+# error "`BT_LOG_MINIMAL_LEVEL` must to defined"
+#endif
+
+/* Internal: portable printf()-like attribute */
+#if defined(__printflike)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __printflike(_str_index, _first_to_check)
+#elif defined(__MINGW_PRINTF_FORMAT)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __attribute__((format(__MINGW_PRINTF_FORMAT, _str_index, _first_to_check)))
+#elif defined(__GNUC__)
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)       \
+       __attribute__((format(__printf__, _str_index, _first_to_check)))
+#else
+# define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check)
+#endif
+
+/*
+ * Runs `_expr` if `_cond` is true.
+ */
+#define BT_LOG_IF(_cond, _expr)        do { if (_cond) { _expr; } } while (0)
+
+/*
+ * Returns whether or not `_lvl` is enabled at build time, that is, it's
+ * equally or less verbose than `BT_LOG_MINIMAL_LEVEL`.
+ *
+ * See `BT_LOG_MINIMAL_LEVEL` to learn more.
+ */
+#define BT_LOG_ENABLED(_lvl)           ((_lvl) >= BT_LOG_MINIMAL_LEVEL)
+#define BT_LOG_ENABLED_TRACE           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_TRACE)
+#define BT_LOG_ENABLED_DEBUG           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_DEBUG)
+#define BT_LOG_ENABLED_INFO            BT_LOG_ENABLED(__BT_LOGGING_LEVEL_INFO)
+#define BT_LOG_ENABLED_WARNING         BT_LOG_ENABLED(__BT_LOGGING_LEVEL_WARNING)
+#define BT_LOG_ENABLED_ERROR           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_ERROR)
+#define BT_LOG_ENABLED_FATAL           BT_LOG_ENABLED(__BT_LOGGING_LEVEL_FATAL)
+
+/*
+ * Returns whether or not `_lvl` is enabled at run time, that is, it's
+ * equally or less verbose than some current (run-time) level
+ * `_cur_lvl`.
+ */
+#define BT_LOG_ON_CUR_LVL(_lvl, _cur_lvl)      \
+       G_UNLIKELY(BT_LOG_ENABLED((_lvl)) && (_lvl) >= (_cur_lvl))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Writes the log message `msg` using the file name `file_name`, the
+ * function name `func_name`, the line number `line_no`, the log level
+ * `lvl`, and the tag `tag`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *msg);
+
+/*
+ * Calls bt_log_write(), formatting the log message through sprintf()
+ * with `fmt` and the following arguments.
+ */
+void bt_log_write_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(6, 7);
+
+/*
+ * Writes the log message `msg` using the file name `file_name`, the
+ * function name `func_name`, the line number `line_no`, the log level
+ * `lvl`, and the tag `tag`, and also dumps `mem_len` bytes of
+ * `mem_data`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write_mem(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const void *mem_data, size_t mem_len,
+               const char *msg);
+
+/*
+ * Calls bt_log_write_mem(), formatting the log message through
+ * sprintf() with `fmt` and the following arguments.
+ */
+void bt_log_write_mem_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const void *mem_data, size_t mem_len,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(8, 9);
+
+/*
+ * Writes:
+ *
+ * 1. `init_msg`
+ * 2. The string `: `
+ * 3. The message corresponding to `errno` (current error number)
+ * 4. `msg`
+ *
+ * This function uses the file name `file_name`, the function name
+ * `func_name`, the line number `line_no`, the log level `lvl`, and the
+ * tag `tag`.
+ *
+ * NOTE: This function writes unconditionally, without checking the
+ * current (run-time) log level.
+ */
+void bt_log_write_errno(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *init_msg, const char *msg);
+
+/*
+ * Calls bt_log_write_errno(), formatting the log message through
+ * sprintf() with `fmt` and the following arguments.
+ */
+void bt_log_write_errno_printf(const char *file_name, const char *func_name,
+               unsigned int line_no, enum bt_log_level lvl, const char *tag,
+               const char *init_msg,
+               const char *fmt, ...) _BT_LOG_PRINTFLIKE(7, 8);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * Calls bt_log_write() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write().
+ */
+#define BT_LOG_WRITE_CUR_LVL(_lvl, _cur_lvl, _tag, _msg)               \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write(__FILE__, __func__, __LINE__,      \
+                               (_lvl), (_tag), (_msg));                \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_printf() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_printf().
+ */
+#define BT_LOG_WRITE_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _fmt, ...)   \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_printf(__FILE__, __func__,         \
+                               __LINE__, (_lvl), (_tag), (_fmt),       \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_mem() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_mem().
+ */
+#define BT_LOG_WRITE_MEM_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _msg) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_mem(__FILE__, __func__, __LINE__,  \
+                               (_lvl), (_tag), (_mem_data),            \
+                               (_mem_len), (_msg));                    \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_mem_printf() if logging is enabled at run time for
+ * the current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_mem_printf().
+ */
+#define BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _fmt, ...) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_mem_printf(__FILE__, __func__,     \
+                               __LINE__, (_lvl), (_tag), (_mem_data),  \
+                               (_mem_len), (_fmt), ##__VA_ARGS__);     \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_errno() if logging is enabled at run time for the
+ * current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_errno().
+ */
+#define BT_LOG_WRITE_ERRNO_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _msg) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_errno(__FILE__, __func__,          \
+                               __LINE__, (_lvl), (_tag), (_init_msg),  \
+                               (_msg));                                \
+               }                                                       \
+       } while (0)
+
+/*
+ * Calls bt_log_write_errno_printf() if logging is enabled at run time
+ * for the current level `_cur_lvl`.
+ *
+ * Passes the current file name, function name, and line number to
+ * bt_log_write_errno_printf().
+ */
+#define BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _fmt, ...) \
+       do {                                                            \
+               if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) {            \
+                       bt_log_write_errno_printf(__FILE__, __func__,   \
+                               __LINE__, (_lvl), (_tag), (_init_msg),  \
+                               (_fmt), ##__VA_ARGS__);                 \
+               }                                                       \
+       } while (0)
+
+/*
+ * Returns the equivalent letter of the log level `level`.
+ *
+ * `level` must be a valid log level.
+ */
+static inline
+char bt_log_get_letter_from_level(const enum bt_log_level level)
+{
+       char letter;
+
+       switch (level) {
+       case BT_LOG_TRACE:
+               letter = 'T';
+               break;
+       case BT_LOG_DEBUG:
+               letter = 'D';
+               break;
+       case BT_LOG_INFO:
+               letter = 'I';
+               break;
+       case BT_LOG_WARNING:
+               letter = 'W';
+               break;
+       case BT_LOG_ERROR:
+               letter = 'E';
+               break;
+       case BT_LOG_FATAL:
+               letter = 'F';
+               break;
+       case BT_LOG_NONE:
+               letter = 'N';
+               break;
+       default:
+               abort();
+       }
+
+       return letter;
+}
+
+/*
+ * Returns the log level as an integer for the string `str`, or -1 if
+ * `str` is not a valid log level string.
+ */
+static inline
+int bt_log_get_level_from_string(const char * const str)
+{
+       int level = -1;
+
+       BT_ASSERT(str);
+
+       if (strcmp(str, "TRACE") == 0 || strcmp(str, "T") == 0) {
+               level = BT_LOG_TRACE;
+       } else if (strcmp(str, "DEBUG") == 0 || strcmp(str, "D") == 0) {
+               level = BT_LOG_DEBUG;
+       } else if (strcmp(str, "INFO") == 0 || strcmp(str, "I") == 0) {
+               level = BT_LOG_INFO;
+       } else if (strcmp(str, "WARN") == 0 ||
+                       strcmp(str, "WARNING") == 0 ||
+                       strcmp(str, "W") == 0) {
+               level = BT_LOG_WARNING;
+       } else if (strcmp(str, "ERROR") == 0 || strcmp(str, "E") == 0) {
+               level = BT_LOG_ERROR;
+       } else if (strcmp(str, "FATAL") == 0 || strcmp(str, "F") == 0) {
+               level = BT_LOG_FATAL;
+       } else if (strcmp(str, "NONE") == 0 || strcmp(str, "N") == 0) {
+               level = BT_LOG_NONE;
+       } else {
+               /* FIXME: Should we warn here? How? */
+       }
+
+       return level;
+}
+
+/*
+ * Returns the log level as an integer for the letter `letter`, or -1 if
+ * `letter` is not a valid log level string.
+ */
+static inline
+int bt_log_get_level_from_letter(const char letter)
+{
+       const char str[] = {letter, '\0'};
+
+       return bt_log_get_level_from_string(str);
+}
+
+/*
+ * Returns the log level for the value of the environment variable named
+ * `env_var_name`, or `BT_LOG_NONE` if not a valid log level string.
+ */
+static inline
+enum bt_log_level bt_log_get_level_from_env(const char *env_var_name)
+{
+       const char * const varval = getenv(env_var_name);
+       enum bt_log_level level = BT_LOG_NONE;
+       int int_level;
+
+       if (!varval) {
+               goto end;
+       }
+
+       int_level = bt_log_get_level_from_string(varval);
+       if (int_level < 0) {
+               /* FIXME: Should we warn here? How? */
+               int_level = BT_LOG_NONE;
+       }
+
+       level = (enum bt_log_level) int_level;
+
+end:
+       return level;
+}
+
+/*
+ * Declares the variable named `_level_sym` as an external symbol
+ * containing a log level.
+ */
+#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym)                         \
+       extern enum bt_log_level _level_sym
+
+/*
+ * 1. Defines the log level variable `_level_sym`, initializing it to
+ *    `BT_LOG_NONE` (logging disabled).
+ *
+ * 2. Defines a library constructor named _bt_log_level_ctor() which
+ *    initializes the log level variable `_level_sym` from the value of
+ *    the environment variable named `_env_var_name`.
+ */
+#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var_name)               \
+       enum bt_log_level _level_sym = BT_LOG_NONE;                                     \
+       static                                                          \
+       void __attribute__((constructor)) _bt_log_level_ctor(void)      \
+       {                                                               \
+               _level_sym = bt_log_get_level_from_env(_env_var_name);  \
+       }
+
+#endif /* BABELTRACE_LOGGING_LOG_API_H */
diff --git a/src/logging/log.c b/src/logging/log.c
deleted file mode 100644 (file)
index 788c00c..0000000
+++ /dev/null
@@ -1,1429 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2016 wonder-mice
- *
- * This is zf_log.c, modified with Babeltrace prefixes.
- * See <https://github.com/wonder-mice/zf_log/>.
- */
-
-#include "common/macros.h"
-#include "common/common.h"
-#include <pthread.h>
-#include "common/assert.h"
-
-#ifdef __CYGWIN__
-extern unsigned long pthread_getsequence_np(pthread_t *);
-#endif
-
-/* 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_WARNING;
- *
- * 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 a globally allocated per thread.
- */
-#ifndef BT_LOG_BUF_SZ
-       #define BT_LOG_BUF_SZ (4 * 4096)
-#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 "common/assert.h"
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#define BT_LOG_OUTPUT_LEVEL dummy
-
-#include "log.h"
-#include <babeltrace2/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>
-       #elif (defined(__sun__) || defined(__CYGWIN__) || defined(__GNU__))
-               /* Solaris, Cygwin and Hurd have no sys/syslimits.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
-#if defined(__GNU__)
-       #include <mach.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__) && !defined(__USE_MINGW_ANSI_STDIO))
-       #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);
-static const char c_hex[] = "0123456789abcdef";
-
-static __thread char logging_buf[4 * 4096];
-
-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_TRACE:
-                       return ANDROID_LOG_VERBOSE;
-               case BT_LOG_DEBUG:
-                       return ANDROID_LOG_DEBUG;
-               case BT_LOG_INFO:
-                       return ANDROID_LOG_INFO;
-               case BT_LOG_WARNING:
-                       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_TRACE:
-                       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_WARNING:
-                       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
-
-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_TRACE:
-               return 'T';
-       case BT_LOG_DEBUG:
-               return 'D';
-       case BT_LOG_INFO:
-               return 'I';
-       case BT_LOG_WARNING:
-               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};
-
-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(__CYGWIN__)
-       pthread_t thr = pthread_self();
-       *tid = (int)pthread_getsequence_np(&thr);
-       #elif defined(__sun__)
-       *tid = (int)pthread_self();
-       #elif defined(__ANDROID__)
-       *tid = gettid();
-       #elif defined(__linux__)
-       *tid = syscall(SYS_gettid);
-       #elif defined(__APPLE__) && defined(__MACH__)
-       *tid = (int)pthread_mach_thread_np(pthread_self());
-       #elif defined(__GNU__)
-       mach_port_t mach_port = mach_thread_self();
-       mach_port_deallocate(mach_task_self(), mach_port);
-       *tid = (int)mach_port;
-       #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', '+'};
-       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)
-
-/*
- * This generates a -Wundef warning.  The issue was reported upstream:
- *
- *   https://github.com/wonder-mice/zf_log/issues/40
- *
- * but there's not much we can do here, so just silence it.
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wundef"
-#if !_BT_LOG_MESSAGE_FORMAT_CONTAINS(TAG, BT_LOG_MESSAGE_TAG_FORMAT)
-       VAR_UNUSED(tag);
-#endif
-#pragma GCC diagnostic pop
-
-#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 _BT_LOG_PRINTFLIKE(2, 0)
-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);
-       }
-}
-
-void bt_log_set_tag_prefix(const char *const prefix)
-{
-       _bt_log_tag_prefix = prefix;
-}
-
-void bt_log_set_mem_width(const unsigned w)
-{
-       _bt_log_global_format.mem_width = w;
-}
-
-void bt_log_set_output_level(const int lvl)
-{
-       _bt_log_global_output_lvl = lvl;
-}
-
-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 _BT_LOG_PRINTFLIKE(6, 0)
-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 = logging_buf;
-       const unsigned mask = log->output->mask;
-       msg.lvl = lvl;
-       msg.tag = tag;
-       g_buffer_cb(&msg, buf);
-       const char *rst_color_p = bt_common_color_reset();
-       const char *rst_color_e = rst_color_p + strlen(rst_color_p);
-       const char *color_p = "";
-       const char *color_e = color_p;
-
-       switch (lvl) {
-       case BT_LOG_INFO:
-               color_p = bt_common_color_fg_blue();
-               color_e = color_p + strlen(color_p);
-               break;
-       case BT_LOG_WARNING:
-               color_p = bt_common_color_fg_yellow();
-               color_e = color_p + strlen(color_p);
-               break;
-       case BT_LOG_ERROR:
-       case BT_LOG_FATAL:
-               color_p = bt_common_color_fg_red();
-               color_e = color_p + strlen(color_p);
-               break;
-       default:
-               break;
-       }
-
-       msg.p = put_stringn(color_p, color_e, msg.p, msg.e);
-
-       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);
-       }
-       msg.p = put_stringn(rst_color_p, rst_color_e, msg.p, msg.e);
-       log->output->callback(&msg, log->output->arg);
-       if (0 != mem && BT_LOG_PUT_MSG & mask)
-       {
-               output_mem(log, &msg, mem);
-       }
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
-
-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);
-}
index 89ac5fc7c54ca3e8fffb858fc1a816fec5762add..93c9d6ed1376e6726099a4c627b60d89f8703197 100644 (file)
  * SPDX-License-Identifier: MIT
  *
  * Copyright (c) 2016 wonder-mice
+ * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com>
  *
- * This is zf_log.h, modified with Babeltrace prefixes.
- * See <https://github.com/wonder-mice/zf_log/>.
+ * This is very inspired by zf_log.h (see
+ * <https://github.com/wonder-mice/zf_log/>), but modified (mostly
+ * stripped down) for the use cases of Babeltrace.
  */
 
-#pragma once
-
-#ifndef BABELTRACE_LOGGING_INTERNAL_H
-#define BABELTRACE_LOGGING_INTERNAL_H
+#ifndef BABELTRACE_LOGGING_LOG_H
+#define BABELTRACE_LOGGING_LOG_H
 
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <glib.h>
 #include <babeltrace2/babeltrace.h>
 
-/* Access private __BT_LOGGING_LEVEL_* macros. */
-#define __BT_IN_BABELTRACE_H
-#include <babeltrace2/logging-defs.h>
-#undef __BT_IN_BABELTRACE_H
-
-#include "common/macros.h"
-#include "common/assert.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 "logging.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_WARNING - 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_TRACE - 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_TRACE   __BT_LOGGING_LEVEL_TRACE
-#define BT_LOG_DEBUG   __BT_LOGGING_LEVEL_DEBUG
-#define BT_LOG_INFO    __BT_LOGGING_LEVEL_INFO
-#define BT_LOG_WARNING __BT_LOGGING_LEVEL_WARNING
-#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_MINIMAL_LOG_LEVEL. BT_MINIMAL_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_MINIMAL_LOG_LEVEL in .c/.cpp/.m files
- * before including bt_log.h:
- *
- *   #define BT_MINIMAL_LOG_LEVEL BT_LOG_TRACE
- *   #include "logging.h"
- *
- * If both BT_LOG_DEF_LEVEL and BT_MINIMAL_LOG_LEVEL are undefined, then
- * BT_LOG_INFO will be used for release builds (BT_DEBUG_MODE is NOT
- * defined) and BT_LOG_DEBUG otherwise (BT_DEBUG_MODE is defined).
- */
-#if defined(BT_MINIMAL_LOG_LEVEL)
-       #define _BT_MINIMAL_LOG_LEVEL BT_MINIMAL_LOG_LEVEL
-#elif defined(BT_LOG_DEF_LEVEL)
-       #define _BT_MINIMAL_LOG_LEVEL BT_LOG_DEF_LEVEL
-#else
-       #ifdef BT_DEBUG_MODE
-               #define _BT_MINIMAL_LOG_LEVEL BT_LOG_DEBUG
-       #else
-               #define _BT_MINIMAL_LOG_LEVEL BT_LOG_INFO
-       #endif
-#endif
+#include "log-api.h"
 
-/* "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_MINIMAL_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.
+/*
+ * `BT_LOG_OUTPUT_LEVEL` (expression having the type `int`, optional):
+ * valid expression evaluating to the current (run-time) log level.
  *
- * 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.
+ * This is configured per translation unit.
  *
- * 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_MINIMAL_LOG_LEVEL or using bt_log_set_output_level()
- * function.
+ * Note that BT_LOG*() statements will evaluate this expression each
+ * time if their level is equally or less verbose than
+ * `BT_LOG_MINIMAL_LEVEL`. Keep this expression as simple as possible,
+ * otherwise it will not only add run-time overhead, but also will
+ * increase the size of call sites (which will result in larger
+ * executable).
  *
  * Example:
  *
- *   #define BT_LOG_OUTPUT_LEVEL g_module_log_level
- *   #include "logging.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_MINIMAL_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 log_level
+ *     #include "logging/log.h"
  *
- *   #define BT_LOG_OUTPUT_LEVEL (g_config.log_level)
- *   #include "logging.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 missing, you may only use logging macros having a `_cur_lvl`
+ * parameter.
  */
-#if defined(BT_LOG_OUTPUT_LEVEL)
-       #define _BT_LOG_OUTPUT_LEVEL BT_LOG_OUTPUT_LEVEL
-#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 "logging.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
-
-#define _BT_LOG_SRCLOC BT_LOG_SRCLOC_LONG
-
-#if BT_LOG_SRCLOC_LONG == _BT_LOG_SRCLOC
-       #define _BT_LOG_SRCLOC_FUNCTION _BT_LOG_FUNCTION
+#ifdef BT_LOG_OUTPUT_LEVEL
+# define _BT_LOG_OUTPUT_LEVEL  BT_LOG_OUTPUT_LEVEL
 #else
-       #define _BT_LOG_SRCLOC_FUNCTION 0
+# define _BT_LOG_OUTPUT_LEVEL  "Please define `BT_LOG_OUTPUT_LEVEL`"
 #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 "logging.h"
- *
- * If both BT_LOG_DEF_CENSORING and BT_LOG_CENSORING are undefined, then
- * BT_LOG_CENSORED will be used for release builds (BT_DEBUG_MODE is NOT
- * defined) and BT_LOG_UNCENSORED otherwise (BT_DEBUG_MODE is 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 BT_DEBUG_MODE
-               #define _BT_LOG_CENSORING BT_LOG_UNCENSORED
-       #else
-               #define _BT_LOG_CENSORING BT_LOG_CENSORED
-       #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 "logging.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_")
+/*
+ * `BT_LOG_TAG` (expression having the type `const char *`, optional):
+ * component or module identifier.
  *
- * 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:
+ * This is configured per translation unit.
  *
- *   // KittyHttpLogging.h
- *   #define BT_LOG_LIBRARY_PREFIX KittyHttp_
- *   #include "logging.h"
+ * Example:
  *
- * 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.
+ *     // ...
+ *     #define BT_LOG_TAG "MY.MODULE"
+ *     #include "logging/log.h"
  */
-#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(str_index, first_to_check) \
-               __printflike(str_index, first_to_check)
-#elif defined(__MINGW_PRINTF_FORMAT)
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \
-               __attribute__((format(__MINGW_PRINTF_FORMAT, str_index, first_to_check)))
-#elif defined(__GNUC__)
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check) \
-               __attribute__((format(__printf__, str_index, first_to_check)))
-#else
-       #define _BT_LOG_PRINTFLIKE(str_index, first_to_check)
-#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))
+#ifdef BT_LOG_TAG
+# define _BT_LOG_TAG    BT_LOG_TAG
 #else
-       #define _BT_LOG_INLINE inline
-       #define _BT_LOG_IF(cond) if(cond)
-       #define _BT_LOG_WHILE(cond) while(cond)
+# define _BT_LOG_TAG    NULL
 #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.
+/*
+ * BT_LOG_ON_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-void bt_log_set_mem_width(const unsigned w);
+#define BT_LOG_ON(_lvl)                BT_LOG_ON_CUR_LVL(_lvl, _BT_LOG_OUTPUT_LEVEL)
+#define BT_LOG_ON_TRACE                BT_LOG_ON(BT_LOG_TRACE)
+#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_WARNING      BT_LOG_ON(BT_LOG_WARNING)
+#define BT_LOG_ON_ERROR                BT_LOG_ON(BT_LOG_ERROR)
+#define BT_LOG_ON_FATAL                BT_LOG_ON(BT_LOG_FATAL)
 
-/* Set "output" log level. See BT_MINIMAL_LOG_LEVEL and BT_LOG_OUTPUT_LEVEL for more
- * info about log levels.
+/*
+ * BT_LOG_WRITE_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-void bt_log_set_output_level(const int lvl);
+#define BT_LOG_WRITE(_lvl, _tag, _msg)                                 \
+       BT_LOG_WRITE_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, (_tag), (_msg))
 
-/* 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
- * (BT_DEBUG_MODE is defined).
+/*
+ * BT_LOG_WRITE_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-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;
+#define BT_LOG_WRITE_PRINTF(_lvl, _tag, _fmt, ...)                     \
+       BT_LOG_WRITE_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,       \
+               (_tag), (_fmt), ##__VA_ARGS__)
 
-/* 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).
+/*
+ * BT_LOG_WRITE_MEM_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the current
+ * (run-time) level.
  */
-typedef void (*bt_log_output_cb)(const bt_log_message *msg, void *arg);
+#define BT_LOG_WRITE_MEM(_lvl, _tag, _mem_data, _mem_len, _msg)                \
+       BT_LOG_WRITE_MEM_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, (_tag),  \
+               (_mem_data), (_mem_len), (_msg))
 
-/* Format options. For more details see bt_log_set_mem_width().
+/*
+ * BT_LOG_WRITE_MEM_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-typedef struct bt_log_format
-{
-       unsigned mem_width; /* Bytes per line in memory (ASCII-HEX) dump */
-}
-bt_log_format;
+#define BT_LOG_WRITE_MEM_PRINTF(_lvl, _tag, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,   \
+               (_tag), (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
 
-/* Output facility.
+/*
+ * BT_LOG_WRITE_ERRNO_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as the
+ * current (run-time) level.
  */
-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;
+#define BT_LOG_WRITE_ERRNO(_lvl, _tag, _init_msg, _msg)                        \
+       BT_LOG_WRITE_ERRNO_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL,        \
+               (_tag), (_init_msg), (_msg))
 
-/* 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.
+/*
+ * BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL() using `BT_LOG_OUTPUT_LEVEL` as
+ * the current (run-time) level.
  */
-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);
-}
+#define BT_LOG_WRITE_ERRNO_PRINTF(_lvl, _tag, _init_msg, _fmt, ...)    \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL((_lvl), _BT_LOG_OUTPUT_LEVEL, \
+               (_tag), (_init_msg), (_fmt), ##__VA_ARGS__)
 
-/* 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
+/* Internal: no-op function when a log level is disabled */
+static inline
+void _bt_log_unused(int dummy __attribute__((unused)), ...)
 {
-       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_MINIMAL_LOG_LEVEL for details.
- */
-#define BT_LOG_ENABLED(lvl)     ((lvl) >= _BT_MINIMAL_LOG_LEVEL)
-#define BT_LOG_ENABLED_TRACE    BT_LOG_ENABLED(BT_LOG_TRACE)
-#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_WARNING  BT_LOG_ENABLED(BT_LOG_WARNING)
-#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_CUR_LVL(lvl, cur_lvl) \
-               G_UNLIKELY(BT_LOG_ENABLED((lvl)) && (lvl) >= (cur_lvl))
-#define BT_LOG_ON(lvl) \
-               G_UNLIKELY(BT_LOG_ENABLED((lvl)) && (lvl) >= _BT_LOG_OUTPUT_LEVEL)
-#define BT_LOG_ON_TRACE     BT_LOG_ON(BT_LOG_TRACE)
-#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_WARNING   BT_LOG_ON(BT_LOG_WARNING)
-#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;
-
-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);
-
-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);
-
-void _bt_log_write(
-               const int lvl, const char *const tag,
-               const char *const fmt, ...) _BT_LOG_PRINTFLIKE(3, 4);
-
-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);
-
-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);
-
-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);
-
-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);
-
-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_LOGT("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, ...)
- *
- * Message and error string (errno) logging macros:
- * - BT_LOGT_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGD_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGI_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGW_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGE_ERRNO("initial message", "format string", args, ...)
- * - BT_LOGF_ERRNO("initial message", "format string", args, ...)
- *
- * Memory logging macros:
- * - BT_LOGT_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_LOGT_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_LOGT_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_LOGT_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, ...)
- *
- * Explicit log level, current log level, and tag:
- * - BT_LOG_WRITE_CUR_LVL(level, cur_level, tag, "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_CUR_LVL(lvl, cur_lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON_CUR_LVL((lvl), (cur_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_CUR_LVL(lvl, cur_lvl, tag, ...) \
-                       do { \
-                               if (BT_LOG_ON_CUR_LVL((lvl), (cur_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
-
-#define BT_LOG_WRITE_ERRNO_CUR_LVL(lvl, cur_lvl, tag, _msg, _fmt, args...) \
-               do { \
-                       const char *error_str; \
-                       error_str = g_strerror(errno); \
-                       BT_LOG_WRITE_CUR_LVL(lvl, cur_lvl, tag, _msg ": %s" _fmt, error_str, ## args); \
-               } _BT_LOG_ONCE
-
-#define BT_LOG_WRITE_ERRNO(lvl, tag, _msg, _fmt, args...) \
-               do { \
-                       BT_LOG_WRITE_ERRNO_CUR_LVL(lvl, _BT_LOG_OUTPUT_LEVEL, tag, _msg, _fmt, ## args); \
-               } _BT_LOG_ONCE
-
-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
+       do { if (0) _bt_log_unused(0, ##__VA_ARGS__); } while (0)
 
+/* Trace level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_TRACE
-       #define BT_LOGT(...) \
-                       BT_LOG_WRITE(BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_TRACE, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGT_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_TRACE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
-       #define BT_LOGT_MEM_AUX(log, d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(log, BT_LOG_TRACE, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGT_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGT_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_TRACE, _BT_LOG_TAG, (_msg))
+# define BT_LOGT_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGT(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGT_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_TRACE, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGT_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGT_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_TRACE, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGT_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_TRACE, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGT_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_TRACE, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGT(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_ERRNO(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_MEM(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGT_MEM_AUX(...) _BT_LOG_UNUSED(__VA_ARGS__)
-#endif
-
+# define BT_LOGT_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGT_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_TRACE */
+
+/* Debug level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_DEBUG
-       #define BT_LOGD(...) \
-                       BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGD_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(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__)
+# define BT_LOGD_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGD_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_DEBUG, _BT_LOG_TAG, (_msg))
+# define BT_LOGD_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGD(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGD_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_DEBUG, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGD_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGD_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_DEBUG, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGD_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_DEBUG, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGD_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_DEBUG, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGD(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGD_ERRNO(...) _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
-
+# define BT_LOGD_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGD_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_DEBUG */
+
+/* Info level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_INFO
-       #define BT_LOGI(...) \
-                       BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGI_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(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__)
+# define BT_LOGI_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGI_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_INFO, _BT_LOG_TAG, (_msg))
+# define BT_LOGI_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGI(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGI_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_INFO, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGI_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGI_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_INFO, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGI_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_INFO, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGI_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_INFO, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGI(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGI_ERRNO(...) _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
-
+# define BT_LOGI_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGI_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_INFO */
+
+/* Warning level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_WARNING
-       #define BT_LOGW(...) \
-                       BT_LOG_WRITE(BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_AUX(log, ...) \
-                       BT_LOG_WRITE_AUX(log, BT_LOG_WARNING, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGW_MEM(d, d_sz, ...) \
-                       BT_LOG_WRITE_MEM(BT_LOG_WARNING, _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_WARNING, _BT_LOG_TAG, d, d_sz, __VA_ARGS__)
+# define BT_LOGW_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGW_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_WARNING, _BT_LOG_TAG, (_msg))
+# define BT_LOGW_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGW(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGW_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_WARNING, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGW_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGW_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_WARNING, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGW_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_WARNING, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGW_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_WARNING, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGW(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGW_ERRNO(...) _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
-
+# define BT_LOGW_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGW_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_WARNING */
+
+/* Error level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_ERROR
-       #define BT_LOGE(...) \
-                       BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGE_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(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__)
+# define BT_LOGE_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGE_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_ERROR, _BT_LOG_TAG, (_msg))
+# define BT_LOGE_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGE(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGE_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_ERROR, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGE_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGE_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_ERROR, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGE_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_ERROR, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGE_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_ERROR, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGE(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGE_ERRNO(...) _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
-
+# define BT_LOGE_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGE_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_ERROR */
+
+/* Fatal level logging macros using `BT_LOG_TAG` */
 #if BT_LOG_ENABLED_FATAL
-       #define BT_LOGF(...) \
-                       BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, __VA_ARGS__)
-       #define BT_LOGF_ERRNO(...) \
-                       BT_LOG_WRITE_ERRNO(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__)
+# define BT_LOGF_STR_CUR_LVL(_cur_lvl, _msg) \
+       BT_LOG_WRITE_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_msg))
+# define BT_LOGF_STR(_msg) \
+       BT_LOG_WRITE(BT_LOG_FATAL, _BT_LOG_TAG, (_msg))
+# define BT_LOGF_CUR_LVL(_cur_lvl, _fmt, ...) \
+       BT_LOG_WRITE_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGF(_fmt, ...) \
+       BT_LOG_WRITE_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_MEM_STR_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGF_MEM_STR(_mem_data, _mem_len, _msg) \
+       BT_LOG_WRITE_MEM(BT_LOG_FATAL, _BT_LOG_TAG, (_mem_data), (_mem_len), (_msg))
+# define BT_LOGF_MEM_CUR_LVL(_cur_lvl, _mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_MEM(_mem_data, _mem_len, _fmt, ...) \
+       BT_LOG_WRITE_MEM_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_mem_data), (_mem_len), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR_CUR_LVL(_cur_lvl, _init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGF_ERRNO_STR(_init_msg, _msg) \
+       BT_LOG_WRITE_ERRNO(BT_LOG_FATAL, _BT_LOG_TAG, (_init_msg), (_msg))
+# define BT_LOGF_ERRNO_CUR_LVL(_cur_lvl, _init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(BT_LOG_FATAL, (_cur_lvl), _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
+# define BT_LOGF_ERRNO(_init_msg, _fmt, ...) \
+       BT_LOG_WRITE_ERRNO_PRINTF(BT_LOG_FATAL, _BT_LOG_TAG, (_init_msg), (_fmt), ##__VA_ARGS__)
 #else
-       #define BT_LOGF(...) _BT_LOG_UNUSED(__VA_ARGS__)
-       #define BT_LOGF_ERRNO(...) _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_LOGT_STR(s) BT_LOGT("%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)
-
-/*
- * Returns the equivalent letter of the log level `level`.
- *
- * `level` must be a valid log level.
- */
-static inline
-char bt_log_get_letter_from_level(int level)
-{
-       char letter;
-
-       switch (level) {
-       case BT_LOG_TRACE:
-               letter = 'T';
-               break;
-       case BT_LOG_DEBUG:
-               letter = 'D';
-               break;
-       case BT_LOG_INFO:
-               letter = 'I';
-               break;
-       case BT_LOG_WARNING:
-               letter = 'W';
-               break;
-       case BT_LOG_ERROR:
-               letter = 'E';
-               break;
-       case BT_LOG_FATAL:
-               letter = 'F';
-               break;
-       case BT_LOG_NONE:
-               letter = 'N';
-               break;
-       default:
-               abort();
-       }
-
-       return letter;
-}
-
-/*
- * Returns the log level for the string `str`, or -1 if `str` is not a
- * valid log level string.
- */
-static inline
-int bt_log_get_level_from_string(const char *str)
-{
-       int level = -1;
-
-       BT_ASSERT(str);
-
-       if (strcmp(str, "TRACE") == 0 ||
-                       strcmp(str, "T") == 0) {
-               level = BT_LOG_TRACE;
-       } else if (strcmp(str, "DEBUG") == 0 ||
-                       strcmp(str, "D") == 0) {
-               level = BT_LOG_DEBUG;
-       } else if (strcmp(str, "INFO") == 0 ||
-                       strcmp(str, "I") == 0) {
-               level = BT_LOG_INFO;
-       } else if (strcmp(str, "WARN") == 0 ||
-                       strcmp(str, "WARNING") == 0 ||
-                       strcmp(str, "W") == 0) {
-               level = BT_LOG_WARNING;
-       } else if (strcmp(str, "ERROR") == 0 ||
-                       strcmp(str, "E") == 0) {
-               level = BT_LOG_ERROR;
-       } else if (strcmp(str, "FATAL") == 0 ||
-                       strcmp(str, "F") == 0) {
-               level = BT_LOG_FATAL;
-       } else if (strcmp(str, "NONE") == 0 ||
-                       strcmp(str, "N") == 0) {
-               level = BT_LOG_NONE;
-       } else {
-               /* FIXME: Should we warn here? How? */
-       }
-
-       return level;
-}
+# define BT_LOGF_STR_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_STR(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_CUR_LVL(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF(...)                  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_STR_CUR_LVL(...)  _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_STR(...)          _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM_CUR_LVL(...)      _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_MEM(...)              _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR_CUR_LVL(...)        _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_STR(...)                _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO_CUR_LVL(...)    _BT_LOG_UNUSED(__VA_ARGS__)
+# define BT_LOGF_ERRNO(...)            _BT_LOG_UNUSED(__VA_ARGS__)
+#endif /* BT_LOG_ENABLED_FATAL */
 
 /*
- * Returns the log level for the letter `letter`, or -1 if `letter` is
- * not a valid log level string.
+ * Tell the world that `log.h` was included without having to rely on
+ * the specific include guard.
  */
-static inline
-int bt_log_get_level_from_letter(char letter)
-{
-       char str[] = {letter, '\0'};
-
-       return bt_log_get_level_from_string(str);
-}
-
-static inline
-int bt_log_get_level_from_env(const char *var)
-{
-       const char *varval = getenv(var);
-       int level = BT_LOG_NONE;
-
-       if (!varval) {
-               goto end;
-       }
-
-       level = bt_log_get_level_from_string(varval);
-       if (level < 0) {
-               /* FIXME: Should we warn here? How? */
-               level = BT_LOG_NONE;
-       }
-
-end:
-       return level;
-}
-
-#define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym)                         \
-       extern int _level_sym
-
-#define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var)                    \
-       int _level_sym = BT_LOG_NONE;                           \
-       static                                                          \
-       void __attribute__((constructor)) _bt_log_level_ctor(void)      \
-       {                                                               \
-               _level_sym = bt_log_get_level_from_env(_env_var);       \
-       }
-
 #define BT_LOG_SUPPORTED
 
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* BABELTRACE_LOGGING_INTERNAL_H */
+#endif /* BABELTRACE_LOGGING_LOG_H */
index b238b6fb0e4bb4775ca2d76ad6244b78a1cec26b..1f7ffe57c388d0930db9fdc843731c15980e8c17 100644 (file)
@@ -14,7 +14,7 @@
 
 #define BT_COMP_LOG_SELF_COMP       (ctx->self_comp)
 #define BT_COMP_LOG_SELF_COMP_CLASS (ctx->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (ctx->log_level)
+#define BT_LOG_OUTPUT_LEVEL         ((enum bt_log_level) ctx->log_level)
 #define BT_LOG_TAG                  "PLUGIN/CTF/META/RESOLVE"
 #include "logging.hpp"
 #include "logging/comp-logging.h"
index 3c56172ae8d35ea6b06481339e642481cb6b2e38..deaeedb45408e80853b2def9ca576a5647d4a0d8 100644 (file)
@@ -15,7 +15,7 @@
 
 #define BT_COMP_LOG_SELF_COMP       (mdec->config.self_comp)
 #define BT_COMP_LOG_SELF_COMP_CLASS (mdec->config.self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL         (mdec->config.log_level)
+#define BT_LOG_OUTPUT_LEVEL         ((enum bt_log_level) mdec->config.log_level)
 #define BT_LOG_TAG                  "PLUGIN/CTF/META/DECODER"
 #include "logging.hpp"
 #include "logging/comp-logging.h"
index 6689e76753d6c120ca3a003306ff75dd137d12c9..058a6b4bef14da6eea0e5bb031d81399a1b61822 100644 (file)
@@ -5,6 +5,6 @@
  */
 
 #define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#include "logging/log.h"
+#include "logging/log-api.h"
 
 BT_LOG_INIT_LOG_LEVEL(ctf_plugin_metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL");
index 53a763dd0f068810a5ab588778475449044ea853..86b4c45ba636b19dc33abc8d9bf8f1425a1d6760 100644 (file)
@@ -13,7 +13,7 @@
 #include <babeltrace2/babeltrace.h>
 
 #define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL   log_level
+#define BT_LOG_OUTPUT_LEVEL   ((enum bt_log_level) log_level)
 #define BT_LOG_TAG            "PLUGIN/SRC.CTF.FS"
 #include "logging/comp-logging.h"
 
index b11d6f81db23e1938305c7f55263393aa058a705..0e3bdc99de9b9ed451d79ad3ea7b060e2c69b7c8 100644 (file)
@@ -12,7 +12,7 @@
 
 #include <babeltrace2/babeltrace.h>
 
-#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_OUTPUT_LEVEL ((enum bt_log_level) log_level)
 #define BT_LOG_TAG          "PLUGIN/SRC.CTF.FS/QUERY"
 #include "logging/comp-logging.h"
 #include "logging/log.h"
index db5900af866e89a292aca85a31c751590af07baa..2f07797833a11d9ec790ecc4d3a52846c1f52376 100644 (file)
@@ -12,7 +12,7 @@
 #include <babeltrace2/babeltrace.h>
 
 #define BT_COMP_LOG_SELF_COMP (viewer_connection->self_comp)
-#define BT_LOG_OUTPUT_LEVEL   (viewer_connection->log_level)
+#define BT_LOG_OUTPUT_LEVEL   ((enum bt_log_level) viewer_connection->log_level)
 #define BT_LOG_TAG            "PLUGIN/SRC.CTF.LTTNG-LIVE/VIEWER"
 #include "logging/comp-logging.h"
 
index 142914a2d13d96ebd6b3203f5ff5080b22ac4c49..17305dd5ef7ab859ea1affa1837144f5c196cfc1 100644 (file)
@@ -84,7 +84,7 @@ void log_python_traceback(int log_level)
                        goto end;
                }
 
-               BT_LOG_WRITE(log_level, BT_LOG_TAG,
+               BT_LOG_WRITE_PRINTF(log_level, BT_LOG_TAG,
                        "Exception occurred: Python traceback:\n%s", exc->str);
        }
 
index f6e74390737e5b16c809ac46832e7c7d0aeb802b..ff24afd5c42afefee4dd956f53f7366bb8d62cef 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright EfficiOS, Inc.
  */
 
-#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_OUTPUT_LEVEL ((enum bt_log_level) log_level)
 #define BT_LOG_TAG "COMMON/FORMAT-ERROR"
 #include <logging/log.h>
 
index cfeba59b156451c8321ab9aa99f9bdae93144d5f..3e6e7a66fd57929bbefafd8752efd79581e79bd5 100644 (file)
@@ -8,7 +8,7 @@
  * Babeltrace SO info tests
  */
 
-#define BT_LOG_OUTPUT_LEVEL BT_LOG_WARNING
+#define BT_LOG_OUTPUT_LEVEL ((bt_logging_level) BT_LOG_WARNING)
 #define BT_LOG_TAG "TEST/BIN-INFO"
 #include "logging/log.h"
 
This page took 0.113797 seconds and 4 git commands to generate.