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