diff options
Diffstat (limited to 'src/zenutil/logging/logging.cpp')
| -rw-r--r-- | src/zenutil/logging/logging.cpp | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/zenutil/logging/logging.cpp b/src/zenutil/logging/logging.cpp index 936e3c4fd..06e8f920e 100644 --- a/src/zenutil/logging/logging.cpp +++ b/src/zenutil/logging/logging.cpp @@ -8,6 +8,7 @@ #include <zencore/logging.h> #include <zencore/logging/ansicolorsink.h> #include <zencore/logging/asyncsink.h> +#include <zencore/logging/backlogsink.h> #include <zencore/logging/broadcastsink.h> #include <zencore/logging/logger.h> #include <zencore/logging/msvcsink.h> @@ -27,6 +28,7 @@ namespace zen { static bool g_IsLoggingInitialized; logging::SinkPtr g_FileSink; Ref<logging::BroadcastSink> g_BroadcastSink; +Ref<logging::BacklogSink> g_BacklogSink; logging::SinkPtr GetFileSink() @@ -40,6 +42,83 @@ GetDefaultBroadcastSink() return g_BroadcastSink; } +Ref<logging::BacklogSink> +GetLogBacklogSink() +{ + return g_BacklogSink; +} + +void +AttachSinkWithBacklogReplay(logging::SinkPtr Sink) +{ + if (!Sink) + { + return; + } + // Drain any AsyncSink queue first so messages emitted just before this + // call have a chance to land in the backlog before we snapshot its + // cursor. Without this we'd still be correct (AsyncSink would deliver + // the queued lines to the new sink directly via the broadcast after + // AddSink runs), but the new sink would observe them out of timestamp + // order — replayed history first, then late drains interleaved with + // fresh post-attach messages. + zen::logging::FlushLogging(); + + if (!g_BroadcastSink) + { + return; + } + + // Subscribe Sink to the broadcast and snapshot the backlog cursor + // atomically. The broadcast's exclusive lock blocks fanout while it's + // held, so during the callback no thread can be inside backlog.Log() + // and the cursor we capture is exact w.r.t. the moment Sink became + // visible to fanout. + // + // After the callback returns and the lock is released, two things are + // true: + // - Any backlog entry at index < Cursor was added before Sink became + // a fanout target, so Sink did NOT receive it via Log() — Replay + // must deliver it. + // - Any backlog entry at index >= Cursor was added after Sink became + // a fanout target, so Sink already received it via Log() — Replay + // must skip it. + // This closes the prior race where a message arriving between Replay + // and AddSink would land in the backlog only and be lost on + // DisableLogBacklog. + size_t Cursor = 0; + g_BroadcastSink->AddSinkAtomic(Sink, [&]() { + if (g_BacklogSink && g_BacklogSink->IsEnabled()) + { + Cursor = g_BacklogSink->Size(); + } + }); + + if (Cursor > 0) + { + g_BacklogSink->Replay(*Sink, Cursor); + } +} + +void +DisableLogBacklog() +{ + if (!g_BacklogSink) + { + return; + } + // Remove from the broadcast first so subsequent log lines don't even + // fan out to a now-no-op sink — one fewer per-log overhead for the + // rest of the process lifetime. Disable() then frees the arena, and + // dropping our own Ref releases the object. + if (g_BroadcastSink) + { + g_BroadcastSink->RemoveSink(logging::SinkPtr(g_BacklogSink.Get())); + } + g_BacklogSink->Disable(); + g_BacklogSink = nullptr; +} + void InitializeLogging(const LoggingOptions& LogOptions) { @@ -129,6 +208,14 @@ BeginInitializeLogging(const LoggingOptions& LogOptions) // a child sink later is immediately visible to every logger. std::vector<logging::SinkPtr> BroadcastChildren; + // Install the backlog sink as the first broadcast child so it sees + // every line emitted from this point until the bootstrap window + // closes (DisableLogBacklog at server-run-loop / CLI-dispatch). Sinks + // attached later via AttachSinkWithBacklogReplay can replay the + // captured window into themselves so they don't miss the early logs. + g_BacklogSink = Ref<logging::BacklogSink>(new logging::BacklogSink()); + BroadcastChildren.push_back(logging::SinkPtr(g_BacklogSink.Get())); + if (LogOptions.NoConsoleOutput) { zen::logging::SuppressConsoleLog(); @@ -274,6 +361,7 @@ ShutdownLogging() g_FileSink = nullptr; g_BroadcastSink = nullptr; + g_BacklogSink = nullptr; } } // namespace zen |