234 lines
5.6 KiB
C
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);
|
|
}
|