-
Notifications
You must be signed in to change notification settings - Fork 1
API Reference
Complete reference for all public APIs in LunarLog. Namespace: minta.
- LunarLog (Main Logger)
- LoggerConfiguration
- LogScope
- ContextScope
- SinkProxy
- ISink
- IFormatter
- FilterRule
- LogEntry
- Enrichers
- Source Location Macros
- Global Logger
- Global Macros
- Enums and Types
- Built-in Sinks
- Built-in Formatters
The main logger class. Non-copyable but move-constructible (move assignment is deleted).
Creates a new logger instance.
| Parameter | Type | Default | Description |
|---|---|---|---|
minLevel |
LogLevel |
INFO |
Minimum log level. Messages below this are dropped. |
addDefaultConsoleSink |
bool |
true |
If true, adds a ConsoleSink with HumanReadableFormatter. |
// Default: INFO level, console sink
minta::LunarLog logger;
// TRACE level, console sink
minta::LunarLog logger(minta::LogLevel::TRACE);
// DEBUG level, no default sink — you must add your own
minta::LunarLog logger(minta::LogLevel::DEBUG, false);Destructor. Calls flush() to ensure all buffered messages are written. All logging threads must be stopped before destroying the logger. See Thread Safety.
Returns a LoggerConfiguration builder for declarative logger setup. Chain configuration methods, then call build() to produce a fully-configured LunarLog instance.
auto logger = minta::LunarLog::configure()
.minLevel(minta::LogLevel::DEBUG)
.writeTo<minta::ConsoleSink>("console")
.writeTo<minta::FileSink, minta::JsonFormatter>("json-out", "app.json.log")
.enrich(minta::Enrichers::threadId())
.build();The returned LoggerConfiguration is move-only. See LoggerConfiguration for all builder methods and Fluent Builder for the full guide.
Changes the minimum log level at runtime. Thread-safe (atomic).
logger.setMinLevel(minta::LogLevel::WARN); // only WARN+ from now onReturns the current minimum log level.
auto level = logger.getMinLevel(); // e.g., LogLevel::INFOEnables or disables capture of file, line, and function for each log entry. Default: false. Thread-safe (atomic).
When enabled, LogEntry::file, LogEntry::line, and LogEntry::function are populated.
logger.setCaptureSourceLocation(true);
logger.info("Where am I?");
// LogEntry will contain file="main.cpp", line=42, function="main"Returns whether source location capture is enabled.
Adds a sink with the default formatter for that sink type. Template parameters are deduced.
logger.addSink<minta::FileSink>("output.log");
logger.addSink<minta::ConsoleSink>();Adds a sink with a specific formatter type.
logger.addSink<minta::FileSink, minta::JsonFormatter>("output.json");
logger.addSink<minta::FileSink, minta::XmlFormatter>("output.xml");Adds a fully custom sink. Use when you need control over both the sink and formatter.
auto sink = minta::detail::make_unique<MyCustomSink>();
logger.addCustomSink(std::move(sink));Important: All
addSink/addCustomSinkcalls must happen before the first log message. Calling after logging starts throwsstd::logic_error.
Adds a named sink. Use named("name") to create a SinkName.
logger.addSink<minta::ConsoleSink>(minta::named("console"));
logger.addSink<minta::FileSink>(minta::named("app-log"), "app.log");Adds a named sink with a custom formatter.
logger.addSink<minta::FileSink, minta::JsonFormatter>(minta::named("json-out"), "app.json");String overload — equivalent to using named().
logger.addSink<minta::FileSink>("errors", "errors.log");Adds a named custom sink.
auto sink = minta::detail::make_unique<MyCustomSink>();
logger.addCustomSink("my-sink", std::move(sink));Returns a SinkProxy for configuring the named sink. Throws std::invalid_argument if name is not found.
logger.sink("json-out").level(minta::LogLevel::INFO);Duplicate names: Adding a sink with an already-registered name throws
std::invalid_argument.
Auto-naming: Unnamed sinks are auto-named
"sink_0","sink_1", etc., skipping names already taken.
All logging methods use Message Templates syntax. For named placeholders, arguments are passed as alternating name-value pairs. For indexed placeholders ({0}, {1}, ...), values are passed as positional arguments.
Generic log method with explicit level.
logger.log(minta::LogLevel::INFO, "User {name} logged in", "name", "Alice");Convenience methods for each log level.
logger.trace("Entering function {fn}", "fn", "process");
logger.debug("Cache size: {size}", "size", 42);
logger.info("Server started on port {port}", "port", 8080);
logger.warn("Disk usage at {pct}%", "pct", 85);
logger.error("Failed to connect: {reason}", "reason", "timeout");
logger.fatal("Out of memory, shutting down");Argument passing: Placeholders are matched by position for formatting, not by name.
- Named placeholders (
{name}) consume values sequentially in template order, while storing values by name for structured output. - Indexed placeholders (
{0},{1}, ...) resolve a specific value slot directly by index. - Out-of-range indexed placeholders render as empty strings, and named placeholders continue to use the next available mapped value without being shifted by skipped index slots.
// {username} gets "Alice", {action} gets "login"
logger.info("User {username} performed {action}", "username", "Alice", "action", "login");
// Indexed usage (positional values)
logger.info("{1} then {name} then {0}", "A", "B");
// => B then A then AAlignment: Add ,N after the placeholder name for fixed-width output. Positive N = right-align, negative N = left-align. Applied after format specifiers and pipe transforms.
logger.info("{name,20}", "name", "Alice"); // right-align in 20 chars
logger.info("{name,-20}", "name", "Alice"); // left-align in 20 chars
logger.info("{price,12:.2f}", "price", 9.99); // spec + alignment
logger.info("{0,10|upper}", "hello"); // indexed + transform + alignmentSee Message Templates — Alignment & Padding for full details.
No arguments: Templates without placeholders work fine.
logger.info("Server started");Type support: Any type convertible to string via std::to_string or operator<< works:
logger.info("Int: {a}, Double: {b}, Bool: {c}, String: {d}",
"a", 42, "b", 3.14, "c", true, "d", "hello");void logWithSourceLocation(LogLevel level, const char* file, int line, const char* function, const std::string& messageTemplate, const Args&... args)
Log with explicit source location (instead of relying on setCaptureSourceLocation).
void logWithContext(LogLevel level, const char* file, int line, const char* function, const std::string& messageTemplate, const Args&... args)
Log with source location and current context snapshot.
⚠️ Deprecated alias:logWithContext()is a backward-compatibility alias oflogWithSourceLocation(). PreferlogWithSourceLocation().
Attach a caught std::exception to a log entry. See Exception Attachment for the full guide.
void log(LogLevel level, const std::exception& ex, const std::string& messageTemplate, const Args&... args)
Log with an exception and message template:
try {
riskyOperation();
} catch (const std::exception& ex) {
logger.log(minta::LogLevel::ERROR, ex, "Failed for {id}", "id", "req-001");
}Log an exception only — ex.what() is used as the message:
try {
riskyOperation();
} catch (const std::exception& ex) {
logger.log(minta::LogLevel::ERROR, ex);
}Per-level convenience methods. Each has two overloads: exception + template + args, and exception only.
try {
riskyDatabaseQuery();
} catch (const std::exception& ex) {
logger.error(ex, "Operation failed for user {name}", "john");
}void logWithSourceLocationAndException(LogLevel level, const char* file, int line, const char* function, const std::exception& ex, const std::string& messageTemplate, const Args&... args)
Combine explicit source location with exception attachment:
try {
process();
} catch (const std::exception& ex) {
logger.logWithSourceLocationAndException(
minta::LogLevel::ERROR, __FILE__, __LINE__, __func__,
ex, "Processing failed for {id}", "id", "req-001");
}Global filters apply to all sinks. See Filtering for detailed guide.
Sets a global predicate filter. Only entries where the predicate returns true are logged. If the predicate throws, entries are let through (fail-open).
logger.setFilter([](const minta::LogEntry& entry) {
return entry.level >= minta::LogLevel::WARN;
});Type: FilterPredicate = std::function<bool(const LogEntry&)>
Removes the global predicate filter.
Adds a DSL filter rule. Multiple rules are AND-combined. See Filtering — DSL Rules for syntax.
logger.addFilterRule("level >= WARN");
logger.addFilterRule("not message contains 'heartbeat'");Removes all global DSL filter rules.
Removes both the global predicate and all global DSL rules in one call.
Static factories for creating rotation policies. Returns a RollingPolicy builder.
Fluent configuration for cleanup and hybrid rotation.
Construct a rolling file sink. Use with addSink<RollingFileSink>(policy) or addCustomSink().
Set formatter type. Must be called during initialization only, before logging begins.
See Rolling File Sink for full documentation.
Adds compact filter rules using shorthand syntax. Space-separated tokens are AND-combined. Thread-safe. See Compact Filter.
logger.filter("WARN+ ~timeout ctx:env=prod");Per-sink filters only affect the specified sink. Sinks are indexed from 0 in the order they were added. The default console sink (if enabled) is index 0.
Sets the minimum log level for a specific sink. The effective level is the stricter of the global and per-sink levels.
logger.setSinkLevel(0, minta::LogLevel::ERROR); // sink 0: errors onlySets a predicate filter on a specific sink.
logger.setSinkFilter(0, [](const minta::LogEntry& entry) {
return entry.message.find("sensitive") == std::string::npos;
});Removes the predicate filter from a specific sink.
Adds a DSL filter rule to a specific sink.
logger.addSinkFilterRule(1, "context env == 'production'");Removes all DSL rules from a specific sink.
Removes both the predicate and all DSL rules from a specific sink.
Global key-value pairs attached to every log entry. See Context Capture.
Sets a context key-value pair. Thread-safe.
logger.setContext("session_id", "abc123");
logger.setContext("environment", "production");Removes a specific context key. Thread-safe.
logger.clearContext("session_id");Removes all context keys. Thread-safe.
Creates an RAII scoped context. Key-value pairs are injected into all log entries on the current thread for the lifetime of the returned LogScope. See Scoped Context (LogScope).
{
auto scope = logger.scope({{"requestId", "req-001"}, {"userId", "u-42"}});
logger.info("Processing");
// context includes requestId and userId
}
// context removed automaticallyNote: Scoped context is thread-wide, not per-logger. See LogScope for details.
Sets the maximum number of parsed templates to cache. Default: 128. Set to 0 to disable caching.
When the cache is full, new templates are parsed on every call but existing cached entries remain. See Structured Output — Template Cache.
logger.setTemplateCacheSize(256); // increase cache
logger.setTemplateCacheSize(0); // disable cachingSets the global locale for culture-specific formatting. Default: "C". Thread-safe. See Culture-Specific Formatting.
logger.setLocale("de_DE");
logger.info("Price: {amount:n}", "amount", 1234.56);
// Output: Price: 1.234,56If the locale is not available on the system, falls back to "C". Accepts locale names like "en_US", "de_DE", "fr_FR", "ja_JP", etc. The .UTF-8 suffix is tried automatically.
Returns the current global locale name.
Sets a locale override for a specific sink. The sink re-renders messages using this locale instead of the global one. Thread-safe.
logger.setLocale("en_US");
logger.addSink<minta::FileSink>("german.log");
logger.setSinkLocale(1, "de_DE");
logger.info("Total: {val:n}", "val", 1234.56);
// sink 0 (en_US): Total: 1,234.56
// sink 1 (de_DE): Total: 1.234,56Set to "" to revert to the entry's original locale.
Flushes all sinks. Called automatically by the destructor.
Registers an enricher function that runs on every log entry. Enrichers populate LogEntry::customContext with metadata before sinks see the entry.
| Parameter | Type | Default | Description |
|---|---|---|---|
fn |
EnricherFn |
— | Function that modifies a LogEntry's context |
Throws: std::logic_error if called after the first log entry has been emitted.
Enrichers run in registration order. Explicit context (setContext / scoped context) overwrites enricher values on key collisions: enricher < setContext < scoped context.
logger.enrich(minta::Enrichers::threadId());
logger.enrich(minta::Enrichers::processId());
logger.enrich(minta::Enrichers::property("service", "auth-api"));
logger.enrich([](minta::LogEntry& entry) {
entry.customContext["correlationId"] = generateId();
});See Enrichers for the full guide.
All functions return EnricherFn. Namespace: minta::Enrichers.
| Function | Context Key | Value | Evaluation |
|---|---|---|---|
threadId() |
threadId |
std::hash<std::thread::id> as decimal string |
Per entry |
processId() |
processId |
PID from getpid() / GetCurrentProcessId()
|
Cached at registration |
machineName() |
machine |
Hostname from gethostname() / GetComputerNameA()
|
Cached at registration |
environment() |
environment |
$APP_ENV then $ENVIRONMENT fallback |
Cached at registration |
property(key, val) |
key | val | Cached at registration |
fromEnv(envVar, key) |
key | Value of env var envVar | Cached at registration |
caller() |
caller |
Source function name | Per entry (requires setCaptureSourceLocation(true)) |
logger.enrich(minta::Enrichers::threadId());
logger.enrich(minta::Enrichers::machineName());
logger.enrich(minta::Enrichers::property("version", "2.1.0"));
logger.enrich(minta::Enrichers::fromEnv("AWS_REGION", "region"));using EnricherFn = std::function<void(LogEntry&)>;Interface for polymorphic enrichers. Wrap with a shared_ptr capture for use with enrich().
class IEnricher {
public:
virtual ~IEnricher() = default;
virtual void enrich(LogEntry& entry) const = 0;
};auto enricher = std::make_shared<MyEnricher>();
logger.enrich([enricher](minta::LogEntry& entry) {
enricher->enrich(entry);
});Fluent proxy for configuring a named sink. Returned by logger.sink("name").
All methods return SinkProxy& for chaining.
| Method | Description |
|---|---|
level(LogLevel lvl) |
Set the sink's minimum log level |
filterRule(const std::string& dsl) |
Add a DSL filter rule |
filter(const std::string& compactExpr) |
Add compact filter rules (shorthand syntax) |
filter(FilterPredicate pred) |
Set a predicate filter |
locale(const std::string& loc) |
Set a per-sink locale |
formatter(std::unique_ptr<IFormatter> f) |
Set formatter (throws std::logic_error after logging starts) |
outputTemplate(const std::string& templateStr) |
Set custom output format for HumanReadableFormatter only |
only(const std::string& tag) |
Add an only-tag for tag routing |
except(const std::string& tag) |
Add an except-tag for tag routing |
clearFilter() |
Remove the predicate filter |
clearFilterRules() |
Remove all DSL filter rules |
clearFilters() |
Remove predicate + DSL rules (not tag filters) |
clearTagFilters() |
Remove all only/except tag filters |
logger.sink("json-out")
.level(minta::LogLevel::INFO)
.filterRule("not message contains 'heartbeat'")
.outputTemplate("[{timestamp:HH:mm:ss}] [{level:u3}] {threadId,6} {message}")
.only("important")
.locale("de_DE");Fluent builder for constructing a fully-configured LunarLog instance. Obtained via LunarLog::configure(). Non-copyable, move-only.
All methods return LoggerConfiguration& for chaining.
| Method | Description | Default |
|---|---|---|
minLevel(LogLevel level) |
Set minimum log level | INFO |
minLevel(std::shared_ptr<LevelSwitch> sw) |
Set shared observable level (runtime-changeable) | — |
watchConfig(const std::string& path, duration interval) |
Watch JSON config file for runtime reload | — |
captureSourceLocation(bool enable) |
Enable/disable source location capture | false |
rateLimit(size_t maxLogs, std::chrono::milliseconds window) |
Set rate limit | 1000 / 1000ms |
templateCacheSize(size_t size) |
Set template cache size (0 = disable) | 128 |
locale(const std::string& loc) |
Set global locale |
"" (C) |
enrich(EnricherFn fn) |
Register an enricher | — |
filter(const std::string& compact) |
Add compact filter rules | — |
filterRule(const std::string& dsl) |
Add a DSL filter rule | — |
minta::LunarLog::configure()
.minLevel(minta::LogLevel::DEBUG)
.captureSourceLocation(true)
.rateLimit(5000, std::chrono::milliseconds(1000))
.templateCacheSize(256)
.locale("en_US")
.enrich(minta::Enrichers::threadId())
.filter("!~heartbeat")
.filterRule("level >= INFO")Add sinks to the builder. Six overloads cover all combinations.
template<typename SinkType, typename... Args>
LoggerConfiguration& writeTo(Args&&... args);
template<typename SinkType, typename FormatterType, typename... Args>
LoggerConfiguration& writeTo(Args&&... args);Auto-named "sink_0", "sink_1", etc. Args are forwarded to the SinkType constructor.
.writeTo<minta::ConsoleSink>()
.writeTo<minta::FileSink, minta::JsonFormatter>("app.json.log")template<typename SinkType, typename... Args>
LoggerConfiguration& writeTo(const std::string& name, Args&&... args);
template<typename SinkType, typename FormatterType, typename... Args>
LoggerConfiguration& writeTo(const std::string& name, Args&&... args);First argument is the sink name; remaining args go to the constructor.
.writeTo<minta::ConsoleSink>("console")
.writeTo<minta::FileSink>("app-log", "app.log")
.writeTo<minta::FileSink, minta::JsonFormatter>("json-out", "app.json.log")SFINAE: The named overload is disabled when
SinkTypeis constructible from(const std::string&, Args...)to prevent the name from being consumed as a constructor argument.
template<typename SinkType, typename ConfigFn, typename... CtorArgs>
LoggerConfiguration& writeTo(const std::string& name, ConfigFn&& configure, CtorArgs&&... args);
template<typename SinkType, typename FormatterType, typename ConfigFn, typename... CtorArgs>
LoggerConfiguration& writeTo(const std::string& name, ConfigFn&& configure, CtorArgs&&... args);The lambda receives a SinkProxy& for inline sink configuration (level, filters, locale, output template, tag routing).
.writeTo<minta::FileSink>("errors",
[](minta::SinkProxy& s) {
s.level(minta::LogLevel::ERROR)
.only("critical")
.outputTemplate("[{timestamp:HH:mm:ss}] [{level:u3}] {message}{exception}");
}, "errors.log")See SinkProxy for all available configuration methods.
LoggerConfiguration& subLogger(const std::string& name, std::function<void(SubLoggerConfiguration&)> configFn)
Adds a sub-logger with independent filters, enrichers, and sinks. The sub-logger receives all entries that pass the parent's global filter, then applies its own pipeline.
.subLogger("errors", [](SubLoggerConfiguration& sub) {
sub.filter("ERROR+")
.enrich(Enrichers::property("pipeline", "alerts"))
.writeTo<CallbackSink>("alert", alertFn);
})SubLoggerConfiguration supports: minLevel(), filter(), filterRule(), enrich(), writeTo() (named and unnamed variants).
See Sub-Logger for the full guide.
Constructs the configured LunarLog instance and returns it by move. Applies all settings, registers enrichers, adds sinks, and validates the configuration.
| Behavior | Description |
|---|---|
| Single use | Throws std::logic_error if called more than once |
| No sinks | Prints warning to stderr; returns a silent logger that discards all messages |
| Return |
LunarLog by move — the builder is consumed |
auto logger = minta::LunarLog::configure()
.writeTo<minta::ConsoleSink>()
.build();See Fluent Builder for the full guide, examples, and comparison with the imperative API.
RAII scoped context that injects key-value pairs into log entries for the lifetime of the scope. Thread-wide (not per-logger). See Scoped Context (LogScope) for the full guide.
Factory: LogScope LunarLog::scope(std::initializer_list<std::pair<std::string, std::string>> pairs)
{
auto scope = logger.scope({{"requestId", "req-001"}});
scope.add("userId", "u-42");
logger.info("Processing");
}
// context removed| Method | Returns | Description |
|---|---|---|
add(const std::string& key, const std::string& value) |
LogScope& |
Append a pair to the scope (chainable). No-op if moved-from. Last value wins for duplicate keys. |
- Non-copyable, move-only — can be returned from functions
- Thread-wide — all loggers on the same thread see the same scope stack
- Inner shadows outer — duplicate keys in nested scopes: inner wins, restored on exit
- Exception-safe — destructor always cleans up
RAII helper that sets a context key on construction and removes it on destruction.
{
minta::ContextScope scope(logger, "request_id", "req-456");
logger.info("Processing");
// LogEntry::customContext contains {"request_id": "req-456"}
}
// "request_id" is automatically removed hereConstructor: ContextScope(LunarLog& logger, const std::string& key, const std::string& value)
Destructor: Calls logger.clearContext(key).
Warning:
ContextScopeholds a reference to the logger. The logger must outlive the scope.
Base class for all sinks. Extend this to create custom sinks.
| Method | Description |
|---|---|
virtual void write(const LogEntry& entry) |
Override this. Called for each log entry that passes filters. |
void setMinLevel(LogLevel level) |
Set per-sink minimum level |
LogLevel getMinLevel() const |
Get per-sink minimum level |
void setFilter(FilterPredicate filter) |
Set per-sink predicate filter |
void clearFilter() |
Remove predicate filter |
void addFilterRule(const std::string& ruleStr) |
Add DSL rule |
void clearFilterRules() |
Remove all DSL rules |
void clearAllFilters() |
Remove predicate + all DSL rules |
void setLocale(const std::string& locale) |
Set per-sink locale (delegates to formatter) |
std::string getLocale() const |
Get per-sink locale |
bool passesFilter(const LogEntry& entry) const |
Check if entry passes this sink's filters |
void setFormatter(std::unique_ptr<IFormatter> fmt) |
Set the formatter (call from constructor). Protected on ISink (used by sink implementations / friend helpers such as SinkProxy). |
void setTransport(std::unique_ptr<ITransport> transport) |
Set the transport (call from constructor). Protected on ISink (used by sink implementations / friend helpers such as SinkProxy). |
IFormatter* formatter() const |
Access the formatter. Protected on ISink (used by sink implementations / friend helpers such as SinkProxy). |
See Custom Sinks and Formatters for examples.
These methods are available on all sinks (called internally or via SinkProxy):
| Method | Description |
|---|---|
void setSinkName(const std::string& name) |
Set the sink's name (called by LogManager) |
const std::string& getSinkName() const |
Get the sink's name |
void addOnlyTag(const std::string& tag) |
Add a tag to the only-list (allowlist) |
void addExceptTag(const std::string& tag) |
Add a tag to the except-list (blocklist) |
void clearOnlyTags() |
Clear the only-list |
void clearExceptTags() |
Clear the except-list |
void clearTagFilters() |
Clear both only and except lists |
bool shouldAcceptTags(const vector<string>& tags) const |
Check if entry tags pass this sink's tag filters |
std::set<std::string> getOnlyTags() const |
Get current only-tags (copy) |
std::set<std::string> getExceptTags() const |
Get current except-tags (copy) |
Tag routing rules:
- If only-tags are set: accept only entries with at least one matching tag
- If except-tags are set: reject entries with any matching tag
- only() takes precedence over except()
- Untagged entries go to sinks with no only() filter
Base class for all formatters.
| Method | Description |
|---|---|
virtual std::string format(const LogEntry& entry) const |
Override this. Returns the formatted log string. |
void setLocale(const std::string& locale) |
Set formatter locale (thread-safe) |
std::string getLocale() const |
Get formatter locale (thread-safe) |
std::string localizedMessage(const LogEntry& entry) const |
Returns re-rendered message if locale differs from entry. Protected on IFormatter (for formatter subclasses). |
-
HumanReadableFormatter—[timestamp] [LEVEL] message -
JsonFormatter— JSON withlevel,timestamp,message,messageTemplate,templateHash,properties -
XmlFormatter— XML with same fields
Parsed DSL filter rule. Usually created via string parsing.
| Method | Description |
|---|---|
static FilterRule parse(const std::string& rule) |
Parse a DSL rule string. Throws on invalid syntax. |
| Method | Description |
|---|---|
bool evaluate(const LogEntry& entry) const |
Returns true if the entry passes this rule. |
See Filtering — DSL Rules for syntax.
The data structure passed to sinks and formatters for each log event.
| Field | Type | Description |
|---|---|---|
level |
LogLevel |
Log level of this entry |
message |
std::string |
Rendered message (placeholders resolved) |
timestamp |
std::chrono::system_clock::time_point |
When the log was created |
templateStr |
std::string |
Original message template |
templateHash |
uint32_t |
FNV-1a hash of the template (for grouping) |
arguments |
vector<pair<string, string>> |
Name-value pairs of resolved placeholders |
file |
std::string |
Source file (if setCaptureSourceLocation(true)) |
line |
int |
Source line (if captured) |
function |
std::string |
Function name (if captured) |
customContext |
map<string, string> |
Context key-value pairs |
properties |
vector<PlaceholderProperty> |
Structured properties with operator info |
tags |
std::vector<std::string> |
Tags parsed from [bracketed] prefixes in the message template |
locale |
std::string |
Locale used when rendering this entry |
exception |
std::unique_ptr<detail::ExceptionInfo> |
nullptr when no exception attached; otherwise holds type, message, chain. Check with hasException() before accessing. |
| Field | Type | Description |
|---|---|---|
name |
std::string |
Placeholder name (without operator prefix) |
value |
std::string |
Resolved value as string |
op |
char |
Operator: '@' (destructure), '$' (stringify), or 0 (none) |
transforms |
std::vector<std::string> |
Pipe transform names applied to this placeholder (e.g. ["upper", "comma"]) |
Transforms are applied after format specifiers during message rendering. The transforms field records the transform names for downstream consumers (custom formatters, log processors).
for (const auto& prop : entry.properties) {
if (prop.op == '@') { /* destructure */ }
if (prop.op == '$') { /* stringify */ }
if (prop.op == 0) { /* no operator */ }
for (const auto& t : prop.transforms) {
// e.g. "upper", "comma", "truncate:10"
}
}Transforms are applied to placeholder values via the | operator: {name|transform}. See Pipe Transforms for the full guide.
All transform functions live in minta::detail and have the signature std::string(const std::string& value) or std::string(const std::string& value, const std::string& arg).
| Function | Transform | Description |
|---|---|---|
transformUpper(value) |
upper |
ASCII uppercase |
transformLower(value) |
lower |
ASCII lowercase |
transformTrim(value) |
trim |
Strip leading/trailing whitespace |
transformTruncate(value, arg) |
truncate:N |
Limit to N UTF-8 codepoints, append … |
transformPad(value, arg) |
pad:N |
Right-pad with spaces to N codepoints |
transformPadLeft(value, arg) |
padl:N |
Left-pad with spaces to N codepoints |
transformQuote(value) |
quote |
Wrap in double quotes |
| Function | Transform | Description |
|---|---|---|
transformComma(value) |
comma |
Thousands separator |
transformHex(value) |
hex |
Hex with 0x prefix |
transformOct(value) |
oct |
Octal with 0 prefix |
transformBin(value) |
bin |
Binary with 0b prefix |
transformBytes(value) |
bytes |
Human-readable byte size |
transformDuration(value) |
duration |
Human-readable time from ms |
transformPct(value) |
pct |
Percentage (×100) |
| Function | Transform | Description |
|---|---|---|
transformJson(value) |
json |
JSON serialization |
transformType(value) |
type |
Detected type name |
| (metadata only) | expand |
Alias for @ operator |
| (metadata only) | str |
Alias for $ operator |
| Function | Description |
|---|---|
parseTransforms(pipeStr) |
Parse "comma|truncate:10" into vector<Transform>
|
applyTransforms(value, transforms) |
Apply a transform chain to a formatted value |
The Transform struct holds name (string) and arg (string, empty if no argument).
Header: <lunar_log/macros.hpp>
Convenience macros that capture __FILE__, __LINE__, and __func__ at the call site. All macros are guarded by #ifndef LUNAR_LOG_NO_MACROS.
Each calls logWithSourceLocation() after a level check that short-circuits argument evaluation.
| Macro | Signature | Calls |
|---|---|---|
LUNAR_LOG |
LUNAR_LOG(logger, level, ...) |
logWithSourceLocation(level, __FILE__, __LINE__, __func__, ...) |
LUNAR_TRACE |
LUNAR_TRACE(logger, ...) |
LUNAR_LOG(logger, TRACE, ...) |
LUNAR_DEBUG |
LUNAR_DEBUG(logger, ...) |
LUNAR_LOG(logger, DEBUG, ...) |
LUNAR_INFO |
LUNAR_INFO(logger, ...) |
LUNAR_LOG(logger, INFO, ...) |
LUNAR_WARN |
LUNAR_WARN(logger, ...) |
LUNAR_LOG(logger, WARN, ...) |
LUNAR_ERROR |
LUNAR_ERROR(logger, ...) |
LUNAR_LOG(logger, ERROR, ...) |
LUNAR_FATAL |
LUNAR_FATAL(logger, ...) |
LUNAR_LOG(logger, FATAL, ...) |
#include <lunar_log/macros.hpp>
LUNAR_INFO(logger, "User {name} logged in", "name", "alice");Each calls logWithSourceLocationAndException() with an attached std::exception.
| Macro | Signature | Calls |
|---|---|---|
LUNAR_LOG_EX |
LUNAR_LOG_EX(logger, level, ex, ...) |
logWithSourceLocationAndException(level, __FILE__, __LINE__, __func__, ex, ...) |
LUNAR_TRACE_EX |
LUNAR_TRACE_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, TRACE, ex, ...) |
LUNAR_DEBUG_EX |
LUNAR_DEBUG_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, DEBUG, ex, ...) |
LUNAR_INFO_EX |
LUNAR_INFO_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, INFO, ex, ...) |
LUNAR_WARN_EX |
LUNAR_WARN_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, WARN, ex, ...) |
LUNAR_ERROR_EX |
LUNAR_ERROR_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, ERROR, ex, ...) |
LUNAR_FATAL_EX |
LUNAR_FATAL_EX(logger, ex, ...) |
LUNAR_LOG_EX(logger, FATAL, ex, ...) |
try {
connectToDatabase();
} catch (const std::exception& ex) {
LUNAR_ERROR_EX(logger, ex, "Connection failed for {host}", "host", "db-01");
}Define before including the header to suppress all 14 macros:
#define LUNAR_LOG_NO_MACROS
#include <lunar_log/macros.hpp> // no macros definedSee Source Location Macros for the full guide, design notes, and comparison with the manual API.
Header: <lunar_log/global.hpp>
minta::Log is a static facade over a process-wide LunarLog instance.
| Method | Signature | Description |
|---|---|---|
configure |
static GlobalLoggerConfiguration configure() |
Returns builder wrapper. build() sets global instance. |
init |
static void init(LunarLog&& logger) |
Install pre-built logger as global instance. |
shutdown |
static void shutdown() |
Clear global logger instance. |
isInitialized |
static bool isInitialized() |
Returns whether a global logger is configured. |
instance |
static std::shared_ptr<LunarLog> instance() |
Returns shared handle to global logger. Throws if not initialized. |
flush |
static void flush() |
Flush all sinks on global logger. |
minta::Log::configure()
.minLevel(minta::LogLevel::INFO)
.writeTo<minta::ConsoleSink>()
.build();
minta::Log::info("Started");
minta::Log::shutdown();| Method | Signature |
|---|---|
trace |
template<typename... Args> static void trace(const std::string& msg, Args&&... args) |
debug |
template<typename... Args> static void debug(const std::string& msg, Args&&... args) |
info |
template<typename... Args> static void info(const std::string& msg, Args&&... args) |
warn |
template<typename... Args> static void warn(const std::string& msg, Args&&... args) |
error |
template<typename... Args> static void error(const std::string& msg, Args&&... args) |
fatal |
template<typename... Args> static void fatal(const std::string& msg, Args&&... args) |
log |
template<typename... Args> static void log(LogLevel level, const std::string& msg, Args&&... args) |
| Method | Signature |
|---|---|
log |
template<typename... Args> static void log(LogLevel level, const std::exception& ex, const std::string& msg, Args&&... args) |
log |
static void log(LogLevel level, const std::exception& ex) |
trace |
template<typename... Args> static void trace(const std::exception& ex, const std::string& msg, Args&&... args) |
trace |
static void trace(const std::exception& ex) |
debug |
template<typename... Args> static void debug(const std::exception& ex, const std::string& msg, Args&&... args) |
debug |
static void debug(const std::exception& ex) |
info |
template<typename... Args> static void info(const std::exception& ex, const std::string& msg, Args&&... args) |
info |
static void info(const std::exception& ex) |
warn |
template<typename... Args> static void warn(const std::exception& ex, const std::string& msg, Args&&... args) |
warn |
static void warn(const std::exception& ex) |
error |
template<typename... Args> static void error(const std::exception& ex, const std::string& msg, Args&&... args) |
error |
static void error(const std::exception& ex) |
fatal |
template<typename... Args> static void fatal(const std::exception& ex, const std::string& msg, Args&&... args) |
fatal |
static void fatal(const std::exception& ex) |
Builder wrapper returned by Log::configure(). Mirrors LoggerConfiguration (minLevel, captureSourceLocation, rateLimit, templateCacheSize, locale, enrich, filter, filterRule, and all writeTo overloads).
Builds a LunarLog via internal LoggerConfiguration and installs it through Log::init().
Header: <lunar_log/global.hpp>
Defined unless LUNAR_LOG_NO_GLOBAL_MACROS is set.
| Macro | Expands to |
|---|---|
LUNAR_GTRACE(...) |
::minta::Log::trace(__VA_ARGS__) |
LUNAR_GDEBUG(...) |
::minta::Log::debug(__VA_ARGS__) |
LUNAR_GINFO(...) |
::minta::Log::info(__VA_ARGS__) |
LUNAR_GWARN(...) |
::minta::Log::warn(__VA_ARGS__) |
LUNAR_GERROR(...) |
::minta::Log::error(__VA_ARGS__) |
LUNAR_GFATAL(...) |
::minta::Log::fatal(__VA_ARGS__) |
Shared, thread-safe observable log level for runtime changes.
#include <lunar_log.hpp>
auto sw = std::make_shared<minta::LevelSwitch>(minta::LogLevel::INFO);| Method | Description |
|---|---|
LevelSwitch(LogLevel level = LogLevel::INFO) |
Construct with initial level |
LogLevel get() const noexcept |
Get current level (atomic) |
void set(LogLevel level) noexcept |
Set level (atomic, immediate effect) |
Pass to LoggerConfiguration::minLevel(sw) to enable runtime level changes. Can be shared across multiple loggers.
Internal class — not directly constructed. Created via LoggerConfiguration::watchConfig().
Polls a JSON config file at a configurable interval. Supports:
- Global
minLevelchanges - Per-sink level overrides (by sink name)
- Filter rule hot-reload (COW)
- Graceful degradation on malformed config
See Dynamic Configuration wiki page for full details.
enum class LogLevel {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
FATAL = 5
};enum class ConsoleStream {
StdOut,
StdErr
};Selects target stream for ConsoleSink / ColorConsoleSink.
using FilterPredicate = std::function<bool(const LogEntry&)>;Tag type for disambiguating named-sink overloads:
struct SinkName {
std::string value;
explicit SinkName(const std::string& n);
explicit SinkName(const char* n);
};Convenience factory for SinkName:
inline SinkName named(const std::string& name);
inline SinkName named(const char* name);| Function | Description |
|---|---|
const char* getLevelString(LogLevel level) |
Returns "TRACE", "DEBUG", etc. |
Writes to standard console stream. Default formatter: HumanReadableFormatter.
logger.addSink<minta::ConsoleSink>(); // StdOut
logger.addSink<minta::ConsoleSink>(minta::ConsoleStream::StdErr);| Constructor | Description |
|---|---|
explicit ConsoleSink(ConsoleStream stream = ConsoleStream::StdOut) |
Select output stream (StdOut / StdErr) |
Drop-in alternative to ConsoleSink that colorizes the [LEVEL] bracket with ANSI escape codes. Message body is left uncolored. Color is auto-disabled when target stream is not a TTY or NO_COLOR / LUNAR_LOG_NO_COLOR is set.
logger.addSink<minta::ColorConsoleSink>(); // StdOut
logger.addSink<minta::ColorConsoleSink>(minta::ConsoleStream::StdErr);
// Runtime toggle
auto& sink = /* get sink reference */;
sink.setColor(false); // disable color| Constructor / Method | Signature |
|---|---|
| Constructor | explicit ColorConsoleSink(ConsoleStream stream = ConsoleStream::StdOut) |
setColor |
void setColor(bool enabled) |
isColorEnabled |
bool isColorEnabled() const |
colorize |
static std::string colorize(const std::string& text, LogLevel level) |
getColorCode |
static const char* getColorCode(LogLevel level) |
| Level | Color |
|---|---|
| TRACE | dim |
| DEBUG | cyan |
| INFO | green |
| WARN | yellow |
| ERROR | red |
| FATAL | bold red |
See Color Console Sink for full documentation.
Sink that forwards entries to user callbacks.
// Raw LogEntry callback
logger.addSink<minta::CallbackSink>(
minta::CallbackSink::EntryCallback(
[](const minta::LogEntry& e) {
// custom handling
}));
// Formatted string callback (CompactJsonFormatter default)
logger.addSink<minta::CallbackSink>(
minta::CallbackSink::StringCallback(
[](const std::string& line) {
// send line to external system
}));| Type / Method | Signature |
|---|---|
EntryCallback |
using EntryCallback = std::function<void(const LogEntry&)> |
StringCallback |
using StringCallback = std::function<void(const std::string&)> |
| Constructor (entry) | explicit CallbackSink(EntryCallback cb) |
| Constructor (string) | explicit CallbackSink(StringCallback cb, std::unique_ptr<IFormatter> fmt = nullptr) |
write |
void write(const LogEntry& entry) override |
flush |
void flush() override (no-op)
|
String variant uses CompactJsonFormatter when fmt == nullptr.
Writes to a file. Default formatter: HumanReadableFormatter.
logger.addSink<minta::FileSink>("app.log");
logger.addSink<minta::FileSink, minta::JsonFormatter>("app.json");| Constructor Parameter | Type | Description |
|---|---|---|
filename |
std::string |
Path to the log file |
Asynchronous sink decorator that wraps another sink type.
logger.addSink<minta::AsyncSink<minta::FileSink>>("app.log");enum class OverflowPolicy {
Block,
DropOldest,
DropNewest
};| Field | Type | Default | Description |
|---|---|---|---|
queueSize |
size_t |
8192 |
Max queued entries |
overflowPolicy |
OverflowPolicy |
DropNewest |
Overflow behavior |
flushIntervalMs |
size_t |
0 |
Periodic flush interval (0 = disabled) |
| Method | Signature |
|---|---|
| Constructor (default options) | template<typename... Args> explicit AsyncSink(Args&&... args) |
| Constructor (custom options) | template<typename... Args> explicit AsyncSink(AsyncOptions opts, Args&&... args) |
write |
void write(const LogEntry& entry) override |
flush |
void flush() override |
droppedCount |
size_t droppedCount() const |
innerSink |
SinkType* innerSink() |
innerSink (const) |
const SinkType* innerSink() const |
Reusable base sink for batch delivery.
| Field | Type | Default | Description |
|---|---|---|---|
batchSize_ |
size_t |
100 |
Flush threshold |
flushIntervalMs_ |
size_t |
5000 |
Timer flush interval (0 = disabled) |
maxQueueSize_ |
size_t |
10000 |
Max buffered entries before drop |
maxRetries_ |
size_t |
3 |
Retry count when writeBatch() throws |
retryDelayMs_ |
size_t |
100 |
Retry delay in ms |
| Setter | Signature |
|---|---|
setBatchSize |
BatchOptions& setBatchSize(size_t n) |
setFlushIntervalMs |
BatchOptions& setFlushIntervalMs(size_t ms) |
setMaxQueueSize |
BatchOptions& setMaxQueueSize(size_t n) |
setMaxRetries |
BatchOptions& setMaxRetries(size_t n) |
setRetryDelayMs |
BatchOptions& setRetryDelayMs(size_t ms) |
| Method | Signature |
|---|---|
| Constructor | explicit BatchedSink(BatchOptions opts = BatchOptions()) |
| Destructor | virtual ~BatchedSink() noexcept |
stopAndFlush |
void stopAndFlush() noexcept |
write |
void write(const LogEntry& entry) final |
flush |
void flush() override |
options |
const BatchOptions& options() const |
droppedCount |
size_t droppedCount() const |
| Method | Signature |
|---|---|
writeBatch |
virtual void writeBatch(const std::vector<const LogEntry*>& batch) |
onFlush |
virtual void onFlush() |
onBatchError |
virtual void onBatchError(const std::exception& e, size_t retryCount) |
POSIX syslog sink (#ifndef _WIN32).
| Field | Type | Default | Description |
|---|---|---|---|
facility_ |
int |
LOG_USER |
Facility code for openlog()
|
logopt_ |
int |
LOG_PID | LOG_NDELAY |
openlog() options |
includeLevel_ |
bool |
false |
Prefix message with [LEVEL]
|
| Setter | Signature |
|---|---|
setFacility |
SyslogOptions& setFacility(int f) |
setLogopt |
SyslogOptions& setLogopt(int o) |
setIncludeLevel |
SyslogOptions& setIncludeLevel(bool b) |
| Method | Signature |
|---|---|
| Constructor | explicit SyslogSink(const std::string& ident, SyslogOptions opts = SyslogOptions()) |
| Destructor | ~SyslogSink() noexcept |
write |
void write(const LogEntry& entry) override |
toSyslogPriority |
static int toSyslogPriority(LogLevel level) |
Batched HTTP sink based on BatchedSink.
| Field | Type | Default | Description |
|---|---|---|---|
url |
std::string |
required | HTTP/HTTPS endpoint |
contentType |
std::string |
"application/json" |
Content-Type value |
headers |
std::map<std::string,std::string> |
{} |
Additional HTTP headers |
timeoutMs |
size_t |
10000 |
Request timeout |
verifySsl |
bool |
true |
HTTPS certificate verification |
batchSize |
size_t |
50 |
Batch size |
flushIntervalMs |
size_t |
5000 |
Timer flush interval |
maxRetries |
size_t |
3 |
Retry count |
maxQueueSize |
size_t |
10000 |
Max buffered entries |
retryDelayMs |
size_t |
1000 |
Retry delay in ms |
| Setter | Signature |
|---|---|
| Constructor | explicit HttpSinkOptions(const std::string& url_) |
setHeader |
HttpSinkOptions& setHeader(const std::string& key, const std::string& val) |
setContentType |
HttpSinkOptions& setContentType(const std::string& ct) |
setTimeoutMs |
HttpSinkOptions& setTimeoutMs(size_t ms) |
setBatchSize |
HttpSinkOptions& setBatchSize(size_t n) |
setFlushIntervalMs |
HttpSinkOptions& setFlushIntervalMs(size_t ms) |
setMaxRetries |
HttpSinkOptions& setMaxRetries(size_t n) |
setMaxQueueSize |
HttpSinkOptions& setMaxQueueSize(size_t n) |
setVerifySsl |
HttpSinkOptions& setVerifySsl(bool v) |
setRetryDelayMs |
HttpSinkOptions& setRetryDelayMs(size_t ms) |
| Method | Signature |
|---|---|
| Constructor | explicit HttpSink(HttpSinkOptions opts) |
| Destructor | ~HttpSink() noexcept |
(write() / flush() are inherited from BatchedSink.)
Output: [2026-02-17 12:00:00.000] [INFO] User Alice logged in
Includes source location if captured: [2026-02-17 12:00:00.000] [INFO] [main.cpp:42] User Alice logged in
HumanReadableFormatter can render logs using a custom output template with these tokens:
-
{timestamp}/{timestamp:FORMAT} -
{level}/{level:u3}/{level:l} {message}{newline}-
{properties}(comma-separatedkey=valuelist) -
{template}(original message template) -
{source}(file:line function) {threadId}-
{exception}(exception type + message + chain; empty if no exception)
Alignment and formatting are supported:
-
{level,6}(width/right-aligned) -
{level,-6}(width/left-aligned) -
{level,6:u3}(u3spec + width) -
{level:-6:l}(lspec + width)
logger.sink("console").outputTemplate("[{timestamp:yyyy-MM-dd HH:mm:ss}] [{level:u3}] {threadId,6} {message}");For JSON and XML formatters, this call is a no-op.
{
"level": "INFO",
"timestamp": "2026-02-17T12:00:00.000Z",
"messageTemplate": "User {username} logged in",
"templateHash": "a1b2c3d4",
"message": "User Alice logged in",
"properties": {
"username": "Alice"
}
}Includes file, line, function if captured. Includes context if set. Properties with @ operator emit native JSON types.
JSONL output optimized for log pipelines. Short @-prefixed keys, flattened properties, single-line output.
{"@t":"2026-02-18T00:30:05.123Z","@l":"WRN","@mt":"Connection to {host} failed","@i":"a1b2c3d4","host":"db-01"}-
@lomitted for INFO level - Properties at top level (not nested)
- User keys starting with
@escaped to@@ -
includeRenderedMessage(true)adds@mfield
logger.addSink<minta::FileSink, minta::CompactJsonFormatter>("logs/app.jsonl");
// Optional: include rendered message
auto fmt = minta::detail::make_unique<minta::CompactJsonFormatter>();
fmt->includeRenderedMessage(true);See Compact JSON Formatter for full details.
<log level="INFO" timestamp="2026-02-17T12:00:00.000Z">
<messageTemplate>User {username} logged in</messageTemplate>
<templateHash>a1b2c3d4</templateHash>
<message>User Alice logged in</message>
<properties>
<property name="username">Alice</property>
</properties>
</log>Properties with @ get destructure="true" attribute. Properties with $ get stringify="true".