📝 clarify root logger target behavior

This commit is contained in:
Nanaloveyuki
2026-06-14 08:38:38 +08:00
parent d777496d51
commit 9162b37acd
2 changed files with 24 additions and 0 deletions
+12
View File
@@ -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(...)` 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. - `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 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. - `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()`. - `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. - 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. 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 Need Configurable Overflow And Flush Behavior
When queue semantics matter for service durability and load: When queue semantics matter for service durability and load:
+12
View File
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- This is a public root struct, not a type alias. - 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 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 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. - `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. - 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. 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 Need To Build A Composed Logging Pipeline
When code should start from one root logger and then derive more specific wrapped forms: When code should start from one root logger and then derive more specific wrapped forms: