rcx

miscellaneous C library
git clone git://git.rr3.xyz/rcx
Log | Files | Refs | README | LICENSE

commit 7a550f904f2c652523c10fb2d493b1d66b0b7797
parent 835af528d383f35a80405c62faf8923245ba8506
Author: Robert Russell <robert@rr3.xyz>
Date:   Sun,  3 Nov 2024 11:32:00 -0800

Upgrade logging module

Diffstat:
Minc/log.h | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/log.c | 9+++------
2 files changed, 71 insertions(+), 18 deletions(-)

diff --git a/inc/log.h b/inc/log.h @@ -1,31 +1,87 @@ #pragma once +#include <stdarg.h> #include <stdbool.h> -#define r_infof(...) r_log(__FILE__, __LINE__, 0, "INFO", "\x1b[32m", 0, __VA_ARGS__) -#define r_warnf(...) r_log(__FILE__, __LINE__, 1, "WARN", "\x1b[33m", 0, __VA_ARGS__) -#define r_errorf(...) r_log(__FILE__, __LINE__, 2, "ERROR", "\x1b[31m", 0, __VA_ARGS__) -#define r_fatalf(...) r_log(__FILE__, __LINE__, 3, "FATAL", "\x1b[31m", 1, __VA_ARGS__) +/* This module provides formatted logging to stderr. All logging functions are + * thread-safe and preserve errno. + * + * The r_{,v}logf_generic functions accept an arbitrary integer log level to + * filter by (see r_log_init) and an associated name (e.g., "info" or "error"), + * color (for when terminal color is enabled via r_log_init), and exit code (to + * terminate the process with if nonzero). This allows users to create their + * own log levels. + * + * The r_{,v}log_basic functions have four fixed log levels (info, warn, error, + * and fatal), with corresponding helper macros. Consult the implementation of + * these macros to see how the main logging functions work. */ + +/* TODO: Support optionally logging __func__ */ + +#define R_LOG_LEVEL_INFO 0 +#define R_LOG_LEVEL_WARN 1 +#define R_LOG_LEVEL_ERROR 2 +#define R_LOG_LEVEL_FATAL 3 -/* Set global settings for r_log. +#define r_logf(level, ...) r_logf_basic(__FILE__, __LINE__, level, __VA_ARGS__) +#define r_infof(...) r_logf(R_LOG_LEVEL_INFO, __VA_ARGS__) +#define r_warnf(...) r_logf(R_LOG_LEVEL_WARN, __VA_ARGS__) +#define r_errorf(...) r_logf(R_LOG_LEVEL_ERROR, __VA_ARGS__) +#define r_fatalf(...) r_logf(R_LOG_LEVEL_FATAL, __VA_ARGS__) + +/* Set global logging settings. * color: <0 force disables color, 0 detects if color should be used, * and >0 force enables color * log_time: enable/disable timestamps in log messages * log_loc: enable/disable source location in log messages * min_level: output log messages iff their level is at least this value * The color detection happens when r_log_init is called, not on each - * subsequent invocation of r_log. + * subsequent invocation of a logging function. * * r_log_init can be called multiple times, but it is not thread-safe. * Typically, you should call r_log_init just once and before starting * multiple threads. * * Calling r_log_init is optional. By default, color is off, time and source - * location are not logged, and the minimum log level is set to 0 (so - * everything is logged). */ + * location are not logged, and the minimum log level is set to 0 (so all basic + * logs are printed). */ void r_log_init(int color, bool log_time, bool log_loc, int min_level); -/* Log a message to stderr. See definition of r_infof, r_warnf, etc. for usage. - * - * r_log is thread-safe and preserves errno. */ -void r_log(char *file, int line, int level, char *name, char *color, int code, char *fmt, ...); +void r_vlogf_generic(char *file, int line, int level, char *name, char *color, int code, char *fmt, va_list args); + +static inline void +r_logf_generic(char *file, int line, int level, char *name, char *color, int code, char *fmt, ...) { + va_list args; + va_start(args, fmt); + r_vlogf_generic(file, line, level, name, color, code, fmt, args); + va_end(args); +} + +static inline void +r_vlogf_basic(char *file, int line, int level, char *fmt, va_list args) { + static char *names[] = { "INFO", "WARN", "ERROR", "FATAL" }; + +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define YELLOW "\x1b[33m" + static char *colors[] = { GREEN, YELLOW, RED, RED }; +#undef YELLOW +#undef GREEN +#undef RED + + static int codes[] = { 0, 0, 0, 1 }; + + /* Clamp level */ + if (level < R_LOG_LEVEL_INFO) level = R_LOG_LEVEL_INFO; + if (level > R_LOG_LEVEL_FATAL) level = R_LOG_LEVEL_FATAL; + + r_vlogf_generic(file, line, level, names[level], colors[level], codes[level], fmt, args); +} + +static inline void +r_logf_basic(char *file, int line, int level, char *fmt, ...) { + va_list args; + va_start(args, fmt); + r_vlogf_basic(file, line, level, fmt, args); + va_end(args); +} diff --git a/src/log.c b/src/log.c @@ -29,7 +29,7 @@ r_log_init(int color, bool log_time_, bool log_loc_, int min_level_) { } else { /* detect */ char *no_color = getenv("NO_COLOR"); /* https://no-color.org */ use_color = isatty(fileno(stderr)) - && !(no_color && no_color[0] != '\0'); + && !(no_color && no_color[0] != '\0'); } log_time = log_time_; log_loc = log_loc_; @@ -46,11 +46,11 @@ iso8601(char *buf, time_t t) { } void -r_log( +r_vlogf_generic( char *file, int line, int level, char *name, char *color, int code, - char *fmt, ... + char *fmt, va_list args ) { if (level < min_level) return; @@ -70,10 +70,7 @@ r_log( if (log_loc && file) fprintf(stderr, "%s%s:%d%s ", SGR(FAINT), file, line, SGR(RESET)); - va_list args; - va_start(args, fmt); vfprintf(stderr, fmt, args); - va_end(args); fputc('\n', stderr); funlockfile(stderr);