107 lines
4.4 KiB
C++
107 lines
4.4 KiB
C++
#pragma once
|
|
|
|
#include <functional>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
namespace fgc {
|
|
|
|
enum class LogLevel { Trace = 0, Debug, Info, Warn, Error, Off };
|
|
|
|
// Wire-tracing categories. Orthogonal to LogLevel: a category emits verbatim
|
|
// TX/RX lines and is toggled independently of the linear level, so you can
|
|
// watch e.g. all serial traffic without turning everything else to Trace.
|
|
// Values are a bitmask (multiple categories enabled at once).
|
|
enum class LogCat : unsigned {
|
|
None = 0,
|
|
Serial = 1u << 0,
|
|
Mqtt = 1u << 1,
|
|
Camera = 1u << 2,
|
|
Control = 1u << 3,
|
|
All = Serial | Mqtt | Camera | Control,
|
|
};
|
|
|
|
// 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);
|
|
|
|
// Wire-trace categories (independent of the linear level above).
|
|
static void setCategories(unsigned mask);
|
|
static unsigned categories();
|
|
static void enableCategory(LogCat cat);
|
|
static void disableCategory(LogCat cat);
|
|
static bool categoryEnabled(LogCat cat);
|
|
|
|
// Parse a comma list: "serial,mqtt" | "all" | "none" (case-insensitive,
|
|
// whitespace-trimmed). Recognized tokens always apply; an unknown token
|
|
// sets *ok=false (when ok != nullptr) but does not discard the rest.
|
|
static unsigned parseCategories(const std::string& csv, bool* ok = nullptr);
|
|
|
|
// Map a single category name to its enum value. Unknown => LogCat::None and
|
|
// *ok=false (when ok != nullptr).
|
|
static LogCat catFromString(const std::string& name, bool* ok = nullptr);
|
|
|
|
// Output sink for finished log lines. The string is the fully formatted line
|
|
// (timestamp + "[LABEL]" + message, no trailing newline); `level` lets a sink
|
|
// colour or route by severity. Installing a sink REPLACES the default
|
|
// stdout/stderr writer entirely - used by the TUI to divert logs into an
|
|
// on-screen pane so they never corrupt the rendered screen. Pass an empty
|
|
// function to restore the default writer. Thread-safe.
|
|
using LogSink = std::function<void(LogLevel level, const std::string& line)>;
|
|
static void setSink(LogSink sink);
|
|
};
|
|
|
|
// 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);
|
|
LogStream(LogLevel level, LogCat cat); // category (wire-trace) line
|
|
bool pending() const { return enabled_ && !done_; }
|
|
void commit();
|
|
std::ostream& stream() { return buffer_; }
|
|
|
|
private:
|
|
LogLevel level_;
|
|
LogCat cat_ = LogCat::None; // None => gated by level only
|
|
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)
|
|
|
|
// Verbatim wire-trace line for one category. Prints when the category is
|
|
// enabled (independent of the linear level, except level Off silences all).
|
|
// The message expression is not evaluated when the category is off.
|
|
#define LOG_TRACE_CAT(cat) \
|
|
for (::fgc::LogStream _fgc_ls(::fgc::LogLevel::Trace, (cat)); _fgc_ls.pending(); \
|
|
_fgc_ls.commit()) \
|
|
_fgc_ls.stream()
|