#pragma once #include #include #include namespace fgc { enum class LogLevel { Trace = 0, Debug, Info, Warn, Error, Off }; // Minimal leveled, thread-safe logger. Each log line is assembled in a // per-statement buffer and written atomically under a shared mutex, so lines // from different threads never interleave. Level filtering is global. // // Usage: LOG_INFO << "starting, rate=" << rate; // Lines at Warn/Error go to stderr; everything else to stdout. class Logger { public: static void setLevel(LogLevel level); static LogLevel level(); static bool enabled(LogLevel level); // Parse "trace"|"debug"|"info"|"warn"|"error"|"off" (case-insensitive). // Returns false (and leaves the level unchanged) on an unknown string. static bool setLevelFromString(const std::string& s); }; // RAII helper that buffers one log line and flushes it on commit(). // Designed to be driven by the LOG_* macros' for-loop guard. class LogStream { public: explicit LogStream(LogLevel level); bool pending() const { return enabled_ && !done_; } void commit(); std::ostream& stream() { return buffer_; } private: LogLevel level_; bool enabled_; bool done_ = false; std::ostringstream buffer_; }; } // namespace fgc // Brace-safe: the whole macro is a single for-statement, so it composes // correctly inside if/else without dangling-else hazards, and the message // expression is never evaluated when the level is disabled. #define FGC_LOG_AT(level) \ for (::fgc::LogStream _fgc_ls(level); _fgc_ls.pending(); _fgc_ls.commit()) \ _fgc_ls.stream() #define LOG_TRACE FGC_LOG_AT(::fgc::LogLevel::Trace) #define LOG_DEBUG FGC_LOG_AT(::fgc::LogLevel::Debug) #define LOG_INFO FGC_LOG_AT(::fgc::LogLevel::Info) #define LOG_WARN FGC_LOG_AT(::fgc::LogLevel::Warn) #define LOG_ERROR FGC_LOG_AT(::fgc::LogLevel::Error)