MayaFlux 0.1.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#include "RingBuffer.hpp"
4
5#include "Ansi.hpp"
6#include "Sink.hpp"
7
8namespace MayaFlux::Journal {
9
11
13public:
14 static constexpr size_t RING_BUFFER_SIZE = 8192;
15
18 , m_worker_running(false)
20 {
21 m_component_filters.fill(true);
22 }
23
25 {
26 m_worker_running.store(false, std::memory_order_release);
27
28 if (m_worker_thread.joinable()) {
29 auto start = std::chrono::steady_clock::now();
30 while (m_worker_thread.joinable() && std::chrono::steady_clock::now() - start < std::chrono::milliseconds(500)) {
31 std::this_thread::sleep_for(std::chrono::milliseconds(10));
32 }
33
34 if (m_worker_thread.joinable()) {
35 m_worker_thread.detach();
36 }
37 }
38 }
39
40 void init()
41 {
42 std::lock_guard lock(m_mutex);
44 return;
45
46 m_initialized = true;
47 m_worker_running.store(true, std::memory_order_release);
49 std::cout << "[MayaFlux::Journal] Initialized\n";
50 }
51
52 void shutdown()
53 {
54 std::unique_lock lock(m_mutex);
56 return;
57
59 m_initialized = false;
60 m_worker_running.store(false, std::memory_order_release);
61
62 lock.unlock();
63
64 if (m_worker_thread.joinable()) {
65 m_worker_thread.join();
66 }
67
68 lock.lock();
71 std::cout << "[MayaFlux::Journal] Shutdown\n";
72 }
73
74 void scribe(const JournalEntry& entry)
75 {
76 if (entry.severity < m_min_severity.load(std::memory_order_relaxed)) {
77 return;
78 }
79
80 auto comp_idx = static_cast<size_t>(entry.component);
81 if (comp_idx >= m_component_filters.size()) {
82 return;
83 }
84
85 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
86 if (!m_component_filters[comp_idx]) {
87 return;
88 }
89
90 std::lock_guard lock(m_mutex);
91
92 if (m_sinks.empty()) {
93 write_to_console(entry);
94 } else {
95 write_to_sinks(entry);
96 }
97 }
98
99 void scribe_rt(Severity severity, Component component, Context context, std::string_view message, std::source_location location)
100 {
101 if (!should_log(severity, component)) {
102 return;
103 }
104
105 RealtimeEntry entry(severity, component, context, message, location);
106
107 if (!m_ring_buffer.try_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 std::cout << AnsiColors::Reset;
183 break;
184 default:
185 std::cout << AnsiColors::Reset;
186 break;
187 }
188 }
189
190 std::cout << "[" << Utils::enum_to_string(entry.severity) << "]" << AnsiColors::Reset;
191
192 if (colors_enabled) {
193 std::cout << AnsiColors::Magenta;
194 }
195 std::cout << "[" << Utils::enum_to_string(entry.component) << "]" << AnsiColors::Reset;
196
197 if (colors_enabled) {
198 std::cout << AnsiColors::Cyan;
199 }
200 std::cout << "[" << Utils::enum_to_string(entry.context) << "]" << AnsiColors::Reset << " ";
201
202 std::cout << entry.message;
203
204 if (entry.location.file_name() != nullptr && entry.location.line() != 0) {
205 if (colors_enabled) {
206 std::cout << AnsiColors::BrightBlue;
207 }
208 std::cout << " (" << entry.location.file_name()
209 << ":" << entry.location.line() << ")" << AnsiColors::Reset;
210 }
211
212 std::cout << '\n';
213 }
214
215 static void write_to_console(const RealtimeEntry& entry)
216 {
217 if (colors_enabled) {
218 switch (entry.severity) {
219 case Severity::TRACE:
220 std::cout << AnsiColors::Cyan;
221 break;
222 case Severity::DEBUG:
223 std::cout << AnsiColors::Blue;
224 break;
225 case Severity::INFO:
226 std::cout << AnsiColors::Green;
227 break;
228 case Severity::WARN:
229 std::cout << AnsiColors::Yellow;
230 break;
231 case Severity::ERROR:
232 std::cout << AnsiColors::BrightRed;
233 break;
234 case Severity::FATAL:
236 break;
237 case Severity::NONE:
238 std::cout << AnsiColors::Reset;
239 break;
240 default:
241 std::cout << AnsiColors::Reset;
242 break;
243 }
244 }
245
246 std::cout << "[" << Utils::enum_to_string(entry.severity) << "]" << AnsiColors::Reset;
247
248 if (colors_enabled) {
249 std::cout << AnsiColors::Magenta;
250 }
251 std::cout << "[" << Utils::enum_to_string(entry.component) << "]" << AnsiColors::Reset;
252
253 if (colors_enabled) {
254 std::cout << AnsiColors::Cyan;
255 }
256 std::cout << "[" << Utils::enum_to_string(entry.context) << "]" << AnsiColors::Reset << " ";
257
258 std::cout << entry.message;
259
260 if (entry.file_name != nullptr) {
261 if (colors_enabled) {
262 std::cout << AnsiColors::BrightBlue;
263 }
264 std::cout << " (" << entry.file_name
265 << ":" << entry.line << ")" << AnsiColors::Reset;
266 }
267
268 std::cout << '\n';
269 }
270
271 std::vector<std::unique_ptr<Sink>> m_sinks;
272
273 void write_to_sinks(const JournalEntry& entry)
274 {
275 for (auto& sink : m_sinks) {
276 if (sink->is_available()) {
277 sink->write(entry);
278 }
279 }
280 }
281
282 void write_to_sinks(const RealtimeEntry& entry)
283 {
284 for (auto& sink : m_sinks) {
285 if (sink->is_available()) {
286 sink->write(entry);
287 }
288 }
289 }
290
292 {
293 m_worker_running.store(true, std::memory_order_release);
294 m_worker_thread = std::thread([this]() { worker_loop(); });
295 }
296
298 {
299 if (m_worker_thread.joinable()) {
300 m_worker_running.store(false, std::memory_order_release);
301 m_worker_thread.join();
302
304 }
305 }
306
308 {
309 using namespace std::chrono_literals;
310
311 while (m_worker_running.load(std::memory_order_acquire)) {
312 if (m_initialized) {
314 }
315 std::this_thread::sleep_for(10ms);
316 }
317
318 if (m_initialized) {
320 }
321 }
322
324 {
325 while (auto entry = m_ring_buffer.try_pop()) {
326 std::lock_guard lock(m_mutex);
327
328 if (m_sinks.empty()) {
329 write_to_console(*entry);
330 } else {
331 write_to_sinks(*entry);
332 }
333 }
334
335 auto dropped = m_dropped_messages.exchange(0, std::memory_order_acquire);
336 if (dropped > 0) {
337 std::lock_guard lock(m_mutex);
338 std::cout << "[MayaFlux::Journal] WARNING: Dropped " << dropped
339 << " realtime log messages (buffer full)\n";
340 }
341 }
342
343 std::mutex m_mutex;
344 std::atomic<Severity> m_min_severity;
345 std::array<bool, magic_enum::enum_count<Component>()> m_component_filters {};
347
349 std::atomic<bool> m_worker_running;
350 std::thread m_worker_thread;
351 std::atomic<uint64_t> m_dropped_messages { 0 };
352
353 std::atomic<bool> m_shutdown_in_progress;
354};
355
357{
358 static Archivist archivist;
359 return archivist;
360}
361
363 : m_impl(std::make_unique<Impl>())
364{
365 m_impl->init();
366}
367
368Archivist::~Archivist() = default;
369
371{
372 instance().m_impl->shutdown();
373}
374
375void Archivist::scribe(Severity severity, Component component, Context context,
376 std::string_view message, std::source_location location)
377{
378 JournalEntry entry(severity, component, context, message, location);
379 m_impl->scribe(entry);
380}
381
382void Archivist::scribe_rt(Severity severity, Component component, Context context,
383 std::string_view message, std::source_location location)
384{
385 m_impl->scribe_rt(severity, component, context, message, location);
386}
387
389 std::string_view message)
390{
391 JournalEntry entry(Severity::NONE, component, context, message, std::source_location {});
392 m_impl->scribe(entry);
393}
394
395void Archivist::add_sink(std::unique_ptr<Sink> sink)
396{
397 m_impl->add_sink(std::move(sink));
398}
399
401{
402 m_impl->clear_sinks();
403}
404
406{
407 m_impl->set_min_severity(min_sev);
408}
409
411{
412 m_impl->set_component_filter(comp, enabled);
413}
414
415} // 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)
void scribe(const JournalEntry &entry)
Definition Archivist.cpp:74
void add_sink(std::unique_ptr< Sink > sink)
static constexpr size_t RING_BUFFER_SIZE
Definition Archivist.cpp:14
std::array< bool, magic_enum::enum_count< Component >()> m_component_filters
RingBuffer< RealtimeEntry, RING_BUFFER_SIZE > m_ring_buffer
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:99
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
Lock-free SPSC (Single Producer Single Consumer) ring buffer.
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:10
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)
Definition EnumUtils.hpp:51
A log entry structure to encapsulate log message details.
char message[MAX_MESSAGE_LENGTH]
Lightweight entry for lock-free ring buffer.