MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Archivist.cpp
Go to the documentation of this file.
1#include "Archivist.hpp"
2#include "RealtimeEntry.hpp"
3
4#include "Ansi.hpp"
5#include "Sink.hpp"
6
8
9namespace MayaFlux::Journal {
10
11namespace {
12 const bool colors_enabled = AnsiColors::initialize_console_colors();
13} // namespace
14
16public:
17 static constexpr size_t RING_BUFFER_SIZE = 8192;
18 static constexpr size_t RT_RING_BUFFER_SIZE = 4096;
19
22 , m_worker_running(false)
23 , m_initialized(false)
26 {
27 for (auto& f : m_component_filters)
28 f.store(true, std::memory_order_relaxed);
29 for (auto& f : m_context_filters)
30 f.store(true, std::memory_order_relaxed);
31 }
32
34 {
35 m_accepting_entries.store(false, std::memory_order_release);
36 m_worker_running.store(false, std::memory_order_release);
37 if (m_worker_thread.joinable())
38 m_worker_thread.join();
39 }
40
41 void init()
42 {
43 if (m_initialized.exchange(true, std::memory_order_acq_rel))
44 return;
45
46 m_worker_running.store(true, std::memory_order_release);
48 std::cout << "[MayaFlux::Journal] Initialized\n";
49 }
50
51 void shutdown()
52 {
53 if (!m_initialized.load(std::memory_order_acquire))
54 return;
55 if (m_shutdown_in_progress.exchange(true, std::memory_order_acq_rel))
56 return;
57
58 m_worker_running.store(false, std::memory_order_release);
59 if (m_worker_thread.joinable())
60 m_worker_thread.join();
61
63 m_initialized.store(false, std::memory_order_release);
64 m_shutdown_in_progress.store(false, std::memory_order_release);
65 std::cout << "[MayaFlux::Journal] Shutdown\n";
66 }
67
68 void scribe(const JournalEntry& entry)
69 {
70 if (!m_accepting_entries.load(std::memory_order_acquire))
71 return;
72
73 if (!should_log(entry.severity, entry.component, entry.context))
74 return;
75
76 RealtimeEntry rt(entry.severity, entry.component, entry.context,
77 entry.message, entry.location);
78
79 while (m_push_lock.test_and_set(std::memory_order_acquire))
80 ;
81 const bool pushed = m_ring_buffer.push(rt);
82 m_push_lock.clear(std::memory_order_release);
83
84 if (!pushed)
85 m_dropped_messages.fetch_add(1, std::memory_order_relaxed);
86 }
87
88 void scribe_rt(Severity severity, Component component, Context context,
89 std::string_view message, std::source_location location)
90 {
91 if (!m_accepting_entries.load(std::memory_order_acquire))
92 return;
93
94 if (!should_log(severity, component, context))
95 return;
96
97 RealtimeEntry entry(severity, component, context, message, location);
98 if (!m_rt_ring_buffer.push(entry))
99 m_dropped_messages.fetch_add(1, std::memory_order_relaxed);
100 }
101
102 /**
103 * @brief Drain all pending ring buffer entries to sinks or console.
104 *
105 * Lock-free. Safe to call from any thread. The worker calls this on its
106 * normal cadence; error() and fatal() call it synchronously before
107 * propagating exceptions or aborting to guarantee visibility.
108 */
110 {
111 while (auto entry = m_rt_ring_buffer.pop()) {
112 if (m_sinks.empty()) {
113 write_to_console(*entry);
114 } else {
115 write_to_sinks(*entry);
116 }
117 }
118
119 while (auto entry = m_ring_buffer.pop()) {
120 if (m_sinks.empty()) {
121 write_to_console(*entry);
122 } else {
123 write_to_sinks(*entry);
124 }
125 }
126
127 const auto dropped = m_dropped_messages.exchange(0, std::memory_order_acq_rel);
128 if (dropped > 0) {
129 std::cout << "[MayaFlux::Journal] WARNING: Dropped "
130 << dropped << " log messages (buffer full)\n";
131 }
132 }
133
134 void add_sink(std::unique_ptr<Sink> sink)
135 {
136 m_sinks.push_back(std::move(sink));
137 }
138
140 {
141 m_sinks.clear();
142 }
143
145 {
146 if (sev == Severity::NONE)
147 return;
148 m_min_severity.store(sev, std::memory_order_relaxed);
149 }
150
151 void set_component_filter(Component comp, bool enabled)
152 {
153 auto comp_idx = static_cast<size_t>(comp);
154 if (comp_idx >= m_component_filters.size())
155 return;
156
157 m_component_filters[comp_idx].store(enabled, std::memory_order_release);
158 }
159
160 void set_context_filter(Context ctx, bool enabled)
161 {
162 auto ctx_idx = static_cast<size_t>(ctx);
163 if (ctx_idx >= m_context_filters.size())
164 return;
165
166 m_context_filters[ctx_idx].store(enabled, std::memory_order_release);
167 }
168
169private:
170 [[nodiscard]] bool should_log(Severity severity, Component component, Context context) const
171 {
172 if (severity != Severity::NONE && severity < m_min_severity.load(std::memory_order_relaxed))
173 return false;
174
175 auto comp_idx = static_cast<size_t>(component);
176 if (comp_idx >= m_component_filters.size()
177 || !m_component_filters[comp_idx].load(std::memory_order_acquire))
178 return false;
179
180 auto ctx_idx = static_cast<size_t>(context);
181
182 return ctx_idx < m_context_filters.size()
183 && m_context_filters[ctx_idx].load(std::memory_order_acquire);
184 }
185
186 static void write_to_console(const RealtimeEntry& entry)
187 {
188 if (colors_enabled) {
189 switch (entry.severity) {
190 case Severity::TRACE:
191 std::cout << AnsiColors::Cyan;
192 break;
193 case Severity::DEBUG:
194 std::cout << AnsiColors::Blue;
195 break;
196 case Severity::INFO:
197 std::cout << AnsiColors::Green;
198 break;
199 case Severity::WARN:
200 std::cout << AnsiColors::Yellow;
201 break;
202 case Severity::ERROR:
203 std::cout << AnsiColors::BrightRed;
204 break;
205 case Severity::FATAL:
207 break;
208 case Severity::NONE:
209 default:
210 std::cout << AnsiColors::Reset;
211 break;
212 }
213 }
214
215 std::cout << "[" << Reflect::enum_to_string(entry.severity) << "]" << AnsiColors::Reset;
216 if (colors_enabled)
217 std::cout << AnsiColors::Magenta;
218 std::cout << "[" << Reflect::enum_to_string(entry.component) << "]" << AnsiColors::Reset;
219 if (colors_enabled)
220 std::cout << AnsiColors::Cyan;
221 std::cout << "[" << Reflect::enum_to_string(entry.context) << "]" << AnsiColors::Reset << " ";
222 std::cout << entry.message;
223
224 if (entry.file_name != nullptr && entry.line != 0) {
225 if (colors_enabled)
226 std::cout << AnsiColors::BrightBlue;
227 std::cout << " (" << entry.file_name << ":" << entry.line << ")" << AnsiColors::Reset;
228 }
229
230 std::cout << '\n';
231 }
232
233 void write_to_sinks(const RealtimeEntry& entry)
234 {
235 for (auto& sink : m_sinks) {
236 if (sink->is_available()) {
237 try {
238 sink->write(entry);
239 } catch (...) {
240 std::cout << "[MayaFlux::Journal] WARNING: sink threw during write, skipping\n";
241 }
242 }
243 }
244 }
245
247 {
248 m_worker_thread = std::thread([this] { worker_loop(); });
249 }
250
252 {
253 using namespace std::chrono_literals;
254 while (m_worker_running.load(std::memory_order_acquire)) {
256 std::this_thread::sleep_for(10ms);
257 }
259 }
260
261 std::vector<std::unique_ptr<Sink>> m_sinks;
262
263 std::atomic<Severity> m_min_severity;
264 std::array<std::atomic<bool>, magic_enum::enum_count<Component>()> m_component_filters {};
265 std::array<std::atomic<bool>, magic_enum::enum_count<Context>()> m_context_filters {};
266 std::atomic<bool> m_initialized;
267 std::atomic<bool> m_worker_running;
268 std::atomic<bool> m_accepting_entries;
269 std::atomic<bool> m_shutdown_in_progress;
270 std::atomic<uint64_t> m_dropped_messages { 0 };
271
272 alignas(64) std::atomic_flag m_push_lock = ATOMIC_FLAG_INIT;
275 std::thread m_worker_thread;
276};
277
279{
280 static Archivist archivist;
281 return archivist;
282}
283
285 : m_impl(std::make_unique<Impl>())
286{
287 m_impl->init();
288}
289
290Archivist::~Archivist() = default;
291
293{
294 instance().m_impl->shutdown();
295}
296
298{
299 m_impl->drain_ring_buffer();
300}
301
303 std::string_view message, std::source_location location)
304{
305 JournalEntry entry(severity, component, context, message, location);
306 m_impl->scribe(entry);
307}
308
310 std::string_view message, std::source_location location)
311{
312 m_impl->scribe_rt(severity, component, context, message, location);
313}
314
316 std::string_view message)
317{
318 JournalEntry entry(Severity::NONE, component, context, message, std::source_location {});
319 m_impl->scribe(entry);
320}
321
322void Archivist::add_sink(std::unique_ptr<Sink> sink)
323{
324 m_impl->add_sink(std::move(sink));
325}
326
328{
329 m_impl->clear_sinks();
330}
331
333{
334 m_impl->set_min_severity(min_sev);
335}
336
338{
339 m_impl->set_component_filter(comp, enabled);
340}
341
343{
344 m_impl->set_context_filter(ctx, enabled);
345}
346
347} // namespace MayaFlux::Journal
std::string severity
Definition Config.cpp:18
std::array< std::atomic< bool >, magic_enum::enum_count< Component >()> m_component_filters
std::atomic< Severity > m_min_severity
std::atomic< bool > m_initialized
void set_component_filter(Component comp, bool enabled)
static constexpr size_t RT_RING_BUFFER_SIZE
Definition Archivist.cpp:18
bool should_log(Severity severity, Component component, Context context) const
std::array< std::atomic< bool >, magic_enum::enum_count< Context >()> m_context_filters
std::atomic< bool > m_shutdown_in_progress
std::atomic< bool > m_accepting_entries
void set_min_severity(Severity sev)
std::atomic< bool > m_worker_running
void write_to_sinks(const RealtimeEntry &entry)
Memory::MPSCQueue< RealtimeEntry, RT_RING_BUFFER_SIZE > m_rt_ring_buffer
static void write_to_console(const RealtimeEntry &entry)
Memory::LockFreeQueue< RealtimeEntry, RING_BUFFER_SIZE > m_ring_buffer
void scribe(const JournalEntry &entry)
Definition Archivist.cpp:68
void add_sink(std::unique_ptr< Sink > sink)
static constexpr size_t RING_BUFFER_SIZE
Definition Archivist.cpp:17
void set_context_filter(Context ctx, bool enabled)
void drain_ring_buffer()
Drain all pending ring buffer entries to sinks or console.
std::vector< std::unique_ptr< Sink > > m_sinks
std::atomic< uint64_t > m_dropped_messages
void scribe_rt(Severity severity, Component component, Context context, std::string_view message, std::source_location location)
Definition Archivist.cpp:88
void scribe_simple(Component component, Context context, std::string_view message)
Log a simple message without source location information.
void add_sink(std::unique_ptr< Sink > sink)
Add a log sink for output.
void scribe_rt(Severity severity, Component component, Context context, std::string_view message, std::source_location location=std::source_location::current())
Log a message from a real-time context with the specified severity, component, and context.
void clear_sinks()
Remove all sinks.
void set_component_filter(Component comp, bool enabled)
Enable or disable logging for a specific component.
void flush()
Synchronously drain all pending ring buffer entries to sinks.
static void shutdown()
Shutdown the logging system.
static Archivist & instance()
Get the singleton instance of the Archivist.
void set_min_severity(Severity min_sev)
Set the minimum severity level for logging.
void scribe(Severity severity, Component component, Context context, std::string_view message, std::source_location location=std::source_location::current())
Log a message with the specified severity, component, and context.
std::unique_ptr< Impl > m_impl
void set_context_filter(Context ctx, bool enabled)
Enable or disable logging for a specific context.
Singleton class responsible for managing log entries.
Definition Archivist.hpp:24
Policy-driven unified circular buffer implementation.
static constexpr std::string_view Yellow
Definition Ansi.hpp:21
static constexpr std::string_view BrightBlue
Definition Ansi.hpp:31
static constexpr std::string_view White
Definition Ansi.hpp:25
static constexpr std::string_view Blue
Definition Ansi.hpp:22
static constexpr std::string_view Reset
Definition Ansi.hpp:15
static constexpr std::string_view Green
Definition Ansi.hpp:20
static constexpr std::string_view Cyan
Definition Ansi.hpp:24
static constexpr std::string_view BrightRed
Definition Ansi.hpp:28
static bool initialize_console_colors()
Definition Ansi.hpp:40
static constexpr std::string_view Magenta
Definition Ansi.hpp:23
Context
Execution contexts for log messages.
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)
A log entry structure to encapsulate log message details.
char message[MAX_MESSAGE_LENGTH]
Lightweight entry for lock-free ring buffer.