commit 7a550f904f2c652523c10fb2d493b1d66b0b7797
parent 835af528d383f35a80405c62faf8923245ba8506
Author: Robert Russell <robert@rr3.xyz>
Date: Sun, 3 Nov 2024 11:32:00 -0800
Upgrade logging module
Diffstat:
| M | inc/log.h | | | 80 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
| M | src/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);