Skip to content

Compact Filter

LunarECL edited this page Feb 17, 2026 · 1 revision

Compact Filter Syntax

Concise, grep-inspired shorthand for defining log filters. Complements the full DSL (addFilterRule) with ergonomic one-liners for common patterns.

Quick Start

// Instead of:
logger.addFilterRule("level >= WARN");
logger.addFilterRule("not message contains 'heartbeat'");

// Write:
logger.filter("WARN+ !~heartbeat");

Syntax Reference

Pattern Meaning Equivalent DSL
LEVEL+ Level >= LEVEL level >= LEVEL
~keyword Message contains keyword message contains 'keyword'
!~keyword NOT contains keyword not message contains 'keyword'
ctx:key Context has key context has 'key'
ctx:key=val Context key equals value context key == 'val'
tpl:pattern Template equals pattern template == 'pattern'
!tpl:pattern Template NOT equals not template == 'pattern'

Rules

  • Space-separated = AND: "WARN+ ~error" means both conditions must pass
  • Level names are case-insensitive: warn+ = WARN+ = Warn+
  • WARNING accepted as alias for WARN: WARNING+ works
  • Keywords are case-sensitive: ~Error~error

Quoting

Keywords or values containing spaces need quoting:

logger.filter("~\"connection reset\"");     // double quotes
logger.filter("ctx:msg=\"hello world\"");   // quoted value

Both single and double quotes work for the outer delimiter, but values cannot contain single quotes (DSL limitation). Use addFilterRule() or setFilter() predicate for values with '.

Global Filters

minta::LunarLog logger(minta::LogLevel::TRACE);

logger.filter("WARN+");                        // only WARN and above
logger.filter("~timeout");                     // must contain "timeout"
logger.filter("ctx:userId");                   // must have userId in context
logger.filter("ctx:env=prod");                 // context env must be "prod"
logger.filter("!~heartbeat");                  // suppress heartbeat messages

// Multiple conditions in one call (AND-combined)
logger.filter("WARN+ ~error ctx:env=prod");

Per-Sink Filters

Works with Named Sinks:

logger.addSink<minta::ConsoleSink>(minta::named("console"));
logger.addSink<minta::FileSink>(minta::named("errors"), "errors.log");
logger.addSink<minta::FileSink>(minta::named("audit"), "audit.log");

logger.sink("errors").filter("ERROR+");
logger.sink("console").filter("!~heartbeat !~health");
logger.sink("audit").filter("ctx:audit=true");

Combining with Full DSL

Compact filters and full DSL rules coexist. All rules are AND-combined:

// Compact for simple rules
logger.filter("WARN+");

// Full DSL for complex rules
logger.addFilterRule("context has 'request_id'");
logger.addFilterRule("not template contains 'internal'");

// Both apply — entry must pass all rules

Error Handling

Invalid compact filter expressions throw std::invalid_argument:

try {
    logger.filter("UNKNOWN+");        // unrecognized token
} catch (const std::invalid_argument& e) {
    // "Unrecognized compact filter token: UNKNOWN+"
}

try {
    logger.filter("~\"unterminated");  // missing closing quote
} catch (const std::invalid_argument& e) {
    // "Unterminated quote in compact filter expression"
}

try {
    logger.filter("~\"it's\"");        // single quote in value
} catch (const std::invalid_argument& e) {
    // suggests using addFilterRule() instead
}

Empty expressions are silently ignored (no rules added):

logger.filter("");   // no-op
logger.filter("  "); // no-op

Thread Safety

  • LunarLog::filter() acquires the global filter mutex — safe to call concurrently with logging
  • SinkProxy::filter() uses atomic batch addition (addFilterRules) — all rules in the expression become visible simultaneously

Clearing Filters

Compact filter rules are stored as regular filter rules. Clear them with the same methods:

logger.clearFilterRules();      // clears all global rules (compact + DSL)
logger.clearAllFilters();       // clears predicate + all rules

// Per-sink
logger.sink("errors").clearFilterRules();
logger.sink("errors").clearFilters();

Limitations

  • No OR logic: space-separated tokens are always AND. For OR, use separate filter() calls or the full DSL
  • No single quotes in values: DSL parser limitation. Use addFilterRule() with predicate for these cases
  • No escape sequences in quoted strings: \" inside quotes is treated literally
  • Context keys with spaces: not supported (DSL limitation)

API Reference

Method Scope Description
logger.filter(expr) Global Parse compact expression, add rules under global mutex
sink.filter(expr) Per-sink Parse compact expression, add rules atomically to sink

See also: Filtering, API Reference

Clone this wiki locally