//////////////////////////////// //~ Global state P_SharedLogCtx P_shared_log_ctx = ZI; //////////////////////////////// //~ Startup void P_LogStartup(String logfile_path) { __prof; P_SharedLogCtx *ctx = &P_shared_log_ctx; 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_SharedLogCtx *ctx = &P_shared_log_ctx; if (!Atomic32Fetch(&ctx->initialized)) { return; } P_Lock lock = P_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; } P_Unlock(&lock); } //////////////////////////////// //~ Append void P__LogAppend(String msg) { __prof; P_SharedLogCtx *ctx = &P_shared_log_ctx; 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_SharedLogCtx *ctx = &P_shared_log_ctx; 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_SharedLogCtx *ctx = &P_shared_log_ctx; 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_SharedLogCtx *ctx = &P_shared_log_ctx; 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_SharedLogCtx *ctx = &P_shared_log_ctx; 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 = P_GetThreadId(); //- Format message P_DateTime datetime = P_LocalTime(); i64 time_ns = P_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 { P_Lock lock = P_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); } } P_Unlock(&lock); } EndScratch(scratch); }