diff --git a/src/log.c b/src/log.c index 1a7626e..ab09208 100644 --- a/src/log.c +++ b/src/log.c @@ -25,144 +25,132 @@ #define MAX_CALLBACKS 32 typedef struct { - log_LogFn fn; - void *udata; - int level; + log_LogFn fn; + void *udata; + int level; } Callback; static struct { - void *udata; - log_LockFn lock; - int level; - bool quiet; - Callback callbacks[MAX_CALLBACKS]; -} L; + void *udata; + log_LockFn lock; + int level; + bool quiet; + Callback callbacks[MAX_CALLBACKS]; +} L = { + NULL, NULL, RXI_LOGC_DEFAULT_LEVEL, false, {0}, +}; +static const char *level_strings[] = {"[[DEBUG]]", "[[TRACE]]", "[[ INFO]]", + "[[ WARN]]", "[[ERROR]]", "[[FATAL]]"}; -static const char *level_strings[] = { - "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" -}; +static inline const char *get_level_string(int level) { + return level_strings[(level + 32) / 32]; +} #ifdef LOG_USE_COLOR -static const char *level_colors[] = { - "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" -}; -#endif +static const char *level_colors[] = {"\x1b[36m", "\x1b[94m", "\x1b[32m", + "\x1b[33m", "\x1b[31m", "\x1b[35m"}; +static inline const char *get_level_color(int level) { + return level_colors[(level + 32) / 32]; +} +#endif static void stdout_callback(log_Event *ev) { - char buf[16]; - buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; + char buf[16]; + buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; #ifdef LOG_USE_COLOR - fprintf( - ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", - buf, level_colors[ev->level], level_strings[ev->level], - ev->file, ev->line); + fprintf(ev->udata, "%s %s%-7s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buf, + get_level_color(ev->level), get_level_string(ev->level), ev->file, + ev->line); #else - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); + fprintf(ev->udata, "%s %-7s %s:%d: ", buf, get_level_string(ev->level), + ev->file, ev->line); #endif - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); } - static void file_callback(log_Event *ev) { - char buf[64]; - buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; - fprintf( - ev->udata, "%s %-5s %s:%d: ", - buf, level_strings[ev->level], ev->file, ev->line); - vfprintf(ev->udata, ev->fmt, ev->ap); - fprintf(ev->udata, "\n"); - fflush(ev->udata); + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + fprintf(ev->udata, "%s %-7s %s:%d: ", buf, get_level_string(ev->level), + ev->file, ev->line); + vfprintf(ev->udata, ev->fmt, ev->ap); + fprintf(ev->udata, "\n"); + fflush(ev->udata); } - -static void lock(void) { - if (L.lock) { L.lock(true, L.udata); } +static void lock(void) { + if (L.lock) { + L.lock(true, L.udata); + } } - static void unlock(void) { - if (L.lock) { L.lock(false, L.udata); } -} - - -const char* log_level_string(int level) { - return level_strings[level]; + if (L.lock) { + L.lock(false, L.udata); + } } - void log_set_lock(log_LockFn fn, void *udata) { - L.lock = fn; - L.udata = udata; + L.lock = fn; + L.udata = udata; } +void log_set_level(int level) { L.level = level; } -void log_set_level(int level) { - L.level = level; -} - - -void log_set_quiet(bool enable) { - L.quiet = enable; -} - +void log_set_quiet(bool enable) { L.quiet = enable; } int log_add_callback(log_LogFn fn, void *udata, int level) { - for (int i = 0; i < MAX_CALLBACKS; i++) { - if (!L.callbacks[i].fn) { - L.callbacks[i] = (Callback) { fn, udata, level }; - return 0; + for (int i = 0; i < MAX_CALLBACKS; i++) { + if (!L.callbacks[i].fn) { + L.callbacks[i] = (Callback){fn, udata, level}; + return 0; + } } - } - return -1; + return -1; } - int log_add_fp(FILE *fp, int level) { - return log_add_callback(file_callback, fp, level); + return log_add_callback(file_callback, fp, level); } - static void init_event(log_Event *ev, void *udata) { - if (!ev->time) { - time_t t = time(NULL); - ev->time = localtime(&t); - } - ev->udata = udata; + if (!ev->time) { + time_t t = time(NULL); + ev->time = localtime_r(&t, &ev->time_buf); + } + ev->udata = udata; } - void log_log(int level, const char *file, int line, const char *fmt, ...) { - log_Event ev = { - .fmt = fmt, - .file = file, - .line = line, - .level = level, - }; - - lock(); - - if (!L.quiet && level >= L.level) { - init_event(&ev, stderr); - va_start(ev.ap, fmt); - stdout_callback(&ev); - va_end(ev.ap); - } - - for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { - Callback *cb = &L.callbacks[i]; - if (level >= cb->level) { - init_event(&ev, cb->udata); - va_start(ev.ap, fmt); - cb->fn(&ev); - va_end(ev.ap); + log_Event ev = { + .fmt = fmt, + .file = file, + .line = line, + .level = level, + }; + + lock(); + + if (!L.quiet && level >= L.level) { + init_event(&ev, stderr); + va_start(ev.ap, fmt); + stdout_callback(&ev); + va_end(ev.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { + Callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + init_event(&ev, cb->udata); + va_start(ev.ap, fmt); + cb->fn(&ev); + va_end(ev.ap); + } } - } - unlock(); + unlock(); } diff --git a/src/log.h b/src/log.h index b1fae24..c01f9be 100644 --- a/src/log.h +++ b/src/log.h @@ -15,35 +15,78 @@ #define LOG_VERSION "0.1.0" +#ifndef RXI_LOGC_DEFAULT_LEVEL +#define RXI_LOGC_DEFAULT_LEVEL LOG_TRACE +#endif + +#if defined __GNUC__ +#define RXI_LOGC_PRINTF_ATTRIB(n, m) __attribute__((format(printf, n, m))) +#else +#define RXI_LOGC_PRINTF_ATTRIB(n, m) +#endif + +enum { + LOG_DEBUG = -32, + LOG_TRACE = 0, + LOG_INFO = 32, + LOG_WARN = 64, + LOG_ERROR = 96, + LOG_FATAL = 128, +}; + typedef struct { - va_list ap; - const char *fmt; - const char *file; - struct tm *time; - void *udata; - int line; - int level; + va_list ap; + const char *fmt; + const char *file; + struct tm *time; + struct tm time_buf; + void *udata; + int line; + int level; } log_Event; typedef void (*log_LogFn)(log_Event *ev); typedef void (*log_LockFn)(bool lock, void *udata); -enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; - -#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) -#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) -#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) -#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) -#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) -#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) +#define log_debug(...) \ + do { \ + if (LOG_DEBUG >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) +#define log_trace(...) \ + do { \ + if (LOG_TRACE >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) +#define log_info(...) \ + do { \ + if (LOG_INFO >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) +#define log_warn(...) \ + do { \ + if (LOG_WARN >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) +#define log_error(...) \ + do { \ + if (LOG_ERROR >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) +#define log_fatal(...) \ + do { \ + if (LOG_FATAL >= RXI_LOGC_DEFAULT_LEVEL) \ + log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) -const char* log_level_string(int level); +const char *log_level_string(int level); void log_set_lock(log_LockFn fn, void *udata); void log_set_level(int level); void log_set_quiet(bool enable); int log_add_callback(log_LogFn fn, void *udata, int level); int log_add_fp(FILE *fp, int level); -void log_log(int level, const char *file, int line, const char *fmt, ...); +void log_log(int level, const char *file, int line, const char *fmt, ...) + RXI_LOGC_PRINTF_ATTRIB(4, 5); #endif