//! Trace messages #ifndef _TRACE_H #define _TRACE_H #ifdef __cplusplus #ifdef _TRACE_USE_FMT #include #include #endif extern "C" { #endif #include #include enum trace_level { TRACE_LEVEL_DEBUG = 0, TRACE_LEVEL_INFO, TRACE_LEVEL_WARN, TRACE_LEVEL_ERROR, TRACE_LEVEL_FATAL, _TRACE_LEVEL_NUM, }; #ifdef DEBUG #define _TRACE_LEVEL_DEFAULT TRACE_LEVEL_DEBUG #else #define _TRACE_LEVEL_DEFAULT TRACE_LEVEL_WARN #endif int _t_fprintf(enum trace_level l, FILE* output, const char* msg, ...); enum trace_level trace_max_level(); void trace_init_with(enum trace_level max_level); // Initialise the tracer with env-var `_TRACE_CONTROL_ENV_NAME`. If the env-var is not found, the max trace level is not modified. If it is found but has an invalid value, this function returns 0. Otherwise it modifies the max trace level value and returns 1. int trace_init(); const char* trace_name_of(enum trace_level lv); extern const char* const _TRACE_CONTROL_ENV_NAME; extern const char* const _trace_level_descriptions[_TRACE_LEVEL_NUM]; #ifdef __cplusplus } #ifdef _TRACE_USE_FMT namespace Trace { //TODO: How to get caller info? template inline constexpr void printf(const char (&msg)[N], Args&&... pack) { if (Level >= trace_max_level()) { fmt::vprint(stderr, msg, fmt::make_format_args(std::forward(pack)...)); std::putc('\n', stderr); }; } #define _TRACE_L(level, name) template \ inline constexpr void name(const char (&msg)[N], Args&&... pack) \ { ::Trace::printf(msg, std::forward(pack)...); } _TRACE_L(DEBUG, Debug); _TRACE_L(DEBUG, Trace); _TRACE_L(INFO, Info); _TRACE_L(WARN, Warn); _TRACE_L(ERROR, Error); template [[noreturn]] inline constexpr void Fatal(const char (&msg)[N], Args&&... pack) { ::Trace::printf(msg, std::forward(pack)...); ::std::terminate(); } #undef _TRACE_L } #endif #endif #endif /* _TRACE_H */