cbase 1.50.0
C/C++ Static Template
Loading...
Searching...
No Matches
base_log.c
Go to the documentation of this file.
1
7
8#include "base/base_log.h"
9#include <string.h>
10#include <time.h>
11
13#define MAX_CALLBACKS 4
14
21
30
32static BaseLogger L = {.level = BASE_LOG_LEVEL_TRACE};
33
35static const char *level_strings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
36
37// Standard ANSI terminal colors
38#define COLOR_TRACE "\x1b[94m"
39#define COLOR_DEBUG "\x1b[36m"
40#define COLOR_INFO "\x1b[32m"
41#define COLOR_WARN "\x1b[33m"
42#define COLOR_ERROR "\x1b[31m"
43#define COLOR_FATAL "\x1b[35m"
44#define COLOR_RESET "\x1b[0m"
45
46#if OS_WINDOWS
47# include <windows.h>
51static b32
52enable_vt_colors(HANDLE handle)
53{
54 DWORD mode = 0;
55 if (!GetConsoleMode(handle, &mode)) return 0;
56 return SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
57}
58
59static b32 vt_stdout_enabled = -1;
60static b32 vt_stderr_enabled = -1;
61
62static const char *
63color_for_stream(b32 is_stderr, const char *color)
64{
65 if (is_stderr) {
66 if (vt_stderr_enabled < 0)
67 vt_stderr_enabled = enable_vt_colors(GetStdHandle(STD_ERROR_HANDLE));
68 return vt_stderr_enabled ? color : "";
69 } else {
70 if (vt_stdout_enabled < 0)
71 vt_stdout_enabled = enable_vt_colors(GetStdHandle(STD_OUTPUT_HANDLE));
72 return vt_stdout_enabled ? color : "";
73 }
74}
75#else
76static const char *
77color_for_stream(b32 is_stderr, const char *color)
78{
79 (void)is_stderr;
80 return color;
81}
82#endif
83
85static const char *level_colors[] =
86 {COLOR_TRACE, COLOR_DEBUG, COLOR_INFO, COLOR_WARN, COLOR_ERROR, COLOR_FATAL};
87
88// Internal Helpers
89
91static void
92lock(void)
93{
94 if (L.lock_fn) {
95 L.lock_fn(L.lock_data, 1);
96 }
97}
98
100static void
102{
103 if (L.lock_fn) {
104 L.lock_fn(L.lock_data, 0);
105 }
106}
107
111static void
113{
114 char buf[16];
115 time_t t = time(NULL);
116 buf[strftime(buf, sizeof(buf), "%H:%M:%S", localtime(&t))] = '\0';
117
118 b32 is_err = (ev->level >= BASE_LOG_LEVEL_WARN);
119 FILE *out = is_err ? stderr : stdout;
120
121 fprintf(out,
122 "%s %s%-5s%s \x1b[90m%s:%d:\x1b[0m ",
123 buf,
124 color_for_stream(is_err, level_colors[ev->level]),
125 level_strings[ev->level],
126 color_for_stream(is_err, COLOR_RESET),
127 ev->file,
128 ev->line);
129
130 vfprintf(out, ev->fmt, ev->ap);
131 fprintf(out, "\n");
132 fflush(out);
133}
134
138static void
139file_callback(BaseLogEvent *ev, void *user_data)
140{
141 FILE *fp = (FILE *)user_data;
142 char buf[64];
143 time_t t = time(NULL);
144 buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&t))] = '\0';
145
146 fprintf(fp, "%s %-5s %s:%d: ", buf, level_strings[ev->level], ev->file, ev->line);
147 vfprintf(fp, ev->fmt, ev->ap);
148 fprintf(fp, "\n");
149 fflush(fp);
150}
151
152void
154{
155 L.level = level;
156}
157
158void
160{
161 L.quiet = enable;
162}
163
164void
166{
167 L.lock_fn = fn;
168 L.lock_data = user_data;
169}
170
171b32
172base_log_add_fp(FILE *fp, BaseLogLevel min_level)
173{
174 return base_log_add_callback(file_callback, fp, min_level);
175}
176
177b32
178base_log_add_callback(BaseLogFn fn, void *user_data, BaseLogLevel min_level)
179{
180 for (int i = 0; i < MAX_CALLBACKS; i++) {
181 if (L.callbacks[i].fn == NULL) {
182 L.callbacks[i].fn = fn;
183 L.callbacks[i].user_data = user_data;
184 L.callbacks[i].min_level = min_level;
185 return 1;
186 }
187 }
188 return 0;
189}
190
191b32
193{
194 for (int i = 0; i < MAX_CALLBACKS; i++) {
195 if (L.callbacks[i].fn == fn) {
196 L.callbacks[i].fn = NULL;
197 L.callbacks[i].user_data = NULL;
198 L.callbacks[i].min_level = 0;
199 return 1;
200 }
201 }
202 return 0;
203}
204
205void
206base_log_message(BaseLogLevel level, const char *file, int line, const char *fmt, ...)
207{
208 if (level < L.level) {
209 return;
210 }
211
212 BaseLogEvent ev = {
213 .fmt = fmt,
214 .file = file,
215 .line = line,
216 .level = level,
217 };
218
219 lock();
220
221 if (!L.quiet) {
222 va_start(ev.ap, fmt);
223 log_stdout(&ev);
224 va_end(ev.ap);
225 }
226
227 for (int i = 0; i < MAX_CALLBACKS; i++) {
228 BaseLogCallback *cb = &L.callbacks[i];
229 if (cb->fn != NULL && level >= cb->min_level) {
230 va_start(ev.ap, fmt);
231 cb->fn(&ev, cb->user_data);
232 va_end(ev.ap);
233 }
234 }
235
236 unlock();
237}
static BaseLogger L
The single global logger instance state.
Definition base_log.c:32
static const char * level_strings[]
Human-readable string representations of the log levels.
Definition base_log.c:35
static void log_stdout(BaseLogEvent *ev)
Formats and writes a log event to standard output or standard error.
Definition base_log.c:112
static void file_callback(BaseLogEvent *ev, void *user_data)
Default callback used to write log events to a standard C FILE*.
Definition base_log.c:139
static const char * level_colors[]
Array mapping log levels to their respective ANSI color codes.
Definition base_log.c:85
static void lock(void)
Acquires the logger lock if a locking function is configured.
Definition base_log.c:92
static void unlock(void)
Releases the logger lock if a locking function is configured.
Definition base_log.c:101
#define MAX_CALLBACKS
Maximum number of custom log callbacks supported simultaneously.
Definition base_log.c:13
Professional, thread-safe, leveled logging system.
b32 base_log_add_fp(FILE *fp, BaseLogLevel min_level)
Adds a standard C FILE* pointer as a logging destination.
Definition base_log.c:172
void base_log_set_quiet(b32 enable)
Mutes or unmutes all console (stdout/stderr) output.
Definition base_log.c:159
void base_log_message(BaseLogLevel level, const char *file, int line, const char *fmt,...)
Internal function that actually processes the log. DO NOT call directly.
Definition base_log.c:206
b32 base_log_remove_callback(BaseLogFn fn)
Removes a previously registered callback from the logger.
Definition base_log.c:192
void base_log_set_lock(BaseLogLockFn fn, void *user_data)
Configures thread-safety by providing a custom locking mechanism.
Definition base_log.c:165
void(* BaseLogLockFn)(void *user_data, b32 lock)
Signature for the thread-synchronization callback.
Definition base_log.h:92
void base_log_set_level(BaseLogLevel level)
Configures the active logging level. Messages below this level are ignored.
Definition base_log.c:153
b32 base_log_add_callback(BaseLogFn fn, void *user_data, BaseLogLevel min_level)
Adds a custom callback function as a logging destination.
Definition base_log.c:178
void(* BaseLogFn)(BaseLogEvent *ev, void *user_data)
Signature for custom logging destination callbacks.
Definition base_log.h:89
BaseLogLevel
The severity level of a log message.
Definition base_log.h:69
@ BASE_LOG_LEVEL_WARN
Definition base_log.h:73
@ BASE_LOG_LEVEL_TRACE
Definition base_log.h:70
int32_t b32
Definition base_types.h:111
Internal structure to hold a logging callback and its configuration.
Definition base_log.c:16
void * user_data
Definition base_log.c:18
BaseLogLevel min_level
Definition base_log.c:19
BaseLogFn fn
Definition base_log.c:17
A structured event containing all metadata for a single log message.
Definition base_log.h:80
const char * fmt
Definition base_log.h:82
va_list ap
Definition base_log.h:81
const char * file
Definition base_log.h:83
BaseLogLevel level
Definition base_log.h:85
Internal state structure for the global logger.
Definition base_log.c:23
BaseLogLevel level
Definition base_log.c:26
void * lock_data
Definition base_log.c:24
b32 quiet
Definition base_log.c:27
BaseLogLockFn lock_fn
Definition base_log.c:25
BaseLogCallback callbacks[4]
Definition base_log.c:28