bux API Reference 1.12.6
Static library of whatever are seen required in general purpose but not directly supported from Modern C++. Or whatever reusable originated from my side projects.
Loading...
Searching...
No Matches
Summary to Logger.h

Header bux/Logger.h wraps the process-wide logger behind a handful of macros. Whatever the sink is, usage always boils down to the same two steps:

  1. Define the sink once — exactly one DEF_LOGGER_XXX line, at namespace scope, in exactly one translation unit of the program. It expands to the definition of bux::user::logger(), which the library-side bux::logger() calls.
  2. Log from anywhere else — any thread, any translation unit which includes <bux/Logger.h>. No logger object is ever passed around.
#include <bux/Logger.h> // DEF_LOGGER_COUT(), LOG(), FUNLOGX1()
#include <iostream> // std::cout
DEF_LOGGER_COUT() // (1) once per program
void foo(int i)
{ // (2) anywhere, any thread
LOG(LL_INFO, "i*2 = {}", i*2);
}
@ LL_INFO
Information worth mentioning about the current status, be it normal or abnormal.
Definition LogLevel.h:13
Summary
#define FUNLOGX1(x)
Definition Logger.h:174
#define DEF_LOGGER_COUT(...)
Definition Logger.h:107
#define LOG(ll, fmtStr,...)
Definition Logger.h:80

1. Pick a sink

Macro (square brackets mark optional arguments) Sink Include before it
DEF_LOGGER_COUT([max_ll]) std::cout <iostream>
DEF_LOGGER_CERR([max_ll]) std::cerr <iostream>
DEF_LOGGER_OSTREAM(out[, max_ll]) any std::ostream lvalue outliving the logger
DEF_LOGGER_FILE(path[, max_ll]) one std::ofstream opened at path <fstream>
DEF_LOGGER_FILES(pathfmt[, max_ll]) file whose name is re-formatted from the current timestamp <bux/FileLog.h>
DEF_FALLBACK_LOGGER_FILES(fsize, paths) same, plus fallback to a finer path format once fsize bytes are exceeded <bux/FileLog.h>
DEF_PARA_LOGGER bux::C_ParaLog facade fanning out to child loggers <bux/ParaLog.h>

max_ll is the most verbose level the sink accepts, defaulting to LL_VERBOSE (i.e. accept everything). See Log levels.

Note
DEF_LOGGER_COUT() and DEF_LOGGER_CERR() are the same macro with a different stream, so using both in one program is an ODR violation, as is any two DEF_LOGGER_XXX lines.

Timestamped file names

pathfmt is a std::format string taking the current timestamp as its single argument, so a new file is opened whenever the formatted name changes:

#include <bux/Logger.h> // DEF_LOGGER_FILES()
#include <bux/FileLog.h> // bux::C_PathFmtLogSnap
DEF_LOGGER_FILES("timelog/{:%y%m%d_%H%M}.log") // one file per minute
#define DEF_LOGGER_FILES(pathfmt,...)
Definition Logger.h:119

Missing directories are created on demand by bux::C_PathFmtLogSnap — unlike DEF_LOGGER_FILE(), whose plain std::ofstream requires the folder to already exist.

DEF_FALLBACK_LOGGER_FILES() additionally caps each file by size: once the current file exceeds fsize bytes, the next path format in paths takes over. Keep paths in a namespace-scope constinit object, because it is consumed by a global initializer:

#include <bux/Logger.h> // DEF_FALLBACK_LOGGER_FILES()
#include <bux/FileLog.h> // bux::C_PathFmtLogSnap
constinit const std::array fallbacks{
"timelog/{:%y%m%d-%H}.log",
"timelog/{:%y%m%d-%H-%M}.log",
"timelog/{:%y%m%d-%H-%M-%S}.log"
};
DEF_FALLBACK_LOGGER_FILES(65536, fallbacks)
#define DEF_FALLBACK_LOGGER_FILES(fsize_in_bytes, fallbackPaths)
Definition Logger.h:127

