224 lines
5.3 KiB
C
224 lines
5.3 KiB
C
#include "log.h"
|
|
#include "scratch.h"
|
|
#include "string.h"
|
|
#include "atomic.h"
|
|
|
|
struct log_event_callback {
|
|
log_event_callback_func *func;
|
|
i32 level;
|
|
struct log_event_callback *next;
|
|
};
|
|
|
|
/* ========================== *
|
|
* Global state
|
|
* ========================== */
|
|
|
|
GLOBAL struct {
|
|
struct atomic_i32 initialized;
|
|
struct sys_mutex mutex;
|
|
struct arena arena;
|
|
log_event_callback_func *callbacks_head;
|
|
struct sys_file file;
|
|
b32 file_valid;
|
|
} G = ZI, DEBUG_ALIAS(G, G_log);
|
|
|
|
GLOBAL READONLY struct log_level_settings g_log_level_settings[LOG_LEVEL_COUNT] = {
|
|
[LOG_LEVEL_CRITICAL] = {
|
|
LIT_NOCAST("CRITICAL"),
|
|
0xFFFF00FF
|
|
},
|
|
|
|
[LOG_LEVEL_ERROR] = {
|
|
LIT_NOCAST("ERROR"),
|
|
0xFFFF0000
|
|
},
|
|
|
|
[LOG_LEVEL_WARNING] = {
|
|
LIT_NOCAST("WARNING"),
|
|
0xFFFFFF00
|
|
},
|
|
|
|
[LOG_LEVEL_INFO] = {
|
|
LIT_NOCAST("INFO"),
|
|
0xFFFFFFFF
|
|
},
|
|
|
|
[LOG_LEVEL_DEBUG] = {
|
|
LIT_NOCAST("DEBUG"),
|
|
0xFF30D5C8
|
|
}
|
|
};
|
|
|
|
/* ========================== *
|
|
* Startup
|
|
* ========================== */
|
|
|
|
struct log_startup_receipt log_startup(struct string logfile_path)
|
|
{
|
|
G.mutex = sys_mutex_alloc();
|
|
G.arena = arena_alloc(GIGABYTE(64));
|
|
if (logfile_path.len > 0) {
|
|
/* Create / wipe log file */
|
|
sys_file_close(sys_file_open_write(logfile_path));
|
|
/* Keep log file open for appending */
|
|
if (sys_is_file(logfile_path)) {
|
|
G.file = sys_file_open_append(logfile_path);
|
|
G.file_valid = true;
|
|
}
|
|
}
|
|
atomic_i32_eval_exchange(&G.initialized, 1);
|
|
return (struct log_startup_receipt) { 0 };
|
|
}
|
|
|
|
/* ========================== *
|
|
* Callback
|
|
* ========================== */
|
|
|
|
void log_register_callback(log_event_callback_func *func)
|
|
{
|
|
/* TODO */
|
|
(UNUSED)func;
|
|
#if 0
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
struct sys_lock lock = sys_mutex_lock_e(&G.mutex);
|
|
sys_mutex_unlock(&lock);
|
|
#endif
|
|
}
|
|
|
|
/* ========================== *
|
|
* Log
|
|
* ========================== */
|
|
|
|
INTERNAL void append_to_logfile(struct string msg)
|
|
{
|
|
__prof;
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
|
|
if (G.file_valid) {
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
struct string msg_line = string_cat(scratch.arena, msg, LIT("\n"));
|
|
sys_file_write(G.file, msg_line);
|
|
scratch_end(scratch);
|
|
}
|
|
}
|
|
|
|
/* Panic log function is separate to enforce zero side effects other than
|
|
* writing to log file. */
|
|
void _log_panic(struct string msg)
|
|
{
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
|
|
if (G.file_valid) {
|
|
sys_file_write(G.file, LIT("******** PANICKING ********\n"));
|
|
sys_file_write(G.file, msg);
|
|
sys_file_write(G.file, LIT("\n***************************\n"));
|
|
}
|
|
}
|
|
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
void _log(i32 level, struct string file, u32 line, struct string msg)
|
|
#else
|
|
void _log(i32 level, struct string msg)
|
|
#endif
|
|
{
|
|
__prof;
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
|
|
if (level < 0 || level >= LOG_LEVEL_COUNT) {
|
|
sys_panic(LIT("Invalid log level"));
|
|
}
|
|
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
|
|
struct sys_datetime lt = sys_local_time();
|
|
u32 tid = sys_thread_id();
|
|
|
|
struct log_level_settings settings = g_log_level_settings[level];
|
|
struct string shorthand = settings.shorthand;
|
|
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
struct string msg_formatted = string_format(
|
|
scratch.arena,
|
|
LIT("[%F:%F:%F.%F] |%F| [%F] <%F:%F> %F"),
|
|
|
|
/* Time */
|
|
FMT_UINT_Z(lt.hour, 2),
|
|
FMT_UINT_Z(lt.minute, 2),
|
|
FMT_UINT_Z(lt.second, 2),
|
|
FMT_UINT_Z(lt.milliseconds, 3),
|
|
|
|
/* TID */
|
|
FMT_UINT_Z(tid, 5),
|
|
|
|
/* Level */
|
|
FMT_STR(shorthand),
|
|
|
|
/* Source location */
|
|
FMT_STR(file),
|
|
FMT_SINT(line),
|
|
|
|
/* Message */
|
|
FMT_STR(msg)
|
|
);
|
|
#else
|
|
struct string msg_formatted = string_format(
|
|
scratch.arena,
|
|
LIT("[%F:%F:%F.%F] |%F| [%F] %F"),
|
|
|
|
/* Time */
|
|
FMT_UINT_Z(lt.hour, 2),
|
|
FMT_UINT_Z(lt.minute, 2),
|
|
FMT_UINT_Z(lt.second, 2),
|
|
FMT_UINT_Z(lt.milliseconds, 3),
|
|
|
|
/* TID */
|
|
FMT_UINT_Z(tid, 5),
|
|
|
|
/* Level */
|
|
FMT_STR(shorthand),
|
|
|
|
/* Message */
|
|
FMT_STR(msg)
|
|
);
|
|
#endif
|
|
|
|
__profmsg((char *)msg.text, msg.len, settings.color);
|
|
append_to_logfile(msg_formatted);
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
void _logfv(i32 level, struct string file, u32 line, struct string fmt, va_list args)
|
|
#else
|
|
void _logfv(i32 level, struct string fmt, va_list args)
|
|
#endif
|
|
{
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
struct string msg = string_formatv(scratch.arena, fmt, args);
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
_log(level, file, line, msg);
|
|
#else
|
|
_log(level, msg);
|
|
#endif
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
void _logf(i32 level, struct string file, u32 line, struct string fmt, ...)
|
|
#else
|
|
void _logf(i32 level, struct string fmt, ...)
|
|
#endif
|
|
{
|
|
if (!atomic_i32_eval(&G.initialized)) { return; }
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
#if LOG_INCLUDE_SOURCE_LOCATION
|
|
_logfv(level, file, line, fmt, args);
|
|
#else
|
|
_logfv(level, fmt, args);
|
|
#endif
|
|
va_end(args);
|
|
}
|