blob: f7c02c2e4ac92ac1a98cad257650195f83c21d8c (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
// Copyright Epic Games, Inc. All Rights Reserved.
#include <zencore/logging/backlogsink.h>
#include <zencore/logging/logmsg.h>
namespace zen::logging {
BacklogSink::BacklogSink(size_t MaxEntries) : m_MaxEntries(MaxEntries)
{
// Reserve up front so push_back never reallocates. The reserve itself
// takes one chunk from the arena; subsequent string duplications carve
// from the same arena, so the entire backlog footprint is one
// contiguous allocation domain released wholesale when this object is
// destroyed (see DisableLogBacklog: it drops the last Ref).
m_Entries.reserve(MaxEntries);
}
BacklogSink::~BacklogSink() = default;
void
BacklogSink::Log(const LogMessage& Msg)
{
// Cheap acquire-load gate: once Disable() has been called we want
// further log calls to do as little as possible. This is the common
// case after the bootstrap window closes — every log line in the
// process flows through here for the rest of the run.
if (!m_Enabled.load(std::memory_order_acquire))
{
return;
}
RwLock::ExclusiveLockScope Lock(m_Lock);
// Re-check under the lock: a Disable() racing with Log() may have
// flipped the flag between our acquire and the lock acquisition.
if (!m_Enabled.load(std::memory_order_relaxed))
{
return;
}
if (m_Entries.size() >= m_MaxEntries)
{
m_DroppedCount.fetch_add(1, std::memory_order_relaxed);
return;
}
Entry& Captured = m_Entries.emplace_back();
Captured.Point = &Msg.GetLogPoint();
Captured.LoggerName = m_Arena.DuplicateString(Msg.GetLoggerName());
Captured.Payload = m_Arena.DuplicateString(Msg.GetPayload());
Captured.Time = Msg.GetTime();
Captured.ThreadId = Msg.GetThreadId();
}
void
BacklogSink::Flush()
{
// Nothing to do — we're an in-memory buffer, not an output device.
}
void
BacklogSink::SetFormatter(std::unique_ptr<Formatter> InFormatter)
{
// We don't format anything — entries are replayed verbatim into the
// target sink, which applies its own formatter. Accept and discard
// for interface compatibility.
(void)InFormatter;
}
void
BacklogSink::Replay(Sink& Target, size_t MaxEntries)
{
RwLock::SharedLockScope Lock(m_Lock);
const size_t Limit = std::min(MaxEntries, m_Entries.size());
for (size_t Index = 0; Index < Limit; ++Index)
{
const Entry& Captured = m_Entries[Index];
if (!Captured.Point)
{
continue;
}
const std::string_view LoggerName = Captured.LoggerName ? std::string_view(Captured.LoggerName) : std::string_view{};
const std::string_view Payload = Captured.Payload ? std::string_view(Captured.Payload) : std::string_view{};
LogMessage Msg(*Captured.Point, LoggerName, Payload);
Msg.SetTime(Captured.Time);
Msg.SetThreadId(Captured.ThreadId);
if (Target.ShouldLog(Msg.GetLevel()))
{
try
{
Target.Log(Msg);
}
catch (const std::exception&)
{
// Match the swallow-on-replay semantics of AsyncSink's
// dispatcher so a misbehaving target doesn't poison the
// rest of the replay.
}
}
}
}
void
BacklogSink::Disable()
{
RwLock::ExclusiveLockScope Lock(m_Lock);
m_Enabled.store(false, std::memory_order_release);
}
size_t
BacklogSink::Size() const
{
RwLock::SharedLockScope Lock(m_Lock);
return m_Entries.size();
}
} // namespace zen::logging
|