Several sinks at once

DEF_PARA_LOGGER defines bux::user::g_paraLog, a bux::C_ParaLog to be populated at run time — each child keeps its own max_ll, and bux::C_ParaLog::partitionBy() routes lines to children by content:

#include <bux/Logger.h> // DEF_PARA_LOGGER
#include <bux/ParaLog.h> // bux::C_ParaLog
#include <bux/FileLog.h> // bux::C_PathFmtLogSnap
int main()
{
using bux::user::g_paraLog;
g_paraLog.addChild(std::cout, LL_WARNING); // console: warnings and above
g_paraLog.addChildT<bux::C_PathFmtLogSnap>([](auto &logger) // rotated files: everything
{
logger.configPath("logs/{:%Y-%m-%d}.log");
});
auto nodes = g_paraLog.partitionBy(std::initializer_list<FC_MatchStr>{"[foo]", "[bar]"});
nodes[0].addChildT<bux::C_PathFmtLogSnap>(...); // lines containing "[foo]"
nodes[1].addChildT<bux::C_PathFmtLogSnap>(...); // lines containing "[bar]"
nodes.matchedNone().addChildT<bux::C_PathFmtLogSnap>(...); // all the other lines
}
@ LL_WARNING
Situation that should be warned but should not have sabotaged anything already.
Definition LogLevel.h:12
#define DEF_PARA_LOGGER
Definition Logger.h:136

2. Log with the macros

Macro Emits
LOG(ll, fmtStr[, args...]) one stamped line, dropped unless ll passes the level filter
LOG1(ll, str) LOG(ll, "{}", str), for text which is not a literal
LOG_RAW(fmtStr[, args...]) the formatted text alone — no stamp, no level filtering
FUNLOG { on the spot and } at end of the enclosing block, tagged with the current function name
FUNLOGX(fmtStr[, args...]) same, with the formatted text as the "arguments" of the scope
FUNLOGX1(x1)FUNLOGX9(x1, ..., x9) same, with the values rendered as a comma-separated list
SCOPELOG(scope) as FUNLOG, but named scope instead of the current function
SCOPELOGX(scope, fmtStr[, args...]) as FUNLOGX(), but named scope
SCOPELOGX1(scope, x1)SCOPELOGX9(scope, x1, ..., x9) as FUNLOGX1()FUNLOGX9(), but named scope

The FUNLOG/SCOPELOG family declares an unnamed bux::C_EntryLog on the stack, so the closing line is written by its destructor — through every return, break, and throw — and nested scopes indent the lines in between.

Log levels

Declared in bux/LogLevel.h and exported to the global namespace, so no bux:: prefix is needed:

Level Letter Meaning
LL_FATAL F The program should shut down right after reporting this.
LL_ERROR E Error the program can keep running after.
LL_WARNING W Worth warning about, nothing sabotaged yet.
LL_INFO I Status worth mentioning, normal or not.
LL_DEBUG D Debug-only, easily suppressed for releases.
LL_VERBOSE V More detail than some would care for.

A line survives when its level is not more verbose than the sink's max_ll, i.e. DEF_LOGGER_COUT(LL_WARNING) keeps LL_FATAL, LL_ERROR, LL_WARNING and drops the rest. Filtering costs no formatting: the arguments of LOG() are never evaluated once the level is rejected.

Anatomy of a line

Output of test/smoke_coutlog.cpp, abridged:

