MayaFlux 0.4.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
12
14public:
15 static constexpr size_t RING_BUFFER_SIZE = 8192;
16
19 , m_worker_running(false)
21 {
22 m_component_filters.fill(true);
23 m_context_filters.fill(true);
24 }
25
27 {
28 m_worker_running.store(false, std::memory_order_release);
29
30 if (m_worker_thread.joinable()) {
31 auto start = std::chrono::steady_clock::now();
32 while (m_worker_thread.joinable() && std::chrono::steady_clock::now() - start < std::chrono::milliseconds(500)) {
33 std::this_thread::sleep_for(std::chrono::milliseconds(10));
34 }
35
36 if (m_worker_thread.joinable()) {
37 m_worker_thread.detach();
38 }
39 }
40 }
41
42 void init()
43 {
44 std::lock_guard lock(m_mutex);
46 return;
47
48 m_initialized = true;
49 m_worker_running.store(true, std::memory_order_release);
51 std::cout << "[MayaFlux::Journal] Initialized\n";
52 }
53
54 void shutdown()
55 {
56 std::unique_lock lock(m_mutex);
58 return;
59
61 m_initialized = false;
62 m_worker_running.store(false, std::memory_order_release);
63
64 lock.unlock();
65
66 if (m_worker_thread.joinable()) {
67 m_worker_thread.join();
68 }
69
70 lock.lock();
73 std::cout << "[MayaFlux::Journal] Shutdown\n";
74 }
75
76 void scribe(const JournalEntry& entry)
77 {
78 if (!should_log(entry.severity, entry.component, entry.context)) {
79 return;
80 }
81
82 std::lock_guard lock(m_mutex);
83
84 if (m_sinks.empty()) {
85 write_to_console(entry);
86 } else {
87 write_to_sinks(entry);
88 }
89 }
90
91 void scribe_rt(Severity severity, Component component, Context context, std::string_view message, std::source_location location)
92 {
93 if (!should_log(severity, component, context)) {
94 return;
95 }
96
97 RealtimeEntry entry(severity, component, context, message, location);
98 if (!m_ring_buffer.push(entry)) {
99 m_dropped_messages.fetch_add(1, std::memory_order_relaxed);
100 }
101 }
102
103 void add_sink(std::unique_ptr<Sink> sink)
104 {
105 std::lock_guard lock(m_mutex);
106 m_sinks.push_back(std::move(sink));
107 }
108
110 {
111 std::lock_guard lock(m_mutex);
112 m_sinks.clear();
113 }
114
116 {
117 if (sev == Severity::NONE) {
118 return;
119 }
120 m_min_severity.store(sev, std::memory_order_relaxed);
121 }
122
123 void set_component_filter(Component comp, bool enabled)
124 {
125 auto comp_idx = static_cast<size_t>(comp);
126 if (comp_idx >= m_component_filters.size()) {
127 return;
128 }
129
130 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
131 m_component_filters[comp_idx] = enabled;
132 }
133
134 void set_context_filter(Context ctx, bool enabled)
135 {
136 auto ctx_idx = static_cast<size_t>(ctx);
137 if (ctx_idx >= m_context_filters.size()) {
138 return;
139 }
140
141 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
142 m_context_filters[ctx_idx] = enabled;
143 }
144
145private:
146 [[nodiscard]] bool should_log(Severity severity, Component component, Context context) const
147 {
148 if (severity != Severity::NONE && severity < m_min_severity.load(std::memory_order_relaxed)) {
149 return false;
150 }
151
152 auto comp_idx = static_cast<size_t>(component);
153 if (comp_idx >= m_component_filters.size() || !m_component_filters[comp_idx]) {
154 return false;
155 }
156
157 auto ctx_idx = static_cast<size_t>(context);
158 if (ctx_idx >= m_context_filters.size() || !m_context_filters[ctx_idx]) {
159 return false;
160 }
161
162 return true;
163 }
164
165 static void write_to_console(const JournalEntry& entry)
166 {
167 if (colors_enabled) {
168 switch (entry.severity) {
169 case Severity::TRACE:
170 std::cout << AnsiColors::Cyan;
171 break;
172 case Severity::DEBUG:
173 std::cout << AnsiColors::Blue;
174 break;
175 case Severity::INFO:
176 std::cout << AnsiColors::Green;
177 break;
178 case Severity::WARN:
179 std::cout << AnsiColors::Yellow;
180 break;
181 case Severity::ERROR:
182 std::cout << AnsiColors::BrightRed;
183 break;
184 case Severity::FATAL:
186 break;
187 case Severity::NONE:
188 default:
189 std::cout << AnsiColors::Reset;
190 break;
191 }
192 }
193
194 std::cout << "[" << Reflect::enum_to_string(entry.severity) << "]" << AnsiColors::Reset;
195
196 if (colors_enabled) {
197 std::cout << AnsiColors::Magenta;
198 }
199 std::cout << "[" << Reflect::enum_to_string(entry.component) << "]" << AnsiColors::Reset;
200
201 if (colors_enabled) {
202 std::cout << AnsiColors::Cyan;
203 }
204 std::cout << "[" << Reflect::enum_to_string(entry.context) << "]" << AnsiColors::Reset << " ";
205
206 std::cout << entry.message;
207
208 if (entry.location.file_name() != nullptr && entry.location.line() != 0) {
209 if (colors_enabled) {
210 std::cout << AnsiColors::BrightBlue;
211 }
212 std::cout << " (" << entry.location.file_name()
213 << ":" << entry.location.line() << ")" << AnsiColors::Reset;
214 }
215
216 std::cout << '\n';
217 }
218
219 static void write_to_console(const RealtimeEntry& entry)
220 {
221 if (colors_enabled) {
222 switch (entry.severity) {
223 case Severity::TRACE:
224 std::cout << AnsiColors::Cyan;
225 break;
226 case Severity::DEBUG:
227 std::cout << AnsiColors::Blue;
228 break;
229 case Severity::INFO:
230 std::cout << AnsiColors::Green;
231 break;
232 case Severity::WARN:
233 std::cout << AnsiColors::Yellow;
234 break;
235 case Severity::ERROR:
236 std::cout << AnsiColors::BrightRed;
237 break;
238 case Severity::FATAL:
240 break;
241 case Severity::NONE:
242 default:
243 std::cout << AnsiColors::Reset;
244 break;
245 }
246 }
247
248 std::cout << "[" << Reflect::enum_to_string(entry.severity) << "]" << AnsiColors::Reset;
249
250 if (colors_enabled) {
251 std::cout << AnsiColors::Magenta;
252 }
253 std::cout << "[" << Reflect::enum_to_string(entry.component) << "]" << AnsiColors::Reset;
254
255 if (colors_enabled) {
256 std::cout << AnsiColors::Cyan;
257 }
258 std::cout << "[" << Reflect::enum_to_string(entry.context) << "]" << AnsiColors::Reset << " ";
259
260 std::cout << entry.message;
261
262 if (entry.file_name != nullptr) {
263 if (colors_enabled) {
264 std::cout << AnsiColors::BrightBlue;
265 }
266 std::cout << " (" << entry.file_name
267 << ":" << entry.line << ")" << AnsiColors::Reset;
268 }
269
270 std::cout << '\n';
271 }
272
273 std::vector<std::unique_ptr<Sink>> m_sinks;
274
275 void write_to_sinks(const JournalEntry& entry)
276 {
277 for (auto& sink : m_sinks) {
278 if (sink->is_available()) {
279 sink->write(entry);
280 }
281 }
282 }
283
284 void write_to_sinks(const RealtimeEntry& entry)
285 {
286 for (auto& sink : m_sinks) {
287 if (sink->is_available()) {
288 sink->write(entry);
289 }
290 }
291 }
292
294 {
295 m_worker_running.store(true, std::memory_order_release);
296 m_worker_thread = std::thread([this]() { worker_loop(); });
297 }
298
300 {
301 if (m_worker_thread.joinable()) {
302 m_worker_running.store(false, std::memory_order_release);
303 m_worker_thread.join();
304
306 }
307 }
308
310 {
311 using namespace std::chrono_literals;
312
313 while (m_worker_running.load(std::memory_order_acquire)) {
314 if (m_initialized) {
316 }
317 std::this_thread::sleep_for(10ms);
318 }
319
320 if (m_initialized) {
322 }
323 }
324
326 {
327 while (auto entry = m_ring_buffer.pop()) {
328
329 std::lock_guard lock(m_mutex);
330 if (m_sinks.empty()) {
331 write_to_console(*entry);
332 } else {
333 write_to_sinks(*entry);
334 }
335 }
336
337 auto dropped = m_dropped_messages.exchange(0, std::memory_order_acquire);
338 if (dropped > 0) {
339 std::lock_guard lock(m_mutex);
340 std::cout << "[MayaFlux::Journal] WARNING: Dropped " << dropped
341 << " realtime log messages (buffer full)\n";
342 }
343 }
344
345 std::mutex m_mutex;
346 std::atomic<Severity> m_min_severity;
347 std::array<bool, magic_enum::enum_count<Component>()> m_component_filters {};
348 std::array<bool, magic_enum::enum_count<Context>()> m_context_filters {};
350
352 std::atomic<bool> m_worker_running;
353 std::thread m_worker_thread;
354 std::atomic<uint64_t> m_dropped_messages { 0 };
355
356 std::atomic<bool> m_shutdown_in_progress;
357};
358
360{
361 static Archivist archivist;
362 return archivist;
363}
364
366 : m_impl(std::make_unique<Impl>())
367{
368 m_impl->init();
369}
370
371Archivist::~Archivist() = default;
372
374{
375 instance().m_impl->shutdown();
376}
377
379 std::string_view message, std::source_location location)
380{
381 JournalEntry entry(severity, component, context, message, location);
382 m_impl->scribe(entry);
383}
384
386 std::string_view message, std::source_location location)
387{
388 m_impl->scribe_rt(severity, component, context, message, location);
389}
390
392 std::string_view message)
393{
394 JournalEntry entry(Severity::NONE, component, context, message, std::source_location {});
395 m_impl->scribe(entry);
396}
397
398void Archivist::add_sink(std::unique_ptr<Sink> sink)
399{
400 m_impl->add_sink(std::move(sink));
401}
402
404{
405 m_impl->clear_sinks();
406}
407
409{
410 m_impl->set_min_severity(min_sev);
411}
412
414{
415 m_impl->set_component_filter(comp, enabled);
416}
417
419{
420 m_impl->set_context_filter(ctx, enabled);
421}
422
423} // namespace MayaFlux::Journal
std::string severity
Definition Config.cpp:16
std::atomic< Severity > m_min_severity
void set_component_filter(Component comp, bool enabled)
bool should_log(Severity severity, Component component, Context context) const
std::atomic< bool > m_shutdown_in_progress
void set_min_severity(Severity sev)
std::atomic< bool > m_worker_running
void write_to_sinks(const RealtimeEntry &entry)
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:76
void add_sink(std::unique_ptr< Sink > sink)
static constexpr size_t RING_BUFFER_SIZE
Definition Archivist.cpp:15
std::array< bool, magic_enum::enum_count< Component >()> m_component_filters
std::array< bool, magic_enum::enum_count< Context >()> m_context_filters
void set_context_filter(Context ctx, bool enabled)
static void write_to_console(const JournalEntry &entry)
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:91
void write_to_sinks(const JournalEntry &entry)
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.
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:17
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.
static bool colors_enabled
Definition Archivist.cpp:11
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.