Skip to content

Async-logger-with-filter

Attach a record predicate to an async logger so only matching records are enqueued. This is the main API for async routing-by-predicate without rewriting sink implementations.

Interface

moonbit
pub fn[S] AsyncLogger::with_filter(
  self : AsyncLogger[S],
  predicate : (@bitlogger.Record) -> Bool,
) -> AsyncLogger[S] {}

input

  • self : AsyncLogger[S] - Base async logger to constrain.
  • predicate : (Record) -> Bool - Record predicate that decides whether a record should be enqueued.

output

  • AsyncLogger[S] - A new async logger value that only enqueues matching records.

Explanation

Detailed rules explaining key parameters and behaviors

  • Filtering happens after record construction and patch application but before enqueue.
  • Existing filter logic is preserved and combined with the new predicate using logical and.
  • The returned logger is derived from self; the original async logger value is not mutated.
  • Only the stored filter pipeline changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same AsyncLogger[S] surface.
  • Filtering avoids unnecessary queue pressure for records that should never be delivered.
  • In the current direct async coverage, derived filters can compose target, level, and message predicates together while the original logger still accepts writes according to its previous filter state.

How to Use

Here are some specific examples provided.

When Keep Only One Async Target Namespace

When an async logger should only enqueue records from a specific subsystem:

moonbit
let logger = async_logger(console_sink(), target="service")
  .with_filter(@bitlogger.target_has_prefix("service.api"))

In this example, non-matching records are dropped before they reach the async queue.

And the returned async logger still keeps the same queue-facing API surface as the source logger.

When Combine Several Async Filter Rules

When filtering depends on multiple conditions:

moonbit
let logger = async_logger(console_sink(), target="api")
  .with_filter(fn(rec) {
    rec.level.enabled(@bitlogger.Level::Warn) && rec.message.contains("timeout")
  })

In this example, only warning-or-higher timeout records are enqueued.

Error Case

e.g.:

  • If the predicate always returns false, the logger silently drops every record before enqueue.

  • If the predicate always returns true, the wrapper behaves like a pass-through filter.

Notes

  1. Use this API for selection logic, not record mutation.

  2. Async filtering is especially useful when queue capacity should be reserved for high-value records.

  3. State helpers such as pending_count(), dropped_count(), is_closed(), and has_failed() remain available on the returned logger because the visible async logger surface is preserved.

  4. Use a derived logger value when one branch should enforce extra filter rules and the base async logger should stay unchanged.

Published from the repository docs folder with VitePress.