2026/08/23 18:17:34.509 tid173271 V:********** LOGS BEGUN **********
2026/08/23 18:17:34.509 tid173271 F:Hello fatal
2026/08/23 18:17:34.509 tid173271 V:@1@int main()(Outer) {
2026/08/23 18:17:34.509 tid173271 W:|Hello warning
2026/08/23 18:17:34.509 tid173271 V:|@2@int main() {
2026/08/23 18:17:34.509 tid173271 I:||Hello info
2026/08/23 18:17:34.509 tid173271 V:|@2@}
2026/08/23 18:17:34.509 tid173271 V:@1@}
  • 2026/08/23 18:17:34.509 — timestamp down to the millisecond, in local time unless told otherwise by LOGGER_USE_LOCAL_TIME_.
  • tid173271 — id of the calling thread.
  • V: — the level letter of the table above.
  • || — one | per enclosing FUNLOG/SCOPELOG scope, counted per thread.
  • @2@{ / @2@} — matching entry/exit pair of one scope; the serial number pairs them up even when other threads interleave. A scope left by an exception closes with @2@} due to 1 uncaught exception instead.
  • ********** LOGS BEGUN ********** is written once, on the first call to bux::logger().

Compile-time switches

Both are consulted by bux/Logger.h at include time, so #define them before including it — ideally on the compiler command line, so all translation units agree.

  • TURN_OFF_LOGGER_ — every macro of the table above expands to nothing and no arguments are evaluated. Note that bux::user::logger() is then never defined either, so code calling bux::logger() directly has to be guarded:
    #ifndef TURN_OFF_LOGGER_
    *u <<"raw text\n";
    #endif
    I_SyncLog & logger()
    Definition Logger.cpp:20
  • LOGGER_USE_LOCAL_TIME_ — the time zone of every timestamp, defaulting to true. Valid values:
    #define LOGGER_USE_LOCAL_TIME_ true // local time
    #define LOGGER_USE_LOCAL_TIME_ std::chrono::get_tzdb().current_zone() // ditto
    #define LOGGER_USE_LOCAL_TIME_ std::chrono::get_tzdb().locate_zone("Asia/Taipei")
    #define LOGGER_USE_LOCAL_TIME_ false // system clock
    #define LOGGER_USE_LOCAL_TIME_ nullptr // ditto

Gotchas

  • Format strings of LOG()/LOG_RAW() must be compile-time constants, since they reach std::format() verbatim. Pass runtime text as an argument — LOG1(ll, s) is exactly that shorthand. The format string of FUNLOGX()/SCOPELOGX() instead goes through std::vformat(), so a mismatch throws std::format_error at run time rather than failing to compile.
  • Scope logs ignore the level filter. FUNLOG, SCOPELOG, LOG_RAW() and manual bux::C_UseLog usage lock the sink without a level, so their lines are written even under DEF_LOGGER_FILE("x.log", LL_INFO), despite being stamped V.
  • Nested logging is fine, and inner lines land first. The sink is held by a std::recursive_mutex for the whole line, so logging from a function called inside a LOG() argument list neither deadlocks nor interleaves; the inner line simply completes first.
  • Each line is flushed when it ends, so nothing is lost on a crash; conversely, a very chatty LL_VERBOSE sink is not free.

Below the macros

LOG() is a thin shorthand for locking the singleton and stamping a line, which is worth doing by hand for output the macros do not shape — multi-line dumps, manipulators, or anything streamed piecewise:

if (bux::C_UseLog u{bux::logger()}) // no other thread writes until u dies
*u <<std::boolalpha <<"raw, unstamped\n";
if (bux::C_UseLog u{bux::logger(), LL_INFO}) // ...unless LL_INFO is filtered out
bux::stamp(u, LL_INFO) <<"stamped like LOG()\n";
std::ostream & stamp(const C_UseLog &u, E_LogLevel level)
Definition Logger.cpp:47

A logger the DEF_LOGGER_XXX table does not cover is defined by writing bux::user::logger() directly. DEF_LOGGER_TAIL_() closes the namespaces for you, which is how test/test_logger.cpp swaps in a fresh sink per test case:

namespace bux { namespace user {
std::unique_ptr<C_GizmoLogger> g_log; // any bux::I_SyncLog will do
I_SyncLog &logger() {
DEF_LOGGER_TAIL_(*g_log) // expands to: return *g_log; }}}
#define DEF_LOGGER_TAIL_(x)
Definition Logger.h:75
I_SyncLog & logger()
THE common namespace of bux library.
Definition AtomiX.cpp:3

See also