aboutsummaryrefslogtreecommitdiff
path: root/src/zenutil/logging
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenutil/logging')
-rw-r--r--src/zenutil/logging/logging.cpp88
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