W32_SharedLogState W32_shared_log_state = ZI; //////////////////////////////////////////////////////////// //~ @hookdef Init hooks void InitLogSystem(String logfile_path) { __prof; W32_SharedLogState *g = &W32_shared_log_state; g->logs_arena = AcquireArena(Gibi(64)); g->log_msgs_arena = AcquireArena(Gibi(64)); g->readable_log_events = ArenaNext(g->logs_arena, LogEvent); if (logfile_path.len > 0) { TempArena scratch = BeginScratchNoConflict(); { wchar_t *path_wstr = WstrFromString(scratch.arena, logfile_path); g->logfile = CreateFileW(path_wstr, FILE_APPEND_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); } EndScratch(scratch); } Atomic32Set(&g->initialized, 1); } //////////////////////////////////////////////////////////// //~ Log void W32_Log(i32 level, String msg) { __prof; W32_SharedLogState *g = &W32_shared_log_state; TempArena scratch = BeginScratchNoConflict(); if (Atomic32Fetch(&g->initialized)) { LogLevelSettings settings = log_settings[level]; if (level < 0 || level >= LogLevel_Count) { Panic(Lit("Invalid log level")); } __profmsg((char *)msg.text, msg.len, settings.color); DateTime datetime = LocalDateTime(); i64 now_ns = TimeNs(); i32 thread_id = GetCurrentThreadId(); i32 fiber_id = FiberId(); //- Log message to file /* TODO: Log asynchronously */ { String shorthand = settings.shorthand; String msg_formatted = StringF( scratch.arena, "[%F:%F:%F.%F] |%F| <%F> [%F] %F\n", /* Time */ FmtUintZ(datetime.hour, 2), FmtUintZ(datetime.minute, 2), FmtUintZ(datetime.second, 2), FmtUintZ(datetime.milliseconds, 3), /* Thread id */ FmtUintZ(thread_id, 5), /* Fiber id */ FmtUintZ(fiber_id, 4), /* Level */ FmtString(shorthand), /* Message */ FmtString(msg) ); WriteFile(g->logfile, msg_formatted.text, msg_formatted.len, 0, 0); } //- Log message to queue /* TODO: Log asynchronously */ LockTicketMutex(&g->logs_tm); { /* Get staged data */ LogEvent *ev = PushStruct(g->logs_arena, LogEvent); ev->msg = PushString(g->log_msgs_arena, msg); ev->datetime = datetime; ev->time_ns = now_ns; ev->level = level; ev->thread_id = thread_id; ev->fiber_id = fiber_id; ev->id = g->logs_count++; ev->level_id = g->log_level_counts[level]++; Atomic64Set(&g->readable_logs_count, g->logs_count); } UnlockTicketMutex(&g->logs_tm); } EndScratch(scratch); } //////////////////////////////////////////////////////////// //~ @hookdef Log hooks /* Panic log function is separate to enforce zero side effects other than * immediately writing to log file. */ void LogPanic(String msg) { W32_SharedLogState *g = &W32_shared_log_state; if (Atomic32Fetch(&g->initialized)) { String beg = Lit("******** PANICKING ********\n"); String end = Lit("\n***************************\n"); WriteFile(g->logfile, beg.text, beg.len, 0, 0); WriteFile(g->logfile, msg.text, msg.len, 0, 0); WriteFile(g->logfile, end.text, end.len, 0, 0); } } void Log_(i32 level, String msg) { W32_Log(level, msg); } void LogF_(i32 level, String fmt, ...) { W32_SharedLogState *g = &W32_shared_log_state; if (Atomic32Fetch(&g->initialized)) { TempArena scratch = BeginScratchNoConflict(); va_list args; va_start(args, fmt); { String msg = FormatStringV(scratch.arena, fmt, args); W32_Log(level, msg); } va_end(args); EndScratch(scratch); } } LogEventsArray GetLogEvents(void) { W32_SharedLogState *g = &W32_shared_log_state; LogEventsArray result = ZI; result.count = Atomic64Fetch(&g->readable_logs_count); if (result.count > 0) { result.logs = g->readable_log_events; } return result; }