Allow controling logging verbosity at runtime with `LOG_LEVEL=<name>` env-var. Default is `DEBUG` (trace) on debug builds, and `WARN` on release builds.
TRACE() is no longer a no-op in release builds. This may be expensive since each call to `_t_fprintf()` does an atomic read. TODO: Migrate true debug-only messages to `dprintf()` instead of `TRACE()`; maybe find a way to make `_t_fprintf()` more efficient. Fortune for naka's current commit: Future blessing − 末吉master
parent
a3f4b80d54
commit
139d741bce
@ -0,0 +1,35 @@
|
|||||||
|
//! Trace messages
|
||||||
|
#ifndef _TRACE_H
|
||||||
|
#define _TRACE_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#endif /* _TRACE_H */
|
@ -0,0 +1,112 @@
|
|||||||
|
// Trace messages
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <trace.h>
|
||||||
|
|
||||||
|
#include <macros.h>
|
||||||
|
#include <ints.h>
|
||||||
|
|
||||||
|
static _Atomic enum trace_level _t_level = _TRACE_LEVEL_DEFAULT;
|
||||||
|
|
||||||
|
const char* const _TRACE_CONTROL_ENV_NAME = "LOG_LEVEL";
|
||||||
|
|
||||||
|
const struct {const char* const name; const enum trace_level level; } _trace_level_names[] = {
|
||||||
|
#define X(name) { #name, TRACE_LEVEL_ ## name }
|
||||||
|
#define Y(name, val) { #name, TRACE_LEVEL_ ## val }
|
||||||
|
X(DEBUG), Y(TRACE, DEBUG),
|
||||||
|
X(INFO),
|
||||||
|
X(WARN),
|
||||||
|
X(ERROR),
|
||||||
|
X(FATAL),
|
||||||
|
#undef Y
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
#define _trace_level_names_n (sizeof(_trace_level_names) / sizeof(_trace_level_names[0]))
|
||||||
|
|
||||||
|
|
||||||
|
static bool _trace_lookup(const char*pIN name, enum trace_level *pOUT olevel)
|
||||||
|
{
|
||||||
|
debug_assert(name);
|
||||||
|
|
||||||
|
for(usize i=0;i<_trace_level_names_n;i++) {
|
||||||
|
let level = &_trace_level_names[i];
|
||||||
|
debug_assert(level && level->name);
|
||||||
|
if(strcmp(name, level->name) == 0) {
|
||||||
|
*olevel =level->level;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _trace_reverse_lookup(enum trace_level level, const char* *pOUT name)
|
||||||
|
{
|
||||||
|
for(usize i=0;i<_trace_level_names_n;i++) {
|
||||||
|
let lv = &_trace_level_names[i];
|
||||||
|
debug_assert(lv && lv->name);
|
||||||
|
if(lv->level == level) {
|
||||||
|
*name = lv->name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* trace_name_of(enum trace_level lv)
|
||||||
|
{
|
||||||
|
const char* ret;
|
||||||
|
if(!_trace_reverse_lookup(lv, &ret)) return NULL;
|
||||||
|
else return ret;
|
||||||
|
}
|
||||||
|
inline enum trace_level trace_max_level()
|
||||||
|
{
|
||||||
|
return _t_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool _trace_hidden(enum trace_level l)
|
||||||
|
{
|
||||||
|
return l < trace_max_level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _t_fprintf(enum trace_level l, FILE* output, const char* msg, ...)
|
||||||
|
{
|
||||||
|
if(_trace_hidden(l)) return 0;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, msg);
|
||||||
|
register int r = vfprintf(output, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_init_with(enum trace_level max_level)
|
||||||
|
{
|
||||||
|
_t_level = max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool _try_get_env(const char* name, const char* *pOUT value)
|
||||||
|
{
|
||||||
|
return !!(*value = (const char*)getenv(name));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int trace_init()
|
||||||
|
{
|
||||||
|
const char* eval;
|
||||||
|
if(!_try_get_env(_TRACE_CONTROL_ENV_NAME, &eval)) return 1;
|
||||||
|
else {
|
||||||
|
assert(eval);
|
||||||
|
if(*eval == 0) return 1; // Count empty string as default.
|
||||||
|
|
||||||
|
enum trace_level level;
|
||||||
|
if(!_trace_lookup(eval, &level)) return 0;
|
||||||
|
else {
|
||||||
|
trace_init_with(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
Loading…
Reference in new issue