Program Listing for File logger.hpp#

Return to documentation for file (src/utils/logger.hpp)

#pragma once
#include <QObject>
#include <QString>
#include <memory>
#include <source_location>

namespace mosaic {

/// @brief Severity levels for log messages.
///
/// Levels are ordered from least to most severe.  The minimum level displayed
/// or written to disk can be set via Logger::set_min_level().
enum class LogLevel : int {
    Trace    = 0, ///< Very detailed tracing — disabled in release builds.
    Debug    = 1, ///< Developer-facing diagnostics.
    Info     = 2, ///< Normal operational events (session start, file paths, …).
    Warning  = 3, ///< Recoverable issues (dropped frame, device fallback, …).
    Error    = 4, ///< Non-fatal errors — operation failed but the app continues.
    Critical = 5, ///< Fatal errors — the application cannot continue.
};

/// @returns A short uppercase C-string label for the given level, e.g. @c "INFO".
[[nodiscard]] const char* log_level_label(LogLevel l) noexcept;

/// @brief Thread-safe application logger with file output and a Qt signal.
///
/// Logger is a **singleton** — access it via Logger::instance().  In practice,
/// prefer the free functions (log_info, log_error, …) which automatically
/// capture the call site via @c std::source_location.
///
/// @par Connecting the UI panel
/// @code{.cpp}
/// // In LoggerPanelW constructor (main thread):
/// connect(&Logger::instance(), &Logger::entry_added,
///         this, &LoggerPanelW::on_entry_added,
///         Qt::QueuedConnection);   // ← always use QueuedConnection
/// @endcode
///
/// @note The entry_added() signal is emitted from the **calling thread**.
///       Always use Qt::QueuedConnection when connecting to a main-thread
///       slot from a background worker.
class Logger : public QObject {
    Q_OBJECT
   public:
    /// @returns The process-wide Logger singleton.
    static Logger& instance();

    ~Logger() override;

    /// @brief Logs a message at the given severity level.
    ///
    /// Thread-safe.  If a log file is open, the message is written to it
    /// (mutex-protected).  entry_added() is then emitted from the calling
    /// thread.
    ///
    /// @param level    Severity level.
    /// @param message  UTF-8 message string.
    /// @param loc      Automatically-captured source location — do not pass manually.
    void log(LogLevel level, const QString& message,
             std::source_location loc = std::source_location::current());

    /// @brief Sets the minimum level written to file and emitted via signal.
    ///
    /// Messages below this level are discarded.  Default: LogLevel::Trace.
    ///
    /// @param level  New minimum level.
    void set_min_level(LogLevel level);

    /// @brief Opens a log file for writing (appending if it already exists).
    ///
    /// @param path  Absolute file path.
    /// @returns     @c true on success.
    bool open_log_file(const QString& path);

    /// @brief Flushes and closes the log file.
    void close_log_file();

   signals:
    /// Emitted for every accepted log entry, from the calling thread.
    ///
    /// @param level      Severity as @c int — cast back with
    ///                   @c static_cast<LogLevel>(level).
    /// @param timestamp  Wall-clock string, e.g. @c "14:32:05.123".
    /// @param location   Source location, e.g. @c "record_manager.cpp:88".
    /// @param message    The log message.
    void entry_added(int level, QString timestamp, QString location, QString message);

   private:
    explicit Logger();
    struct Impl;
    std::unique_ptr<Impl> d;
};

// ── Convenience free functions ─────────────────────────────────────────────

/// @brief Logs at Trace level.  @p loc is captured automatically.
void log_trace(const QString& msg, std::source_location loc = std::source_location::current());
/// @brief Logs at Debug level.  @p loc is captured automatically.
void log_debug(const QString& msg, std::source_location loc = std::source_location::current());
/// @brief Logs at Info level.  @p loc is captured automatically.
void log_info(const QString& msg, std::source_location loc = std::source_location::current());
/// @brief Logs at Warning level.  @p loc is captured automatically.
void log_warning(const QString& msg, std::source_location loc = std::source_location::current());
/// @brief Logs at Error level.  @p loc is captured automatically.
void log_error(const QString& msg, std::source_location loc = std::source_location::current());
/// @brief Logs at Critical level.  @p loc is captured automatically.
void log_critical(const QString& msg, std::source_location loc = std::source_location::current());

} // namespace mosaic