From 9162b37acd7b7cea0e44229e53638433cb5c3eb8 Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Sun, 14 Jun 2026 08:38:38 +0800 Subject: [PATCH] :memo: clarify root logger target behavior --- docs/api/async-logger.md | 12 ++++++++++++ docs/api/logger.md | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/docs/api/async-logger.md b/docs/api/async-logger.md index 12bbadb..fecc9ae 100644 --- a/docs/api/async-logger.md +++ b/docs/api/async-logger.md @@ -46,6 +46,7 @@ Detailed rules explaining key parameters and behaviors - `async_logger(...)` only builds the logger. Actual background draining is started by `run()`. - `async_logger(...)` returns the full `AsyncLogger[S]` surface directly. It is therefore the underlying constructor used by both application-facing async aliases and the narrower `LibraryAsyncLogger[S]` wrapper line. - The constructed logger starts with `is_closed=false`, `is_running=false`, `has_failed=false`, `last_error=""`, and zeroed pending/dropped counters. +- The constructed logger also keeps the core async target contract unchanged: `log(..., target=...)` can override the target for one call, while fixed-level helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`. - `ApplicationAsyncLogger` and `ApplicationTextAsyncLogger` are only alias names over concrete `AsyncLogger[...]` shapes, so they keep the same lifecycle, queue, failure, and state helpers without adding a wrapper layer. - `LibraryAsyncLogger[S]` wraps an `AsyncLogger[S]` value instead of aliasing it. That library facade preserves queue-backed logging behavior, but it narrows the directly exposed helper surface until callers recover the full logger with `to_async_logger()`. - In non-native targets, the implementation uses compatibility behavior while keeping the same public surface. @@ -76,6 +77,17 @@ In this example, the worker drains queued records in the background and `shutdow And the logging call path stays queue-oriented rather than direct-sink oriented. +#### When Need A One-call Target Override On The Root Async Logger + +When async code should keep one logger value but emit a single record under a different target: +```moonbit +logger.log(@bitlogger.Level::Error, "boom", target="app.async.audit") +``` + +In this example, the emitted record uses `app.async.audit` only for that one call. + +And later `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`. + #### When Need Configurable Overflow And Flush Behavior When queue semantics matter for service durability and load: diff --git a/docs/api/logger.md b/docs/api/logger.md index d7fe768..b91f99d 100644 --- a/docs/api/logger.md +++ b/docs/api/logger.md @@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors - This is a public root struct, not a type alias. - The current fields are `min_level : Level`, `sink : S`, `target : String`, and `timestamp : Bool`. - The sink type parameter is preserved across composition, which is why helpers such as `with_context_fields(...)`, `with_filter(...)`, `with_patch(...)`, and `with_queue(...)` can return more specific logger shapes. +- The root logger also preserves the core target contract used across the sync API surface: `log(..., target=...)` can override the target for one call, while fixed-level helpers such as `trace(...)`, `debug(...)`, `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`. - `Logger::new(...)` constructs this type as the main synchronous entry point. - This root type is also what sits underneath both facade families: `ApplicationLogger` is a direct alias over the configured `Logger[RuntimeSink]` line, while `LibraryLogger[S]` is a narrowing wrapper around a `Logger[S]` value. @@ -53,6 +54,17 @@ let logger : Logger[ConsoleSink] = Logger::new(console_sink(), target="app") In this example, the root logger keeps the concrete console sink type visible for later typed composition. +#### When Need A One-call Target Override On The Root Logger + +When sync code should keep one logger value but emit a single record under a different target: +```moonbit +logger.log(Level::Error, "boom", target="app.audit") +``` + +In this example, the emitted record uses `app.audit` only for that one call. + +And later `trace(...)`, `debug(...)`, `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`. + #### When Need To Build A Composed Logging Pipeline When code should start from one root logger and then derive more specific wrapped forms: