| /* | |
| * SPDX-License-Identifier: MIT | |
| * Copyright (c) 2017 wonder-mice | |
| * | |
| * zf log taken from: https://github.com/wonder-mice/zf_log | |
| * | |
| * "Software source code can not be printed on paper and sticked to a cat." | |
| */ | |
| #pragma once | |
| #include <utils/attribute.h> | |
| /* To detect incompatible changes you can define ZF_LOG_VERSION_REQUIRED to | |
| * the current value of ZF_LOG_VERSION before including this file (or via | |
| * compiler command line): | |
| * | |
| * #define ZF_LOG_VERSION_REQUIRED 1 | |
| * #include <zf_log.h> | |
| * | |
| * In that case compilation will fail when included file has incompatible | |
| * version. | |
| */ | |
| #define ZF_LOG_VERSION 1 | |
| #if defined(ZF_LOG_VERSION_REQUIRED) | |
| #if ZF_LOG_VERSION_REQUIRED != ZF_LOG_VERSION | |
| #error different zf_log version required | |
| #endif | |
| #endif | |
| /* Log level guideline: | |
| * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected. | |
| * Process can't continue and must be terminated. In other words, semantic is | |
| * close to assert(). zf_log will call abort() after printing the log message. | |
| * Example: division by zero, unexpected modifications from other thread. | |
| * - ZF_LOG_ERROR - happened something impossible and absolutely unexpected, but | |
| * process is able to recover and continue execution. | |
| * Example: out of memory (could also be FATAL if not handled properly). | |
| * - ZF_LOG_WARN - happened something that *usually* should not happen and | |
| * significantly changes application behavior for some period of time. | |
| * Example: configuration file not found, auth error. | |
| * - ZF_LOG_INFO - happened significant life cycle event or major state | |
| * transition. | |
| * Example: app started, user logged in. | |
| * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the | |
| * execution path. | |
| * - ZF_LOG_VERBOSE - all other events. | |
| * | |
| * Ideally, log file of debugged, well tested, production ready application | |
| * should be empty (no messages with level ZF_LOG_INFO or higher) or very small. | |
| */ | |
| #define ZF_LOG_VERBOSE 1 | |
| #define ZF_LOG_DEBUG 2 | |
| #define ZF_LOG_INFO 3 | |
| #define ZF_LOG_WARN 4 | |
| #define ZF_LOG_ERROR 5 | |
| #define ZF_LOG_FATAL 0xFFFF | |
| #define ZF_LOG_NONE 0xFFFF | |
| /* Log level configuration: | |
| * - ZF_LOG_DEF_LEVEL - defines current log level. Only messages with that level | |
| * and higher will be logged (if ZF_LOG_LEVEL is undefined). | |
| * - ZF_LOG_LEVEL - overrides current log level. Only messages with that level | |
| * and higher will be logged. | |
| * | |
| * Current log level is a compile time check and has no runtime overhead. | |
| * | |
| * Common pattern is to define ZF_LOG_DEF_LEVEL in the build script (e.g. | |
| * Makefile, CMakeLists.txt) for the entire project/target: | |
| * | |
| * CC_ARGS := -DZF_LOG_DEF_LEVEL=ZF_LOG_WARN | |
| * | |
| * And when necessary override it with ZF_LOG_LEVEL in .c/.cpp files (before | |
| * including zf_log.h): | |
| * | |
| * #define ZF_LOG_LEVEL ZF_LOG_VERBOSE | |
| * #include <zf_log.h> | |
| * | |
| * Defining either ZF_LOG_DEF_LEVEL or ZF_LOG_LEVEL in header file is usually | |
| * undesired and produces weird results. | |
| * | |
| * If both ZF_LOG_DEF_LEVEL and ZF_LOG_LEVEL are undefined, then ZF_LOG_INFO | |
| * will be used for release builds (NDEBUG is defined) and ZF_LOG_DEBUG | |
| * otherwise. | |
| * | |
| * When log message has level bellow current log level it will be compiled out | |
| * and its arguments will NOT be evaluated (no "unused variable" warning will be | |
| * generated for variables that are only used in compiled out log messages). | |
| */ | |
| #if defined(ZF_LOG_LEVEL) | |
| #define _ZF_LOG_LEVEL ZF_LOG_LEVEL | |
| #elif defined(ZF_LOG_DEF_LEVEL) | |
| #define _ZF_LOG_LEVEL ZF_LOG_DEF_LEVEL | |
| #else | |
| #ifdef NDEBUG | |
| #define _ZF_LOG_LEVEL ZF_LOG_INFO | |
| #else | |
| #define _ZF_LOG_LEVEL ZF_LOG_DEBUG | |
| #endif | |
| #endif | |
| /* Log tag configuration: | |
| * - ZF_LOG_DEF_TAG - defines default log tag. | |
| * - ZF_LOG_TAG - overrides default log tag. | |
| * | |
| * When defined, value must be a string constant (in double quotes): | |
| * | |
| * #define ZF_LOG_TAG "MAIN" | |
| * #include <zf_log.h> | |
| * | |
| * Defining either ZF_LOG_DEF_TAG or ZF_LOG_TAG in header files usually | |
| * undesired and produces weird results. | |
| * | |
| * If both ZF_LOG_DEF_TAG and ZF_LOG_TAG are undefined no tag will be added to | |
| * the log message. | |
| */ | |
| #if defined(ZF_LOG_TAG) | |
| #define _ZF_LOG_TAG ZF_LOG_TAG | |
| #elif defined(ZF_LOG_DEF_TAG) | |
| #define _ZF_LOG_TAG ZF_LOG_DEF_TAG | |
| #else | |
| #define _ZF_LOG_TAG 0 | |
| #endif | |
| /* When defined, all produced linker symbols will be prefixed with the specified | |
| * value. That allows to use zf_log privately in another library without | |
| * exposing zf_log symbols in their original form (so library will not have | |
| * externaly visible dependency on zf_log). Value must be without quotes: | |
| * | |
| * CC_ARGS := -DZF_LOG_LIBRARY_PREFIX=my_lib | |
| */ | |
| #ifdef ZF_LOG_LIBRARY_PREFIX | |
| #define _ZF_LOG_DECOR__(prefix, name) prefix ## name | |
| #define _ZF_LOG_DECOR_(prefix, name) _ZF_LOG_DECOR__(prefix, name) | |
| #define _ZF_LOG_DECOR(name) _ZF_LOG_DECOR_(ZF_LOG_LIBRARY_PREFIX, name) | |
| #define zf_log_set_tag_prefix _ZF_LOG_DECOR(zf_log_set_tag_prefix) | |
| #define zf_log_set_mem_width _ZF_LOG_DECOR(zf_log_set_mem_width) | |
| #define zf_log_set_output_level _ZF_LOG_DECOR(zf_log_set_output_level) | |
| #define zf_log_set_output_callback _ZF_LOG_DECOR(zf_log_set_output_callback) | |
| #define _zf_log_output_lvl _ZF_LOG_DECOR(_zf_log_output_lvl) | |
| #define _zf_log_write_d _ZF_LOG_DECOR(_zf_log_write_d) | |
| #define _zf_log_write _ZF_LOG_DECOR(_zf_log_write) | |
| #define _zf_log_write_mem_d _ZF_LOG_DECOR(_zf_log_write_mem_d) | |
| #define _zf_log_write_mem _ZF_LOG_DECOR(_zf_log_write_mem) | |
| #endif | |
| /* Runtime configuration */ | |
| #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 target) name (e.g. to separate client and server process). | |
| */ | |
| void zf_log_set_tag_prefix(const char *const prefix); | |
| /* Set number of bytes per log line in memory dump. | |
| */ | |
| void zf_log_set_mem_width(const unsigned w); | |
| /* Set output log level. Output log level is a run time check and has low | |
| * overhead of compare operation and conditional jump. When the log message has | |
| * level bellow output log level it will not be logged and its arguments will | |
| * NOT be evaluated. | |
| * | |
| * Since all messages that are below current log level are compiled out, | |
| * only messages that are on or above the current log level are affected by the | |
| * output log level check. | |
| * | |
| * Output log level can be changed at any time during program execution. | |
| */ | |
| void zf_log_set_output_level(const int lvl); | |
| typedef struct zf_log_output_ctx | |
| { | |
| int lvl; | |
| const char *tag; | |
| 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) */ | |
| } | |
| zf_log_output_ctx; | |
| typedef void (*zf_log_output_cb)(zf_log_output_ctx *ctx); | |
| /* Set output callback function. It will be called for each log line allowed | |
| * by both current log level and output log level. Callback function is allowed | |
| * to modify content of the buffers pointed by the ctx, but it's not allowed to | |
| * modify buffer pointers and other fields. | |
| */ | |
| void zf_log_set_output_callback(const zf_log_output_cb cb); | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| /* Checking current log level at compile time (ignoring output log level). | |
| * For example: | |
| * | |
| * #if ZF_LOG_ALLOW_DEBUG | |
| * const char *const g_enum_strings[] = { | |
| * "enum_value_0", "enum_value_1", "enum_value_2" | |
| * }; | |
| * #endif | |
| * // ... | |
| * #if ZF_LOG_ALLOW_DEBUG | |
| * ZF_LOGD("enum value: %s", g_enum_strings[v]); | |
| * #endif | |
| */ | |
| #define ZF_LOG_ALLOW(lvl) ((lvl) >= _ZF_LOG_LEVEL) | |
| #define ZF_LOG_ALLOW_VERBOSE ZF_LOG_ALLOW(ZF_LOG_VERBOSE) | |
| #define ZF_LOG_ALLOW_DEBUG ZF_LOG_ALLOW(ZF_LOG_DEBUG) | |
| #define ZF_LOG_ALLOW_INFO ZF_LOG_ALLOW(ZF_LOG_INFO) | |
| #define ZF_LOG_ALLOW_WARN ZF_LOG_ALLOW(ZF_LOG_WARN) | |
| #define ZF_LOG_ALLOW_ERROR ZF_LOG_ALLOW(ZF_LOG_ERROR) | |
| #define ZF_LOG_ALLOW_FATAL ZF_LOG_ALLOW(ZF_LOG_FATAL) | |
| /* Checking output log level at run time (taking into account current log | |
| * level). For example: | |
| * | |
| * if (ZF_LOG_OUTPUT_DEBUG) | |
| * { | |
| * char hash[65]; | |
| * sha256(data_ptr, data_sz, hash); | |
| * ZF_LOGD("data: len=%u, sha256=%s", data_sz, hash); | |
| * } | |
| */ | |
| #define ZF_LOG_OUTPUT(lvl) \ | |
| (ZF_LOG_ALLOW((lvl)) && (lvl) >= _zf_log_output_lvl) | |
| #define ZF_LOG_OUTPUT_VERBOSE ZF_LOG_OUTPUT(ZF_LOG_VERBOSE) | |
| #define ZF_LOG_OUTPUT_DEBUG ZF_LOG_OUTPUT(ZF_LOG_DEBUG) | |
| #define ZF_LOG_OUTPUT_INFO ZF_LOG_OUTPUT(ZF_LOG_INFO) | |
| #define ZF_LOG_OUTPUT_WARN ZF_LOG_OUTPUT(ZF_LOG_WARN) | |
| #define ZF_LOG_OUTPUT_ERROR ZF_LOG_OUTPUT(ZF_LOG_ERROR) | |
| #define ZF_LOG_OUTPUT_FATAL ZF_LOG_OUTPUT(ZF_LOG_FATAL) | |
| #define _ZF_LOG_PRINTFLIKE(a, b) FORMAT(printf, a, b) | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| extern int _zf_log_output_lvl; | |
| void _zf_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, ...) _ZF_LOG_PRINTFLIKE(6, 7); | |
| void _zf_log_write(const int lvl, const char *const tag, | |
| const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(3, 4); | |
| void _zf_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, ...) _ZF_LOG_PRINTFLIKE(8, 9); | |
| void _zf_log_write_mem(const int lvl, const char *const tag, | |
| const void *const d, const unsigned d_sz, | |
| const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(5, 6); | |
| #ifdef __cplusplus | |
| } | |
| #endif | |
| /* Message logging macros: | |
| * - ZF_LOGV("format string", args, ...) | |
| * - ZF_LOGD("format string", args, ...) | |
| * - ZF_LOGI("format string", args, ...) | |
| * - ZF_LOGW("format string", args, ...) | |
| * - ZF_LOGF("format string", args, ...) | |
| * | |
| * Memory logging macros: | |
| * - ZF_LOGV_MEM(data_ptr, data_sz, "format string", args, ...) | |
| * - ZF_LOGD_MEM(data_ptr, data_sz, "format string", args, ...) | |
| * - ZF_LOGI_MEM(data_ptr, data_sz, "format string", args, ...) | |
| * - ZF_LOGW_MEM(data_ptr, data_sz, "format string", args, ...) | |
| * - ZF_LOGF_MEM(data_ptr, data_sz, "format string", args, ...) | |
| * | |
| * Format string follows printf() conventions. Both data_ptr and data_sz could | |
| * be 0. | |
| */ | |
| #ifdef NDEBUG | |
| #define _ZF_LOG_IMP(lvl, tag, ...) \ | |
| do { \ | |
| if (ZF_LOG_OUTPUT(lvl)) \ | |
| _zf_log_write(lvl, tag, __VA_ARGS__); \ | |
| } while (0) | |
| #define _ZF_LOG_MEM_IMP(lvl, tag, d, d_sz, ...) \ | |
| do { \ | |
| if (ZF_LOG_OUTPUT(lvl)) \ | |
| _zf_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \ | |
| } while (0) | |
| #else | |
| #define _ZF_LOG_IMP(lvl, tag, ...) \ | |
| do { \ | |
| if (ZF_LOG_OUTPUT(lvl)) \ | |
| _zf_log_write_d(__func__, __FILE__, __LINE__, \ | |
| lvl, tag, __VA_ARGS__); \ | |
| } while (0) | |
| #define _ZF_LOG_MEM_IMP(lvl, tag, d, d_sz, ...) \ | |
| do { \ | |
| if (ZF_LOG_OUTPUT(lvl)) \ | |
| _zf_log_write_mem_d(__func__, __FILE__, __LINE__, \ | |
| lvl, tag, d, d_sz, __VA_ARGS__); \ | |
| } while (0) | |
| #endif | |
| static inline void _zf_log_unused(const int dummy, ...) {(void)dummy;} | |
| #define _ZF_LOG_UNUSED(...) \ | |
| do { if (0) _zf_log_unused(0, __VA_ARGS__); } while (0) | |
| #if ZF_LOG_ALLOW_VERBOSE | |
| #define ZF_LOGV(...) \ | |
| _ZF_LOG_IMP(ZF_LOG_VERBOSE, _ZF_LOG_TAG, __VA_ARGS__) | |
| #define ZF_LOGV_MEM(...) \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_VERBOSE, _ZF_LOG_TAG, __VA_ARGS__) | |
| #else | |
| #define ZF_LOGV(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGV_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif | |
| #if ZF_LOG_ALLOW_DEBUG | |
| #define ZF_LOGD(...) \ | |
| _ZF_LOG_IMP(ZF_LOG_DEBUG, _ZF_LOG_TAG, __VA_ARGS__) | |
| #define ZF_LOGD_MEM(...) \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_DEBUG, _ZF_LOG_TAG, __VA_ARGS__) | |
| #else | |
| #define ZF_LOGD(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGD_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif | |
| #if ZF_LOG_ALLOW_INFO | |
| #define ZF_LOGI(...) \ | |
| _ZF_LOG_IMP(ZF_LOG_INFO, _ZF_LOG_TAG, __VA_ARGS__) | |
| #define ZF_LOGI_MEM(...) \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_INFO, _ZF_LOG_TAG, __VA_ARGS__) | |
| #else | |
| #define ZF_LOGI(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGI_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif | |
| #if ZF_LOG_ALLOW_WARN | |
| #define ZF_LOGW(...) \ | |
| _ZF_LOG_IMP(ZF_LOG_WARN, _ZF_LOG_TAG, __VA_ARGS__) | |
| #define ZF_LOGW_MEM(...) \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_WARN, _ZF_LOG_TAG, __VA_ARGS__) | |
| #else | |
| #define ZF_LOGW(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGW_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif | |
| #if ZF_LOG_ALLOW_ERROR | |
| #define ZF_LOGE(...) \ | |
| _ZF_LOG_IMP(ZF_LOG_ERROR, _ZF_LOG_TAG, __VA_ARGS__) | |
| #define ZF_LOGE_MEM(...) \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_ERROR, _ZF_LOG_TAG, __VA_ARGS__) | |
| #else | |
| #define ZF_LOGE(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGE_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif | |
| #if ZF_LOG_ALLOW_FATAL | |
| #define ZF_LOGF(...) do { \ | |
| _ZF_LOG_IMP(ZF_LOG_FATAL, _ZF_LOG_TAG, __VA_ARGS__); UNREACHABLE();\ | |
| } while (0) | |
| #define ZF_LOGF_MEM(...) do { \ | |
| _ZF_LOG_MEM_IMP(ZF_LOG_FATAL, _ZF_LOG_TAG, __VA_ARGS__); UNREACHABLE();\ | |
| } while (0) | |
| #else | |
| #define ZF_LOGF(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #define ZF_LOGF_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__) | |
| #endif |