power_play/src/log.c

222 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] = {
STR_NOCAST("CRITICAL"),
0xFFFF00FF
},
[LOG_LEVEL_ERROR] = {
STR_NOCAST("ERROR"),
0xFFFF0000
},
[LOG_LEVEL_WARNING] = {
STR_NOCAST("WARNING"),
0xFFFFFF00
},
[LOG_LEVEL_INFO] = {
STR_NOCAST("INFO"),
0xFFFFFFFF
},
[LOG_LEVEL_DEBUG] = {
STR_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, STR("\n"));
sys_file_write(G.file, BUFFER_FROM_STRING(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, BUFFER_FROM_STRING(STR("******** PANICKING ********\n")));
sys_file_write(G.file, BUFFER_FROM_STRING(msg));
sys_file_write(G.file, BUFFER_FROM_STRING(STR("\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(STR("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,
STR("%F:%F:%F |Thread %F| [%F] <%F:%F> %F"),
/* Time */
FMT_UINT(lt.hour),
FMT_UINT(lt.minute),
FMT_UINT(lt.second),
/* TID */
FMT_UINT(tid),
/* 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,
STR("%F:%F:%F |Thread %F| [%F] %F"),
/* Time */
FMT_UINT(lt.hour),
FMT_UINT(lt.minute),
FMT_UINT(lt.second),
/* TID */
FMT_UINT(tid),
/* 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);
}