power_play/src/platform/platform_log.c
2025-08-05 10:26:13 -05:00

234 lines
5.6 KiB
C

////////////////////////////////
//~ Shared state
P_SharedLogState P_shared_log_state = ZI;
////////////////////////////////
//~ Startup
void P_LogStartup(String logfile_path)
{
__prof;
P_SharedLogState *ctx = &P_shared_log_state;
ctx->callbacks_arena = AllocArena(Mebi(8));
if (logfile_path.len > 0)
{
/* Create / wipe log file */
P_CloseFIle(P_OpenFileWrite(logfile_path));
/* Keep log file open for appending */
if (P_IsFile(logfile_path))
{
ctx->file = P_OpenFileAppend(logfile_path);
ctx->file_valid = 1;
}
}
Atomic32FetchSet(&ctx->initialized, 1);
}
////////////////////////////////
//~ Callback registration
void P_RegisterLogCallback(P_LogEventCallbackFunc *func, i32 level)
{
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
Lock lock = LockE(&ctx->callbacks_mutex);
{
LogEventCallback *callback = PushStruct(ctx->callbacks_arena, LogEventCallback);
callback->func = func;
callback->level = level;
if (ctx->last_callback)
{
ctx->last_callback->next = callback;
}
else
{
ctx->first_callback = callback;
}
ctx->last_callback = callback;
}
Unlock(&lock);
}
////////////////////////////////
//~ Append
void P_LogAppend_(String msg)
{
__prof;
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
if (ctx->file_valid)
{
TempArena scratch = BeginScratchNoConflict();
String msg_line = CatString(scratch.arena, msg, Lit("\n"));
P_WriteFile(ctx->file, msg_line);
EndScratch(scratch);
}
}
////////////////////////////////
//~ Panic
/* Panic log function is separate to enforce zero side effects other than
* writing to log file. */
void P_LogPanic_(String msg)
{
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
if (ctx->file_valid)
{
P_WriteFile(ctx->file, Lit("******** PANICKING ********\n"));
P_WriteFile(ctx->file, msg);
P_WriteFile(ctx->file, Lit("\n***************************\n"));
}
}
////////////////////////////////
//~ Logfv
#if P_IncludeLogSourceLocation
void P_LogFV_(i32 level, String file, u32 line, String fmt, va_list args)
#else
void P_LogFV_(i32 level, String fmt, va_list args)
#endif
{
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
TempArena scratch = BeginScratchNoConflict();
String msg = StringFormatV(scratch.arena, fmt, args);
#if P_IncludeLogSourceLocation
P_Log_(level, file, line, msg);
#else
P_Log_(level, msg);
#endif
EndScratch(scratch);
}
////////////////////////////////
//~ Logf
#if P_IncludeLogSourceLocation
void P_LogF_(i32 level, String file, u32 line, String fmt, ...)
#else
void P_LogF_(i32 level, String fmt, ...)
#endif
{
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
va_list args;
va_start(args, fmt);
#if P_IncludeLogSourceLocation
P_LogFV_(level, file, line, fmt, args);
#else
P_LogFV_(level, fmt, args);
#endif
va_end(args);
}
////////////////////////////////
//~ Log
#if P_IncludeLogSourceLocation
void P_Log_(i32 level, String file, u32 line, String msg)
#else
void P_Log_(i32 level, String msg)
#endif
{
__prof;
P_SharedLogState *ctx = &P_shared_log_state;
if (!Atomic32Fetch(&ctx->initialized)) { return; }
TempArena scratch = BeginScratchNoConflict();
P_LogLevelSettings settings = P_log_settings[level];
if (level < 0 || level >= P_LogLevel_Count)
{
P_Panic(Lit("Invalid log level"));
}
u32 tid = ThreadId();
//- Format message
P_DateTime datetime = P_LocalTime();
i64 time_ns = TimeNs();
String shorthand = settings.shorthand;
#if P_IncludeLogSourceLocation
String msg_formatted = StringFormat(
scratch.arena,
Lit("[%F:%F:%F.%F] |%F| [%F] <%F:%F> %F"),
/* Time */
FmtUintZ(datetime.hour, 2),
FmtUintZ(datetime.minute, 2),
FmtUintZ(datetime.second, 2),
FmtUintZ(datetime.milliseconds, 3),
/* TID */
FmtUintZ(tid, 5),
/* Level */
FmtString(shorthand),
/* Source location */
FmtString(file),
FmtSint(line),
/* Message */
FmtString(msg)
);
#else
String msg_formatted = StringFormat(
scratch.arena,
Lit("[%F:%F:%F.%F] |%F| [%F] %F"),
/* Time */
FmtUintZ(datetime.hour, 2),
FmtUintZ(datetime.minute, 2),
FmtUintZ(datetime.second, 2),
FmtUintZ(datetime.milliseconds, 3),
/* TID */
FmtUintZ(tid, 5),
/* Level */
FmtString(shorthand),
/* Message */
FmtString(msg)
);
#endif
__profmsg((char *)msg.text, msg.len, settings.color);
P_LogAppend_(msg_formatted);
//- Run callbacks
P_LogEvent event = ZI;
event.level = level;
event.msg = msg;
event.datetime = datetime;
event.time_ns = time_ns;
#if P_IncludeLogSourceLocation
event.file = file;
event.line = line;
#endif
{
Lock lock = LockS(&ctx->callbacks_mutex);
for (LogEventCallback *callback = ctx->first_callback; callback; callback = callback->next)
{
if (level <= callback->level)
{
__profn("Run log callback");
callback->func(event);
}
}
Unlock(&lock);
}
EndScratch(scratch);
}