MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Archivist.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "JournalEntry.hpp"
4
5#include <format>
6
7namespace MayaFlux::Journal {
8
9template <typename... Args>
10inline std::string format_runtime(std::string_view fmt_str, Args&&... args)
11{
12 return std::vformat(fmt_str, std::make_format_args(args...));
13}
14
15class Sink;
16
17/**
18 * @class Archivist
19 * @brief Singleton class responsible for managing log entries.
20 *
21 * The Archivist class provides methods to log messages with various severity levels,
22 * components, and contexts. It supports both standard and real-time logging.
23 */
24class MAYAFLUX_API Archivist {
25
26public:
27 /**
28 * @brief Get the singleton instance of the Archivist.
29 * @return Reference to the Archivist instance.
30 */
31 static Archivist& instance();
32
33 /**
34 * @brief Shutdown the logging system.
35 * This should be called once at the end of the application.
36 */
37 static void shutdown();
38
39 /**
40 * @brief Log a message with the specified severity, component, and context.
41 *
42 * This method captures the source location automatically.
43 *
44 * @param severity The severity level of the log message.
45 * @param component The component generating the log message.
46 * @param context The execution context of the log message.
47 * @param message The log message content.
48 * @param location The source location (file, line, function) of the log call.
49 */
50 void scribe(Severity severity, Component component, Context context,
51 std::string_view message,
52 std::source_location location = std::source_location::current());
53
54 /**
55 * @brief Log a message from a real-time context with the specified severity, component, and context.
56 * This method is optimized for real-time contexts and captures the source location automatically.
57 * @param severity The severity level of the log message.
58 * @param component The component generating the log message.
59 * @param context The execution context of the log message.
60 * @param message The log message content.
61 * @param location The source location (file, line, function) of the log call.
62 */
63 void scribe_rt(Severity severity, Component component, Context context,
64 std::string_view message,
65 std::source_location location = std::source_location::current());
66
67 /**
68 * @brief Log a simple message without source location information.
69 * This method is intended for use in contexts where source location is not available or needed.
70 * It is not effected by severity filters, so use sparingly.
71 *
72 * @param component The component generating the log message.
73 * @param context The execution context of the log message.
74 * @param message The log message content.
75 */
76 void scribe_simple(Component component, Context context,
77 std::string_view message);
78
79 /**
80 * @brief Add a log sink for output
81 * @param sink Unique pointer to a LogSink implementation
82 */
83 void add_sink(std::unique_ptr<Sink> sink);
84
85 /**
86 * @brief Remove all sinks
87 */
88 void clear_sinks();
89
90 /**
91 * @brief Synchronously drain all pending ring buffer entries to sinks.
92 *
93 * Blocks until the ring buffer is empty. Called automatically by error(),
94 * error_rethrow(), and fatal() before propagating. Available for any
95 * non-RT caller that needs a guaranteed visibility point.
96 */
97 void flush();
98
99 /**
100 * @brief Set the minimum severity level for logging.
101 * Messages with a severity lower than this level will be ignored.
102 * @param min_sev The minimum severity level to log.
103 */
104 void set_min_severity(Severity min_sev);
105
106 /**
107 * @brief Enable or disable logging for a specific component.
108 * @param comp The component to enable or disable.
109 * @param enabled True to enable logging for the component, false to disable.
110 */
111 void set_component_filter(Component comp, bool enabled);
112
113 /**
114 * @brief Restrict logging to these components; an empty list includes all.
115 * Disabled component filters still apply.
116 */
117 void set_component_includes(const std::vector<Component>& components);
118
119 /**
120 * @brief Enable or disable logging for a specific context.
121 * @param ctx The context to enable or disable.
122 * @param enabled True to enable logging for the context, false to disable.
123 */
124 void set_context_filter(Context ctx, bool enabled);
125
126 /**
127 * @brief Restrict logging to these contexts; an empty list includes all.
128 * Disabled context filters still apply.
129 */
130 void set_context_includes(const std::vector<Context>& contexts);
131
132 Archivist(const Archivist&) = delete;
133 Archivist& operator=(const Archivist&) = delete;
134 Archivist(Archivist&&) = delete;
136
137private:
138 Archivist();
140
141 class Impl;
142 std::unique_ptr<Impl> m_impl;
143};
144
145/**
146 * @brief Log a message with the specified severity, component, and context.
147 *
148 * Captures the source location automatically.
149 *
150 * @param severity The severity level of the log message.
151 * @param component The component generating the log message.
152 * @param context The execution context of the log message.
153 * @param location Source location (file, line, function) of the log call.
154 * @param message The log message content.
155 */
156inline void scribe(Severity severity, Component component, Context context,
157 std::source_location location, std::string_view message)
158{
159 Archivist::instance().scribe(severity, component, context, message, location);
160}
161
162/**
163 * @brief printf-style overload of scribe().
164 *
165 * @copydoc scribe(Severity,Component,Context,std::string_view,std::source_location)
166 *
167 * @param msg_or_fmt The format string.
168 * @param args The format arguments.
169 */
170template <typename... Args>
171void scribe(Severity severity, Component component, Context context,
172 std::source_location location, const char* msg_or_fmt, Args&&... args)
173{
174 if constexpr (sizeof...(Args) == 0) {
175 Archivist::instance().scribe(severity, component, context,
176 std::string_view(msg_or_fmt), location);
177 } else {
178 auto msg = format_runtime(msg_or_fmt, std::forward<Args>(args)...);
179 Archivist::instance().scribe(severity, component, context, msg, location);
180 }
181}
182
183/**
184 * @brief Log a message in a real-time context with automatic source location.
185 *
186 * Optimized for real-time usage and captures the source location automatically.
187 *
188 * @param severity The severity level of the log message.
189 * @param component The component generating the log message.
190 * @param context The execution context of the log message.
191 * @param location Source location (file, line, function) of the log call.
192 * @param message The log message content.
193 */
194inline void scribe_rt(Severity severity, Component component, Context context,
195 std::source_location location, std::string_view message)
196{
197 Archivist::instance().scribe_rt(severity, component, context, message, location);
198}
199
200/**
201 * @brief printf-style overload of scribe_rt().
202 *
203 * @copydoc scribe_rt(Severity,Component,Context,std::string_view,std::source_location)
204 *
205 * @param msg_or_fmt The format string.
206 * @param args The format arguments.
207 */
208template <typename... Args>
209void scribe_rt(Severity severity, Component component, Context context,
210 std::source_location location,
211 const char* msg_or_fmt, Args&&... args)
212{
213 if constexpr (sizeof...(Args) == 0) {
214 Archivist::instance().scribe_rt(severity, component, context,
215 std::string_view(msg_or_fmt), location);
216 } else {
217 auto msg = format_runtime(msg_or_fmt, std::forward<Args>(args)...);
218 Archivist::instance().scribe_rt(severity, component, context, msg, location);
219 }
220}
221
222/**
223 * @brief Log a simple message without source-location.
224 *
225 * Intended for contexts where source location is unavailable or unnecessary.
226 * It is not effected by severity filters, so use sparingly.
227 *
228 * @param component The component generating the log message.
229 * @param context The execution context of the log message.
230 * @param message The log message content.
231 */
232inline void log(Component component, Context context,
233 std::string_view message)
234{
235 Archivist::instance().scribe_simple(component, context, message);
236}
237
238/**
239 * @brief printf-style overload of log().
240 *
241 * @copydoc log(Component,Context,std::string_view)
242 *
243 * @param msg_or_fmt The format string.
244 * @param args The format arguments.
245 */
246template <typename... Args>
247void log(Component component, Context context,
248 const char* msg_or_fmt, Args&&... args)
249{
250 if constexpr (sizeof...(Args) == 0) {
251 Archivist::instance().scribe_simple(component, context,
252 std::string_view(msg_or_fmt));
253 } else {
254 auto msg = format_runtime(msg_or_fmt, std::forward<Args>(args)...);
255 Archivist::instance().scribe_simple(component, context, msg);
256 }
257}
258
259/**
260 * @brief Print formatted output directly to stdout with MayaFlux prefix.
261 *
262 * Intended for immediate, unfiltered output without journal infrastructure.
263 * No component/context tagging, no severity filtering, no source location.
264 * Output includes [MayaFlux] prefix to identify framework origin.
265 *
266 * @param message The message or format string.
267 */
268inline void format_print(std::string_view message)
269{
270 std::cout << "[MayaFlux] " << message << '\n';
271}
272
273/**
274 * @brief Printf-style overload of format_print().
275 *
276 * @copydoc format_print(std::string_view)
277 *
278 * @param msg_or_fmt The format string.
279 * @param args The format arguments.
280 */
281template <typename... Args>
282inline void format_print(std::format_string<std::remove_cvref_t<Args>...> fmt_str, Args&&... args)
283{
284 std::cout << "[MayaFlux] " << format(fmt_str, std::forward<Args>(args)...) << '\n';
285}
286
287/**
288 * @brief Printf-style overload for runtime format strings.
289 *
290 * @copydoc format_print(std::string_view)
291 *
292 * @param fmt_str The runtime format string.
293 * @param args The format arguments.
294 */
295template <typename... Args>
296inline void format_print(const char* fmt_str, Args&&... args)
297{
298 std::cout << "[MayaFlux] " << format_runtime(fmt_str, std::forward<Args>(args)...) << '\n';
299}
300
301/**
302 * @brief Log a fatal message and abort the program.
303 *
304 * @param component The component generating the log message.
305 * @param context The execution context of the log message.
306 * @param location Source location (file, line, function) of the log call.
307 * @param message The fatal message content.
308 */
309[[noreturn]] inline void fatal(Component component, Context context,
310 std::source_location location, std::string_view message)
311{
312 Archivist::instance().scribe(Severity::FATAL, component, context, message, location);
314 std::abort();
315}
316
317/**
318 * @brief fmt-style overload of fatal().
319 *
320 * @copydoc fatal(Component,Context,std::string_view,std::source_location)
321 *
322 * @tparam Args Types of the format arguments.
323 * @param fmt_str The format string.
324 * @param args The format arguments.
325 */
326template <typename... Args>
327[[noreturn]] void fatal(Component component, Context context,
328 std::source_location location, std::format_string<Args...> fmt_str, Args&&... args)
329{
330 auto msg = format(fmt_str, std::forward<Args>(args)...);
331 Archivist::instance().scribe(Severity::FATAL, component, context, msg, location);
333 std::abort();
334}
335
336/**
337 * @brief Log an error message and optionally throw an exception.
338 *
339 * @tparam ExceptionType The exception type to throw when behavior == LogAndThrow.
340 * @param context The execution context of the log message.
341 * @param location Source location (file, line, function) of the log call.
342 * @param message The error message content.
343 */
344template <typename ExceptionType = std::runtime_error>
345[[noreturn]] void error(Component component, Context context,
346 std::source_location location,
347 std::string_view message)
348{
349 Archivist::instance().scribe(Severity::ERROR, component, context, message, location);
351 throw ExceptionType(std::string(message));
352}
353
354/**
355 * @brief fmt-style overload of error().
356 *
357 * @copydoc error(Component,Context,ExceptionBehavior,std::string_view,std::source_location)
358 *
359 * @tparam ExceptionType The exception type to throw when behavior == LogAndThrow.
360 * @tparam Args Types of the format arguments.
361 * @param fmt_str The format string.
362 * @param args The format arguments.
363 */
364template <typename ExceptionType = std::runtime_error, typename... Args>
365[[noreturn]] void error(Component component, Context context,
366 std::source_location location, const char* fmt_str, Args&&... args)
367{
368 if constexpr (sizeof...(Args) == 0) {
369 Archivist::instance().scribe(Severity::ERROR, component, context,
370 std::string_view(fmt_str), location);
372 throw ExceptionType(std::string(fmt_str));
373 } else {
374 auto msg = format_runtime(fmt_str, std::forward<Args>(args)...);
375 Archivist::instance().scribe(Severity::ERROR, component, context, msg, location);
377 throw ExceptionType(msg);
378 }
379}
380
381/**
382 * @brief Catch and log an exception, then rethrow it.
383 * This function is intended to be called within a catch block.
384 * @param Component The component generating the log message.
385 * @param Context The execution context of the log message.
386 * @param location The source location (file, line, function) of the log call.
387 * @param additional_context Optional additional context to prepend to the exception message.
388 */
389[[noreturn]] inline void error_rethrow(Component component, Context context,
390 std::source_location location = std::source_location::current(),
391 std::string_view additional_context = "")
392{
393 auto ep = std::current_exception();
394 if (!ep) {
395 Archivist::instance().scribe(Severity::ERROR, component, context,
396 "error_rethrow called outside of a catch", location);
398 std::terminate();
399 }
400
401 try {
402 std::rethrow_exception(ep);
403 } catch (const std::exception& e) {
404 std::string msg = std::string(e.what());
405 if (!additional_context.empty()) {
406 msg = std::string(additional_context) + ": " + msg;
407 }
408 Archivist::instance().scribe(Severity::ERROR, component, context, msg, location);
410 std::rethrow_exception(ep);
411 } catch (...) {
412 std::string msg = "Unknown exception";
413 if (!additional_context.empty()) {
414 msg = std::string(additional_context) + ": " + msg;
415 }
416 Archivist::instance().scribe(Severity::ERROR, component, context, msg, location);
418 std::rethrow_exception(ep);
419 }
420}
421
422/**
423 * @brief fmt-style overload of error_rethrow().
424 *
425 * @copydoc error_rethrow(Component,Context,std::source_location,std::string_view)
426 *
427 * @tparam Args Types of the format arguments.
428 * @param fmt_str The format string.
429 * @param args The format arguments.
430 */
431template <typename... Args>
432[[noreturn]] inline void error_rethrow(Component component, Context context,
433 std::source_location location, const char* fmt_str, Args&&... args)
434{
435 auto msg = format_runtime(fmt_str, std::forward<Args>(args)...);
436 error_rethrow(component, context, location, std::string_view(msg));
437}
438
439} // namespace MayaFlux::Journal
440
441// ============================================================================
442// CONVENIENCE MACROS (for regular logging only)
443// ============================================================================
444
445#define MF_TRACE(comp, ctx, ...) \
446 MayaFlux::Journal::scribe(MayaFlux::Journal::Severity::TRACE, comp, ctx, \
447 std::source_location::current(), __VA_ARGS__)
448
449#define MF_DEBUG(comp, ctx, ...) \
450 MayaFlux::Journal::scribe(MayaFlux::Journal::Severity::DEBUG, comp, ctx, \
451 std::source_location::current(), __VA_ARGS__)
452
453#define MF_INFO(comp, ctx, ...) \
454 MayaFlux::Journal::scribe(MayaFlux::Journal::Severity::INFO, comp, ctx, \
455 std::source_location::current(), __VA_ARGS__)
456
457#define MF_WARN(comp, ctx, ...) \
458 MayaFlux::Journal::scribe(MayaFlux::Journal::Severity::WARN, comp, ctx, \
459 std::source_location::current(), __VA_ARGS__)
460
461#define MF_ERROR(comp, ctx, ...) \
462 MayaFlux::Journal::scribe(MayaFlux::Journal::Severity::ERROR, comp, ctx, \
463 std::source_location::current(), __VA_ARGS__)
464
465#define MF_ASSERT(comp, ctx, condition, ...) \
466 do { \
467 if (!(condition)) [[unlikely]] { \
468 MayaFlux::Journal::scribe( \
469 MayaFlux::Journal::Severity::ERROR, comp, ctx, \
470 std::source_location::current(), \
471 "Assertion failed: " #condition ". " __VA_ARGS__); \
472 MayaFlux::Journal::Archivist::instance().shutdown(); \
473 std::abort(); \
474 } \
475 } while (false)
476
477// ============================================================================
478// CONVENIENCE MACROS for REAL-TIME LOGGING ONLY
479// ============================================================================
480
481#define MF_RT_TRACE(comp, ctx, ...) \
482 MayaFlux::Journal::scribe_rt(MayaFlux::Journal::Severity::TRACE, comp, ctx, \
483 std::source_location::current(), __VA_ARGS__)
484
485#define MF_RT_WARN(comp, ctx, ...) \
486 MayaFlux::Journal::scribe_rt(MayaFlux::Journal::Severity::WARN, comp, ctx, \
487 std::source_location::current(), __VA_ARGS__)
488
489#define MF_RT_ERROR(comp, ctx, ...) \
490 MayaFlux::Journal::scribe_rt(MayaFlux::Journal::Severity::ERROR, comp, ctx, \
491 std::source_location::current(), __VA_ARGS__)
492
493#define MF_RT_DEBUG(comp, ctx, ...) \
494 MayaFlux::Journal::scribe_rt(MayaFlux::Journal::Severity::DEBUG, comp, ctx, \
495 std::source_location::current(), __VA_ARGS__)
496
497// ============================================================================
498// CONVENIENCE MACROS for SIMPLE LOGGING (no source-location)
499// ============================================================================
500#define MF_LOG(comp, ctx, ...) MayaFlux::Journal::log(comp, ctx, __VA_ARGS__)
501
502// ============================================================================
503// CONVENIENCE MACROS for QUICK OUTPUT (no journal system)
504// ============================================================================
505#define MF_PRINT(...) MayaFlux::Journal::format_print(__VA_ARGS__)
std::string severity
Definition Config.cpp:18
bool format
vk::Instance instance
Definition VKDevice.cpp:39
void scribe_simple(Component component, Context context, std::string_view message)
Log a simple message without source location information.
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 flush()
Synchronously drain all pending ring buffer entries to sinks.
Archivist(const Archivist &)=delete
static Archivist & instance()
Get the singleton instance of the Archivist.
Archivist & operator=(Archivist &&)=delete
Archivist & operator=(const Archivist &)=delete
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.
Archivist(Archivist &&)=delete
std::unique_ptr< Impl > m_impl
Singleton class responsible for managing log entries.
Definition Archivist.hpp:24
Context
Execution contexts for log messages.
void error_rethrow(Component component, Context context, std::source_location location=std::source_location::current(), std::string_view additional_context="")
Catch and log an exception, then rethrow it.
void error(Component component, Context context, std::source_location location, std::string_view message)
Log an error message and optionally throw an exception.
void format_print(std::string_view message)
Print formatted output directly to stdout with MayaFlux prefix.
void fatal(Component component, Context context, std::source_location location, std::string_view message)
Log a fatal message and abort the program.
std::string format_runtime(std::string_view fmt_str, Args &&... args)
Definition Archivist.hpp:10
void scribe(Severity severity, Component component, Context context, std::source_location location, std::string_view message)
Log a message with the specified severity, component, and context.
void log(Component component, Context context, std::string_view message)
Log a simple message without source-location.
void scribe_rt(Severity severity, Component component, Context context, std::source_location location, std::string_view message)
Log a message in a real-time context with automatic source location.