mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
📝 consolidate logger API and async lifecycle guidance
This commit is contained in:
+8
-5
@@ -3,7 +3,7 @@
|
||||
BitLogger is a structured logging library for MoonBit projects.
|
||||
|
||||
- [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [Chinese README](../README.md)
|
||||
- [Chinese README](https://github.com/Nanaloveyuki/BitLogger/blob/main/README.md)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -30,8 +30,9 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
|
||||
## Support Status
|
||||
|
||||
- Currently verified targets: `native`, `js`, `wasm`, `wasm-gc`
|
||||
- `llvm` is still treated as experimental in the current release context
|
||||
- Current local verification covers `native`, `js`, `wasm`, and `wasm-gc` for the main `src` package and `src-async`
|
||||
- `llvm` is still treated as experimental in the current release context and was not locally re-verified in the current environment
|
||||
- The source packages still declare `wasm` support alongside `native`, `llvm`, `js`, and `wasm-gc`; see [`target-verification.md`](./api/target-verification.md) for the current release-facing verification boundary
|
||||
- File output is a native capability; check `native_files_supported()` in cross-target code
|
||||
- `src-async` is available, while `examples/async_basic` is still shipped as a native entry example
|
||||
|
||||
@@ -56,7 +57,9 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API index](./api/index.md)
|
||||
- [src package README](../src/README.mbt.md)
|
||||
- [API index](./api/index.md): canonical API reference, organized as one public API per file
|
||||
- [src package README](https://github.com/Nanaloveyuki/BitLogger/blob/main/src/README.mbt.md): package-level usage notes and target reminders
|
||||
- [`docs/changes/`](./changes/): versioned release notes and publish-facing change summaries
|
||||
- `docs/dev/`: developer reference material kept in the repository, intentionally excluded from the public static docs site
|
||||
|
||||
Common entry points: `text_console(...)`, `file(...)`, `with_queue(...)`, `build_logger(...)`, `build_async_logger(...)`
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: application-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the runtime-sink async logger surface.
|
||||
update-time: 20260614
|
||||
description: Application-facing alias for the runtime-sink async logger surface, preserving the same async calling semantics as AsyncLogger.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -31,6 +31,15 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same async lifecycle helpers such as `run()`, `shutdown()`, `pending_count()`, and `state()`.
|
||||
- Because it is `AsyncLogger[@bitlogger.RuntimeSink]`, the alias also keeps ordinary async logger composition and target behavior such as `with_target(...)`, `child(...)`, and per-call `log(..., target=...)` overrides.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- Unlike the synchronous application alias, async `with_context_fields(...)` and `bind(...)` preserve the visible `ApplicationAsyncLogger` shape because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- Because this is only an alias, methods that are async on `AsyncLogger[@bitlogger.RuntimeSink]` remain async here as well.
|
||||
- The alias therefore keeps the same runtime-sink lifecycle, queue, failure-state, and runtime-dependent post-close semantics already documented on `AsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
- In the current direct alias coverage, values built through `build_application_async_logger(...)` keep the same serialized state snapshot shape, queue counters, lifecycle flags, failure fields, and runtime-sink helper surface that the underlying runtime-sink async logger exposes directly.
|
||||
- When the value is built through `build_application_async_logger(...)`, the sync-first builder route also stays visible through the alias: any optional `LoggerConfig.queue` was already applied before async wrapping, and `logger.sink.kind` had already selected the concrete `RuntimeSink` variant before the application alias reused that value.
|
||||
- That includes queued runtime-sink behavior and file-backed runtime helpers when the configured sink path supports them.
|
||||
- Those helpers remain directly callable on `ApplicationAsyncLogger` itself; callers do not need an unwrap step to reach queue counters, lifecycle state, or `RuntimeSink` file helpers.
|
||||
- The alias exists to give application boot code a clearer public type name for the standard runtime-sink async logger.
|
||||
- Builders such as `build_application_async_logger(...)` and `parse_and_build_application_async_logger(...)` return this alias.
|
||||
|
||||
@@ -53,22 +62,66 @@ In this example, the application alias keeps the same underlying async logger be
|
||||
|
||||
When top-level boot code or services should expose an application-oriented async logger type:
|
||||
```moonbit
|
||||
fn start_async(logger : ApplicationAsyncLogger) -> Unit {
|
||||
async fn start_async(logger : ApplicationAsyncLogger) -> Unit {
|
||||
logger.run()
|
||||
}
|
||||
```
|
||||
|
||||
In this example, callers see the app-facing alias instead of the more explicit generic async logger spelling.
|
||||
In this example, callers see the app-facing alias instead of the more explicit generic async logger spelling, while `run()` keeps its async calling contract.
|
||||
|
||||
And the inherited async logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Direct Async Runtime Helpers On The Application Alias
|
||||
|
||||
When app code should inspect queue or file-backed runtime state without leaving the alias surface:
|
||||
```moonbit
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, lifecycle and queue helpers are called directly on `ApplicationAsyncLogger`.
|
||||
|
||||
And unlike `LibraryAsyncLogger[@bitlogger.RuntimeSink]`, no `to_async_logger()` unwrap is required first.
|
||||
|
||||
#### When Need A One-call Target Override Without Rebuilding The Alias
|
||||
|
||||
When app-level async code should keep the same alias value but emit one 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 alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Async Application Alias
|
||||
|
||||
When app-level async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `ApplicationAsyncLogger`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- Because this is only an alias, any runtime-sink limitations or target-specific async behavior still apply unchanged.
|
||||
|
||||
- If the value came from `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)`, the alias also does not undo the underlying sync-first sink selection; queued and file-backed runtime behavior still depend on the already-built `RuntimeSink` variant.
|
||||
|
||||
- If code needs a narrower public surface than the full async logger API, `LibraryAsyncLogger` is the better facade.
|
||||
|
||||
- If callers need queued runtime-sink helpers or file-backed runtime helpers, they remain directly available on this alias because no wrapper layer strips them away.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This alias is about naming and public intent, not a different async implementation.
|
||||
|
||||
2. Use `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)` for the usual construction paths.
|
||||
2. Inherited `AsyncLogger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)` for the usual construction paths.
|
||||
|
||||
4. Use `ApplicationTextAsyncLogger` or `build_application_text_async_logger(...)` when application code should keep the narrower text-console sink type instead of the broader runtime-sink alias.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: application-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the configured sync runtime logger surface.
|
||||
description: Application-facing alias for the configured sync runtime logger surface, preserving the full ConfiguredLogger helper set.
|
||||
key-word:
|
||||
- application
|
||||
- facade
|
||||
@@ -31,6 +31,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same logging, queue, and file helper APIs exposed by `ConfiguredLogger`.
|
||||
- Because `ConfiguredLogger` is itself `Logger[RuntimeSink]`, the alias also keeps ordinary logger composition and write behavior such as `with_target(...)`, `child(...)`, and `log(..., target=...)`.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- Like the underlying synchronous logger line, `with_context_fields(...)` and `bind(...)` do not preserve the alias spelling. They return a `Logger[ContextSink[RuntimeSink]]` shape because sync shared-field binding extends the sink pipeline instead of storing extra alias-level context metadata.
|
||||
- Because this is only an alias, the application-facing type does not hide any configured-runtime helpers or broader logger surface.
|
||||
- That means queue, drain, flush, and file runtime helpers remain directly callable on `ApplicationLogger` itself; callers do not need an unwrap step to reach the underlying configured runtime logger behavior.
|
||||
- The alias exists to give application boot code a clearer public entry name.
|
||||
- Builders such as `build_application_logger(...)` and `parse_and_build_application_logger(...)` return this alias.
|
||||
|
||||
@@ -58,6 +63,44 @@ fn start(logger : ApplicationLogger) -> Unit {
|
||||
|
||||
In this example, callers see the app-facing alias instead of the lower-level `ConfiguredLogger` name.
|
||||
|
||||
And the same queue/file/runtime helpers remain directly callable because no narrowing wrapper is added.
|
||||
|
||||
#### When Need Direct Runtime Helpers On The Application Alias
|
||||
|
||||
When app code should inspect queue state or file controls without leaving the alias surface:
|
||||
```moonbit
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.flush())
|
||||
```
|
||||
|
||||
In this example, the runtime helpers are called directly on `ApplicationLogger`.
|
||||
|
||||
And unlike `LibraryLogger[RuntimeSink]`, no `to_logger()` unwrap is required first.
|
||||
|
||||
And the inherited logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need A One-call Target Override Without Rebuilding The Alias
|
||||
|
||||
When app-level sync code should keep the same alias value but emit one 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 `info(...)`, `warn(...)`, or `error(...)` calls still use the alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Sync Application Alias
|
||||
|
||||
When app-level sync code should attach stable metadata to later records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value has the visible type `Logger[ContextSink[RuntimeSink]]` rather than the alias name `ApplicationLogger`.
|
||||
|
||||
And that type-shape change is expected because sync context binding extends the sink pipeline.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -69,4 +112,8 @@ e.g.:
|
||||
|
||||
1. This alias is about naming and public intent, not a different runtime implementation.
|
||||
|
||||
2. Use `build_application_logger(...)` or `parse_and_build_application_logger(...)` for the usual construction paths.
|
||||
2. Inherited `Logger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_logger(...)` or `parse_and_build_application_logger(...)` for the usual construction paths.
|
||||
|
||||
4. Use `LibraryLogger` instead when a library boundary should intentionally hide configured-runtime helper methods behind a narrower facade.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: application-text-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the text-console async logger surface.
|
||||
update-time: 20260614
|
||||
description: Application-facing alias for the text-console async logger surface, preserving the full AsyncLogger helper set for the concrete text sink shape.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -31,6 +31,16 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same async lifecycle helpers as other async logger aliases.
|
||||
- Because it is `AsyncLogger[@bitlogger.FormattedConsoleSink]`, the alias also keeps ordinary async logger composition and target behavior such as `with_target(...)`, `child(...)`, and per-call `log(..., target=...)` overrides.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- Like the broader runtime-sink async alias, `with_context_fields(...)` and `bind(...)` preserve the visible `ApplicationTextAsyncLogger` shape because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- Because this is only an alias, methods that are async on `AsyncLogger[@bitlogger.FormattedConsoleSink]` remain async here as well.
|
||||
- The alias therefore keeps the same text-console-specific builder and lifecycle semantics already documented on `AsyncLogger[@bitlogger.FormattedConsoleSink]`, including the concrete sink shape plus the same close, queue, and failure-state behavior.
|
||||
- The application-facing type does not hide any async state or lifecycle helpers; queue/backlog/failure inspection remains directly available on this alias just as it is on the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- In the current direct alias coverage, values built through `build_application_text_async_logger(...)` keep the same serialized state snapshot shape, formatter behavior, queue counters, lifecycle flags, and failure fields that the underlying text-console async logger exposes directly.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, the direct async counters come from the outer async logger only; any optional sync queue configured on `LoggerConfig.queue` is not carried into this text-specific build path.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, `logger.sink.kind` also does not decide the runtime sink shape. The builder still constructs `FormattedConsoleSink` from `logger.sink.text_formatter`, even if the config said `Console`, `JsonConsole`, or `File`.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, `flush_policy()` still reports the configured async policy, but the text-specific build path keeps the default no-op async flush callback instead of wiring an explicit sink flush step.
|
||||
- The alias exists to give application code a clearer public name when it wants the concrete text-console sink shape explicitly.
|
||||
- `build_application_text_async_logger(...)` returns this alias.
|
||||
|
||||
@@ -53,12 +63,38 @@ In this example, the application alias keeps the same underlying async logger be
|
||||
|
||||
When caller code should know it is working with the text-console variant:
|
||||
```moonbit
|
||||
fn start_text_async(logger : ApplicationTextAsyncLogger) -> Unit {
|
||||
async fn start_text_async(logger : ApplicationTextAsyncLogger) -> Unit {
|
||||
logger.run()
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the app-facing alias communicates the concrete text-console async shape directly.
|
||||
In this example, the app-facing alias communicates the concrete text-console async shape directly, while `run()` keeps its async calling contract.
|
||||
|
||||
And the same pending-count, state, and failure helpers remain directly available because no narrowing wrapper is added.
|
||||
|
||||
And the inherited async logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need A One-call Target Override On The Text-console Alias
|
||||
|
||||
When app-level text-console async code should keep the same alias value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.text.async.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.text.async.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Text-console Async Alias
|
||||
|
||||
When app-level text-console async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `ApplicationTextAsyncLogger`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
### Error Case
|
||||
|
||||
@@ -67,8 +103,18 @@ e.g.:
|
||||
|
||||
- If code does not need the concrete text-console sink shape, `ApplicationAsyncLogger` is the broader runtime-sink async alias.
|
||||
|
||||
- If the value came from `build_application_text_async_logger(...)`, carrying `logger.sink.kind=File` in the config still does not make this alias file-backed; that builder keeps the formatter-driven text-console path.
|
||||
|
||||
- If callers depend on the concrete formatter or direct text-console helper surface, they remain available on this alias because no wrapper layer narrows them away.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This alias is about naming and public intent, not a different async implementation.
|
||||
|
||||
2. Use `build_application_text_async_logger(...)` when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
2. Inherited `AsyncLogger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_text_async_logger(...)` when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
|
||||
4. Use `ApplicationAsyncLogger` when application code should keep the broader runtime-sink shape instead of the text-console-specific one.
|
||||
|
||||
5. Use `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]` instead when a library boundary should intentionally narrow the exposed async surface while still preserving the concrete text sink type.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-flush-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public flush policy alias used by AsyncLoggerConfig and async worker flushing.
|
||||
update-time: 20260614
|
||||
description: Public flush policy alias used by AsyncLoggerConfig, async parser labels, and async worker flushing.
|
||||
key-word:
|
||||
- async
|
||||
- flush
|
||||
@@ -34,6 +34,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `AsyncFlushPolicy::Batch` calls the configured flush function after each processed batch.
|
||||
- `AsyncFlushPolicy::Shutdown` calls the configured flush function once after the worker loop exits.
|
||||
- The current flush policy is also exposed through `AsyncLogger::flush_policy()` and included in `AsyncLoggerState`.
|
||||
- The canonical labels `Never`, `Batch`, and `Shutdown` are also the labels emitted by async config and logger-state serializers, so parsing and diagnostics share one public vocabulary.
|
||||
- Async config parsing accepts the canonical label `Never` and also the compatibility alias `None`, both mapping to the same public enum variant.
|
||||
- `Batch` flushing happens after the worker finishes one drained batch, not after every individual record write.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -62,10 +65,16 @@ In this example, flushing happens when the worker loop exits instead of after ea
|
||||
e.g.:
|
||||
- If async config text uses unsupported flush policy text, async config parsing raises a failure.
|
||||
|
||||
- The parser error path for unsupported flush text is the same `Failure` surface used by the async config utilities.
|
||||
|
||||
- If a sink never needs explicit flushing, `Batch` or `Shutdown` can add unnecessary work without changing output.
|
||||
|
||||
- If the configured flush callback raises, the worker records failure state and stops instead of silently hiding the error.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This policy only affects the async logger path and only matters when the configured sink has a meaningful flush function.
|
||||
|
||||
2. `AsyncFlushPolicy::Never` is the default in `AsyncLoggerConfig::new(...)`.
|
||||
|
||||
3. Serialized config uses the canonical `Never` label even though the parser also accepts `None`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncLoggerBuildConfig into a JSON value for exporting complete async logger build settings.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncLoggerBuildConfig into a JSON value for exporting the full shared async build shape that can later feed either async builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -38,7 +38,13 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The output always includes `logger` and `async_config`.
|
||||
- Logger export is delegated to `@bitlogger.logger_config_to_json(...)`.
|
||||
- Async export is delegated to `async_logger_config_to_json(...)`.
|
||||
- Because both sections are always materialized, parsed defaults that were originally omitted in JSON input become explicit again in the exported build-config shape.
|
||||
- This helper is useful when generated setup should preserve both sink/logger behavior and async runtime behavior together.
|
||||
- The exported `logger` section keeps the full `LoggerConfig` shape, including fields that only matter on the full sync-first builder path such as the optional sync queue layer.
|
||||
- That means the JSON shape is broader than the consumption pattern of `build_async_text_logger(...)`, which only uses selected text-oriented logger fields when building the sink.
|
||||
- In particular, the exported `logger.sink.kind` remains whatever the config currently says, but a later `build_async_text_logger(...)` call still ignores that sink-kind branch and constructs `FormattedConsoleSink` from `logger.sink.text_formatter`.
|
||||
- The same exported object is also the shared handoff shape for the application and library facade routes after parse. `parse_async_logger_build_config_text(...)` can read this JSON back into `AsyncLoggerBuildConfig`, and that parsed value can then flow unchanged into `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, or `build_library_async_text_logger(...)`.
|
||||
- Because of that, the exported structure is descriptive config data rather than a commitment to one public async type. The later builder or facade API still decides whether the runtime-sink line or the text-console line is taken.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,6 +64,12 @@ let payload = async_logger_build_config_to_json(
|
||||
|
||||
In this example, both layers of configuration are exported together.
|
||||
|
||||
And later consumers can still choose whether to rebuild through `build_async_logger(...)` or the narrower `build_async_text_logger(...)` path.
|
||||
|
||||
And that later text-specific builder choice still matters more than the serialized `logger.sink.kind` value, because only the formatter-backed text path is consumed there.
|
||||
|
||||
And the same exported object can just as well be parsed and then routed into the application or library facade builders when the next consumer wants a narrower public async type.
|
||||
|
||||
#### When Need Roundtrip-friendly Build Config Data
|
||||
|
||||
When generated build config should later be parsed again:
|
||||
@@ -74,3 +86,19 @@ e.g.:
|
||||
|
||||
- If callers want direct text output, they should use `stringify_async_logger_build_config(...)` instead.
|
||||
|
||||
- Exporting the full `logger` section does not imply that every async builder will later consume every logger field equally.
|
||||
|
||||
- Exporting `logger.sink.kind="file"` or `"console"` also does not force the later text-specific builder path to branch that way; only `build_async_logger(...)` follows sink kind when constructing the runtime sink.
|
||||
|
||||
- Choosing an application or library facade builder later does not change the meaning of the exported config by itself; those facade APIs inherit the same runtime-sink-versus-text-console split from the direct builder they delegate to.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when tools or tests need a structured JSON object instead of text.
|
||||
|
||||
2. Use `stringify_async_logger_build_config(...)` when the same build shape should be emitted as JSON text directly.
|
||||
|
||||
3. The resulting object round-trips through `parse_async_logger_build_config_text(...)`, even though different async builders later consume different parts of the embedded `LoggerConfig`.
|
||||
|
||||
4. After parsing, that same object can also feed the application or library facade builders; export preserves one shared build-config shape, not a direct-builder-only route.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async build config alias combining the base logger config and async runtime config.
|
||||
update-time: 20260614
|
||||
description: Public async build config alias combining the base logger config and async runtime config for both the general async builder path and the specialized text-console builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-build-config-type
|
||||
|
||||
`AsyncLoggerBuildConfig` is the public config object that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. It is a direct alias to the build-config model used by async builder APIs, parsers, and serializers.
|
||||
`AsyncLoggerBuildConfig` is the public config object that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. It is a direct alias to the build-config model used by async builder APIs, parsers, and serializers, even though the available builders consume different parts of the embedded sync config.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -31,8 +31,15 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a built logger instance.
|
||||
- The current fields are `logger : LoggerConfig` and `async_config : AsyncLoggerConfig`.
|
||||
- The public `src-async` surface forwards this alias directly from `@utils.AsyncLoggerBuildConfig`, so constructor, parser, export, and stringify helpers all operate on one shared underlying build-config model.
|
||||
- `AsyncLoggerBuildConfig::new(...)` constructs this type as the main handoff object for async build flows.
|
||||
- `build_async_logger(...)`, `build_async_text_logger(...)`, `parse_async_logger_build_config_text(...)`, `async_logger_build_config_to_json(...)`, and `stringify_async_logger_build_config(...)` all consume or produce this same public shape.
|
||||
- The same typed object also feeds the application and library facade builders. `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, and `build_library_async_text_logger(...)` all accept this exact config type and then delegate to one of the two direct builder lines.
|
||||
- The parse-and-build facade helpers use the same model as well. `parse_and_build_application_async_logger(...)` and `parse_and_build_library_async_logger(...)` first parse JSON into this build-config shape and then forward into their respective facade builders.
|
||||
- `build_async_logger(...)` consumes the full sync build path by calling `build_logger(config.logger)` first, so `LoggerConfig.sink`, `LoggerConfig.queue`, and the resulting runtime sink behavior all participate before the async layer is added.
|
||||
- `build_async_text_logger(...)` is narrower: it builds a text console sink directly from `config.logger.sink.text_formatter` and the top-level `min_level`, `target`, and `timestamp` fields, without applying `LoggerConfig.queue`.
|
||||
- On that text-specific path, `config.logger.sink.kind` also does not decide the runtime sink shape. `build_async_text_logger(...)` still constructs `FormattedConsoleSink` from `config.logger.sink.text_formatter` even if the config says `Console`, `JsonConsole`, or `File`.
|
||||
- In practice, the builder function you choose is what decides how the embedded `LoggerConfig` is interpreted. The runtime-sink line (`build_async_logger(...)` and the application/library facades layered on it) preserves the full sync build path, while the text-console line (`build_async_text_logger(...)` and the facades layered on it) ignores the optional sync queue and does not branch on `logger.sink.kind`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -50,6 +57,12 @@ let config : AsyncLoggerBuildConfig = AsyncLoggerBuildConfig::new(
|
||||
|
||||
In this example, both layers of logger setup are kept in one typed value.
|
||||
|
||||
And downstream code can still choose between the full sync-first builder path and the narrower text-console builder path.
|
||||
|
||||
And if downstream code chooses `build_async_text_logger(...)`, that builder choice still matters more than `logger.sink.kind` because only the formatter-backed text path is consumed there.
|
||||
|
||||
And the same typed object can be handed unchanged to `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, or `build_library_async_text_logger(...)` when the caller wants a different public facade over the same underlying build decision.
|
||||
|
||||
#### When Need To Export Or Inspect The Full Build Shape
|
||||
|
||||
When application code should inspect the combined async build configuration before constructing the logger:
|
||||
@@ -67,8 +80,20 @@ e.g.:
|
||||
|
||||
- If only async runtime policy is needed and the base sync logger config is irrelevant, this type may be broader than necessary and `AsyncLoggerConfig` is the smaller fit.
|
||||
|
||||
- If callers expect every `LoggerConfig` field to affect every async builder in the same way, that assumption is too broad: the text-specific builder intentionally ignores the optional sync queue layer.
|
||||
|
||||
- In particular, carrying `logger.sink.kind=File` inside this config type does not force the later text-specific builder path to create a file-backed async logger; only `build_async_logger(...)` branches on sink kind.
|
||||
|
||||
- Likewise, choosing an application or library facade builder does not change these config semantics by itself. Those facade APIs inherit the same runtime-sink-versus-text-console split from the direct builder they delegate to.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLoggerBuildConfig::new(...)` when one object should carry both sync and async logger setup.
|
||||
|
||||
2. Use `parse_async_logger_build_config_text(...)` when the same shape should come from JSON text instead of handwritten code.
|
||||
|
||||
3. Pick `build_async_logger(...)` when the full synchronous config path, including `LoggerConfig.queue`, should be preserved before async wrapping.
|
||||
|
||||
4. Pick `build_async_text_logger(...)` when the goal is specifically a concrete text console sink and only the selected text-oriented `LoggerConfig` fields should apply.
|
||||
|
||||
5. Pick the application or library facade builders when the same config should drive one of those narrower public surfaces; the chosen facade changes the exposed type, but the direct builder line underneath still determines whether queue and sink-kind settings are preserved or ignored.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Create the combined sync-and-async build config used by async logger builder APIs.
|
||||
update-time: 20260614
|
||||
description: Create the combined sync-and-async build config used by async logger builder APIs, whether callers later choose the full sync-first builder path or the specialized text-console builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-build-config
|
||||
|
||||
Create an `AsyncLoggerBuildConfig` value that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. This is the constructor used when async builder APIs should receive one typed object carrying both layers of setup.
|
||||
Create an `AsyncLoggerBuildConfig` value that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. This is the constructor used when async builder APIs should receive one typed object carrying both layers of setup, even though different builders later consume different parts of the embedded sync config.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn AsyncLoggerBuildConfig::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `logger : LoggerConfig` - Base synchronous logger config describing the sink, level, target, and related sync logger settings.
|
||||
- `logger : LoggerConfig` - Base synchronous logger config describing the sink, level, target, related sync logger settings, and any optional synchronous queue wrapper.
|
||||
- `async_config : AsyncLoggerConfig` - Async runtime config describing queue, batching, linger, and flush behavior.
|
||||
|
||||
#### output
|
||||
@@ -40,6 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Omitting `logger` uses `default_logger_config()`.
|
||||
- Omitting `async_config` uses `AsyncLoggerConfig::new()`.
|
||||
- The constructor simply packages both config objects into one public build shape.
|
||||
- The constructor does not normalize or reinterpret either embedded config beyond those defaults; any normalization has already happened inside the `LoggerConfig` or `AsyncLoggerConfig` values passed in.
|
||||
- When passed to `build_async_logger(...)`, the `logger` portion is built first through the normal synchronous config path before the outer async queue layer is applied.
|
||||
- When passed to `build_async_text_logger(...)`, the same `logger` portion is consumed more narrowly: `text_formatter`, `min_level`, `target`, and `timestamp` are used directly to build a text console sink, while `LoggerConfig.queue` is not applied.
|
||||
- On that text-specific path, `logger.sink.kind` also does not decide the runtime sink shape. `build_async_text_logger(...)` still constructs `FormattedConsoleSink` from `logger.sink.text_formatter` even if the config says `Console`, `JsonConsole`, or `File`.
|
||||
- This helper is the main code-side counterpart to `parse_async_logger_build_config_text(...)`.
|
||||
|
||||
### How to Use
|
||||
@@ -58,6 +62,10 @@ let config = AsyncLoggerBuildConfig::new(
|
||||
|
||||
In this example, the builder input keeps both configuration layers in one typed value.
|
||||
|
||||
And later code can still decide whether that shared config should flow into the full sync-first builder or the narrower text-console builder.
|
||||
|
||||
And if later code chooses `build_async_text_logger(...)`, that builder choice still matters more than `logger.sink.kind` because only the formatter-backed text path is consumed there.
|
||||
|
||||
#### When Need Defaulted Async Build Settings
|
||||
|
||||
When code only wants the standard combined config shape with few overrides:
|
||||
@@ -74,8 +82,16 @@ e.g.:
|
||||
|
||||
- If callers only need async runtime policy and not the full builder input shape, `AsyncLoggerConfig::new(...)` is the smaller API.
|
||||
|
||||
- If callers expect every field inside `LoggerConfig` to affect every async builder equally, that assumption is too broad: `build_async_text_logger(...)` intentionally skips the optional sync queue layer.
|
||||
|
||||
- In particular, carrying `logger.sink.kind=File` inside this config does not force the later text-specific builder path to create a file-backed async logger; only `build_async_logger(...)` branches on sink kind.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when async builder APIs should receive one combined config object.
|
||||
|
||||
2. Pair it with `build_async_logger(...)`, `build_async_text_logger(...)`, or `parse_async_logger_build_config_text(...)` depending on whether the source is code or JSON text.
|
||||
|
||||
3. Prefer `build_async_logger(...)` after constructing this value when configured sync sink behavior, including `LoggerConfig.queue`, should be preserved before async wrapping.
|
||||
|
||||
4. Prefer `build_async_text_logger(...)` after constructing this value when the goal is specifically config-driven text console output with a concrete `FormattedConsoleSink`.
|
||||
|
||||
@@ -28,16 +28,18 @@ pub fn[S] AsyncLogger::child(self : AsyncLogger[S], target : String) -> AsyncLog
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger whose default target is the composed child path.
|
||||
- `AsyncLogger[S]` - A new async logger value whose default target is the composed child path.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- If the parent target is empty, the child target becomes the full target.
|
||||
- If the child target is empty, the parent target is preserved.
|
||||
- If both are non-empty, they are joined with `.`.
|
||||
- Queue settings, sink wiring, and runtime behavior are preserved in the returned logger.
|
||||
- Only the stored target changes. Queue settings, sink wiring, flush policy, level gating, and lifecycle state remain shared with the same underlying async logger setup.
|
||||
- In the current direct async coverage, `timestamp` is also preserved on the derived child logger while the source logger keeps its previous parent target.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -53,6 +55,8 @@ let worker = logger.child("worker")
|
||||
|
||||
In this example, `worker` emits under `service.worker` while keeping the same async queue behavior.
|
||||
|
||||
And `logger` still keeps its original stored target, because `child(...)` returns a derived async logger value instead of mutating the parent.
|
||||
|
||||
#### When Build Deep Async Scopes Step By Step
|
||||
|
||||
When deeper target composition should remain readable:
|
||||
@@ -76,3 +80,7 @@ e.g.:
|
||||
1. This is the preferred API for hierarchical async logger naming.
|
||||
|
||||
2. Composition changes the target only and does not rebuild the queue or sink.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` still operate on the same async logger state after derivation.
|
||||
|
||||
4. Use `child("")` when code should keep the current target while still following a target-composition code path.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-close
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Close the async logger queue and optionally clear pending records immediately.
|
||||
update-time: 20260614
|
||||
description: Close the async logger queue immediately and optionally convert current pending backlog into dropped records before queue closure.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -38,6 +38,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `clear=false` closes the queue without explicitly abandoning pending records in the helper itself.
|
||||
- `clear=true` counts pending records as dropped and resets `pending_count` to `0` before closing the queue.
|
||||
- This helper does not itself wait for the worker to finish.
|
||||
- `close(...)` also does not change `has_failed()` or `last_error()`; it is a closure primitive, not a failure reset API.
|
||||
- Because this is a low-level close primitive, it does not first run `wait_idle()` or apply the runtime-dependent fallback logic used by `shutdown()`.
|
||||
- After closure, later log attempts do not add new pending or dropped counts, but backend behavior can still differ before the closed queue rejects the record.
|
||||
- In the current direct coverage, compatibility runtimes short-circuit before patch-path work on late log attempts, while native-worker runtimes may still build and patch the record before the closed queue rejects it.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,6 +70,10 @@ In this example, queued backlog is counted as dropped instead of waiting for fur
|
||||
e.g.:
|
||||
- If `clear=true`, pending records are intentionally discarded and contribute to `dropped_count()`.
|
||||
|
||||
- If `clear=false`, pending records may still exist after closure until worker drain or later cleanup resolves them.
|
||||
|
||||
- If callers perform late log attempts after closure, backlog counters still stay unchanged, but patch-path side effects are runtime-dependent and should not be treated as a portable post-close contract.
|
||||
|
||||
- If callers need graceful waiting for drain completion, `shutdown()` is usually the better API.
|
||||
|
||||
### Notes
|
||||
@@ -73,3 +81,5 @@ e.g.:
|
||||
1. This is a low-level lifecycle helper; prefer `shutdown()` for normal graceful teardown.
|
||||
|
||||
2. Use `clear=true` only when backlog loss is an acceptable shutdown tradeoff.
|
||||
|
||||
3. Pair it with `pending_count()`, `dropped_count()`, or `state()` when you need to observe what happened to existing backlog after closure.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncLoggerConfig into a JSON value for export, persistence, or generated async config output.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncLoggerConfig into a JSON value for export, persistence, or generated async config output using the stable serialized policy labels.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -36,6 +36,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The output includes `max_pending`, `max_batch`, `linger_ms`, `overflow`, and `flush`.
|
||||
- Policy fields are serialized using the stable labels accepted by the config parser.
|
||||
- This helper exports effective typed config after constructor normalization has already happened.
|
||||
- If `max_pending` is negative in the config object, the exported JSON preserves that negative value because runtime queue clamping happens later, not during serialization.
|
||||
- The JSON shape matches the `async_config` section used by async build config parsing.
|
||||
|
||||
### How to Use
|
||||
@@ -67,5 +68,13 @@ In this example, the exported JSON stays aligned with parser expectations.
|
||||
e.g.:
|
||||
- If `max_batch` or `linger_ms` were normalized during construction, the exported JSON reflects the normalized values rather than the original invalid inputs.
|
||||
|
||||
- If callers need to understand runtime queue behavior for negative `max_pending`, they should document that separately because serialization preserves the config value rather than the later queue-kind clamp.
|
||||
|
||||
- If callers want direct text output instead of a JSON value, they should use `stringify_async_logger_config(...)` instead.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Serialized policy labels round-trip through `parse_async_logger_config_text(...)`.
|
||||
|
||||
2. The serializer emits canonical labels like `DropNewest` and `Never`, even though the parser also accepts compatibility aliases such as `DropLatest` and `None`.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async logger config alias used for queue, batching, linger, and flush settings.
|
||||
update-time: 20260614
|
||||
description: Public async logger config alias used for async queue interpretation, batching, linger, and flush settings.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -31,8 +31,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a runtime logger handle.
|
||||
- The current fields are `max_pending : Int`, `overflow : AsyncOverflowPolicy`, `max_batch : Int`, `linger_ms : Int`, and `flush : AsyncFlushPolicy`.
|
||||
- `AsyncLoggerConfig::new(...)` constructs this type with the same normalization rules used by the runtime.
|
||||
- The public `src-async` surface forwards this alias directly from `@utils.AsyncLoggerConfig`, so constructor, parser, export, and stringify helpers all operate on one shared underlying config model.
|
||||
- `AsyncLoggerConfig::new(...)` normalizes `max_batch` and `linger_ms`, but it preserves the provided `max_pending` value.
|
||||
- Runtime queue creation later interprets negative `max_pending` as `0` when choosing the internal queue kind.
|
||||
- `parse_async_logger_config_text(...)`, `async_logger_config_to_json(...)`, and `stringify_async_logger_config(...)` all operate on this same public config shape.
|
||||
- The parser accepts the stable serialized labels such as `DropNewest` and `Never`, plus compatibility aliases such as `DropLatest` and `None`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,10 +68,14 @@ In this example, the same public config object supports both inspection and late
|
||||
e.g.:
|
||||
- `AsyncLoggerConfig` itself does not have a runtime failure mode.
|
||||
|
||||
- Constructor normalization still applies when the value is created through `AsyncLoggerConfig::new(...)`, so very small or negative timing and batch inputs may be adjusted before later serialization or use.
|
||||
- Constructor normalization still applies when the value is created through `AsyncLoggerConfig::new(...)`, so very small batch sizes and negative linger values may be adjusted before later serialization or use.
|
||||
|
||||
- Negative `max_pending` is not rewritten inside the config object itself; it is only clamped later when the async queue kind is derived at runtime.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLoggerConfig::new(...)` when you need a value of this type in code.
|
||||
|
||||
2. Use `AsyncLoggerBuildConfig` when the async config should travel together with the base synchronous `LoggerConfig`.
|
||||
|
||||
3. Use `parse_async_logger_config_text(...)` when the same shape should come from JSON text, including accepted aliases like `DropLatest` and `None`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Create the queue, batching, linger, and flush policy config used by async loggers.
|
||||
update-time: 20260614
|
||||
description: Create the async queue, batching, linger, and flush policy config used by async loggers.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -29,7 +29,7 @@ pub fn AsyncLoggerConfig::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `max_pending : Int` - Maximum queued records.
|
||||
- `max_pending : Int` - Requested maximum queued records before overflow policy matters; negative values are preserved in the config object and later treated as `0` by runtime queue creation.
|
||||
- `overflow : AsyncOverflowPolicy` - Queue overflow strategy.
|
||||
- `max_batch : Int` - Maximum records drained per batch.
|
||||
- `linger_ms : Int` - Optional wait window for batch accumulation.
|
||||
@@ -45,6 +45,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `max_batch <= 1` is normalized to `1`.
|
||||
- `linger_ms < 0` is normalized to `0`.
|
||||
- `max_pending` is stored as provided by the constructor.
|
||||
- When the async logger runtime later builds its internal queue, negative `max_pending` is interpreted as `0`.
|
||||
- `overflow` and `flush` define the most important queue/runtime behavior tradeoffs.
|
||||
- This type is used directly by `async_logger(...)` and embedded in `AsyncLoggerBuildConfig`.
|
||||
|
||||
@@ -79,12 +81,14 @@ In this example, the config becomes a stable JSON payload.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `max_batch` is set to `0` or below, runtime config normalizes it to `1`.
|
||||
- If `max_batch` is set to `0` or below, constructor normalization changes it to `1`.
|
||||
|
||||
- If `linger_ms` is negative, it is normalized to `0`.
|
||||
- If `linger_ms` is negative, constructor normalization changes it to `0`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This type controls async runtime behavior, not synchronous queue wrapping.
|
||||
|
||||
2. Prefer explicit values for production services so overflow and flush semantics are visible.
|
||||
|
||||
3. If queue limit semantics for negative values matter, document that behavior explicitly in calling code because the config object preserves the negative value while the runtime queue later clamps it to `0`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-debug
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a debug-level record through the async logger using the built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a debug-level record through the async logger using the built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::debug(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Debug, ...)`.
|
||||
- This helper delegates to `log(Level::Debug, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Debug records are useful for development and targeted diagnostics.
|
||||
- Use this helper when a named debug call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When intermediate async flow details should be visible during debugging:
|
||||
```moonbit
|
||||
await logger.debug("loaded worker config")
|
||||
logger.debug("loaded worker config")
|
||||
```
|
||||
|
||||
In this example, the call site communicates its intended diagnostic level directly.
|
||||
@@ -61,7 +62,7 @@ In this example, the call site communicates its intended diagnostic level direct
|
||||
|
||||
When a debug event should include extra fields:
|
||||
```moonbit
|
||||
await logger.debug(
|
||||
logger.debug(
|
||||
"dispatch start",
|
||||
fields=[@bitlogger.field("job_id", "42")],
|
||||
)
|
||||
@@ -80,4 +81,4 @@ e.g.:
|
||||
|
||||
1. Prefer this helper when the event is semantically debug-level.
|
||||
|
||||
2. Use `log(...)` when the level must be chosen dynamically.
|
||||
2. Use `log(...)` when the level must be chosen dynamically or one call needs a target override.
|
||||
|
||||
@@ -35,6 +35,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The counter increases when overflow policy discards records.
|
||||
- The counter can also increase when `close(clear=true)` or `shutdown(clear=true)` abandons queued records.
|
||||
- It can also increase when shutdown fallback logic converts remaining pending records into dropped records during a clear-close path.
|
||||
- After a worker failure short-circuits `wait_idle()`, that shutdown-fallback increase is runtime-dependent in the current implementation: native-worker shutdown can convert leftover pending records into additional dropped records, while compatibility shutdown can leave that closed-queue remainder visible in `pending_count()` instead.
|
||||
- That also means `dropped_count()` can still stay unchanged even after `is_closed=true` and retained failure state on compatibility-style shutdown paths, because closure there does not necessarily convert the leftover failed backlog into dropped records.
|
||||
- Later log attempts against an already closed queue do not add to this counter by themselves.
|
||||
- This is a cumulative counter for the lifetime of the logger value.
|
||||
- Use this helper when you need a focused loss metric rather than a full `state()` snapshot.
|
||||
|
||||
@@ -70,6 +74,8 @@ e.g.:
|
||||
|
||||
- If callers need to know why records were lost, they must interpret this metric together with overflow policy and shutdown behavior.
|
||||
|
||||
- If `wait_idle()` returned early because the worker failed, `dropped_count()` may still stay unchanged on compatibility shutdown paths even though one leftover closed-queue item remains visible in `pending_count()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This helper reports record loss only; it does not explain the full logger state.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-error
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue an error-level record through the async logger using the highest built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an error-level record through the async logger using the highest built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::error(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Error, ...)`.
|
||||
- The record is still subject to patching, filtering, and overflow policy.
|
||||
- This helper delegates to `log(Level::Error, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Error records represent the highest built-in severity in this logger API.
|
||||
- Use this helper when a named error call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When an operation should emit a high-severity failure event:
|
||||
```moonbit
|
||||
await logger.error("worker execution failed")
|
||||
logger.error("worker execution failed")
|
||||
```
|
||||
|
||||
In this example, failure intent is explicit at the call site.
|
||||
@@ -61,7 +62,7 @@ In this example, failure intent is explicit at the call site.
|
||||
|
||||
When an error event should include diagnostic fields:
|
||||
```moonbit
|
||||
await logger.error(
|
||||
logger.error(
|
||||
"dispatch failed",
|
||||
fields=[@bitlogger.field("job_id", "42")],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.error(
|
||||
|
||||
In this example, the error record carries structured context without falling back to the generic `log(...)` form.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,6 @@ e.g.:
|
||||
|
||||
1. Use this helper for high-severity async application failures.
|
||||
|
||||
2. Emitting an error record is separate from the logger worker itself entering failure state.
|
||||
2. Use `log(...)` instead when an error call needs a one-off target override.
|
||||
|
||||
3. Emitting an error record is separate from the logger worker itself entering failure state.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-flush-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the async logger flush policy currently governing batch and shutdown flushing behavior.
|
||||
update-time: 20260614
|
||||
description: Read the async logger flush policy currently governing batch-end and shutdown-end flushing behavior.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -34,9 +34,11 @@ pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy {
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned value reflects the policy captured when the async logger was created.
|
||||
- `Batch` causes explicit flush calls after worker batch processing.
|
||||
- `Shutdown` causes explicit flush calls at worker shutdown.
|
||||
- `Never` leaves flushing entirely to sink behavior or external control.
|
||||
- `Batch` means the worker invokes the stored async flush callback after each completed drained batch, not after every individual record write.
|
||||
- `Shutdown` means the worker invokes the stored async flush callback once after the worker loop exits.
|
||||
- `Never` leaves that explicit callback path unused and relies entirely on sink behavior or external control.
|
||||
- This helper reports callback timing policy, not a guarantee that a particular sink type has a meaningful built-in flush effect on every constructor path.
|
||||
- In particular, text-specific async builder paths keep the default no-op flush callback even when the visible policy is `Batch` or `Shutdown`, so the reported policy can describe when the callback would run without implying extra sink work actually happens on that path.
|
||||
|
||||
### How to Use
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-has-failed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger worker has encountered a failure during queue drain.
|
||||
update-time: 20260614
|
||||
description: Read whether the async logger worker has recorded a runtime failure after run() startup reset and drain execution.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -34,7 +34,10 @@ pub fn[S] AsyncLogger::has_failed(self : AsyncLogger[S]) -> Bool {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` clears previous failure state at startup.
|
||||
- `run()` also clears the stored `last_error()` string at startup before drain work begins.
|
||||
- If the worker loop raises an error, the logger records that failure and exposes it through this flag.
|
||||
- Once set by a failed run, the flag stays `true` until a later `run()` invocation actually starts and resets it.
|
||||
- Failure-driven shutdown does not clear this flag by itself, so `has_failed()` can remain `true` even after the logger is already closed.
|
||||
- This helper is intentionally compact and should usually be paired with `last_error()` for details.
|
||||
- Failure state is about runtime drain execution, not whether records were dropped due to overflow policy.
|
||||
|
||||
@@ -67,10 +70,19 @@ In this example, the helper exposes a simple pass-fail runtime indicator.
|
||||
e.g.:
|
||||
- If `has_failed()` is `false`, queue pressure or dropped records may still exist for non-failure reasons.
|
||||
|
||||
- If `has_failed()` becomes `true`, `wait_idle()` may stop early while pending records still remain until a later close or clear path handles them.
|
||||
- In the current regression coverage, that later handling can be an explicit `close(clear=true)` that resets pending backlog immediately or a runtime-dependent `shutdown(...)` path that either clears pending into dropped records or leaves the closed-queue remainder visible.
|
||||
|
||||
- If `has_failed()` is still `true` after shutdown, that does not by itself mean cleanup failed; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
|
||||
|
||||
- If `has_failed()` is `true`, callers should inspect `last_error()` or `state()` for more context.
|
||||
|
||||
- `close()` or `shutdown()` do not clear this flag by themselves; only a later `run()` that has already started resets it.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This helper reports worker failure, not general queue stress.
|
||||
|
||||
2. Pair it with `last_error()` when you need actionable detail.
|
||||
|
||||
3. Pair it with `is_running()` or `state()` when you also need to know whether the worker has already exited and whether backlog remains.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-info
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue an info-level record through the async logger using the most common built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an info-level record through the async logger using the most common built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::info(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Info, ...)`.
|
||||
- This helper delegates to `log(Level::Info, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Info is often the default operational logging level for async application events.
|
||||
- Use this helper when explicit info intent is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When async code should report routine progress or lifecycle events:
|
||||
```moonbit
|
||||
await logger.info("worker started")
|
||||
logger.info("worker started")
|
||||
```
|
||||
|
||||
In this example, the event is expressed at the most common operational logging level.
|
||||
@@ -61,7 +62,7 @@ In this example, the event is expressed at the most common operational logging l
|
||||
|
||||
When an info event should include stable structured detail:
|
||||
```moonbit
|
||||
await logger.info(
|
||||
logger.info(
|
||||
"job queued",
|
||||
fields=[@bitlogger.field("queue", "sync")],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.info(
|
||||
|
||||
In this example, the record remains concise while still carrying useful metadata.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,4 @@ e.g.:
|
||||
|
||||
1. This is often the most common convenience method for normal async application events.
|
||||
|
||||
2. Use `log(...)` when the call site needs a dynamic level or target override.
|
||||
2. Use `log(...)` when the call site needs a dynamic level or a one-off target override.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-is-closed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger has been closed and should no longer accept normal new queue traffic.
|
||||
update-time: 20260614
|
||||
description: Read whether the async logger has entered closed lifecycle state and should no longer be treated as a normal active enqueue target.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -36,7 +36,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `close(...)` sets the closed state immediately.
|
||||
- `shutdown(...)` also results in a closed logger by the end of its lifecycle flow.
|
||||
- A closed logger should no longer be treated as a normal active enqueue target.
|
||||
- This helper is only a direct read of the current `is_closed` ref; it does not wait for drain completion or clear any other state.
|
||||
- This helper reflects lifecycle state only and does not indicate whether the worker is still draining.
|
||||
- `is_closed()` becoming `true` does not imply the logger reached a clean success state. Failure flags, retained `last_error()`, and remaining backlog-related counters can still reflect the earlier worker outcome.
|
||||
- After shutdown on a worker-failure path, `is_closed()` can therefore be `true` while backlog cleanup remains runtime-dependent: native-worker runtimes can convert leftover pending records into dropped ones, while compatibility runtimes can still report the remaining queue entries as pending.
|
||||
- Exact post-close logging behavior is runtime-dependent: compatibility runtimes can short-circuit before patch and enqueue work, while native-worker runtimes may still build and patch a record before the closed queue rejects it, so `is_closed()` should be read as a lifecycle signal rather than a full enqueue-policy contract.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -70,8 +74,16 @@ e.g.:
|
||||
|
||||
- If callers need to know whether the worker is still active, they should also inspect `is_running()`.
|
||||
|
||||
- If callers need to know whether closure also prevented later log attempts on the current backend, they must interpret this together with the active runtime behavior rather than this flag alone.
|
||||
|
||||
- Closing a logger does not by itself reset `has_failed()` or `last_error()`.
|
||||
|
||||
- A closed logger can still report leftover `pending_count()` or `dropped_count()` values from the shutdown path, so `is_closed()` alone is not enough to infer whether backlog was fully drained or explicitly abandoned.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Closed state and running state are related but not identical.
|
||||
|
||||
2. Use this helper when lifecycle control matters more than queue counters.
|
||||
|
||||
3. Pair it with `pending_count()`, `is_running()`, or `state()` when you need to understand what closure means for remaining backlog on a live logger instance.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-is-running
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger worker is currently running and draining the queue.
|
||||
update-time: 20260614
|
||||
description: Read whether the async logger worker loop is currently running, regardless of queue backlog or recorded failure state.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -36,7 +36,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `run()` sets the running state while the worker loop is active.
|
||||
- The flag is cleared when the worker exits normally or after failure handling finishes.
|
||||
- A logger may be closed while still running briefly during final drain or shutdown processing.
|
||||
- After a worker failure, the logger can already be `is_running=false` while still retaining `has_failed=true`, the recorded `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
|
||||
- This helper is only a direct read of the current `is_running` ref; it does not wait, synchronize, or infer why the value is what it is.
|
||||
- This helper focuses on worker activity rather than queue size or failure details.
|
||||
- `is_running()` can be `false` even when `pending_count()` is still nonzero, for example if the worker was never started or if it exited after a recorded failure.
|
||||
- A later `run()` attempt can flip `is_running()` back to `true` before that retained failure/backlog state has been fully drained, because the restarted worker clears stale failure state only once the new run has actually started.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,10 +71,18 @@ In this example, callers watch the worker lifecycle directly.
|
||||
e.g.:
|
||||
- If `is_running()` is `false`, pending records may still exist if the worker was never started or has already failed.
|
||||
|
||||
- If `is_running()` is `false`, the logger may also already be closed while still retaining the previous failure record and a runtime-dependent pending-versus-dropped cleanup result from shutdown.
|
||||
|
||||
- If callers need a one-shot lifecycle flow, `shutdown()` is usually better than manual polling.
|
||||
|
||||
- If `is_running()` is `true`, that still does not guarantee healthy drain progress; callers may need `has_failed()` or `state()` for failure context.
|
||||
|
||||
- Under concurrent activity, the returned value may change immediately after it is read.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper for worker activity checks, not as a complete health signal.
|
||||
|
||||
2. Pair it with `has_failed()` or `state()` when diagnosing stalled pipelines.
|
||||
|
||||
3. Pair it with `pending_count()` when you need to distinguish an idle worker from a stopped logger that still has backlog.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-last-error
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the last error recorded by the async logger worker during runtime failure handling.
|
||||
update-time: 20260614
|
||||
description: Read the last error string recorded by the async logger worker after run() resets and runtime failure capture.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -33,10 +33,13 @@ pub fn[S] AsyncLogger::last_error(self : AsyncLogger[S]) -> String {}
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` resets the stored error string when the worker starts.
|
||||
- A later `run()` clears the stored error string only after that run has actually started.
|
||||
- If the worker loop fails, the error text is captured from the raised exception.
|
||||
- An empty string normally means no failure has been recorded.
|
||||
- Once a failure string is recorded, it stays in place until a later `run()` invocation actually starts and clears it.
|
||||
- Failure-driven shutdown does not clear the stored error string by itself, so the same `last_error()` text can remain visible even after the logger is already closed.
|
||||
- This helper reports worker execution errors, not ordinary overflow or backpressure conditions.
|
||||
- That reset happens before the restarted worker resumes drain work.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,10 +70,18 @@ In this example, the helper provides the textual failure detail without building
|
||||
e.g.:
|
||||
- If no runtime failure has occurred, the method returns an empty string.
|
||||
|
||||
- An empty string does not prove the queue is empty or the worker is idle; it only means no failure string is currently recorded.
|
||||
|
||||
- If callers need broader context than just the error text, they should use `state()`.
|
||||
|
||||
- `close()` or `shutdown()` do not clear a previously recorded error string by themselves; the reset happens only after a later `run()` has already started.
|
||||
|
||||
- If the same error string is still present after shutdown, that does not by itself mean cleanup was skipped; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Read this helper together with `has_failed()` when interpreting worker health.
|
||||
|
||||
2. The stored value is a diagnostic string, not a typed error object.
|
||||
|
||||
3. Pair it with `is_running()` or `pending_count()` when you need to know whether failure left the logger with unfinished backlog, because the previous error string can coexist with remaining pending records until later cleanup or restart.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-log
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
update-time: 20260614
|
||||
description: Enqueue a record into the async logger with an explicit level, message, optional fields, and optional target override.
|
||||
key-word:
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-log
|
||||
|
||||
Enqueue a record into the async logger with an explicit level and message. This is the core write API behind all async level-specific convenience methods.
|
||||
Enqueue a record into the async logger with an explicit level and message. This is the core write API behind all async level-specific convenience methods and the only built-in async write API that accepts a per-call target override.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -43,10 +43,14 @@ pub async fn[S] AsyncLogger::log(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The logger checks `is_enabled(level)` before building a record.
|
||||
- Compatibility runtimes that guard closed writes first can return immediately when `is_closed()` is already `true`, before level checks, record construction, patch logic, filter logic, or queue work.
|
||||
- Otherwise the logger checks `is_enabled(level)` before building a record.
|
||||
- If `target` is empty, the logger uses its stored default target. If `target` is non-empty, that value overrides the stored target for this call only.
|
||||
- Context fields, patch logic, and filter logic are applied before enqueue.
|
||||
- If timestamping is enabled, `@env.now()` is captured before the record enters the queue.
|
||||
- Overflow behavior depends on the configured `AsyncOverflowPolicy`.
|
||||
- Closed-on-log behavior is runtime-dependent: compatibility runtimes short-circuit before record-building work, while native-worker runtimes may still build, patch, and filter the record before queue operations treat a closed queue as a non-accepted write.
|
||||
- In the tested blocking-policy path, a late write against an already closed logger does not become a newly accepted pending record and does not add another dropped record; it simply fails to enter the queue after any runtime-specific pre-queue work finishes.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,7 +60,7 @@ Here are some specific examples provided.
|
||||
|
||||
When code should choose level, fields, and target per event:
|
||||
```moonbit
|
||||
await logger.log(
|
||||
logger.log(
|
||||
@bitlogger.Level::Info,
|
||||
"worker started",
|
||||
fields=[@bitlogger.field("job", "sync")],
|
||||
@@ -64,13 +68,22 @@ await logger.log(
|
||||
)
|
||||
```
|
||||
|
||||
In this example, all per-record inputs are supplied explicitly.
|
||||
In this example, the record overrides the logger's stored target only for this call.
|
||||
|
||||
#### When Reuse The Stored Async Target
|
||||
|
||||
When a call should keep the logger's existing target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
```
|
||||
|
||||
In this example, the logger falls back to its stored target because no `target=` override is provided.
|
||||
|
||||
#### When Build Higher-level Async Logging Helpers
|
||||
|
||||
When application code wants a custom wrapper around the base API:
|
||||
```moonbit
|
||||
await logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
```
|
||||
|
||||
In this example, `log(...)` acts as the common primitive under custom wrappers or convenience methods.
|
||||
@@ -82,8 +95,14 @@ e.g.:
|
||||
|
||||
- If the logger is closed or overflow policy rejects the record, enqueue may not proceed as a normal accepted write.
|
||||
|
||||
- A closed queue does not count as a newly accepted pending record.
|
||||
|
||||
- On compatibility runtimes, a late call after close can be skipped before patch or filter logic runs at all.
|
||||
|
||||
- On native-worker runtimes, a late call after close can still execute patch and filter logic before the queue rejects the write.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when the call site needs full control instead of a fixed severity helper.
|
||||
|
||||
2. Prefer `info()`, `warn()`, and the other shortcuts when only the level differs.
|
||||
2. Prefer `info()`, `warn()`, and the other shortcuts when only the level differs and no per-call target override is needed.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-pending-count
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current number of queued records that have not yet been drained by the async logger worker.
|
||||
update-time: 20260614
|
||||
description: Read the current number of queued records that have not yet been drained or cleared from the async logger pipeline.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -35,6 +35,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The count increases when records are accepted into the queue.
|
||||
- The count decreases as the worker drains records.
|
||||
- The count is also reset to `0` when queued records are abandoned through clear-close paths such as `close(clear=true)`.
|
||||
- Log attempts against a closed queue do not create new pending backlog.
|
||||
- This is a point-in-time metric and may change immediately after it is read.
|
||||
- Use this helper when a single backlog number is enough and a full `state()` snapshot is unnecessary.
|
||||
|
||||
@@ -67,6 +69,12 @@ In this example, the queue backlog is checked directly.
|
||||
e.g.:
|
||||
- If the worker is not running, `pending_count()` may stay above `0` until records are drained or cleared.
|
||||
|
||||
- If `wait_idle()` returns early because `has_failed()` became `true`, `pending_count()` may still be above `0` until later cleanup or clear-close handling runs.
|
||||
- In the current tested split, that later cleanup can either force the leftover pending item into `dropped_count()` on native-worker shutdown paths or leave the closed-queue remainder visible in `pending_count()` on compatibility shutdown paths.
|
||||
- That means `pending_count()` can still stay above `0` even after `is_closed=true` on compatibility-style shutdown paths, because closure there does not necessarily convert the leftover failed backlog into dropped records.
|
||||
|
||||
- A later restarted `run()` can still drain that retained backlog after failure-reset startup, so a nonzero pending count after failure is not necessarily a permanent terminal state.
|
||||
|
||||
- If the queue is empty, the method simply returns `0`.
|
||||
|
||||
### Notes
|
||||
@@ -74,3 +82,5 @@ e.g.:
|
||||
1. Use `state()` when you also need dropped counts, failure state, or runtime mode.
|
||||
|
||||
2. This helper is useful for lightweight health checks and tests.
|
||||
|
||||
3. Pair it with `is_running()` or `has_failed()` when backlog alone is not enough to explain whether the worker is actively draining, stopped, or failed.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-run
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Start the async logger worker loop so queued records are drained to the underlying sink.
|
||||
update-time: 20260614
|
||||
description: Start the async logger worker loop so queued records are drained to the underlying sink while lifecycle and failure state are reset and updated around execution.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-run
|
||||
|
||||
Start the async logger worker loop. This is the core runtime API that drains queued records to the underlying sink and updates worker lifecycle state.
|
||||
Start the async logger worker loop. This is the core runtime API that drains queued records to the underlying sink and updates worker lifecycle state around that drain loop.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -33,10 +33,14 @@ pub async fn[S : @bitlogger.Sink] AsyncLogger::run(self : AsyncLogger[S]) -> Uni
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` sets `is_running` to `true` while the worker loop is active.
|
||||
- It clears previous failure state before worker execution begins.
|
||||
- On failure, the logger records `has_failed=true` and stores the error text in `last_error`.
|
||||
- The worker exits when the queue is closed or when a failure aborts processing.
|
||||
- `run()` sets `is_running` to `true` before worker execution begins.
|
||||
- Every invocation clears previous failure state first by setting `has_failed=false` and `last_error()` to an empty string once that `run()` call has actually started executing.
|
||||
- The method then keeps draining records until `queue.get()` stops with `AsyncLoggerClosed` or a worker error escapes.
|
||||
- On a normal queue-close exit, `run()` clears `is_running` and returns normally.
|
||||
- On failure, the logger records `has_failed=true`, stores the error text in `last_error`, clears `is_running`, and then raises the error back out of `run()`.
|
||||
- A worker failure does not guarantee the async backlog was fully drained first. If the failure happens after some records were already written, later queued records can remain pending when `run()` exits.
|
||||
- A later `run()` invocation can resume draining that retained backlog, but the stale failure flag and stale `last_error()` value are only cleared after the new worker call actually begins running.
|
||||
- This helper does not enforce a single-worker guard by itself, so the public contract should be treated as application-controlled worker startup rather than an API that deduplicates repeated `run()` calls.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,12 +72,22 @@ In this example, the application decides when the worker begins instead of hidin
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the worker loop fails, `has_failed()` becomes `true` and `last_error()` stores the error text.
|
||||
- If the worker loop fails, `has_failed()` becomes `true`, `last_error()` stores the error text, and `run()` raises that failure to the caller.
|
||||
|
||||
- After a failed run, `pending_count()` can still be greater than zero until later shutdown or restart logic finishes handling the retained backlog.
|
||||
|
||||
- If `run()` is never started, accepted records may remain queued and not reach the sink.
|
||||
|
||||
- A later `run()` attempt starts from a fresh failure flag and empty `last_error()` string once that retrying worker has actually started, even if an earlier run failed.
|
||||
|
||||
- Starting more than one `run()` task for the same logger is not prevented by this method and can produce application-level worker coordination bugs.
|
||||
|
||||
### Notes
|
||||
|
||||
1. `async_logger(...)` only constructs the logger; `run()` is what activates queue draining.
|
||||
|
||||
2. Pair this API with `shutdown()` for a complete worker lifecycle.
|
||||
|
||||
3. Pair it with `has_failed()`, `last_error()`, or `state()` when tests need to inspect how a worker exit affected logger health.
|
||||
|
||||
4. Start one deliberate worker task per logger unless your own code is intentionally coordinating a different pattern.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-shutdown
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Gracefully stop an async logger by waiting for idle or clearing queued work, then waiting for the worker to finish.
|
||||
update-time: 20260614
|
||||
description: Gracefully stop an async logger by waiting for idle or clearing queued work, with worker-wait behavior depending on the active async runtime.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -35,9 +35,14 @@ pub async fn[S] AsyncLogger::shutdown(self : AsyncLogger[S], clear? : Bool = fal
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `clear=false` first waits for idle, then closes the logger.
|
||||
- If backlog still remains after waiting, shutdown falls back to `close(clear=true)`.
|
||||
- `clear=true` immediately closes and abandons pending records.
|
||||
- The method waits until `is_running()` becomes `false` before returning.
|
||||
- In runtimes where shutdown clearing after idle is enabled, remaining backlog after `wait_idle()` triggers a fallback `close(clear=true)`.
|
||||
- `clear=true` immediately closes and abandons pending records, even if no worker was ever started for that logger.
|
||||
- In runtimes where shutdown waits for workers, the method then waits until `is_running()` becomes `false` before returning.
|
||||
- In the current backend split, native-worker runtimes enable both the post-`wait_idle()` clear fallback and the final wait-for-worker phase, while compatibility runtimes skip both extra steps.
|
||||
- That means a failure-short-circuited `wait_idle()` can still be followed by forced pending-to-dropped cleanup on native-worker runtimes, while compatibility runtimes close without that extra forced clear step.
|
||||
- In the current tested failure path, native-worker shutdown turns the leftover pending item into one more dropped record, while compatibility shutdown leaves that leftover closed-queue count in `pending_count()` instead.
|
||||
- Shutdown itself does not clear retained worker failure state. If a previous `run()` already recorded `has_failed=true` and a non-empty `last_error()`, those diagnostics can remain visible after shutdown completes.
|
||||
- Because `clear=false` delegates to `wait_idle()` first, shutdown can also wait indefinitely when pending records exist but no worker is making progress and no failure flag is raised.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -47,7 +52,7 @@ Here are some specific examples provided.
|
||||
|
||||
When a service should stop logging only after queued records are drained:
|
||||
```moonbit
|
||||
await logger.shutdown()
|
||||
logger.shutdown()
|
||||
```
|
||||
|
||||
In this example, the logger waits for normal drain behavior before final closure.
|
||||
@@ -56,7 +61,7 @@ In this example, the logger waits for normal drain behavior before final closure
|
||||
|
||||
When teardown should prefer speed over preserving backlog:
|
||||
```moonbit
|
||||
await logger.shutdown(clear=true)
|
||||
logger.shutdown(clear=true)
|
||||
```
|
||||
|
||||
In this example, pending work is abandoned intentionally so shutdown can complete sooner.
|
||||
@@ -66,10 +71,27 @@ In this example, pending work is abandoned intentionally so shutdown can complet
|
||||
e.g.:
|
||||
- If `clear=true`, pending records are intentionally dropped rather than drained.
|
||||
|
||||
- If `wait_idle()` returns early because the worker failed, shutdown behavior after that point still depends on the active runtime's fallback and worker-wait rules.
|
||||
|
||||
- After a worker failure, native-worker shutdown may convert the remaining backlog into dropped records, while compatibility shutdown can leave the pending counter reflecting that leftover closed queue state.
|
||||
- In the current direct regression coverage, that split appears as `pending_count() == 0` and `dropped_count()` increasing on native-worker runtimes, versus `pending_count() > 0` and no extra dropped cleanup on compatibility runtimes.
|
||||
|
||||
- Even after shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` from an earlier worker failure.
|
||||
|
||||
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
|
||||
|
||||
- If pending work exists but no worker was started, `shutdown(clear=false)` may never reach its later close step because it is still waiting inside `wait_idle()`.
|
||||
|
||||
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API over raw `close()` in normal application shutdown paths.
|
||||
|
||||
2. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
2. Exact post-close waiting behavior depends on the active async runtime mode.
|
||||
|
||||
3. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
|
||||
4. Pair it with `state()` or focused counters when tests need to assert whether shutdown drained backlog or converted it into dropped records.
|
||||
|
||||
5. Prefer `shutdown(clear=true)` when teardown must not depend on a still-running drain worker.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-new
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Construct an AsyncLoggerState snapshot from explicit runtime, queue, lifecycle, and failure values.
|
||||
update-time: 20260614
|
||||
description: Construct an AsyncLoggerState snapshot from explicit runtime, queue, lifecycle, failure, and flush-policy values without probing a live logger.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -52,7 +52,12 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This constructor simply packages the supplied fields into one public snapshot value.
|
||||
- It does not inspect a live logger instance by itself.
|
||||
- `AsyncLogger::state()` is the higher-level API that reads these values from a concrete logger.
|
||||
- It also does not validate whether the supplied fields represent a combination that could come from one real logger instant.
|
||||
- The supplied `runtime : AsyncRuntimeState` is stored exactly as provided; this constructor does not recompute `mode` or `background_worker` from the active backend.
|
||||
- The constructed value matches the same public shape used by async logger serializers.
|
||||
- Because `AsyncLoggerState` is only a data snapshot type, this constructor is mainly useful for tests, adapters, and synthetic diagnostics rather than ordinary logger inspection.
|
||||
- Serialization helpers accept any `AsyncLoggerState` value, including hand-built ones from this constructor.
|
||||
- That includes combinations such as `has_failed=true` together with non-zero `pending_count` or a retained `last_error`, and even a manually chosen runtime snapshot that does not match the current backend, all of which are valid for diagnostic snapshots and test fixtures.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -101,8 +106,16 @@ e.g.:
|
||||
|
||||
- If callers want a snapshot directly from one live logger instance, `AsyncLogger::state()` is the simpler API.
|
||||
|
||||
- If callers manually combine a runtime snapshot, counters, or flush policy that do not actually belong together, the constructor still accepts that synthetic snapshot.
|
||||
|
||||
- If callers want the current backend-derived runtime pair instead of a synthetic one, they must pass `async_runtime_state()` explicitly or use `AsyncLogger::state()`.
|
||||
|
||||
- This constructor does not apply cleanup semantics such as clearing `last_error` on restart or draining pending records; callers must provide those fields exactly as they want them represented.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when code should construct an `AsyncLoggerState` value explicitly.
|
||||
|
||||
2. Pair it with `async_logger_state_to_json(...)` or `stringify_async_logger_state(...)` when the snapshot should be exported.
|
||||
|
||||
3. Prefer `AsyncLogger::state()` when the goal is to report the actual current state of one live logger instance.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert an AsyncLoggerState snapshot into a JSON value for diagnostics and transport.
|
||||
update-time: 20260614
|
||||
description: Convert an AsyncLoggerState snapshot into a JSON value for diagnostics and transport using the canonical nested runtime shape and flush-policy labels.
|
||||
key-word:
|
||||
- async
|
||||
- state
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-state-to-json
|
||||
|
||||
Convert `AsyncLoggerState` into a `JsonValue`. This helper is the structured export path for async logger runtime snapshots when callers want machine-readable diagnostics instead of a plain string.
|
||||
Convert `AsyncLoggerState` into a `JsonValue`. This helper is the structured export path for async logger runtime snapshots or manually constructed state values when callers want machine-readable diagnostics instead of a plain string.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
|
||||
#### input
|
||||
|
||||
- `state : AsyncLoggerState` - Snapshot produced by `AsyncLogger::state()`.
|
||||
- `state : AsyncLoggerState` - Snapshot produced by `AsyncLogger::state()` or any manually constructed `AsyncLoggerState` value.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -34,9 +34,15 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The JSON includes runtime mode, worker support, queue counters, lifecycle flags, last error, and flush policy.
|
||||
- The top-level fields are `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
|
||||
- The nested `runtime` field reuses `async_runtime_state_to_json(...)`, and `flush_policy` is serialized with the canonical labels `Never`, `Batch`, or `Shutdown`.
|
||||
- The public helper returns the same internal JSON snapshot shape used by `stringify_async_logger_state(...)`, so both export paths stay aligned without duplicate field assembly logic.
|
||||
- This helper is suitable for health endpoints, diagnostics payloads, and custom serialization flows.
|
||||
- It shares the same stable field names used by `stringify_async_logger_state(...)`.
|
||||
- The state must already have been captured before serialization.
|
||||
- The state must already have been captured or constructed before serialization.
|
||||
- Serialization preserves whatever snapshot combination it receives, including failure flags together with remaining backlog counts.
|
||||
- This helper never rereads a logger instance by itself. If callers pass an older or manually constructed `AsyncLoggerState`, the JSON reflects that provided value exactly rather than refreshing fields from live runtime state.
|
||||
- It also does not normalize mixed diagnostic combinations. If the provided snapshot says `is_closed=true` together with retained `has_failed=true`, `last_error`, or non-zero backlog counters, those exact combinations are emitted unchanged.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,9 +74,17 @@ e.g.:
|
||||
|
||||
- If the queue is empty, `pending_count` and `dropped_count` are still serialized normally as numeric values.
|
||||
|
||||
- If `has_failed` is `true`, serialization does not force `pending_count` to `0` or clear `last_error`; it reports the snapshot exactly as provided.
|
||||
|
||||
- If callers need newer logger data, they must capture a fresh `AsyncLogger::state()` first instead of expecting JSON conversion itself to refresh stale fields.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when downstream code wants a JSON value rather than a ready-made string.
|
||||
1. This helper preserves the nested runtime snapshot instead of flattening `mode` and `background_worker` onto the top level.
|
||||
|
||||
2. Pair it with `AsyncLogger::state()` to capture the snapshot first.
|
||||
2. The resulting object matches the compact string form produced by `stringify_async_logger_state(...)` after JSON stringification.
|
||||
|
||||
3. Use this API when downstream code wants a JSON value rather than a ready-made string.
|
||||
|
||||
4. Pair it with `AsyncLogger::state()` when you want current logger data, or with `AsyncLoggerState::new(...)` when tests or adapters are exporting a synthetic snapshot.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async logger state alias used for queue, lifecycle, and runtime diagnostics.
|
||||
update-time: 20260614
|
||||
description: Public async logger state alias used for queue, lifecycle, runtime, and flush-policy diagnostics.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -33,6 +33,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The `runtime` field embeds an `AsyncRuntimeState` snapshot.
|
||||
- The remaining fields capture queue counts, lifecycle status, failure state, last error text, and active flush policy.
|
||||
- `AsyncLogger::state()` returns this type directly, while `async_logger_state_to_json(...)` and `stringify_async_logger_state(...)` export the same data shape for diagnostics.
|
||||
- `AsyncLogger::state()` currently builds this snapshot from `async_runtime_state()` plus the logger's current counters, lifecycle flags, last error, and flush policy.
|
||||
- When the value comes from `AsyncLogger::state()`, the fields are read one by one rather than through a transactional snapshot primitive.
|
||||
- `AsyncLoggerState::new(...)` can also construct this type manually, but manual construction is synthetic snapshot data and does not read one live logger instant by itself.
|
||||
- The type itself does not distinguish live logger snapshots from hand-built ones; callers must track whether a given `AsyncLoggerState` value came from `AsyncLogger::state()` or from manual construction.
|
||||
- When a live snapshot is taken after worker failure, `has_failed=true`, a retained `last_error`, and non-zero `pending_count` may legitimately coexist until later cleanup or restart.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,8 +71,16 @@ e.g.:
|
||||
|
||||
- `last_error` may be an empty string when no failure has occurred, which is normal and not a special error condition by itself.
|
||||
|
||||
- Because this is just a data shape, manual construction can represent combinations that do not come from a live logger at one exact instant.
|
||||
|
||||
- Receiving an `AsyncLoggerState` value alone does not prove it came from one current logger read rather than from a synthetic constructor path.
|
||||
|
||||
- This type does not imply cleanup semantics by itself; values only report the supplied or captured fields and do not drain backlog or clear recorded failure state.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLogger::state()` when you need a value of this type from one logger instance.
|
||||
|
||||
2. Use `AsyncRuntimeState` when only backend-level capability information is needed and logger-instance state is unnecessary.
|
||||
|
||||
3. Use `async_logger_state_to_json(...)` or `stringify_async_logger_state(...)` when this snapshot should leave typed space and become stable diagnostic output.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read a full async logger runtime snapshot including queue counters, lifecycle flags, and runtime mode.
|
||||
update-time: 20260614
|
||||
description: Read a full async logger runtime snapshot including the embedded runtime snapshot, queue counters, lifecycle flags, last error, and flush policy.
|
||||
key-word:
|
||||
- async
|
||||
- state
|
||||
@@ -34,9 +34,16 @@ pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `AsyncLoggerState` includes `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
|
||||
- `state()` returns a point-in-time snapshot rather than a live handle.
|
||||
- `state()` returns a value snapshot rather than a live handle.
|
||||
- This helper is equivalent to `AsyncLoggerState::new(async_runtime_state(), self.pending_count(), self.dropped_count(), self.is_closed(), self.is_running(), self.has_failed(), self.last_error(), self.flush_policy())`.
|
||||
- `async_logger_state_to_json(...)` and `stringify_async_logger_state(...)` convert the snapshot to stable diagnostic output.
|
||||
- `runtime` embeds the result of `async_runtime_state()` so callers do not need to join separate helpers manually.
|
||||
- `runtime` embeds the result of `async_runtime_state()` from the moment `state()` is called, so callers do not need to join separate helpers manually.
|
||||
- The runtime portion is therefore recomputed from the active backend helper on each call, while the remaining fields come from the logger's current counters, flags, and flush policy at that same general moment.
|
||||
- Because the snapshot is assembled field by field when `state()` is called, later logger changes require calling `state()` again rather than reusing an older `AsyncLoggerState` value as if it refreshed itself.
|
||||
- That field-by-field assembly also means this helper is not an atomic freeze across all refs; under concurrent logger activity, neighboring fields can reflect slightly different instants.
|
||||
- After a worker failure, `has_failed=true`, a non-empty `last_error`, and `pending_count>0` can legitimately appear together in one snapshot until later cleanup or a later started `run()` changes them.
|
||||
- After shutdown cleanup on an already failed logger, snapshots can also legitimately show `is_closed=true` together with retained `has_failed=true` and the same `last_error()`, while `pending_count` versus `dropped_count` still reflects the active runtime's cleanup path.
|
||||
- `state()` only reports the current field values; it does not clear failure state, drain backlog, or synchronize pending work by itself.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,6 +73,8 @@ if state.has_failed {
|
||||
|
||||
In this example, the same snapshot object works for conditional diagnostics and serialization.
|
||||
|
||||
And the reported failure fields can still appear together with non-zero backlog when a worker stopped early.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -73,8 +82,22 @@ e.g.:
|
||||
|
||||
- If the queue is empty, `pending_count` is `0`; this is normal and not a special error condition.
|
||||
|
||||
- `flush_policy` reports the logger's configured async flush mode, not whether a flush has already happened.
|
||||
|
||||
- The embedded `runtime` object is also just a snapshot of the current backend helper result at read time; `state()` does not cache it onto the logger for later reuse.
|
||||
|
||||
- If concurrent logger activity is still changing counters or flags while `state()` runs, the returned value is still useful for diagnostics but should not be treated as a transactional snapshot.
|
||||
|
||||
- A snapshot showing `has_failed=true` does not imply `pending_count` is already `0`; remaining queued records may still be visible until later cleanup or restart.
|
||||
|
||||
- A snapshot showing `is_closed=true` also does not imply failure state was cleared; after failure-driven shutdown, `has_failed=true` and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API over manually combining `pending_count()`, `dropped_count()`, and runtime-mode helpers.
|
||||
|
||||
2. Use `pretty=true` when emitting logs for humans and the compact form for machine-oriented payloads.
|
||||
|
||||
3. Use `AsyncLoggerState::new(...)` only when tests or adapters need to construct a manual snapshot instead of reading one directly from a logger instance.
|
||||
|
||||
4. If consumers need stronger cross-field consistency than a diagnostic snapshot, they should not assume `state()` provides an atomic read barrier.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-to-library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Convert a full async logger into the narrower library-facing async facade.
|
||||
update-time: 20260614
|
||||
description: Convert a full async logger into the narrower library-facing async facade without rebuilding or detaching the underlying async state.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -34,8 +34,16 @@ pub fn[S] AsyncLogger::to_library_async_logger(self : AsyncLogger[S]) -> Library
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This conversion does not rebuild the queue, sink, or runtime state.
|
||||
- Target, min level, async config, and flush behavior are preserved.
|
||||
- Target, min level, async config, flush behavior, pending counts, and failure state are preserved because the same underlying async logger value is wrapped.
|
||||
- The original `AsyncLogger[S]` handle remains the same live logger. If caller code keeps that original value, later facade calls and later unwraps still observe the same shared queue, counters, sink helpers, and lifecycle mutations.
|
||||
- The returned facade keeps library-facing async operations including `log(...)`, `run()`, and `shutdown(...)`.
|
||||
- Async inspection helpers and broader composition APIs remain on the underlying `AsyncLogger[S]` and are intentionally hidden until `to_async_logger()` is used again.
|
||||
- If later facade-level `run()` or `shutdown()` calls record worker failure, leave backlog behind, or follow runtime-dependent shutdown cleanup rules, unwrapping later still exposes that same post-call state instead of a translated facade copy.
|
||||
- That includes states where delegated shutdown already finished with `is_closed=true` while retained `has_failed()` and `last_error()` remain on the wrapped logger, together with the same runtime-dependent pending-versus-dropped cleanup outcome.
|
||||
- When `S` itself exposes richer runtime helpers, projecting to the library facade does not strip those capabilities from the wrapped logger; they are still reachable after `to_async_logger()`.
|
||||
- When `S` is `RuntimeSink`, projection also preserves queued runtime state and file-backed runtime helper behavior behind the facade instead of replacing them with a library-specific copy.
|
||||
- Unwrapping later with `to_async_logger()` therefore exposes the same queue counters, failure snapshots, file state, and runtime file controls that the original async logger already carried.
|
||||
- That also means runtime sink mutations still alias in both directions: changing file append mode, auto-flush, rotation, or other sink helper state through a later unwrapped logger changes the same live runtime sink that the original `AsyncLogger[S]` already held.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -51,6 +59,19 @@ let public_logger = logger.to_library_async_logger()
|
||||
|
||||
In this example, `public_logger` keeps the same async behavior but exposes the library-facing facade.
|
||||
|
||||
#### When Need To Narrow Surface Without Resetting Runtime State
|
||||
|
||||
When an already-used async logger should be projected to a library boundary without changing its current state:
|
||||
```moonbit
|
||||
let full = build_async_logger(config)
|
||||
let public_logger = full.to_library_async_logger()
|
||||
ignore(public_logger.is_enabled(@bitlogger.Level::Info))
|
||||
```
|
||||
|
||||
In this example, the projection changes the exposed type only; it does not rebuild queue or lifecycle state.
|
||||
|
||||
If `full` is still kept elsewhere, it continues sharing that same live runtime state with `public_logger`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -58,8 +79,20 @@ e.g.:
|
||||
|
||||
- The conversion does not clear pending items or reset runtime state.
|
||||
|
||||
- If callers later need state helpers such as `pending_count()` or `state()`, they must unwrap again with `to_async_logger()`.
|
||||
|
||||
- Projection does not normalize failure snapshots; a later unwrap can still show combinations such as retained `last_error()` with remaining `pending_count()` when the wrapped async logger really ended up in that state.
|
||||
|
||||
- Projection also does not normalize delegated shutdown results; a later unwrap can still show `is_closed=true` together with retained `has_failed()`, the same `last_error()`, and the same runtime-dependent leftover backlog or dropped-count cleanup that accumulated behind the facade.
|
||||
|
||||
- Projection also does not normalize richer runtime sink state; if the original async logger already carried queued runtime data or file-backed helper state, a later unwrap still exposes that same live state.
|
||||
|
||||
- Projection does not create an isolated wrapper copy. If callers keep the original `AsyncLogger[S]`, then later facade-level writes, shutdown, or sink-helper mutations still affect that original handle too.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this when package boundaries should avoid exposing the full async logger type.
|
||||
|
||||
2. This is a projection API, not a reconfiguration step.
|
||||
|
||||
3. Use `build_library_async_logger(...)` or `build_library_async_text_logger(...)` when construction and narrowing should happen together from config.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-trace
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a trace-level record through the async logger using the lowest built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a trace-level record through the async logger using the lowest built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::trace(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Trace, ...)`.
|
||||
- This helper delegates to `log(Level::Trace, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Trace records are often skipped in production because they are the lowest built-in severity.
|
||||
- Use this helper when explicit trace intent is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When low-level execution flow should be observable during debugging:
|
||||
```moonbit
|
||||
await logger.trace("entered reconciliation step")
|
||||
logger.trace("entered reconciliation step")
|
||||
```
|
||||
|
||||
In this example, the call site makes trace intent explicit.
|
||||
@@ -61,7 +62,7 @@ In this example, the call site makes trace intent explicit.
|
||||
|
||||
When a trace event should carry extra fields:
|
||||
```moonbit
|
||||
await logger.trace(
|
||||
logger.trace(
|
||||
"cache probe",
|
||||
fields=[@bitlogger.field("key", "user:42")],
|
||||
)
|
||||
@@ -80,4 +81,6 @@ e.g.:
|
||||
|
||||
1. Prefer this helper when trace intent is more readable than `log(Level::Trace, ...)`.
|
||||
|
||||
2. Trace-level async logging can increase queue pressure quickly under verbose workloads.
|
||||
2. Use `log(...)` instead when one trace call needs a one-off target override.
|
||||
|
||||
3. Trace-level async logging can increase queue pressure quickly under verbose workloads.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public asynchronous logger root type used for queue-backed sink-preserving logging pipelines.
|
||||
update-time: 20260614
|
||||
description: Public asynchronous logger root type used for queue-backed sink-preserving logging pipelines with explicit lifecycle, failure, and flush-callback state.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -51,8 +51,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a public root struct, not a type alias.
|
||||
- The current fields cover targeting, overflow policy, batching, linger timing, flush policy, the wrapped sink, context/filter/patch behavior, queue state, and worker lifecycle flags.
|
||||
- `flush_sink : (S) -> Int raise` stores the raising flush callback used by batch and shutdown flush policies.
|
||||
- `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, and `last_error` are mutable runtime refs that power the higher-level lifecycle and diagnostics helpers.
|
||||
- The sink type parameter is preserved across async composition, which is why helpers such as `with_target(...)`, `with_context_fields(...)`, `with_filter(...)`, and `with_patch(...)` keep returning `AsyncLogger[S]`.
|
||||
- `async_logger(...)` constructs this type as the main asynchronous entry point.
|
||||
- This root type is also what sits underneath both async facade families: `ApplicationAsyncLogger` is a direct alias over the runtime-sink line, while `LibraryAsyncLogger[S]` is a narrowing wrapper around an `AsyncLogger[S]` value.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -85,8 +88,14 @@ e.g.:
|
||||
|
||||
- Actual enqueue, worker, and flush behavior still depends on the wrapped sink `S`, async runtime support, and the configured queue policy.
|
||||
|
||||
- Post-close logging behavior is not a property of the struct shape alone; it also depends on the active runtime implementation.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `async_logger(...)`, `build_async_logger(...)`, or `build_async_text_logger(...)` when you need a value of this type.
|
||||
|
||||
2. Use `ApplicationAsyncLogger` or `LibraryAsyncLogger[S]` when a more intention-specific async wrapper or alias fits the calling boundary better.
|
||||
2. Use `ApplicationAsyncLogger` when application code wants the same full async lifecycle and state helper surface under an app-facing alias.
|
||||
|
||||
3. Use `LibraryAsyncLogger[S]` when a library boundary should intentionally narrow the public async surface and expose broader lifecycle/state helpers only through `to_async_logger()`.
|
||||
|
||||
4. Prefer the method helpers such as `state()`, `has_failed()`, `last_error()`, `is_running()`, and `shutdown()` instead of reading these public fields conceptually as if they were a stable manual mutation surface.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-wait-idle
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Wait until the async logger backlog drains to zero or a worker failure interrupts normal progress.
|
||||
update-time: 20260614
|
||||
description: Wait until the async logger backlog drains to zero or a worker failure interrupts normal progress, using the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -34,8 +34,12 @@ pub async fn[S] AsyncLogger::wait_idle(self : AsyncLogger[S]) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The helper keeps yielding while `pending_count() > 0`.
|
||||
- If `has_failed()` becomes `true`, waiting stops early instead of looping forever.
|
||||
- If `has_failed()` becomes `true`, waiting stops early instead of continuing to spin.
|
||||
- This API does not close the logger or stop the worker.
|
||||
- A return from `wait_idle()` therefore means either backlog reached `0` or failure interrupted normal drain progress.
|
||||
- `wait_idle()` does not clear retained failure state by itself, so if pending records remain after a worker failure, later `wait_idle()` calls also short-circuit until another path changes that state.
|
||||
- A later `run()` can make `wait_idle()` meaningful again for the retained backlog, but only after that new worker invocation has actually started and reset the stale failure flag.
|
||||
- If no worker is draining the queue and no failure flag is raised, `wait_idle()` can wait indefinitely.
|
||||
- It is narrower than `shutdown()` and is useful when the logger should continue to be used later.
|
||||
|
||||
### How to Use
|
||||
@@ -46,7 +50,7 @@ Here are some specific examples provided.
|
||||
|
||||
When code should wait for queued work to flush before continuing:
|
||||
```moonbit
|
||||
await logger.wait_idle()
|
||||
logger.wait_idle()
|
||||
```
|
||||
|
||||
In this example, the caller waits for backlog drain but leaves the logger usable afterward.
|
||||
@@ -55,7 +59,7 @@ In this example, the caller waits for backlog drain but leaves the logger usable
|
||||
|
||||
When a test wants to ensure earlier async logs were processed:
|
||||
```moonbit
|
||||
await logger.wait_idle()
|
||||
logger.wait_idle()
|
||||
println("phase complete")
|
||||
```
|
||||
|
||||
@@ -66,10 +70,17 @@ In this example, the wait acts as a barrier between test phases.
|
||||
e.g.:
|
||||
- If the worker has failed, `wait_idle()` stops waiting even if pending records remain.
|
||||
|
||||
- If the worker was never started, pending records may not drain and callers should not expect idle progress automatically.
|
||||
- If the worker was never started, or if nothing is making pending records decrease, `wait_idle()` can block indefinitely.
|
||||
|
||||
- If callers need backlog cleanup after a failure-short-circuit, they still need a later `close(clear=true)` or `shutdown(...)` path.
|
||||
- In the current direct coverage, `wait_idle()` can return with `pending_count() > 0` after a worker failure, and the later cleanup path may either clear that backlog explicitly or follow the runtime-dependent shutdown split documented on `shutdown(...)`.
|
||||
|
||||
- If callers retry `wait_idle()` immediately after such a failure without restarting the worker or forcing cleanup first, the call can return again with the same retained pending backlog.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when you want a drain barrier without closing the logger.
|
||||
|
||||
2. Prefer `shutdown()` when lifecycle completion matters more than continued reuse.
|
||||
|
||||
3. Pair it with `pending_count()` or `state()` when you need to distinguish true idleness from an early stop caused by worker failure.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-warn
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a warning-level record through the async logger using the built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a warning-level record through the async logger using the built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::warn(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Warn, ...)`.
|
||||
- This helper delegates to `log(Level::Warn, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Warning records are useful for degraded but non-fatal runtime conditions.
|
||||
- Use this helper when a named warning call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When the system should report a non-fatal problem:
|
||||
```moonbit
|
||||
await logger.warn("retry budget running low")
|
||||
logger.warn("retry budget running low")
|
||||
```
|
||||
|
||||
In this example, the event is surfaced at warning severity without using the generic `log(...)` form.
|
||||
@@ -61,7 +62,7 @@ In this example, the event is surfaced at warning severity without using the gen
|
||||
|
||||
When a warning event should include context:
|
||||
```moonbit
|
||||
await logger.warn(
|
||||
logger.warn(
|
||||
"queue near capacity",
|
||||
fields=[@bitlogger.field("pending", logger.pending_count().to_string())],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.warn(
|
||||
|
||||
In this example, the warning carries structured operational detail.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,4 @@ e.g.:
|
||||
|
||||
1. Use this helper for notable but non-fatal async runtime conditions.
|
||||
|
||||
2. Pair warnings with structured fields when operators need quick context.
|
||||
2. Use `log(...)` instead when one warning call must override the target without deriving a new logger value.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_context_fields(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger carrying the shared field set.
|
||||
- `AsyncLogger[S]` - A new async logger value carrying the shared field set.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -40,7 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Context fields are merged during `log(...)` before enqueue.
|
||||
- When a log call also passes per-record fields, the context fields are placed before those per-call fields.
|
||||
- This API returns a new logger value; it does not mutate the original async logger.
|
||||
- The provided `fields` array replaces the previously stored shared field set on the returned async logger; it does not append onto whatever `context_fields` the source logger already had.
|
||||
- Unlike synchronous `Logger::with_context_fields(...)`, this async variant stores fields directly on `AsyncLogger` instead of changing the visible sink type.
|
||||
- Only the stored `context_fields` value changes. Target, minimum level, timestamp flag, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, the original logger keeps its previous `context_fields`, while the derived logger prepends the stored shared fields ahead of per-call fields exactly once when records are built.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -59,6 +62,8 @@ let logger = async_logger(console_sink(), target="billing")
|
||||
|
||||
In this example, both fields are attached before each record enters the queue.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Build Child Async Loggers For Subsystems
|
||||
|
||||
When a subsystem has both a target and fixed fields:
|
||||
@@ -75,6 +80,8 @@ In this example, target composition and field binding stay separate but work tog
|
||||
e.g.:
|
||||
- If `fields` is empty, the logger remains valid and just adds no extra metadata.
|
||||
|
||||
- If a derived async logger already had shared context fields, calling `with_context_fields(...)` again replaces that stored shared field set on the new derived logger rather than stacking both sets together.
|
||||
|
||||
- If duplicate field keys are provided, all fields are still emitted; conflict handling is left to the consumer side.
|
||||
|
||||
### Notes
|
||||
@@ -82,3 +89,9 @@ e.g.:
|
||||
1. Use this for stable metadata, not highly dynamic event-specific values.
|
||||
|
||||
2. This async variant preserves the visible `AsyncLogger[S]` type while still injecting shared fields.
|
||||
|
||||
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 fresh derived logger when one code path needs shared metadata and another should stay unchanged.
|
||||
|
||||
5. If you need to combine two shared field sets, combine them in the `fields` argument yourself instead of expecting repeated `with_context_fields(...)` calls to accumulate them.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_filter(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that only enqueues matching records.
|
||||
- `AsyncLogger[S]` - A new async logger value that only enqueues matching records.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -39,8 +39,10 @@ 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 original async logger is not mutated.
|
||||
- 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
|
||||
|
||||
@@ -56,6 +58,8 @@ let logger = async_logger(console_sink(), target="service")
|
||||
|
||||
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:
|
||||
@@ -80,3 +84,7 @@ e.g.:
|
||||
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.
|
||||
|
||||
@@ -39,8 +39,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `log(...)` checks `is_enabled(level)` before creating a record or touching the queue.
|
||||
- Lower-severity records below `min_level` are skipped before enqueue.
|
||||
- This API replaces the logger threshold and does not alter queue configuration.
|
||||
- The returned logger keeps the same sink, target, and timestamp settings.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- This API replaces the stored threshold and does not alter queue configuration.
|
||||
- Only `min_level` changes. Sink, target, timestamp flag, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, the derived logger reports the new threshold through `is_enabled(...)`, while the original logger keeps its previous minimum level and still accepts records that remain enabled there.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,6 +58,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, lower-severity records are skipped before queue pressure increases.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Derive A More Verbose Async Branch
|
||||
|
||||
When one branch of code should keep a different threshold:
|
||||
@@ -78,3 +82,7 @@ e.g.:
|
||||
1. This API reduces async queue pressure by dropping disabled levels before enqueue.
|
||||
|
||||
2. Use it before adding more complex async filtering rules.
|
||||
|
||||
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 tighten the threshold and the base async logger should keep its broader level gate.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_patch(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that rewrites each record before filtering and queue insertion.
|
||||
- `AsyncLogger[S]` - A new async logger value that rewrites each record before filtering and queue insertion.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -39,8 +39,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Patch logic runs after record creation and field merging but before filtering and enqueue.
|
||||
- Existing patch logic is preserved and composed so the new patch wraps the current one.
|
||||
- The original async logger is not mutated.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- Only the stored patch pipeline changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- Patching can normalize, redact, or enrich records before they consume queue capacity.
|
||||
- In the current direct async coverage, patched target/message/field changes are visible to later filter logic, and the original async logger still emits unpatched records when used separately.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,6 +60,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, the added fields are part of the record before filtering and queue insertion.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Need Redaction Before Queueing
|
||||
|
||||
When sensitive fields should be removed early:
|
||||
@@ -80,3 +84,7 @@ e.g.:
|
||||
1. Use patches for transformation, not filtering decisions.
|
||||
|
||||
2. Redaction before enqueue helps keep sensitive data out of the queued pipeline.
|
||||
|
||||
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. Derive a patched logger when one path needs rewritten records and another should keep the original async record shape.
|
||||
|
||||
@@ -38,6 +38,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This API replaces the default target instead of composing it.
|
||||
- Per-call `target?` arguments on `log(...)` can still override the default target.
|
||||
- The original logger value is not mutated.
|
||||
- In the current direct async coverage, derived loggers keep existing flags such as `timestamp`, while the original logger still retains its previous target.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -76,3 +77,5 @@ e.g.:
|
||||
1. Use this API for replacement, not parent-child target composition.
|
||||
|
||||
2. It is useful when several subsystems should share one async queue policy.
|
||||
|
||||
3. Use it when you want a derived logger value; the original async logger keeps its earlier default target.
|
||||
|
||||
@@ -37,7 +37,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- When enabled, `log(...)` captures `@env.now()` before placing the record into the queue.
|
||||
- When disabled, emitted records use `0UL` as the timestamp value.
|
||||
- This setting affects later emitted records only.
|
||||
- Queue, batching, and flush behavior are unchanged.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- Only the stored `timestamp` flag changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, a derived timestamp-enabled logger records non-zero timestamps while the original logger continues emitting `0UL` timestamps when it was left disabled.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -53,6 +55,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, each record captures its timestamp before entering the async queue.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Need Deterministic Async Records
|
||||
|
||||
When timestamps should be disabled for tests or reduced output:
|
||||
@@ -75,3 +79,7 @@ e.g.:
|
||||
1. This API controls record creation before enqueue, not formatter display policy.
|
||||
|
||||
2. It is useful for tests, deterministic snapshots, and production timing.
|
||||
|
||||
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 only one branch should capture timestamps and the base logger should remain deterministic.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Create an async logger with bounded queueing, overflow policy, lifecycle helpers, and background run control.
|
||||
update-time: 20260614
|
||||
description: Create an async logger with bounded queueing, overflow policy, lifecycle helpers, background run control, and a raising flush callback.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -23,7 +23,7 @@ pub fn[S] async_logger(
|
||||
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
|
||||
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
|
||||
target~ : String = "",
|
||||
flush~ : (S) -> Int = fn(_) { 0 },
|
||||
flush~ : (S) -> Int raise = fn(_) { 0 },
|
||||
) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn[S] async_logger(
|
||||
- `config : AsyncLoggerConfig` - Queue size, overflow behavior, batching, linger, and flush policy.
|
||||
- `min_level : Level` - Level gate applied before enqueue.
|
||||
- `target : String` - Default target for emitted records.
|
||||
- `flush : (S) -> Int` - Flush callback used by batch/shutdown flush policies.
|
||||
- `flush : (S) -> Int raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -44,10 +44,20 @@ pub fn[S] async_logger(
|
||||
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(...)`.
|
||||
- Unlike synchronous `Logger`, async `with_context_fields(...)` and `bind(...)` preserve the visible `AsyncLogger[S]` type because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- `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.
|
||||
- `src-async` is designed for `native / llvm / js / wasm / wasm-gc`, but current release-facing local verification is stronger for `native / js / wasm / wasm-gc` than for `llvm`.
|
||||
- `llvm` should currently be read as experimental and locally unverified in this environment rather than as a stable checked target.
|
||||
- `flush` is used only when batch or shutdown policy wants explicit flushing.
|
||||
- If the supplied flush callback raises, worker failure state is recorded through `has_failed()` and `last_error()`.
|
||||
- `wait_idle()` is failure-aware rather than a pure backlog-to-zero guarantee. If a worker failure sets `has_failed=true`, waiting stops early and the logger can still report `pending_count() > 0` until later cleanup or restart work happens.
|
||||
- A later `run()` attempt starts by clearing stale failure state back to `has_failed=false` and `last_error=""` before it resumes draining any backlog still left in the queue.
|
||||
- The exact behavior of late log attempts after closure is runtime-dependent, so callers should use lifecycle helpers like `is_closed()` and `shutdown()` instead of assuming identical post-close enqueue semantics everywhere.
|
||||
- Queue overflow behavior depends on `AsyncOverflowPolicy`.
|
||||
|
||||
### How to Use
|
||||
@@ -70,6 +80,28 @@ 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 Shared Context On A Root Async Logger
|
||||
|
||||
When async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `AsyncLogger[S]`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
#### When Need Configurable Overflow And Flush Behavior
|
||||
|
||||
When queue semantics matter for service durability and load:
|
||||
@@ -103,3 +135,7 @@ e.g.:
|
||||
3. Example entrypoint limitations such as `async fn main` support are separate from the library-level portability of this API.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current local verification matrix.
|
||||
|
||||
5. Pair this constructor with `run()` and `shutdown()` when you need the full worker lifecycle rather than just a configured async logger value.
|
||||
|
||||
6. Choose the facade name based on boundary intent: use `AsyncLogger[S]` for the full surface, `ApplicationAsyncLogger` or `ApplicationTextAsyncLogger` for application-facing alias names, and `LibraryAsyncLogger[S]` when a package boundary should intentionally narrow what downstream code can call directly.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-overflow-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public overflow policy alias used by AsyncLoggerConfig and async queue behavior.
|
||||
update-time: 20260614
|
||||
description: Public overflow policy alias used by AsyncLoggerConfig, async parser labels, and runtime queue behavior.
|
||||
key-word:
|
||||
- async
|
||||
- overflow
|
||||
@@ -34,6 +34,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `AsyncOverflowPolicy::DropOldest` lets the underlying async queue discard older pending records when capacity is limited.
|
||||
- `AsyncOverflowPolicy::DropNewest` discards the incoming record instead of blocking.
|
||||
- The same enum is used by `AsyncLoggerConfig::new(...)`, async config parsing, and the queue-kind mapping inside `AsyncLogger`.
|
||||
- The canonical labels `Blocking`, `DropOldest`, and `DropNewest` are also the labels emitted again by async config serializers, so export and parse stay aligned around one public vocabulary.
|
||||
- Async config parsing accepts the canonical label `DropNewest` and also the compatibility alias `DropLatest`, both mapping to the same public enum variant.
|
||||
- When `try_put(...)` reports that a record was not accepted under a drop policy, `dropped_count()` increases for that rejected enqueue.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -62,10 +65,16 @@ In this example, the incoming record is dropped if the queue cannot accept it.
|
||||
e.g.:
|
||||
- If async config text uses unsupported overflow text, async config parsing raises a failure.
|
||||
|
||||
- The parser error path for unsupported overflow text is the same `Failure` surface used by the async config utilities.
|
||||
|
||||
- Under drop policies, sustained overload increases `dropped_count()` instead of guaranteeing delivery.
|
||||
|
||||
- The precise record discarded by the underlying queue behavior depends on the selected policy and queue implementation, so callers should not assume `dropped_count()` alone identifies which message was lost.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This policy applies to `bitlogger_async`, not synchronous `QueuedSink`.
|
||||
|
||||
2. Choose `Blocking` only when producer-side waiting is acceptable for the caller.
|
||||
|
||||
3. Serialized config uses the canonical `DropNewest` label even though the parser also accepts `DropLatest`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-mode-label
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncRuntimeMode into a stable string label for logs, JSON, and diagnostics.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncRuntimeMode into its canonical stable string label for logs, JSON, and diagnostics.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -36,7 +36,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The returned value is intended for diagnostics and stable output, not just debugging prints.
|
||||
- It keeps mode serialization logic in one place.
|
||||
- This helper is used by async runtime JSON helpers.
|
||||
- `async_runtime_state_to_json(...)` serializes the `mode` field through this helper, so runtime snapshots and direct label rendering share the same canonical text.
|
||||
- Labels are more stable for telemetry and docs than ad hoc manual matching at call sites.
|
||||
- The current canonical labels are exactly `native_worker` for `NativeWorker` and `compatibility` for `Compatibility`.
|
||||
- This helper is only a pure enum-to-string mapping. It does not inspect the active backend or verify that the supplied enum still matches the current runtime environment.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,5 +68,13 @@ In this example, code gets a stable string without duplicating enum matching log
|
||||
e.g.:
|
||||
- This API assumes a valid `AsyncRuntimeMode` input and does not expose a normal runtime error path.
|
||||
|
||||
- If callers need the current backend-derived mode rather than a previously stored enum value, they must call `async_runtime_mode()` first and then label that result.
|
||||
|
||||
- If callers need the whole runtime object rather than a string label, use `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when mode values should be rendered as stable text instead of manual enum matching.
|
||||
|
||||
2. `async_runtime_state_to_json(...)` uses these exact labels when serializing runtime snapshots.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-mode
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current async runtime mode and distinguish native worker behavior from compatibility behavior.
|
||||
update-time: 20260614
|
||||
description: Read the current async runtime mode and distinguish native-worker behavior from compatibility behavior using the same mode contract exposed through async runtime snapshots.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -37,6 +37,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `NativeWorker` is the expected mode on native-style backends, while `Compatibility` is the expected mode on targets without native background-worker semantics.
|
||||
- `async_runtime_mode_label(...)` converts the enum into a stable string value.
|
||||
- `async_runtime_supports_background_worker()` is a narrower boolean probe built on the same idea.
|
||||
- `async_runtime_state()` packages this mode together with the current background-worker capability into one `AsyncRuntimeState` snapshot.
|
||||
- In the current backend implementations, `NativeWorker` pairs with `background_worker=true` and `Compatibility` pairs with `background_worker=false`.
|
||||
- Concretely, the native runtime entrypoint returns `native_worker_async_runtime_mode()`, while the compatibility stub returns `compatibility_async_runtime_mode()`.
|
||||
- The enum is therefore selected directly by the active backend helper on each call rather than read back out of a cached runtime snapshot object.
|
||||
- This API is intentionally small and useful for lightweight branching.
|
||||
- The mode result describes runtime behavior only; it should not be read as proof that every backend has been equally re-verified in the current release cycle.
|
||||
|
||||
@@ -70,6 +74,8 @@ In this example, the output becomes a stable string instead of an enum pattern-m
|
||||
e.g.:
|
||||
- This API does not have a normal runtime failure mode; it reflects the compiled backend behavior.
|
||||
|
||||
- If callers need the current mode paired with the matching worker-support flag, prefer a fresh `async_runtime_state()` call instead of combining `async_runtime_mode()` with an older saved runtime snapshot.
|
||||
|
||||
- If you need worker support as a direct boolean, use `async_runtime_supports_background_worker()` instead.
|
||||
|
||||
### Notes
|
||||
@@ -78,6 +84,8 @@ e.g.:
|
||||
|
||||
2. Use `async_runtime_state()` when you also want worker support packaged into one object.
|
||||
|
||||
3. This mode distinction is about runtime behavior, not whether `src-async` itself is expected to compile for the target.
|
||||
3. Use `async_runtime_mode_label(...)` when the result should leave enum space and become stable text such as `native_worker` or `compatibility`.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification status of individual targets.
|
||||
4. This mode distinction is about runtime behavior, not whether `src-async` itself is expected to compile for the target.
|
||||
|
||||
5. See [target-verification.md](./target-verification.md) for the current verification status of individual targets.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-new
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Construct an AsyncRuntimeState snapshot from explicit runtime mode and worker-support values.
|
||||
update-time: 20260614
|
||||
description: Construct an AsyncRuntimeState snapshot from explicit runtime mode and worker-support values without probing or validating the live backend.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -40,7 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This constructor simply packages `mode` and `background_worker` into one public snapshot value.
|
||||
- It does not query the current backend automatically.
|
||||
- `async_runtime_state()` is the higher-level API that reads these values from the live runtime environment.
|
||||
- It also does not validate whether the supplied pair matches the current backend contract.
|
||||
- The supplied `mode` and `background_worker` values are stored exactly as provided; this constructor does not recompute, normalize, or cross-check either field.
|
||||
- The constructed value matches the same public shape used by async runtime serializers.
|
||||
- Because `AsyncRuntimeState` is only a data snapshot type, this constructor is mainly useful for tests, adapters, and synthetic diagnostics rather than ordinary runtime probing.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -73,8 +76,14 @@ e.g.:
|
||||
|
||||
- If callers want the current backend snapshot directly, `async_runtime_state()` is the simpler API.
|
||||
|
||||
- If callers manually pair `NativeWorker` with `false` or `Compatibility` with `true`, the constructor still accepts that snapshot because it does not enforce backend consistency.
|
||||
|
||||
- If callers want the currently probed runtime pair instead of a synthetic one, they must pass `async_runtime_mode()` plus `async_runtime_supports_background_worker()` explicitly or use `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when code should construct an `AsyncRuntimeState` value explicitly.
|
||||
|
||||
2. Pair it with `AsyncLoggerState::new(...)` when assembling a full async logger snapshot by hand.
|
||||
|
||||
3. Prefer `async_runtime_state()` when the goal is to report the actual current backend pair rather than an arbitrary constructed snapshot.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncRuntimeState into a JSON value for runtime capability and mode diagnostics.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncRuntimeState into a JSON value for runtime capability and mode diagnostics using the canonical mode labels and snapshot field names.
|
||||
key-word:
|
||||
- async
|
||||
- state
|
||||
@@ -35,8 +35,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The output includes `mode` and `background_worker`.
|
||||
- `mode` is serialized through `async_runtime_mode_label(...)`.
|
||||
- The compact serialized shape is `{"mode":"native_worker|compatibility","background_worker":true|false}`.
|
||||
- This helper focuses on runtime capabilities rather than queue counters or logger lifecycle flags.
|
||||
- The exported JSON is suitable for diagnostics endpoints and startup environment checks.
|
||||
- This helper serializes the provided `AsyncRuntimeState` exactly as given; it does not call `async_runtime_state()` or recheck backend capability by itself.
|
||||
- That means manually constructed runtime snapshots are exported unchanged, with `mode` relabeled through `async_runtime_mode_label(...)` and `background_worker` kept exactly as supplied.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,3 +70,11 @@ e.g.:
|
||||
|
||||
- If the runtime is in compatibility mode, the helper still serializes normally using the matching mode label.
|
||||
|
||||
- If callers need the current backend-derived runtime rather than an older or synthetic snapshot, they must capture a fresh `async_runtime_state()` first.
|
||||
|
||||
### Notes
|
||||
|
||||
1. The output field names are fixed as `mode` and `background_worker`.
|
||||
|
||||
2. The `mode` field always uses the canonical labels from `async_runtime_mode_label(...)`, not enum names like `NativeWorker` or `Compatibility`.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async runtime state alias used for backend capability snapshots and diagnostics.
|
||||
update-time: 20260614
|
||||
description: Public async runtime state alias used for backend capability snapshots and diagnostics, pairing runtime mode with background-worker support.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -32,7 +32,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This is a type alias, not a live runtime controller.
|
||||
- The current fields are `mode : AsyncRuntimeMode` and `background_worker : Bool`.
|
||||
- `async_runtime_state()` returns this type directly as an environment-level snapshot.
|
||||
- `async_runtime_state()` currently builds that snapshot from `async_runtime_mode()` and `async_runtime_supports_background_worker()`.
|
||||
- `async_runtime_state_to_json(...)` and `stringify_async_runtime_state(...)` serialize the same snapshot shape for diagnostics.
|
||||
- `AsyncRuntimeState::new(...)` can also construct this type manually, but manual construction is synthetic data and does not probe the current backend by itself.
|
||||
- The type itself does not distinguish live backend snapshots from hand-built ones; callers must track whether a given `AsyncRuntimeState` value came from `async_runtime_state()` or from manual construction.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,8 +69,14 @@ e.g.:
|
||||
|
||||
- The snapshot is point-in-time diagnostic data; it should not be treated as proof that every target was re-verified in the current release cycle.
|
||||
|
||||
- Because this is just a data shape, manual construction can represent combinations that do not come from the current backend probe.
|
||||
|
||||
- Receiving an `AsyncRuntimeState` value alone does not prove it came from the current backend rather than from a synthetic constructor path.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `async_runtime_state()` when you need a value of this type from the current backend.
|
||||
|
||||
2. Use `AsyncLoggerState` when logger-instance queue and lifecycle information is also required.
|
||||
|
||||
3. Use `async_runtime_mode_label(...)` when the `mode` field should be rendered as stable text such as `native_worker` or `compatibility`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current backend-specific async runtime mode and worker capability.
|
||||
update-time: 20260614
|
||||
description: Read the current backend-specific async runtime snapshot as the paired result of runtime mode and background-worker capability.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -35,8 +35,12 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `mode` is derived from the active backend implementation.
|
||||
- `background_worker` tells callers whether native worker semantics are available.
|
||||
- This helper is equivalent to `AsyncRuntimeState::new(async_runtime_mode(), async_runtime_supports_background_worker())`.
|
||||
- The returned pair is rebuilt from those two lower-level helpers on each call rather than read from a cached runtime object.
|
||||
- In the current backend implementations, the resulting pair is `NativeWorker + true` or `Compatibility + false`.
|
||||
- `async_runtime_state_to_json(...)` and `stringify_async_runtime_state(...)` serialize this state.
|
||||
- This API is environment-scoped and does not depend on a particular `AsyncLogger` instance.
|
||||
- The returned value is a snapshot data object, not a live runtime handle, so later backend checks require calling `async_runtime_state()` again rather than reusing an older value as if it refreshed itself.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,6 +72,8 @@ In this example, branch decisions are based on actual runtime capability instead
|
||||
e.g.:
|
||||
- This API does not normally expose a dynamic error path; it reports the currently compiled backend behavior.
|
||||
|
||||
- The returned `AsyncRuntimeState` value is not cached onto the helper. If callers need a newer backend read, they must call `async_runtime_state()` again instead of expecting an older value to refresh itself.
|
||||
|
||||
- If callers need richer runtime state, they should use `AsyncLogger::state()` on a logger instance instead.
|
||||
|
||||
### Notes
|
||||
@@ -75,3 +81,5 @@ e.g.:
|
||||
1. Use this API for environment-level diagnostics.
|
||||
|
||||
2. Use `AsyncLogger::state()` for logger-instance diagnostics.
|
||||
|
||||
3. Use `AsyncRuntimeState::new(...)` only when code or tests need to construct a manual snapshot instead of probing the current backend.
|
||||
|
||||
@@ -36,6 +36,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `true` indicates native worker capability.
|
||||
- `false` indicates compatibility-mode behavior.
|
||||
- This helper is derived from backend-specific async runtime implementation choice.
|
||||
- The boolean is read from the active backend helper on each call rather than from a cached runtime snapshot object.
|
||||
- In the current backend split, the native implementation returns `true` while the compatibility stub returns `false`, matching the same mode pair exposed through `async_runtime_mode()` and `async_runtime_state()`.
|
||||
- The async library still targets multiple backends even when this helper returns `false`.
|
||||
- Use it when an enum branch is unnecessary and a boolean capability check is enough.
|
||||
|
||||
@@ -68,6 +70,8 @@ In this example, a simple boolean can drive compact status output.
|
||||
e.g.:
|
||||
- This API does not normally fail at runtime; it reflects compiled backend behavior.
|
||||
|
||||
- If callers need the current paired mode and worker flag together, prefer a fresh `async_runtime_state()` call instead of mixing this boolean with an older saved mode value.
|
||||
|
||||
- If you need the exact mode name rather than a boolean, use `async_runtime_mode()` or `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-application-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the application-facing runtime-sink async logger alias from an AsyncLoggerBuildConfig through the sync-first async builder path.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-application-async-logger
|
||||
|
||||
Build an `ApplicationAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-facing async facade over `build_async_logger(...)`.
|
||||
Build an `ApplicationAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-facing runtime-sink async alias returned through `build_async_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,19 @@ pub fn build_application_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_logger(...)`.
|
||||
- The returned logger keeps the standard async lifecycle and state helper surface.
|
||||
- Use this facade when application code wants a dedicated async app-level entry point.
|
||||
- This API delegates to `build_async_logger(...)` directly.
|
||||
- That means the embedded `LoggerConfig` is built first through the normal synchronous config path before the outer async layer is applied.
|
||||
- Any optional synchronous queue layer and runtime-sink controls chosen by `build_logger(config.logger)` remain active under the returned logger.
|
||||
- In particular, a sync queue configured on `LoggerConfig.queue` is preserved inside the wrapped `RuntimeSink` variant instead of being stripped away by the application alias.
|
||||
- Because the result is only the `ApplicationAsyncLogger` alias over `AsyncLogger[@bitlogger.RuntimeSink]`, this builder does not hide any async helpers or introduce a wrapper layer.
|
||||
- The broader async helper surface is therefore preserved and directly exposed on the returned alias rather than being rebuilt or hidden behind an unwrap step.
|
||||
- The returned alias also keeps inherited async logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- The returned logger keeps the full async lifecycle and state helper surface directly, including helpers such as `run()`, `shutdown()`, `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, and `last_error()`.
|
||||
- It also keeps the same queue counters, failure state, sink shape, and runtime-dependent post-close behavior as the underlying runtime-sink async logger.
|
||||
- Because this facade delegates to `build_async_logger(...)`, `Batch` and `Shutdown` also keep the underlying runtime-sink flush wiring: the returned alias uses the built sink's real `flush()` path instead of the default no-op callback used by the text-specific builder line.
|
||||
- In the current direct builder coverage, this alias matches `build_async_logger(config)` all the way through serialized state snapshots, runtime-sink variant choice, queue counters, lifecycle flags, and later failure fields after `run()` or `shutdown()`.
|
||||
- File-backed runtime helpers on the returned `RuntimeSink` also stay aligned with the direct builder result instead of being hidden behind a separate application-layer wrapper.
|
||||
- Use this alias-oriented builder when application code wants the standard runtime-sink async shape without narrowing the public surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,6 +67,23 @@ let logger = build_application_async_logger(
|
||||
|
||||
In this example, the app-facing async facade is built directly from typed config.
|
||||
|
||||
And any configured synchronous runtime sink controls remain available through the returned `RuntimeSink`-backed async logger.
|
||||
|
||||
And unlike `build_library_async_logger(...)`, no `to_async_logger()` unwrap is required to reach queue, lifecycle, or file-backed runtime helpers.
|
||||
|
||||
The returned value also keeps the ordinary async logger target semantics because the facade does not wrap or narrow the underlying `AsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
|
||||
#### When Need Async State Helpers Immediately After App Construction
|
||||
|
||||
When application code should keep the ordinary async helper surface directly:
|
||||
```moonbit
|
||||
let logger = build_application_async_logger(config)
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, the application alias exposes async state helpers directly because no narrowing wrapper is added.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -64,8 +91,16 @@ e.g.:
|
||||
|
||||
- If the logger is never `run()`, enqueue behavior and lifecycle state still follow the normal async logger rules.
|
||||
|
||||
- If callers rely on file-backed runtime helpers, they should treat this builder as the same runtime-sink result as `build_async_logger(config)`, not as a reduced alias with different helper behavior.
|
||||
|
||||
- If callers specifically want the text-console async path where `Batch` and `Shutdown` keep the default no-op flush callback, `build_application_text_async_logger(...)` is the different contract; this runtime-sink alias preserves the real sink `flush()` behavior from `build_async_logger(...)`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a facade over the existing async runtime logger builder.
|
||||
|
||||
2. Use `parse_and_build_application_async_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_application_text_async_logger(...)` instead when callers should keep the concrete text-console sink type and the direct text-builder path.
|
||||
|
||||
4. Use `build_library_async_logger(...)` instead when a library boundary should narrow the exposed async surface.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: build-application-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing configured logger facade from a LoggerConfig.
|
||||
description: Build the application-facing configured logger alias from a LoggerConfig by delegating directly to the normal runtime logger build path.
|
||||
key-word:
|
||||
- application
|
||||
- facade
|
||||
@@ -33,9 +33,13 @@ pub fn build_application_logger(config : LoggerConfig) -> ApplicationLogger {
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_logger(...)`.
|
||||
- The returned value keeps the same public logging, queue, and file runtime helper surface as `ConfiguredLogger`.
|
||||
- Use this facade when application boot code wants an app-specific entry name without exposing lower-level builder naming in its own code.
|
||||
- This API delegates to `build_logger(...)` directly.
|
||||
- The embedded config still goes through the normal runtime logger build path, including runtime sink selection, optional queue wrapping, and timestamp application.
|
||||
- Because the result is only the `ApplicationLogger` alias over `ConfiguredLogger`, this builder does not hide any queue, drain, flush, or file runtime helper methods.
|
||||
- The configured runtime helper surface is therefore preserved and directly exposed on the returned alias rather than being rebuilt or hidden behind an unwrap step.
|
||||
- The returned alias also keeps inherited `Logger` behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- That means `log(..., target=...)` can override the target for one write, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- Use this alias-oriented entrypoint when application boot code wants an app-specific name without changing the underlying configured runtime logger surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,6 +56,24 @@ let logger = build_application_logger(
|
||||
|
||||
In this example, the application facade builds the same configured runtime logger shape as `build_logger(...)`.
|
||||
|
||||
And any queue/file/runtime helpers selected by the config remain directly available on the returned alias value.
|
||||
|
||||
And unlike `build_library_logger(...)`, no `to_logger()` unwrap is required to reach that helper surface.
|
||||
|
||||
The returned value also keeps the ordinary logger target semantics because the facade does not wrap or narrow the underlying `ConfiguredLogger`.
|
||||
|
||||
#### When Need A Per-call Target Override After App-oriented Build
|
||||
|
||||
When typed app config should still build a logger that supports a one-write target override:
|
||||
```moonbit
|
||||
let logger = build_application_logger(config)
|
||||
logger.log(Level::Error, "boom", target="app.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.audit` for that write.
|
||||
|
||||
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(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -64,3 +86,5 @@ e.g.:
|
||||
1. This is a facade API, not a separate runtime implementation.
|
||||
|
||||
2. Use `parse_and_build_application_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_library_logger(...)` instead when the public surface should intentionally hide configured-runtime helper methods.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-application-text-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing text-console async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the application-facing text-console async logger alias from an AsyncLoggerBuildConfig using the direct concrete text-sink builder path.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-application-text-async-logger
|
||||
|
||||
Build an `ApplicationTextAsyncLogger` from `AsyncLoggerBuildConfig`. This facade is the application-oriented async builder for the text-console runtime sink shape returned by `build_async_text_logger(...)`.
|
||||
Build an `ApplicationTextAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-oriented async alias builder for the concrete text-console sink shape returned by `build_async_text_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,22 @@ pub fn build_application_text_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_text_logger(...)`.
|
||||
- This API delegates to `build_async_text_logger(...)` directly.
|
||||
- It is intended for config-driven async text console output where callers want the concrete text sink shape rather than the broader runtime sink enum wrapper.
|
||||
- The returned logger keeps the usual async lifecycle helpers.
|
||||
- The builder always creates a `FormattedConsoleSink` from `config.logger.sink.text_formatter` instead of selecting among sink kinds.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this facade still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- Unlike `build_application_async_logger(...)`, this alias-oriented builder does not go through the full synchronous configured-logger build path first.
|
||||
- It uses the selected text-oriented `LoggerConfig` fields directly and therefore does not apply `LoggerConfig.queue` or preserve sync runtime sink controls.
|
||||
- A sync queue configured on `LoggerConfig.queue` is therefore ignored by this builder instead of being preserved under the returned async logger.
|
||||
- Because the result is only the `ApplicationTextAsyncLogger` alias over `AsyncLogger[@bitlogger.FormattedConsoleSink]`, this builder returns the same underlying async logger value as `build_async_text_logger(...)` and does not hide any async helpers or introduce a wrapper layer.
|
||||
- The returned alias also keeps inherited async logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- The returned logger keeps the full async lifecycle and state helper surface directly, including helpers such as `run()`, `shutdown()`, `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, and `last_error()`.
|
||||
- It also keeps the same close, queue, and failure-state semantics as the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- In the current direct text-builder coverage, this alias matches `build_async_text_logger(config)` through serialized state snapshots, formatter behavior, queue counters, lifecycle flags, and later failure fields after worker execution.
|
||||
- Its configured `flush_policy` is still visible on the returned alias, but this text-specific build path does not wire the explicit sink flush callback that `build_application_async_logger(...)` inherits through `build_async_logger(...)`.
|
||||
- That means `Batch` and `Shutdown` only drive the default no-op async flush callback here; they do not add an extra explicit sink flush step beyond ordinary `FormattedConsoleSink` writes.
|
||||
- Use `build_library_async_text_logger(...)` instead when the next boundary should keep the same concrete text sink type but intentionally narrow the directly exposed async helper surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,15 +70,50 @@ let logger = build_application_text_async_logger(
|
||||
|
||||
In this example, the async logger is built for text-console output specifically.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this facade still uses the text formatter directly because it always builds a `FormattedConsoleSink`.
|
||||
|
||||
And the returned value keeps the ordinary async logger target semantics because this facade does not wrap or narrow the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
|
||||
#### When Need A Per-call Target Override After App Text Construction
|
||||
|
||||
When typed app async text config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_application_text_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.text.audit` for that call.
|
||||
|
||||
And later `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 Async State Helpers Immediately After Text App Construction
|
||||
|
||||
When application code should keep the ordinary async helper surface directly on the text-console variant:
|
||||
```moonbit
|
||||
let logger = build_application_text_async_logger(config)
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, the application text alias exposes async state helpers directly because no narrowing wrapper is added.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the embedded logger config selects a non-text sink shape, the caller should use the general async builder facade instead.
|
||||
- If callers need sink-kind-driven branching such as JSON console or file-backed async output, they should use `build_application_async_logger(...)` instead.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this facade still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If runtime draining is never started, records still follow the normal async queue lifecycle rules.
|
||||
|
||||
- If callers rely on the formatter or state shape after text-builder construction, they should treat this as the same direct `build_async_text_logger(config)` result rather than a reduced alias with different helper behavior.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a narrower text-console async facade than `build_application_async_logger(...)`.
|
||||
|
||||
2. It is most useful when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
|
||||
3. Use `build_application_async_logger(...)` instead when callers need the broader runtime-sink build path, including sync queue application through `build_logger(config.logger)`.
|
||||
|
||||
4. Use `build_library_async_text_logger(...)` instead when the public boundary should narrow the exposed async surface while preserving the same concrete text sink type.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-async-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Build an async logger from combined logger and async config without manually wiring the runtime sink.
|
||||
update-time: 20260614
|
||||
description: Build an async logger from combined logger and async config by first building the sync runtime logger and then wrapping its sink in the async layer.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -34,9 +34,18 @@ pub fn build_async_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@bitlo
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The `logger` section is built through the same config machinery used by synchronous configured loggers.
|
||||
- That means `LoggerConfig.sink` and the optional synchronous `LoggerConfig.queue` are applied before the async layer is added.
|
||||
- The resulting async logger inherits `min_level`, `target`, and timestamp behavior from the built synchronous logger.
|
||||
- File, queue, and formatter choices all come from config rather than direct code-side sink wiring.
|
||||
- File, formatter, and any configured synchronous queue choices all come from config rather than direct code-side sink wiring.
|
||||
- The returned sink type is `RuntimeSink`, which keeps configured control helpers available where relevant.
|
||||
- This builder returns the underlying `AsyncLogger[@bitlogger.RuntimeSink]` value directly. `build_application_async_logger(...)` only re-exports the same result under the `ApplicationAsyncLogger` alias, while `build_library_async_logger(...)` wraps the same result in `LibraryAsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
- The returned async logger also keeps ordinary target rules unchanged: `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- Because this path starts from `build_logger(config.logger)`, it preserves the broader runtime-sink build path, including sync-side queue decoration when `LoggerConfig.queue` is present.
|
||||
- Because this path starts from `build_logger(config.logger)`, `config.logger.sink.kind` has already selected the concrete `RuntimeSink` variant before async wrapping, and optional sync queue decoration can further turn that built sink into queued runtime variants such as `QueuedConsole` or `QueuedFile`.
|
||||
- This runtime-sink path also wires the explicit async flush callback as `flush=fn(sink) { sink.flush() }`, so `Batch` and `Shutdown` policies invoke the built runtime sink's real flush behavior instead of the default no-op callback.
|
||||
- In the current direct builder coverage, the returned logger exposes the expected serialized async state snapshot, runtime-sink variant choice, queue counters, lifecycle flags, and later failure fields after `run()` or `shutdown()`.
|
||||
- File-backed runtime helpers also stay directly available on the returned `RuntimeSink` with the same behavior later observed through the application and library facade equivalence tests.
|
||||
- Use `build_async_text_logger(...)` instead when you want the direct text-console async builder path with `FormattedConsoleSink` and without the sync runtime-sink construction layer.
|
||||
- The `src-async` library is designed to compile on `native / llvm / js / wasm / wasm-gc`, but runtime mode differs by backend.
|
||||
- Current local release-facing verification is explicit for `native / js / wasm / wasm-gc`.
|
||||
- `llvm` remains experimental and did not complete local verification in this environment.
|
||||
@@ -60,6 +69,18 @@ In this example, parsing and async runtime wiring are separated cleanly.
|
||||
|
||||
And the returned logger can immediately be started with `run()`.
|
||||
|
||||
#### When Need A Per-call Target Override After Typed Async Build
|
||||
|
||||
When typed async config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.audit` for that call.
|
||||
|
||||
And later `debug(...)`, `info(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Runtime Sink Features After Async Build
|
||||
|
||||
When the sink shape is configured but runtime features still matter:
|
||||
@@ -70,6 +91,10 @@ println(stringify_async_logger_state(logger.state(), pretty=true))
|
||||
|
||||
In this example, the built async logger remains introspectable even though construction was config-driven.
|
||||
|
||||
And any configured synchronous runtime sink controls are preserved inside the returned `RuntimeSink`.
|
||||
|
||||
And if `AsyncFlushPolicy::Batch` or `Shutdown` is configured, this builder uses the runtime sink's real `flush()` path rather than the default no-op callback used by the text-specific builder line.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -77,6 +102,12 @@ e.g.:
|
||||
|
||||
- If the configured sink shape is unsupported for a specific capability, the resulting runtime behavior follows the existing sink/runtime rules rather than inventing a separate builder-only failure model.
|
||||
|
||||
- If callers depend on queued runtime-sink helpers or file-backed runtime helpers, this builder is the direct API that preserves them rather than a reduced facade path.
|
||||
|
||||
- If callers depend on the exact runtime-sink variant, they should read this builder as preserving the sync-first sink selection result from `build_logger(config.logger)`, not as deferring sink-kind branching until after the async layer is added.
|
||||
|
||||
- If callers need the text-console path where `Batch` and `Shutdown` keep the default no-op flush callback, `build_async_text_logger(...)` is the different builder contract; this runtime-sink path intentionally wires the sink's real `flush()` behavior.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API when applications externalize both sync sink choice and async queue behavior.
|
||||
@@ -86,3 +117,5 @@ e.g.:
|
||||
3. Library portability is broader than example portability: a runnable `async fn main` example may still be target-limited even when the async library itself compiles for that backend.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification boundary.
|
||||
|
||||
5. If the next boundary is library-facing rather than application-facing, build here and then narrow with `build_library_async_logger(...)` instead of documenting the broader runtime helper surface directly.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-async-text-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260520
|
||||
description: Build an async logger with a concrete text-console sink from combined logger and async config.
|
||||
update-time: 20260614
|
||||
description: Build an async logger with a concrete text-console sink from combined logger and async config, using only the selected text-oriented LoggerConfig fields instead of the full sync build path.
|
||||
key-word:
|
||||
- async
|
||||
- text
|
||||
@@ -34,7 +34,16 @@ pub fn build_async_text_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This builder converts `config.logger.sink.text_formatter` into a runtime `TextFormatter` and wires it into `text_console_sink(...)`.
|
||||
- It always constructs a `FormattedConsoleSink` directly instead of branching on `config.logger.sink.kind`.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this builder still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- The returned logger inherits `min_level`, `target`, and timestamp behavior from `config.logger`.
|
||||
- Unlike `build_async_logger(...)`, this helper does not run the full synchronous `build_logger(config.logger)` path first.
|
||||
- That means it uses `config.logger.sink.text_formatter`, `min_level`, `target`, and `timestamp` directly, but it does not apply `LoggerConfig.queue` or preserve other sync runtime sink controls.
|
||||
- This builder returns the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]` value directly. `build_application_text_async_logger(...)` only re-exports that same result under the `ApplicationTextAsyncLogger` alias, while `build_library_async_text_logger(...)` wraps the same result in `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- The returned async logger also keeps ordinary target rules unchanged: `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- In the current direct text-builder coverage, the returned logger exposes the expected serialized async state snapshot, formatter behavior, queue counters, lifecycle flags, and later failure fields after worker execution.
|
||||
- The async `flush_policy` still comes from `config.async_config`, but this text-specific builder does not supply the explicit `flush=fn(sink) { sink.flush() }` callback used by `build_async_logger(...)`.
|
||||
- In practice, `Batch` and `Shutdown` therefore only trigger the default no-op async flush callback on this path, while each record write still follows whatever immediate behavior `FormattedConsoleSink` already has on its own.
|
||||
- This helper is best suited to text-console output paths where callers want the concrete formatted sink type instead of `RuntimeSink`.
|
||||
- This async text path follows the same target story as the broader async library: `native / js / wasm / wasm-gc` have stronger local verification, while `llvm` remains experimental and locally unverified in this environment.
|
||||
|
||||
@@ -56,13 +65,31 @@ let logger = build_async_text_logger(
|
||||
|
||||
In this example, the async logger is built around a text console sink rather than the generic runtime sink enum.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this helper still uses the text formatter directly because it always builds a `FormattedConsoleSink`.
|
||||
|
||||
#### When Need A Per-call Target Override After Built Async Text Construction
|
||||
|
||||
When typed async text config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_async_text_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="async.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `async.text.audit` for that call.
|
||||
|
||||
And later `debug(...)`, `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the logger config was not intended for text-console style output, the broader `build_async_logger(...)` path may be a better fit.
|
||||
- If callers need sink-kind-driven branching across console, JSON, text, or file output, `build_async_logger(...)` is the better fit.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this builder still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If the logger is never `run()`, pending records still follow the normal async queue lifecycle rules.
|
||||
|
||||
- If callers rely on the concrete formatter or post-run state shape, this builder is the direct API that preserves those text-console details rather than a reduced alias or wrapper.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This API is narrower than `build_async_logger(...)` because it preserves a concrete text sink type.
|
||||
@@ -70,3 +97,5 @@ e.g.:
|
||||
2. It is the base builder used by the application and library async text facades.
|
||||
|
||||
3. See [target-verification.md](./target-verification.md) for the current local verification matrix.
|
||||
|
||||
4. Use this direct builder when callers should keep the full async helper surface on the concrete text sink type rather than a naming alias or a narrowed library wrapper.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the library-facing async logger facade from an AsyncLoggerBuildConfig while intentionally hiding direct async state helpers.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
@@ -35,9 +35,19 @@ pub fn build_library_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds the general async runtime logger and then wraps it in the narrower `LibraryAsyncLogger` facade.
|
||||
- This API builds the general async runtime logger and then wraps it in the narrower `LibraryAsyncLogger[@bitlogger.RuntimeSink]` facade.
|
||||
- The embedded `LoggerConfig` still goes through the normal synchronous config path first, so sink shape and any optional synchronous queue layer are already applied before the outer async layer is wrapped and then narrowed.
|
||||
- The returned facade wraps the same underlying `AsyncLogger[@bitlogger.RuntimeSink]` value that `build_async_logger(...)` would produce directly.
|
||||
- The result keeps async lifecycle operations such as `run()` and `shutdown()` while narrowing the public shape.
|
||||
- The broader async helper surface is preserved rather than rebuilt, but it is intentionally not directly exposed on the returned facade. Queue counters, lifecycle state, idle-wait helpers, and file-backed runtime helpers stay behind `to_async_logger()` instead of disappearing.
|
||||
- The narrower facade does not change the underlying runtime-sink queue counters, failure state, sink shape, or runtime-dependent post-close behavior; it only hides the broader helper surface until `to_async_logger()` is used.
|
||||
- Because this facade starts from `build_async_logger(...)`, the wrapped async logger also keeps the runtime-sink flush wiring: `Batch` and `Shutdown` use the built sink's real `flush()` path instead of the default no-op callback used by the text-specific builder line.
|
||||
- The facade still preserves the underlying async target rules on its exposed write methods: `log(..., target=...)` can override the target for one call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derived another logger with `with_target(...)` or `child(...)`.
|
||||
- In the current direct builder coverage, unwrapping this facade produces the same async state snapshot, runtime-sink variant, queue counters, lifecycle flags, and failure fields as calling `build_async_logger(config)` directly.
|
||||
- That same unwrap also preserves file-backed runtime helpers on `RuntimeSink` values rather than replacing them with facade-specific behavior.
|
||||
- Async state helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, and failure-status inspection remain on the underlying `AsyncLogger`, not on the returned facade itself.
|
||||
- `to_async_logger()` can be used to recover the underlying full async logger.
|
||||
- Use this builder when the boundary should preserve the runtime-sink async build path but still hide broader async inspection helpers from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,15 +67,46 @@ let logger = build_library_async_logger(
|
||||
|
||||
In this example, async runtime construction is hidden behind the library facade.
|
||||
|
||||
#### When Need Async State Helpers After Library-oriented Construction
|
||||
|
||||
When config-built async state inspection is still needed internally:
|
||||
```moonbit
|
||||
let logger = build_library_async_logger(config)
|
||||
let full = logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
```
|
||||
|
||||
In this example, the library async facade is unwrapped before using async state helpers.
|
||||
|
||||
And unlike `ApplicationAsyncLogger`, the narrower builder result does not expose those broader async helpers directly on the facade surface.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Async Builder Facade
|
||||
|
||||
When typed library async config should still build a facade that allows a one-call target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="lib.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.audit` for that call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If backend-specific sink limitations exist, they still apply under the facade.
|
||||
|
||||
- If callers need methods outside the library facade, they must unwrap with `to_async_logger()`.
|
||||
- If callers need async state, failure-status, or idle-wait helpers outside the library facade, they must unwrap with `to_async_logger()`.
|
||||
|
||||
- If callers need file-backed runtime helpers such as file-state or queued runtime inspection, they must unwrap first, but the helper behavior itself stays aligned with the direct `build_async_logger(config)` result.
|
||||
|
||||
- If callers instead want the text-console builder path where `Batch` and `Shutdown` keep the default no-op flush callback, they should use `build_library_async_text_logger(...)`; this runtime-sink facade preserves the real sink `flush()` behavior from `build_async_logger(...)`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API when library boundaries should stay narrow.
|
||||
|
||||
2. Use `parse_and_build_library_async_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_library_async_text_logger(...)` instead when the library-facing async type should preserve the narrower `FormattedConsoleSink` shape rather than `RuntimeSink`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-async-text-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing text-console async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the library-facing text-console async logger facade from an AsyncLoggerBuildConfig using the concrete text-console builder path.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-library-async-text-logger
|
||||
|
||||
Build a `LibraryAsyncLogger[FormattedConsoleSink]` from `AsyncLoggerBuildConfig`. This facade is the library-oriented async builder for text-console runtime output.
|
||||
Build a `LibraryAsyncLogger[FormattedConsoleSink]` from `AsyncLoggerBuildConfig`. This facade is the library-oriented async builder for the concrete text-console sink shape returned by `build_async_text_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,20 @@ pub fn build_library_async_text_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_text_logger(...)` and then wraps the result as `LibraryAsyncLogger`.
|
||||
- It is useful when library code wants a narrow async facade while preserving a concrete text-console sink type.
|
||||
- This API delegates to `build_async_text_logger(...)` and then narrows the result to `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- It always produces a concrete `FormattedConsoleSink` from `config.logger.sink.text_formatter` instead of branching on sink kinds.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this facade still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- Unlike `build_library_async_logger(...)`, this facade does not go through the full synchronous configured-logger build path first.
|
||||
- It uses the selected text-oriented `LoggerConfig` fields directly and therefore does not apply `LoggerConfig.queue` or preserve sync runtime sink controls.
|
||||
- A sync queue configured on `LoggerConfig.queue` is therefore ignored by this builder instead of being preserved behind the wrapped text-console async logger.
|
||||
- The returned facade wraps the same underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]` value that `build_async_text_logger(...)` would return directly, so `run()`, `shutdown()`, and queue or failure-state behavior are unchanged under the narrower public type.
|
||||
- In the current direct text-builder coverage, unwrapping this facade yields the same async state snapshot, formatter behavior, queue counters, lifecycle flags, and later failure fields as calling `build_async_text_logger(config)` directly.
|
||||
- The configured `flush_policy` is still carried by that underlying async logger, but this text-specific builder path does not provide the explicit sink flush callback used by `build_library_async_logger(...)` through `build_async_logger(...)`.
|
||||
- As a result, `Batch` and `Shutdown` only invoke the default no-op async flush callback on this text-console path unless downstream code unwraps and adds different behavior elsewhere.
|
||||
- The facade still preserves the underlying async target rules on its exposed write methods: `log(..., target=...)` can override the target for one call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derived another logger with `with_target(...)` or `child(...)`.
|
||||
- Async state helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, and failure-status inspection remain on the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`, not on the returned facade itself.
|
||||
- `to_async_logger()` can recover the underlying full async logger if needed.
|
||||
- Use this builder when the boundary should preserve the concrete text-console sink type while still hiding broader async inspection and helper APIs from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,10 +68,41 @@ let logger = build_library_async_text_logger(
|
||||
|
||||
In this example, the async text sink shape is preserved under the library facade.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this facade still uses the text formatter directly because it always builds a `FormattedConsoleSink` before narrowing to `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
|
||||
#### When Need Async State Helpers After Library Text Construction
|
||||
|
||||
When library-facing text-console construction should still allow internal async inspection later:
|
||||
```moonbit
|
||||
let logger = build_library_async_text_logger(config)
|
||||
let full = logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
```
|
||||
|
||||
In this example, the facade is unwrapped before using async state helpers.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Async Text Builder Facade
|
||||
|
||||
When typed library async text config should still build a facade that allows a one-call target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_async_text_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="lib.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.text.audit` for that call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the embedded logger config does not describe text-console output, the caller should use the broader async facade instead.
|
||||
- If callers need sink-kind-driven branching such as JSON console or file-backed async output, they should use `build_library_async_logger(...)` instead.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this facade still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If callers expect async state or idle-wait helpers directly on the returned facade, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
- If callers need to inspect the actual formatter-backed async state after library-level `run()` or `shutdown()` calls, unwrapping exposes the same post-call counters and failure fields that the direct text builder would have produced.
|
||||
|
||||
- Normal async lifecycle expectations still apply if the logger is never run.
|
||||
|
||||
@@ -69,3 +111,5 @@ e.g.:
|
||||
1. This is the library-side counterpart to `build_application_text_async_logger(...)`.
|
||||
|
||||
2. It is most useful when a concrete text-console async sink type matters to the caller boundary.
|
||||
|
||||
3. Use `build_library_async_logger(...)` instead when the library-facing async type should keep the broader `RuntimeSink` build path, including sync queue application through `build_logger(config.logger)`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing sync logger facade from a LoggerConfig.
|
||||
update-time: 20260613
|
||||
description: Build the library-facing sync logger facade from a LoggerConfig by delegating to the configured runtime logger build path and then wrapping the result.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -33,9 +33,15 @@ pub fn build_library_logger(config : LoggerConfig) -> LibraryLogger[RuntimeSink]
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds a configured runtime logger first and then wraps it as `LibraryLogger`.
|
||||
- This API builds a configured runtime logger first and then wraps that same value as `LibraryLogger[RuntimeSink]`.
|
||||
- The embedded config still goes through the normal runtime logger build path, including runtime sink selection, optional queue wrapping, and timestamp application.
|
||||
- The returned facade wraps the same underlying `ConfiguredLogger` value that `build_logger(...)` would produce directly.
|
||||
- The facade intentionally exposes a smaller logging surface than the full configured runtime logger.
|
||||
- In particular, the configured runtime helper surface is preserved rather than rebuilt, but it is intentionally not directly exposed on the returned facade. Queue metrics, flush or drain helpers, and file controls stay behind `to_logger()` instead of disappearing.
|
||||
- The facade still preserves the underlying logger target rules on its exposed write methods: `log(..., target=...)` can override the target for one write, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derived another logger with `with_target(...)` or `child(...)`.
|
||||
- Queue metrics, flush and drain helpers, and file runtime controls remain on the underlying `ConfiguredLogger`, not on the returned facade itself.
|
||||
- Call `to_logger()` if a caller must recover the underlying full logger object.
|
||||
- Use this builder when the boundary should preserve the configured runtime logger path but still hide broader runtime helper methods from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,15 +58,44 @@ let logger = build_library_logger(
|
||||
|
||||
In this example, the logger is built from config and then narrowed to the library facade.
|
||||
|
||||
#### When Need Runtime Helpers After Library-oriented Construction
|
||||
|
||||
When config-built runtime queue or file controls are still needed internally:
|
||||
```moonbit
|
||||
let logger = build_library_logger(config)
|
||||
let full = logger.to_logger()
|
||||
ignore(full.sink.pending_count())
|
||||
```
|
||||
|
||||
In this example, the library facade is unwrapped before using runtime-specific helpers through the preserved `RuntimeSink` value.
|
||||
|
||||
And the unwrapped value still carries the same `RuntimeSink` pipeline built from the original config.
|
||||
|
||||
And unlike `ApplicationLogger`, the narrower builder result does not expose those runtime helpers directly on the facade surface.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Builder Facade
|
||||
|
||||
When typed library config should still build a facade that allows a one-write target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_logger(config)
|
||||
logger.log(Level::Error, "boom", target="lib.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.audit` for that write.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If backend-specific sink limitations exist, they still apply after the facade is built.
|
||||
|
||||
- If code later needs methods outside the library facade, it must unwrap with `to_logger()`.
|
||||
- If code later needs queue metrics, flush or drain helpers, or file runtime controls, it must unwrap with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this facade when library APIs should not expose the full configured runtime logger type.
|
||||
|
||||
2. Use `parse_and_build_library_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `to_logger()` when internal code later needs broader logger composition or direct access to the preserved `RuntimeSink` value without changing the public facade type.
|
||||
|
||||
@@ -33,9 +33,12 @@ pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {}
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `build_logger(...)` constructs the runtime sink shape based on `SinkConfig` and optional queue wrapper.
|
||||
- `build_logger(...)` first constructs a base `RuntimeSink` from `config.sink`, then applies `config.queue` when present, and finally builds `Logger::new(...)` with `config.min_level`, `config.target`, and `config.timestamp`.
|
||||
- The returned logger still supports normal logging methods because `ConfiguredLogger` is `Logger[RuntimeSink]`.
|
||||
- The returned logger also keeps inherited logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- That means `log(..., target=...)` can override the target for one write, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- Queue metrics and file controls remain available through forwarding helpers on the configured logger.
|
||||
- `build_application_logger(...)` only re-exports this same configured runtime logger result under the `ApplicationLogger` alias, while `build_library_logger(...)` wraps the same result in `LibraryLogger[RuntimeSink]`.
|
||||
- This API is deterministic and data-driven, making it suitable for bootstrapping from parsed config.
|
||||
|
||||
### How to Use
|
||||
@@ -57,7 +60,19 @@ let logger = build_logger(
|
||||
|
||||
In this example, no JSON parsing is required because config objects were built directly.
|
||||
|
||||
And the runtime logger is ready immediately.
|
||||
And the runtime logger is ready immediately, with the same ordinary logger target semantics as any other `Logger` value.
|
||||
|
||||
#### When Need A Per-call Target Override After Typed Config Build
|
||||
|
||||
When typed config should still build a logger that supports a one-write target override:
|
||||
```moonbit
|
||||
let logger = build_logger(config)
|
||||
logger.log(Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.audit` for that write.
|
||||
|
||||
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 Config-built Queue Or File Runtime Helpers
|
||||
|
||||
@@ -83,3 +98,5 @@ e.g.:
|
||||
|
||||
2. Use `parse_and_build_logger(...)` when the starting point is raw JSON text.
|
||||
|
||||
3. Use the application or library facade builders only when the boundary name or exposed surface should differ; they do not change the underlying configured runtime logger pipeline built here.
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This helper delegates directly to `self.sink.close()`.
|
||||
- Plain file sinks forward to `FileSink::close()` through `RuntimeSink`.
|
||||
- Queue-backed file sinks close the wrapped file sink instead of only the queue wrapper.
|
||||
- Generic `close()` on queued file sinks does not drain pending queue entries first; it closes the wrapped file sink directly.
|
||||
- Console-style sinks and queue-wrapped console-style sinks return `true` as a no-op success because they do not expose a meaningful close step here.
|
||||
- For file-backed configured loggers, later `close()` calls return `false` after the wrapped file handle has already been cleared.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,10 +67,14 @@ In this example, callers can branch on the reported close result.
|
||||
e.g.:
|
||||
- If the configured runtime sink shape has no real close action, the helper may still return `true` as a no-op success.
|
||||
|
||||
- If the configured logger shares the same wrapped runtime sink state with another facade and one path already closed that file-backed sink, a later close attempt returns `false`.
|
||||
|
||||
- If callers need a file-specific close path with queue flush nuances, `file_close()` may be the better API.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is the generic configured runtime close helper.
|
||||
|
||||
2. Prefer file-specific helpers when the configured sink shape is known to be file-backed.
|
||||
2. Prefer `file_close()` when the configured sink shape is file-backed and queued records should be flushed before teardown.
|
||||
|
||||
3. Converting the same configured logger through wrapper paths such as library projection still shares the wrapped runtime sink state, so close effects are visible across those facades.
|
||||
|
||||
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Queued file sinks flush queue work before closing the wrapped inner file sink.
|
||||
- Non-file sinks return `false`.
|
||||
- This helper is narrower than generic `close()` because it specifically targets file sink shutdown.
|
||||
- After a file-backed configured logger has already cleared its file handle, later `file_close()` calls return `false`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,10 +66,14 @@ In this example, the result describes file close behavior rather than generic si
|
||||
e.g.:
|
||||
- If the configured sink is not file-backed, the method returns `false`.
|
||||
|
||||
- If the file handle was already closed earlier through this logger or another facade sharing the same wrapped runtime sink state, the method returns `false`.
|
||||
|
||||
- If callers only need generic sink teardown, `close()` is the broader API.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this helper when file-backed runtime behavior matters specifically.
|
||||
|
||||
2. Queued file sinks may flush pending records before closing the file.
|
||||
2. Queued file sinks flush pending records before closing the file, unlike generic `close()`.
|
||||
|
||||
3. Library or application facades derived from the same configured runtime logger still observe the same underlying file-close state.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: configured-logger-file-flush
|
||||
group: api
|
||||
category: runtime
|
||||
update-time: 20260512
|
||||
update-time: 20260614
|
||||
description: Flush the file sink behind a configured runtime logger when one is present.
|
||||
key-word:
|
||||
- logger
|
||||
@@ -35,8 +35,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Plain file sinks forward directly to file flush behavior through the wrapped `RuntimeSink`.
|
||||
- Queued file sinks first flush queued records, then flush the wrapped inner file sink.
|
||||
- For queued file sinks, the returned `Bool` comes from the inner file flush rather than the queue flush step.
|
||||
- For queued file sinks, this step may also be the point where queued records finally hit the inner file sink, so write-failure counters can change here even if earlier log calls only queued records.
|
||||
- Non-file sinks return `false`.
|
||||
- This helper is narrower than generic `flush()` because it targets file sink behavior specifically.
|
||||
- After a file-backed configured logger has already cleared its file handle, later `file_flush()` calls return `false`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,10 +68,14 @@ In this example, callers can distinguish file flush success from a non-file sink
|
||||
e.g.:
|
||||
- If the configured sink is not file-backed, the method returns `false`.
|
||||
|
||||
- If the file handle was already closed earlier through this logger or another facade sharing the same wrapped runtime sink state, the method returns `false`.
|
||||
|
||||
- If callers want generic queue or sink advancement instead of file-specific behavior, `flush()` is the broader API.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this helper when the configured sink is known to be file-backed.
|
||||
|
||||
2. Queued file sinks may perform both queue flush and file flush work here.
|
||||
2. Queued file sinks may perform both queue flush and file flush work here, including surfacing delayed write failures from previously queued records.
|
||||
|
||||
3. Library or application facades derived from the same configured runtime logger still observe the same underlying file-flush availability state.
|
||||
|
||||
@@ -72,4 +72,4 @@ e.g.:
|
||||
|
||||
1. Use this helper after diagnostics or recovery, not before capturing needed evidence.
|
||||
|
||||
2. It is the reset companion for the file failure-counter helpers.
|
||||
2. For queued file sinks, it resets the inner file counters only; it does not clear still-pending queued records that may produce new failures on a later `file_flush()`.
|
||||
|
||||
@@ -35,6 +35,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- File-backed sinks report their recorded write-failure count through the wrapped `RuntimeSink`.
|
||||
- Queued file sinks forward the metric from the wrapped inner file sink.
|
||||
- For queued file sinks, enqueueing a record does not increment this counter by itself. The counter changes only when queued records are drained into the inner file sink, such as through `file_flush()`.
|
||||
- Non-file sinks return `0`.
|
||||
- The counter is cumulative until reset.
|
||||
|
||||
@@ -71,4 +72,4 @@ e.g.:
|
||||
|
||||
1. Use this helper for focused write-failure visibility.
|
||||
|
||||
2. Pair it with `file_flush_failures()` when diagnosing output-path instability.
|
||||
2. Pair it with `file_flush()` and `file_flush_failures()` when diagnosing queued output-path instability.
|
||||
|
||||
@@ -31,8 +31,13 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a separate wrapper implementation.
|
||||
- It preserves normal logger methods such as `info(...)`, `warn(...)`, `error(...)`, and the other `Logger` APIs.
|
||||
- Because it is `Logger[RuntimeSink]`, it also keeps ordinary logger composition and target behavior such as `with_target(...)`, `child(...)`, and per-call `log(..., target=...)` overrides.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- It also exposes configured runtime helpers such as `flush()`, `drain()`, `pending_count()`, `dropped_count()`, and file-specific controls when the runtime sink supports them.
|
||||
- Because `ConfiguredLogger` is only an alias over `Logger[RuntimeSink]`, ordinary sync composition helpers such as `with_target(...)`, `child(...)`, `with_timestamp(...)`, `with_filter(...)`, and `with_patch(...)` keep the same visible runtime-sink logger line rather than stripping away the configured helper surface.
|
||||
- In current direct builder coverage, derived configured loggers still preserve queue counters, drain or flush behavior, runtime sink variant choice, and file helper access instead of collapsing back to a narrower non-runtime shape.
|
||||
- Builders such as `build_logger(...)` and `parse_and_build_logger(...)` return this alias as the main sync config-to-runtime result.
|
||||
- `ApplicationLogger` is another direct alias-oriented name for this same configured runtime surface, while `LibraryLogger[RuntimeSink]` is the narrowing facade variant that intentionally hides these runtime helpers until `to_logger()` is used.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,6 +62,21 @@ ignore(logger.pending_count())
|
||||
|
||||
In this example, the alias keeps ordinary logging calls while still exposing runtime diagnostics.
|
||||
|
||||
And the inherited logger target rules stay unchanged: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
And later derived values such as `logger.child("worker")` or `logger.with_patch(...)` still keep the runtime helper surface because the configured logger line is only being recomposed, not narrowed or rebuilt into a different facade.
|
||||
|
||||
#### When Need A One-call Target Override On The Configured Runtime Logger
|
||||
|
||||
When config-built sync code should keep the same logger value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.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(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -68,4 +88,8 @@ e.g.:
|
||||
|
||||
1. Use `build_logger(...)` or `parse_and_build_logger(...)` when you need a value of this type.
|
||||
|
||||
2. Use `ApplicationLogger` or `LibraryLogger[RuntimeSink]` when a more intention-specific facade name fits the calling code better.
|
||||
2. Inherited `Logger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `ApplicationLogger` when application code wants the same full configured-runtime helper surface under an app-facing name.
|
||||
|
||||
4. Use `LibraryLogger[RuntimeSink]` when a library boundary should intentionally hide configured-runtime helper methods behind a narrower facade.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: default-library-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Create the default library-facing console logger facade from shared global defaults.
|
||||
description: Create the default library-facing console logger facade by wrapping the current shared default logger at call time.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -31,7 +31,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API wraps `default_logger()` as a library facade.
|
||||
- Each call reflects the current shared default minimum level and default target at that moment.
|
||||
- The returned facade wraps the same underlying `Logger[ConsoleSink]` value that `default_logger()` would produce directly at that moment.
|
||||
- The returned value exposes the narrower `LibraryLogger` surface rather than the full `Logger` surface.
|
||||
- Later changes to shared defaults do not mutate an already-created facade value because the wrapped logger is captured when `default_library_logger()` is called.
|
||||
- Call `default_library_logger()` again after shared default changes if a fresh narrowed value should reflect the updated defaults.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -49,6 +52,8 @@ if logger.is_enabled(Level::Info) {
|
||||
|
||||
In this example, the library facade mirrors the current global defaults.
|
||||
|
||||
And if the caller later unwraps it with `to_logger()`, the same captured default target and minimum level are still present on the full logger value.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -61,3 +66,5 @@ e.g.:
|
||||
1. Use `to_logger()` if callers need the full sync logger surface.
|
||||
|
||||
2. This helper is useful for library-facing APIs that should stay narrower than `Logger`.
|
||||
|
||||
3. Call `default_library_logger()` again after shared default changes if a fresh facade value should reflect the new defaults.
|
||||
|
||||
@@ -31,6 +31,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper returns the same baseline config shape used by `LoggerConfig::new()` defaults.
|
||||
- It includes the default minimum level, empty target, disabled timestamp, default sink config, and no queue wrapper.
|
||||
- The implementation is exactly `LoggerConfig::new()`, so it stays aligned with the constructor defaults rather than duplicating a separate preset object.
|
||||
- That means `build_logger(...)` can consume the returned value directly, and `build_async_logger(...)` reuses the same sync-side defaults before adding the outer async layer.
|
||||
- It is useful for explicit config composition when callers want a known baseline object.
|
||||
|
||||
### How to Use
|
||||
@@ -58,3 +60,5 @@ e.g.:
|
||||
1. This helper is the top-level default config entry point.
|
||||
|
||||
2. It is useful for explicit config-first workflows and tests.
|
||||
|
||||
3. Use `default_sink_config()` when callers only want the baseline sink portion rather than the full top-level logger config.
|
||||
|
||||
@@ -31,7 +31,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned logger is built from `default_console_sink`, `default_min_level_ref`, and `default_target_ref`.
|
||||
- Each call reflects the current shared configuration at that moment.
|
||||
- `default_logger()` constructs a fresh `Logger::new(...)` value on each call while reusing the shared default console sink and the current stored default level and target values.
|
||||
- Later calls to `set_default_min_level(...)` or `set_default_target(...)` do not mutate a logger value that was already returned earlier.
|
||||
- The logger writes to the standard console sink path.
|
||||
- The global helper functions such as `log(...)`, `info(...)`, and `error(...)` call `default_logger()` for each write instead of holding one long-lived default logger instance.
|
||||
- This helper is useful when you want the same baseline behavior as the global shortcuts but still need the explicit `Logger` object for chaining or inspection.
|
||||
|
||||
### How to Use
|
||||
@@ -48,6 +51,8 @@ logger.info("service started")
|
||||
|
||||
In this example, the logger starts from global defaults and then gains extra instance-level behavior.
|
||||
|
||||
Later shared-default changes still require calling `default_logger()` again if a fresh explicit logger value should observe them.
|
||||
|
||||
#### When Inspect Current Shared Behavior
|
||||
|
||||
When code should branch using the same threshold as global helpers:
|
||||
@@ -73,3 +78,5 @@ e.g.:
|
||||
|
||||
2. It is the bridge between the simple global API and the explicit typed logger workflow.
|
||||
|
||||
3. Call `default_logger()` again after `set_default_min_level(...)` or `set_default_target(...)` if a fresh explicit logger value should reflect the updated shared defaults.
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper returns the same baseline sink config used by `LoggerConfig::new(...)` defaults.
|
||||
- The default sink config is console-oriented unless later replaced by explicit config composition.
|
||||
- The implementation is exactly `SinkConfig::new()`, so the returned value uses the standard constructor defaults: `kind=Console`, empty `path`, `append=true`, `auto_flush=true`, no rotation, and the default text formatter config.
|
||||
- `parse_logger_config_text(...)` also falls back to this same helper when the `sink` object is omitted from JSON input.
|
||||
- It is a typed config object, not a runtime sink instance.
|
||||
|
||||
### How to Use
|
||||
@@ -58,3 +60,5 @@ e.g.:
|
||||
1. This helper is for config-driven assembly, not runtime sink wiring.
|
||||
|
||||
2. It is most useful in explicit config composition code.
|
||||
|
||||
3. Use `default_logger_config()` when callers want the full top-level config default instead of only the sink portion.
|
||||
|
||||
@@ -30,6 +30,7 @@ pub fn default_text_formatter_config() -> TextFormatterConfig {
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper returns the same baseline config value used by `text_console(...)`, `file(...)`, and `SinkConfig::new(...)` defaults.
|
||||
- The implementation is exactly `TextFormatterConfig::new()`, so it stays aligned with the constructor defaults rather than duplicating a separate preset object.
|
||||
- It is a config object, not a runtime `TextFormatter`.
|
||||
- Call `to_formatter()` when a runtime formatter is needed.
|
||||
|
||||
@@ -58,3 +59,5 @@ e.g.:
|
||||
1. Use this helper for explicit config composition.
|
||||
|
||||
2. Use `text_formatter(...)` when you want a runtime formatter directly.
|
||||
|
||||
3. `parse_logger_config_text(...)` also falls back to this same baseline when a sink omits its nested `text_formatter` object.
|
||||
|
||||
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- On flush failure, `flush_failures` is incremented.
|
||||
- On success, failure counters are left unchanged.
|
||||
- This helper does not reopen the sink automatically.
|
||||
- If another alias already closed the same underlying sink handle, this method also returns `false`.
|
||||
|
||||
### How to Use
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ pub fn debug(message : String, fields~ : Array[Field] = []) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `default_logger().debug(...)`.
|
||||
- The record uses the current shared minimum level and target configuration.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- It uses the shared console sink and current default target.
|
||||
- Debug output is typically intended for development and troubleshooting.
|
||||
- This helper is best suited to small apps or code paths that intentionally rely on the shared logger.
|
||||
|
||||
@@ -48,10 +49,13 @@ Here are some specific examples provided.
|
||||
When code wants quick diagnostics without wiring a logger variable:
|
||||
```moonbit
|
||||
set_default_min_level(Level::Debug)
|
||||
set_default_target("cache")
|
||||
debug("cache refreshed")
|
||||
```
|
||||
|
||||
In this example, the shared global path starts accepting debug records.
|
||||
In this example, the shared global path starts accepting debug records under the `cache` target.
|
||||
|
||||
If `set_default_target(...)` or `set_default_min_level(...)` changes later, future `debug(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Debug Context
|
||||
|
||||
@@ -73,5 +77,7 @@ e.g.:
|
||||
|
||||
1. Prefer explicit loggers once application logging becomes subsystem-specific.
|
||||
|
||||
2. Use `set_default_min_level(...)` before expecting global debug output to appear.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. Use `set_default_min_level(...)` before expecting global debug output to appear.
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ pub fn error(message : String, fields~ : Array[Field] = []) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `default_logger().error(...)`.
|
||||
- It uses the shared console sink, default target, and current threshold configuration.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- It uses the shared console sink and current default target.
|
||||
- `Error` is the highest built-in severity in the global sync helper set.
|
||||
- This helper is useful when a small app wants a direct shared failure-reporting path.
|
||||
|
||||
@@ -47,10 +48,13 @@ Here are some specific examples provided.
|
||||
|
||||
When a small application should emit an explicit failure log:
|
||||
```moonbit
|
||||
set_default_target("worker")
|
||||
error("worker execution failed")
|
||||
```
|
||||
|
||||
In this example, the shared default logger emits a high-severity error record.
|
||||
In this example, the shared default logger emits a high-severity error record under the `worker` target.
|
||||
|
||||
If `set_default_target(...)` or `set_default_min_level(...)` changes later, future `error(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Error Context
|
||||
|
||||
@@ -72,5 +76,7 @@ e.g.:
|
||||
|
||||
1. Use this helper for simple shared error reporting.
|
||||
|
||||
2. Explicit `Logger` values are usually better once the application needs richer routing or ownership boundaries.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. Explicit `Logger` values are usually better once the application needs richer routing or ownership boundaries.
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ pub fn info(message : String, fields~ : Array[Field] = []) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `default_logger().info(...)`.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- It uses the shared console sink and current default target.
|
||||
- `Info` is the default global threshold unless changed through `set_default_min_level(...)`.
|
||||
- This is the simplest entry point for normal app-level informational events.
|
||||
@@ -53,6 +54,8 @@ info("service started")
|
||||
|
||||
In this example, the shared default logger emits an informational record under the `app` target.
|
||||
|
||||
If `set_default_target(...)` or `set_default_min_level(...)` changes later, future `info(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Informational Data
|
||||
|
||||
When a global info event should include metadata:
|
||||
@@ -73,5 +76,7 @@ e.g.:
|
||||
|
||||
1. This is the most common global write helper for small applications or scripts.
|
||||
|
||||
2. Prefer explicit loggers when the codebase grows beyond one shared logging path.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. Prefer explicit loggers when the codebase grows beyond one shared logging path.
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ pub fn log(level : Level, message : String, fields~ : Array[Field] = []) -> Unit
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The function calls `default_logger().log(level, message, fields=fields)`.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- It uses the shared console sink and the current global default threshold and target.
|
||||
- Per-call target override is not exposed by this global shortcut.
|
||||
- This helper is most useful when convenience matters more than explicit logger ownership.
|
||||
@@ -53,6 +54,8 @@ log(Level::Info, "service started")
|
||||
|
||||
In this example, the shared default logger handles the record without requiring an explicit logger variable.
|
||||
|
||||
If `set_default_min_level(...)` or `set_default_target(...)` changes later, future `log(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Metadata Globally
|
||||
|
||||
When a global event should still include fields:
|
||||
@@ -73,5 +76,7 @@ e.g.:
|
||||
|
||||
1. This API is convenient but intentionally less configurable than an explicit logger value.
|
||||
|
||||
2. Prefer explicit loggers when different subsystems need different sink or target behavior.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. Prefer explicit loggers when different subsystems need different sink or target behavior.
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ pub fn trace(message : String, fields~ : Array[Field] = []) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `default_logger().trace(...)`.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- Trace is the lowest built-in severity and is commonly disabled unless the shared minimum level is lowered.
|
||||
- The shared default target and console sink are used.
|
||||
- It uses the shared console sink and current default target.
|
||||
- This helper favors convenience over explicit per-component logger ownership.
|
||||
|
||||
### How to Use
|
||||
@@ -48,10 +49,13 @@ Here are some specific examples provided.
|
||||
When a script or small app wants temporary low-level diagnostics:
|
||||
```moonbit
|
||||
set_default_min_level(Level::Trace)
|
||||
set_default_target("loop")
|
||||
trace("entered sync loop")
|
||||
```
|
||||
|
||||
In this example, the shared global path begins accepting trace records.
|
||||
In this example, the shared global path begins accepting trace records under the `loop` target.
|
||||
|
||||
If `set_default_target(...)` or `set_default_min_level(...)` changes later, future `trace(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Trace Context
|
||||
|
||||
@@ -73,5 +77,7 @@ e.g.:
|
||||
|
||||
1. Global trace logging is convenient for scripts but easy to overuse in larger systems.
|
||||
|
||||
2. This helper always goes through the shared default logger path.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. This helper always goes through the shared default logger path.
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ pub fn warn(message : String, fields~ : Array[Field] = []) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `default_logger().warn(...)`.
|
||||
- The shared current threshold and target determine whether and how the record is emitted.
|
||||
- Each call therefore reads the current shared default threshold and target at write time instead of holding one long-lived logger value internally.
|
||||
- It uses the shared console sink and current default target.
|
||||
- Warning logs are suited to degraded or suspicious states that do not necessarily stop execution.
|
||||
- This helper offers convenience for a single shared logging path.
|
||||
|
||||
@@ -47,10 +48,13 @@ Here are some specific examples provided.
|
||||
|
||||
When a recoverable issue should be surfaced without creating a logger variable:
|
||||
```moonbit
|
||||
set_default_target("cache")
|
||||
warn("cache miss ratio increased")
|
||||
```
|
||||
|
||||
In this example, the shared default logger emits a warning-severity record.
|
||||
In this example, the shared default logger emits a warning-severity record under the `cache` target.
|
||||
|
||||
If `set_default_target(...)` or `set_default_min_level(...)` changes later, future `warn(...)` calls will observe those updated shared defaults automatically.
|
||||
|
||||
#### When Attach Structured Warning Context
|
||||
|
||||
@@ -72,5 +76,7 @@ e.g.:
|
||||
|
||||
1. Global warnings are convenient for simple apps and scripts.
|
||||
|
||||
2. Warning-level global events are often the first candidate for separate monitoring or routing later.
|
||||
2. Future calls pick up later shared-default changes because the helper forwards through a fresh `default_logger()` each time.
|
||||
|
||||
3. Warning-level global events are often the first candidate for separate monitoring or routing later.
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ key-word:
|
||||
|
||||
BitLogger API navigation.
|
||||
|
||||
<ApiOverview />
|
||||
|
||||
## Target and verification
|
||||
|
||||
- [target-verification.md](./target-verification.md)
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-async-logger-bind
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Attach reusable structured fields to a LibraryAsyncLogger facade through the bind alias.
|
||||
description: Attach reusable structured fields to a LibraryAsyncLogger facade through the bind alias while preserving the same underlying async logger state.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -38,8 +38,11 @@ pub fn[S] LibraryAsyncLogger::bind(
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `bind(...)` delegates directly to `with_context_fields(...)`.
|
||||
- That means the provided `fields` array replaces the previously stored shared field set on the wrapped async logger.
|
||||
- Shared fields are then prepended to every later log call emitted through the returned facade.
|
||||
- Sink type, queue state, async config, and failure/lifecycle state remain the same because the method only rewraps the updated async logger value.
|
||||
- Async state helpers remain hidden behind the narrower facade after rewrapping; use `to_async_logger()` if later code needs them.
|
||||
- The original facade value is not mutated; a wrapped facade is returned.
|
||||
- Shared fields are applied to every later log call emitted through the returned facade.
|
||||
- This alias is useful when you prefer shorter chaining syntax in library async code.
|
||||
|
||||
### How to Use
|
||||
@@ -68,15 +71,21 @@ let worker = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="app")
|
||||
|
||||
In this example, `bind(...)` communicates intent without changing underlying behavior.
|
||||
|
||||
And because it is only an alias, the resulting facade behaves the same as calling `with_context_fields(...)` directly.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `fields` is empty, the returned facade remains valid and simply adds no extra metadata.
|
||||
- If `fields` is empty, the returned facade remains valid and simply stores an empty shared field set.
|
||||
|
||||
- If duplicate field keys are bound, all copies are preserved for downstream formatting or inspection.
|
||||
|
||||
- If callers want event-specific fields without replacing the shared bound set, they should pass those through `log(..., fields=...)` on the returned facade.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `bind(...)` and `with_context_fields(...)` interchangeably; choose the one that reads better in context.
|
||||
|
||||
2. This alias exists for ergonomics, not for different semantics.
|
||||
|
||||
3. Use `with_context_fields(...)` when the longer name makes the shared-field replacement behavior clearer at the call site.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-child
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Derive a child LibraryAsyncLogger facade by composing the current target with a child segment.
|
||||
update-time: 20260614
|
||||
description: Derive a child LibraryAsyncLogger facade by composing the current target with a child segment while rewrapping the derived async logger state behind the same library-facing surface.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -37,10 +37,13 @@ pub fn[S] LibraryAsyncLogger::child(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to the wrapped async logger's `child(...)` behavior and then re-wraps the result.
|
||||
- This API delegates to the wrapped async logger's `child(...)` behavior and then re-wraps the result as another `LibraryAsyncLogger[S]`.
|
||||
- If the parent target is empty, the child target becomes the full target.
|
||||
- If the child target is empty, the parent target is preserved.
|
||||
- If both are non-empty, they are joined with `.`.
|
||||
- The original facade is not mutated. The returned facade stores the derived child target while the source facade keeps its previous parent target.
|
||||
- Timestamp behavior, enabled-level gating, sink type, queue state, async config, and failure/lifecycle state remain the same because only the derived default target changes.
|
||||
- Async state helpers remain hidden behind the narrower facade after rewrapping; use `to_async_logger()` if later code needs them.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,6 +71,10 @@ let client = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="sdk")
|
||||
|
||||
In this example, the final facade emits under `sdk.http.client`.
|
||||
|
||||
And the target derivation does not rebuild or reset the wrapped async logger state.
|
||||
|
||||
The derived facade still uses the same timestamp and level-gating behavior as the source facade.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,3 +87,5 @@ e.g.:
|
||||
1. This is the preferred library-facing API for hierarchical async target naming.
|
||||
|
||||
2. Composition changes the target only and does not rebuild the queue or sink.
|
||||
|
||||
3. Use `with_target(...)` instead when the new target should replace the current target rather than extend it.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-error
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Enqueue an error-level record through a LibraryAsyncLogger facade using the highest built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an error-level record through a LibraryAsyncLogger facade by delegating to the wrapped async logger's error shortcut.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -39,10 +39,12 @@ pub async fn[S] LibraryAsyncLogger::error(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Error, ...)` on the wrapped async logger.
|
||||
- The record is still subject to patching, filtering, and overflow policy.
|
||||
- This helper delegates to `error(...)` on the wrapped async logger, which in turn uses `log(Level::Error, ...)`.
|
||||
- The record is still subject to min-level gating, stored shared context fields, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the facade's stored target unless the facade was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Error records represent the highest built-in severity in this async facade API.
|
||||
- Use this helper when a named error call is clearer than a raw `log(...)` call.
|
||||
- Async state helpers remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,7 +54,7 @@ Here are some specific examples provided.
|
||||
|
||||
When an operation should emit a high-severity failure event:
|
||||
```moonbit
|
||||
await logger.error("worker execution failed")
|
||||
logger.error("worker execution failed")
|
||||
```
|
||||
|
||||
In this example, failure intent is explicit at the call site.
|
||||
@@ -61,7 +63,7 @@ In this example, failure intent is explicit at the call site.
|
||||
|
||||
When an error event should include diagnostic fields:
|
||||
```moonbit
|
||||
await logger.error(
|
||||
logger.error(
|
||||
"dispatch failed",
|
||||
fields=[@bitlogger.field("job_id", "42")],
|
||||
)
|
||||
@@ -69,15 +71,25 @@ await logger.error(
|
||||
|
||||
In this example, the error record carries structured context without falling back to the generic `log(...)` form.
|
||||
|
||||
And any shared context fields already stored on the facade are still prepended before these per-call fields.
|
||||
|
||||
And the write still uses the facade's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the logger is closed or overflow policy prevents acceptance, even an error-level record may not become a normal queued record.
|
||||
|
||||
- If the logger minimum level is above `Error`, the helper still follows the same level gate, although that usually requires a custom higher threshold outside the common built-in levels.
|
||||
|
||||
- If callers need to inspect worker failure rather than emit an error record, `has_failed()` and `last_error()` are the relevant APIs on the full async logger.
|
||||
|
||||
- If callers need a per-call target override, they should use `log(...)` instead of this fixed-level shortcut.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper for high-severity async application failures.
|
||||
|
||||
2. Emitting an error record is separate from the logger worker itself entering failure state.
|
||||
|
||||
3. Use `to_async_logger()` first when later code needs queue or failure inspection rather than another write shortcut.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-info
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Enqueue an info-level record through a LibraryAsyncLogger facade using the informational severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an info-level record through a LibraryAsyncLogger facade by delegating to the wrapped async logger's info shortcut.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -39,10 +39,12 @@ pub async fn[S] LibraryAsyncLogger::info(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Info, ...)` on the wrapped async logger.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper delegates to `info(...)` on the wrapped async logger, which in turn uses `log(Level::Info, ...)`.
|
||||
- The record is still subject to min-level gating, stored shared context fields, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the facade's stored target unless the facade was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- `Info` is often the default operational logging level for async application events.
|
||||
- Use this helper when explicit info intent is clearer than a raw `log(...)` call.
|
||||
- Async state helpers remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,7 +54,7 @@ Here are some specific examples provided.
|
||||
|
||||
When async package code should report routine progress or lifecycle events:
|
||||
```moonbit
|
||||
await logger.info("worker started")
|
||||
logger.info("worker started")
|
||||
```
|
||||
|
||||
In this example, the event is expressed at the common operational logging level through the narrower facade.
|
||||
@@ -61,7 +63,7 @@ In this example, the event is expressed at the common operational logging level
|
||||
|
||||
When an info event should include stable structured detail:
|
||||
```moonbit
|
||||
await logger.info(
|
||||
logger.info(
|
||||
"job queued",
|
||||
fields=[@bitlogger.field("queue", "sync")],
|
||||
)
|
||||
@@ -69,6 +71,10 @@ await logger.info(
|
||||
|
||||
In this example, the record remains concise while still carrying useful metadata.
|
||||
|
||||
And any shared context fields already stored on the facade are still prepended before these per-call fields.
|
||||
|
||||
And the write still uses the facade's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -76,8 +82,12 @@ e.g.:
|
||||
|
||||
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
|
||||
|
||||
- If callers need a per-call target override, they should use `log(...)` instead of this fixed-level shortcut.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is often the most common convenience method for normal async library events.
|
||||
|
||||
2. Use `log(...)` when the call site needs a dynamic level or target override.
|
||||
|
||||
3. Use `to_async_logger()` first when later code needs queue or failure inspection rather than another write shortcut.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-is-enabled
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Check whether a LibraryAsyncLogger facade would accept a record at a given level.
|
||||
update-time: 20260614
|
||||
description: Check whether a LibraryAsyncLogger facade passes the wrapped async logger's min-level gate for a given severity.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -38,8 +38,9 @@ pub fn[S] LibraryAsyncLogger::is_enabled(
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped async logger's `is_enabled(...)` behavior.
|
||||
- It only checks logger-level severity gating; it does not evaluate later filter or overflow behavior.
|
||||
- The result reflects the current facade value, so derived facades with different thresholds can return different answers.
|
||||
- It only checks logger-level severity gating through `min_level`; it does not evaluate later filter, patch, closed-on-log, or overflow behavior.
|
||||
- The result reflects the current wrapped logger state, so derived facades with different thresholds can return different answers.
|
||||
- This check does not require unwrapping because it is part of the narrower library-facing surface.
|
||||
- Use it when message construction or field gathering is expensive enough to justify a pre-check.
|
||||
|
||||
### How to Use
|
||||
@@ -51,7 +52,7 @@ Here are some specific examples provided.
|
||||
When debug data should only be built if needed:
|
||||
```moonbit
|
||||
if logger.is_enabled(@bitlogger.Level::Info) {
|
||||
await logger.info(build_summary())
|
||||
logger.info(build_summary())
|
||||
}
|
||||
```
|
||||
|
||||
@@ -62,12 +63,14 @@ In this example, expensive preparation is skipped unless the current threshold a
|
||||
When package code should branch based on current logger behavior:
|
||||
```moonbit
|
||||
if logger.is_enabled(@bitlogger.Level::Warn) {
|
||||
await logger.warn("slow path active")
|
||||
logger.warn("slow path active")
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the check mirrors the same severity gate used by later async write calls.
|
||||
|
||||
And a `true` result still means only that the level gate passed, not that the record is guaranteed to reach the sink.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -75,8 +78,12 @@ e.g.:
|
||||
|
||||
- A `true` result does not guarantee final delivery if later queue or filter behavior rejects the record.
|
||||
|
||||
- If callers need to inspect queue or failure state instead of only checking the level gate, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a cheap severity check, not a full end-to-end delivery guarantee.
|
||||
|
||||
2. Prefer using it only when precomputing the async log payload is meaningfully expensive.
|
||||
|
||||
3. Use `log(...)` or the severity shortcuts directly when no expensive precomputation needs to be avoided.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-log
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Enqueue a record through a LibraryAsyncLogger facade with explicit level, message, fields, and optional target override.
|
||||
update-time: 20260614
|
||||
description: Enqueue a record through a LibraryAsyncLogger facade with explicit level, message, fields, and optional target override by delegating to the wrapped async logger.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -45,8 +45,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped async logger's `log(...)` behavior.
|
||||
- The logger checks `is_enabled(level)` before building a record.
|
||||
- Context fields, patch logic, and filter logic are applied before enqueue.
|
||||
- The narrower library facade does not change enqueue semantics; it only limits the visible API surface.
|
||||
- Stored shared context fields are prepended ahead of per-call `fields`, then patch and filter logic are applied before enqueue.
|
||||
- If `target` is empty, the logger uses its current default target; otherwise the provided per-call target overrides that default for this one write.
|
||||
- The narrower library facade does not change enqueue semantics, overflow handling, or runtime-dependent closed-on-log behavior; in particular, compatibility runtimes can short-circuit before patch and enqueue work, while native-worker runtimes may still reach wrapped record construction and then treat the closed queue as a non-accepted write. The facade only limits the visible API surface.
|
||||
- Async state helpers such as `pending_count()`, `dropped_count()`, `state()`, `has_failed()`, and `last_error()` remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,7 +58,7 @@ Here are some specific examples provided.
|
||||
|
||||
When library code should choose level, fields, and target per event:
|
||||
```moonbit
|
||||
await logger.log(
|
||||
logger.log(
|
||||
@bitlogger.Level::Info,
|
||||
"worker started",
|
||||
fields=[@bitlogger.field("job", "sync")],
|
||||
@@ -70,11 +72,25 @@ In this example, the call site controls every major record property while keepin
|
||||
|
||||
When package code defines its own logging helpers:
|
||||
```moonbit
|
||||
await logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
```
|
||||
|
||||
In this example, `log(...)` acts as the shared primitive under custom library-facing wrappers.
|
||||
|
||||
#### When Need Shared Context Plus Event-specific Fields
|
||||
|
||||
When a facade already carries shared metadata but a single write needs additional detail:
|
||||
```moonbit
|
||||
let logger = base.with_context_fields([@bitlogger.field("service", "cache")])
|
||||
logger.log(
|
||||
@bitlogger.Level::Info,
|
||||
"entry refreshed",
|
||||
fields=[@bitlogger.field("key", "user:42")],
|
||||
)
|
||||
```
|
||||
|
||||
In this example, the stored shared fields remain in front of the per-call fields when the record is built.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -82,8 +98,12 @@ e.g.:
|
||||
|
||||
- If the logger is closed or overflow policy rejects the record, enqueue may not proceed as a normal accepted write.
|
||||
|
||||
- If callers need to inspect queue or failure state after writing, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when the call site needs full control instead of a fixed severity helper.
|
||||
|
||||
2. Prefer `info()`, `warn()`, `error()`, and the other shortcuts when only the level differs.
|
||||
|
||||
3. Use the optional `target` argument when one write should temporarily override the facade's default target without rebuilding or rewrapping it.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-async-logger-new
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Create a narrower library-facing async logger facade from any sink implementation.
|
||||
description: Create a narrower library-facing async logger facade by building a regular async logger and wrapping the same underlying state.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -29,11 +29,11 @@ pub fn[S] LibraryAsyncLogger::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `sink : S` - Any sink value implementing `@bitlogger.Sink`, such as `@bitlogger.console_sink()` or a composed sink.
|
||||
- `sink : S` - Underlying sink value that should receive drained records, such as `@bitlogger.console_sink()` or a composed sink.
|
||||
- `config : AsyncLoggerConfig` - Queue size, overflow behavior, batching, linger, and flush policy.
|
||||
- `min_level : Level` - Minimum enabled level. Messages below this threshold are ignored before enqueue.
|
||||
- `target : String` - Default target attached to emitted records unless later overridden.
|
||||
- `flush : (S) -> Int raise` - Flush callback used when async batch or shutdown policy needs explicit flushing.
|
||||
- `flush : (S) -> Int raise` - Flush callback used when async batch or shutdown policy needs explicit flushing, and allowed to raise if flushing fails.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -43,10 +43,12 @@ pub fn[S] LibraryAsyncLogger::new(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds a regular `async_logger(...)` internally and then wraps it as `LibraryAsyncLogger`.
|
||||
- This API builds a regular `async_logger(...)` internally and then wraps that same value as `LibraryAsyncLogger`.
|
||||
- The returned facade intentionally exposes a smaller method set than the full `AsyncLogger[S]` type.
|
||||
- Queue settings, flush policy, and runtime state are preserved inside the wrapped async logger.
|
||||
- Call `to_async_logger()` if later code must recover the full async logger surface.
|
||||
- Queue settings, flush policy, target, minimum level, and runtime state are preserved inside the wrapped async logger.
|
||||
- This constructor does not start background draining by itself; `run()` is still the step that activates queue processing.
|
||||
- If the supplied flush callback later raises during async drain or shutdown, failure state is still recorded on the wrapped async logger.
|
||||
- Call `to_async_logger()` if later code must recover the broader async logger surface for state inspection or additional composition helpers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -77,6 +79,17 @@ let logger = LibraryAsyncLogger::new(
|
||||
|
||||
In this example, the facade still keeps the queue-backed async behavior while hiding the full async logger type.
|
||||
|
||||
#### When Need Full Async State Helpers Later
|
||||
|
||||
When library-facing construction should still allow later access to the broader async helper set:
|
||||
```moonbit
|
||||
let logger = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="lib.async")
|
||||
let full = logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
```
|
||||
|
||||
In this example, construction starts from the narrower facade, but the same underlying async logger can still be recovered later.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -84,8 +97,12 @@ e.g.:
|
||||
|
||||
- If later code needs methods outside the facade, it must unwrap with `to_async_logger()`.
|
||||
|
||||
- Constructing the facade alone does not drain queued records; callers still need `run()` for active background processing.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is the direct constructor counterpart to `async_logger(...)` for library-oriented async APIs.
|
||||
|
||||
2. Prefer this when public package boundaries should stay narrower than `AsyncLogger[S]`.
|
||||
|
||||
3. Use `async_logger(...)` directly when the full async lifecycle, state, and composition surface should stay public from the start.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-async-logger-run
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Start the LibraryAsyncLogger worker loop so queued records are drained to the underlying sink.
|
||||
description: Start the LibraryAsyncLogger worker loop by delegating to the wrapped async logger's run behavior.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -36,7 +36,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This method delegates directly to the wrapped async logger's `run()` behavior.
|
||||
- It starts the worker loop that makes accepted queued records reach the underlying sink.
|
||||
- Failure and lifecycle state are still tracked by the wrapped async logger.
|
||||
- Once a delegated `run()` call has actually started, it clears any previous `has_failed()` flag and stored `last_error()` string on that same wrapped logger before draining resumes.
|
||||
- The narrower library facade does not hide the need to explicitly activate queue draining.
|
||||
- If the worker fails, the wrapped async logger records that through its failure-state helpers, which still require `to_async_logger()` for inspection from library-facing code.
|
||||
- Unwrapping after a delegated `run()` exposes the same failure and backlog state that accumulated behind the facade; it does not create a second runtime view.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,6 +68,8 @@ group.spawn_bg(() => logger.run())
|
||||
|
||||
In this example, the caller decides when the worker begins instead of hiding that lifecycle step.
|
||||
|
||||
And the started worker loop is still the same wrapped async logger runtime rather than a separate library-specific execution path.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -72,8 +77,14 @@ e.g.:
|
||||
|
||||
- If `run()` is never started, accepted records may remain queued and not reach the sink.
|
||||
|
||||
- A later delegated `run()` attempt starts from a fresh failure flag and empty `last_error()` string on the same wrapped logger, even if an earlier facade-level run failed.
|
||||
|
||||
- If callers need to inspect `is_running()`, `has_failed()`, or `last_error()` directly, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. `LibraryAsyncLogger::new(...)` only constructs the facade; `run()` is what activates queue draining.
|
||||
|
||||
2. Pair this API with `shutdown()` for a complete worker lifecycle.
|
||||
|
||||
3. Use `to_async_logger()` first when operational code needs direct lifecycle or failure inspection in addition to starting the worker.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-shutdown
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Gracefully stop a LibraryAsyncLogger by draining or clearing queued work, then waiting for the worker to finish.
|
||||
update-time: 20260614
|
||||
description: Gracefully stop a LibraryAsyncLogger by delegating to the wrapped async logger's shutdown behavior, including runtime-dependent drain and worker-wait rules.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -39,8 +39,14 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped async logger's `shutdown(...)` behavior.
|
||||
- `clear=false` first waits for idle, then closes the logger.
|
||||
- `clear=true` immediately closes and abandons pending records.
|
||||
- The method waits until the worker is no longer running before returning.
|
||||
- If the active async runtime uses shutdown clearing after idle and backlog still remains, the wrapped logger falls back to `close(clear=true)`.
|
||||
- `clear=true` immediately closes and abandons pending records, even if the facade never started a drain worker for the wrapped logger.
|
||||
- In runtimes where shutdown waits for workers, the method then waits until the worker is no longer running before returning.
|
||||
- After a worker-failure short-circuit, native-worker backends can still convert remaining backlog into dropped records, while compatibility backends skip that extra forced-clear step.
|
||||
- In the current direct facade coverage, the wrapped logger ends that path with `pending_count() == 0` and one more dropped item on native-worker runtimes, versus a still-nonzero pending count and no extra dropped cleanup on compatibility runtimes.
|
||||
- Delegated shutdown also does not clear retained worker failure state by itself. If the wrapped logger had already recorded a worker error, later inspection through `to_async_logger()` can still show `has_failed=true` and the same `last_error()` string after shutdown completes.
|
||||
- The narrower library facade does not change any of these runtime-dependent shutdown rules; it only keeps the broader inspection helpers out of the direct public surface.
|
||||
- Inspecting the logger later through `to_async_logger()` reveals the same delegated shutdown result rather than a rebuilt or translated lifecycle snapshot.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -50,7 +56,7 @@ Here are some specific examples provided.
|
||||
|
||||
When a service should stop logging only after queued records are drained:
|
||||
```moonbit
|
||||
await logger.shutdown()
|
||||
logger.shutdown()
|
||||
```
|
||||
|
||||
In this example, the facade waits for normal drain behavior before final closure.
|
||||
@@ -59,20 +65,34 @@ In this example, the facade waits for normal drain behavior before final closure
|
||||
|
||||
When teardown should prefer speed over preserving backlog:
|
||||
```moonbit
|
||||
await logger.shutdown(clear=true)
|
||||
logger.shutdown(clear=true)
|
||||
```
|
||||
|
||||
In this example, pending work is abandoned intentionally so shutdown can complete sooner.
|
||||
|
||||
And the decision still applies to the same wrapped async logger state rather than a separate library-specific queue.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `clear=true`, pending records are intentionally dropped rather than drained.
|
||||
|
||||
- `clear=true` can therefore be used as the facade-level no-worker cleanup path when queued records were accepted but no `run()` task was ever started.
|
||||
|
||||
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
|
||||
|
||||
- Even when delegated shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` on the unwrapped logger if the worker had already failed before shutdown cleanup completed.
|
||||
|
||||
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
|
||||
|
||||
- If callers need to inspect `pending_count()`, `dropped_count()`, `is_running()`, or failure state during shutdown analysis, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API over low-level closure control in normal library shutdown paths.
|
||||
|
||||
2. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
2. Exact post-close waiting behavior depends on the active async runtime mode.
|
||||
|
||||
3. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
|
||||
4. Use `to_async_logger()` first when shutdown orchestration also needs direct lifecycle or backlog inspection.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-to-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Recover the underlying full async logger from the library-facing async facade.
|
||||
update-time: 20260614
|
||||
description: Recover the underlying full async logger from the library-facing async facade without rebuilding, copying, or detaching the wrapped async state.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -34,8 +34,18 @@ pub fn[S] LibraryAsyncLogger::to_async_logger(self : LibraryAsyncLogger[S]) -> A
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This conversion unwraps the existing async logger instead of rebuilding it.
|
||||
- Queue state, sink wiring, target, and flush policy remain the same.
|
||||
- Queue state, sink wiring, target, min level, flush policy, and current failure/lifecycle state remain the same.
|
||||
- The returned `AsyncLogger[S]` is still that same live wrapped value, so later facade writes or lifecycle calls continue mutating the very logger instance that was unwrapped earlier.
|
||||
- Repeated `to_async_logger()` calls therefore do not create independent snapshots. They keep exposing the same underlying logger state that the facade already wraps.
|
||||
- Use this when code needs wider async logger APIs outside the library facade.
|
||||
- This is the step required for helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, `last_error()`, or broader composition methods that are intentionally hidden by `LibraryAsyncLogger[S]`.
|
||||
- It is also the step that exposes the real post-run or post-shutdown state after facade-level lifecycle calls, because those calls delegated to this same wrapped logger all along.
|
||||
- That includes failure/backlog combinations and runtime-dependent shutdown results exactly as they accumulated behind the facade.
|
||||
- Because the same live value is shared, code can unwrap once, keep that `AsyncLogger[S]` handle, and then observe `pending_count()`, `state()`, `is_closed()`, or failure fields changing as later facade-level `info(...)`, `log(...)`, `run()`, or `shutdown(...)` calls execute.
|
||||
- That also means the unwrapped logger can already be both `is_closed=true` and `has_failed=true`, with the same retained `last_error()` string and the same runtime-dependent pending-versus-dropped cleanup outcome that delegated shutdown left behind.
|
||||
- If the wrapped sink type has richer helper APIs, unwrapping also restores access to that same sink helper surface through the original `AsyncLogger[S]` value.
|
||||
- When the wrapped sink is `RuntimeSink`, that same unwrap also preserves queued runtime state and file-backed helper behavior exactly as they existed behind the facade, including runtime file state snapshots and file control methods.
|
||||
- Runtime sink helper mutations are still live too. Changing file append mode, auto-flush, rotation, reopen state, or related helper-managed file state through the unwrapped logger changes the same wrapped runtime sink that later facade writes and shutdown behavior continue using.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -45,12 +55,25 @@ Here are some specific examples provided.
|
||||
|
||||
When a library-facing async logger must be widened for additional async logger composition:
|
||||
```moonbit
|
||||
let library_logger = LibraryAsyncLogger::new(console_sink(), target="lib.async")
|
||||
let library_logger = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="lib.async")
|
||||
let full_logger = library_logger.to_async_logger().with_timestamp()
|
||||
```
|
||||
|
||||
In this example, the facade is unwrapped so the caller can access the full async logger API again.
|
||||
|
||||
#### When Need Direct Async State Inspection
|
||||
|
||||
When library-facing code later needs runtime diagnostics from the wrapped logger:
|
||||
```moonbit
|
||||
let full = library_logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
ignore(full.state())
|
||||
```
|
||||
|
||||
In this example, unwrapping exposes the broader async inspection helpers on the same underlying logger state.
|
||||
|
||||
The same handle keeps reflecting later facade writes instead of becoming a detached snapshot.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -58,8 +81,22 @@ e.g.:
|
||||
|
||||
- Unwrapping does not start or stop the background runtime by itself.
|
||||
|
||||
- Recovering the full async logger does not clear queue contents or reset failure state.
|
||||
|
||||
- Unwrapping does not create an isolated snapshot. If later facade calls enqueue more records or change lifecycle state, the previously unwrapped logger handle reflects those same live mutations.
|
||||
|
||||
- Unwrapping does not convert facade lifecycle history into a simplified snapshot; any retained `last_error()`, pending backlog, dropped counts, or sink runtime details stay exactly as they were on the wrapped logger.
|
||||
|
||||
- If facade-level `run()` or `shutdown()` already ended in a failure-retaining state, unwrapping can therefore expose combinations such as `is_closed=true` together with `has_failed=true`, the same `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
|
||||
|
||||
- Recovering the full async logger also does not translate runtime sink helper state into a simpler snapshot; queue counters, file availability, file failure counters, and runtime file controls stay exactly as they were on the wrapped logger.
|
||||
|
||||
- Recovering the full async logger also does not isolate later runtime sink helper mutations. If unwrapped code changes file helper state, later facade writes and shutdown still run against that same updated wrapped sink.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this only when the narrower async facade is no longer sufficient.
|
||||
|
||||
2. This is the inverse projection of `AsyncLogger::to_library_async_logger()`.
|
||||
|
||||
3. Use `LibraryAsyncLogger[S]` directly when the narrower package-facing surface is still sufficient.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-warn
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Enqueue a warning-level record through a LibraryAsyncLogger facade using the built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a warning-level record through a LibraryAsyncLogger facade by delegating to the wrapped async logger's warn shortcut.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -39,10 +39,12 @@ pub async fn[S] LibraryAsyncLogger::warn(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Warn, ...)` on the wrapped async logger.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper delegates to `warn(...)` on the wrapped async logger, which in turn uses `log(Level::Warn, ...)`.
|
||||
- The record is still subject to min-level gating, stored shared context fields, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the facade's stored target unless the facade was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Warning records are useful for degraded but non-fatal runtime conditions.
|
||||
- Use this helper when a named warning call is clearer than a raw `log(...)` call.
|
||||
- Async state helpers remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,7 +54,7 @@ Here are some specific examples provided.
|
||||
|
||||
When the system should report a non-fatal problem:
|
||||
```moonbit
|
||||
await logger.warn("retry budget running low")
|
||||
logger.warn("retry budget running low")
|
||||
```
|
||||
|
||||
In this example, the event is surfaced at warning severity without using the generic `log(...)` form.
|
||||
@@ -61,7 +63,7 @@ In this example, the event is surfaced at warning severity without using the gen
|
||||
|
||||
When a warning event should include context:
|
||||
```moonbit
|
||||
await logger.warn(
|
||||
logger.warn(
|
||||
"queue near capacity",
|
||||
fields=[@bitlogger.field("pending", "64")],
|
||||
)
|
||||
@@ -69,6 +71,10 @@ await logger.warn(
|
||||
|
||||
In this example, the warning carries structured operational detail.
|
||||
|
||||
And any shared context fields already stored on the facade are still prepended before these per-call fields.
|
||||
|
||||
And the write still uses the facade's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -76,8 +82,12 @@ e.g.:
|
||||
|
||||
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
|
||||
|
||||
- If callers need a per-call target override, they should use `log(...)` instead of this fixed-level shortcut.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper for notable but non-fatal async runtime conditions.
|
||||
|
||||
2. Pair warnings with structured fields when operators need quick context.
|
||||
|
||||
3. Use `to_async_logger()` first when later code needs queue or failure inspection rather than another write shortcut.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-async-logger-with-context-fields
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Attach reusable structured fields to a LibraryAsyncLogger facade.
|
||||
description: Attach reusable structured fields to a LibraryAsyncLogger facade while rewrapping the same underlying async logger state.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -27,7 +27,7 @@ pub fn[S] LibraryAsyncLogger::with_context_fields(
|
||||
#### input
|
||||
|
||||
- `self : LibraryAsyncLogger[S]` - Base facade that should gain shared fields.
|
||||
- `fields : Array[@bitlogger.Field]` - Structured fields that will be prepended to each emitted record.
|
||||
- `fields : Array[@bitlogger.Field]` - Structured fields stored on the facade and prepended to each emitted record.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -38,8 +38,13 @@ pub fn[S] LibraryAsyncLogger::with_context_fields(
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to the wrapped async logger's `with_context_fields(...)` behavior and then narrows the result back to `LibraryAsyncLogger`.
|
||||
- Context fields are merged during `log(...)` before enqueue.
|
||||
- The provided `fields` array replaces the previously stored shared context field set on the wrapped async logger.
|
||||
- During later `log(...)` calls, those stored shared fields are prepended ahead of per-call fields before enqueue.
|
||||
- Sink type, queue state, async config, and failure/lifecycle state remain the same because only the stored shared field set changes.
|
||||
- Repeated `with_context_fields(...)` calls on derived facades therefore replace the stored shared field set on the next returned facade instead of stacking multiple shared field layers together.
|
||||
- Unlike synchronous `LibraryLogger::with_context_fields(...)`, this async variant preserves the visible `LibraryAsyncLogger[S]` type instead of changing the visible sink type.
|
||||
- Async state helpers remain hidden behind the narrower facade after rewrapping; use `to_async_logger()` if later code needs them.
|
||||
- The original facade value is not mutated; rewrapping returns a new facade over the updated async logger value.
|
||||
- `bind(...)` is an ergonomic alias for this same behavior.
|
||||
|
||||
### How to Use
|
||||
@@ -71,15 +76,23 @@ let worker = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="sdk")
|
||||
|
||||
In this example, target composition and field binding stay separate but compose cleanly.
|
||||
|
||||
And the returned facade keeps the same underlying async runtime state while carrying a different shared field set.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `fields` is empty, the returned facade remains valid and simply adds no extra metadata.
|
||||
- If `fields` is empty, the returned facade remains valid and simply stores an empty shared field set.
|
||||
|
||||
- If a derived library async facade already carried shared context fields, calling `with_context_fields(...)` again replaces that stored shared field set on the new returned facade rather than appending another shared layer.
|
||||
|
||||
- If duplicate field keys are provided, all fields are still emitted for downstream formatting or inspection.
|
||||
|
||||
- If callers want to add event-specific fields without replacing the shared set, they should pass those through `log(..., fields=...)` on the returned facade.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this for stable shared metadata, not highly dynamic event-specific values.
|
||||
|
||||
2. The narrower `LibraryAsyncLogger` surface is preserved after field binding.
|
||||
|
||||
3. Use `bind(...)` when the shorter alias reads better in chained library code.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-with-target
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Replace the default target carried by a LibraryAsyncLogger facade.
|
||||
update-time: 20260614
|
||||
description: Replace the default target carried by a LibraryAsyncLogger facade while rewrapping the derived async logger state behind the same library-facing surface.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -37,10 +37,11 @@ pub fn[S] LibraryAsyncLogger::with_target(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to the wrapped async logger's `with_target(...)` behavior and then re-wraps the result.
|
||||
- The returned value keeps the same queue state, sink type, and async config.
|
||||
- This API delegates to the wrapped async logger's `with_target(...)` behavior and then re-wraps the result as another `LibraryAsyncLogger[S]`.
|
||||
- The original facade is not mutated. The returned facade stores the replacement target while the source facade keeps its previous target.
|
||||
- Timestamp behavior, enabled-level gating, sink type, queue state, async config, and failure/lifecycle state stay the same because only the default target field changes.
|
||||
- This replaces the default target instead of composing it.
|
||||
- The original facade value is not mutated.
|
||||
- Async state helpers remain hidden behind the narrower facade after rewrapping; use `to_async_logger()` if later code needs them.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -70,6 +71,10 @@ let io = base.with_target("io")
|
||||
|
||||
In this example, one base facade becomes several target-specific async facades.
|
||||
|
||||
And each derived facade still wraps the same kind of queue-backed async logger state, differing only in the default target it carries.
|
||||
|
||||
The derived facade keeps the same timestamp and level-gating behavior as the source facade.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -82,3 +87,5 @@ e.g.:
|
||||
1. Use this API for replacement, not target-path composition.
|
||||
|
||||
2. It keeps the narrower `LibraryAsyncLogger` boundary intact.
|
||||
|
||||
3. Use `child(...)` when the new target should be combined with the current one instead of replacing it.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Public library-facing async logger facade type used to expose a narrower surface than AsyncLogger.
|
||||
update-time: 20260614
|
||||
description: Public library-facing async logger facade type used to expose a narrower surface than AsyncLogger while preserving sink typing.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
@@ -33,8 +33,20 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a public facade struct, not a type alias.
|
||||
- The wrapped sink type `S` is preserved, so sink-specific typing remains available through the facade type parameter.
|
||||
- The facade keeps common library-safe async operations such as `new(...)`, `with_target(...)`, `child(...)`, `bind(...)`, `is_enabled(...)`, `run()`, `shutdown()`, and the main async write methods.
|
||||
- The facade keeps common library-safe async operations such as `new(...)`, `with_target(...)`, `child(...)`, `bind(...)`, `is_enabled(...)`, `run()`, `shutdown()`, and the main async write methods `log(...)`, `info(...)`, `warn(...)`, and `error(...)`.
|
||||
- `LibraryAsyncLogger::new(...)` delegates to `async_logger(...)`, so the same async queue, raising flush callback, and runtime-dependent lifecycle behavior still exist behind the narrower facade.
|
||||
- Because `new(...)` delegates directly, `AsyncLoggerConfig` values such as pending limits, overflow mode, batch sizing, linger timing, and flush policy are preserved exactly rather than being translated into facade-specific defaults.
|
||||
- The facade also preserves the wrapped async logger's target rules on its exposed write methods: `log(..., target=...)` can override the target for one call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derives another logger with `with_target(...)` or `child(...)`.
|
||||
- Most facade reshaping helpers keep the same visible sink type, and unlike synchronous `LibraryLogger`, async `with_context_fields(...)` and `bind(...)` also preserve the visible `LibraryAsyncLogger[S]` type because they update stored async context metadata instead of changing the exposed sink wrapper.
|
||||
- It does not expose the wider `AsyncLogger[S]` composition surface or the async state and lifecycle inspection helpers directly.
|
||||
- Call `to_async_logger()` when later code must recover the full underlying `AsyncLogger[S]` surface.
|
||||
- That recovered logger is the same live async value, so queue counters, retained failure state, runtime-dependent shutdown outcomes, and sink helper access are preserved rather than recomputed.
|
||||
- If the delegated async flush callback fails, the facade preserves the same `has_failed()`, `last_error()`, `is_running()`, pending-backlog, and shutdown-cleanup semantics that the underlying `AsyncLogger[S]` would have exposed directly.
|
||||
- If `S` is `RuntimeSink`, queued runtime state and file-backed helper behavior also stay on that same wrapped logger value; the facade only hides direct access until `to_async_logger()` is used.
|
||||
- If `S` is `RuntimeSink` and the value came from `build_library_async_logger(...)` or `parse_and_build_library_async_logger(...)`, the wrapped logger also keeps that sync-first builder route unchanged: any optional `LoggerConfig.queue` was already applied before async wrapping, and `logger.sink.kind` had already decided the concrete `RuntimeSink` variant before the facade narrowed the surface.
|
||||
- In that runtime-sink route, unwrapping therefore exposes the same queued runtime variant choice and file-backed helper behavior that `build_async_logger(...)` would have returned directly.
|
||||
- If `S` is `FormattedConsoleSink` and the value came from `build_library_async_text_logger(...)`, the wrapped logger keeps that builder's formatter-driven text-console semantics as well: the optional sync queue layer was not applied before wrapping, and `logger.sink.kind` did not decide the sink shape.
|
||||
- In that text-builder route, unwrapping therefore exposes the same concrete formatter behavior and outer async queue counters that `build_async_text_logger(...)` would have returned directly, even if the original config carried `sink.kind=File` or another non-text kind.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,6 +64,28 @@ let logger : LibraryAsyncLogger[@bitlogger.ConsoleSink] = LibraryAsyncLogger::ne
|
||||
|
||||
In this example, the package boundary exposes the library async facade instead of the full async logger type.
|
||||
|
||||
#### When Need A One-call Target Override Through The Library Facade
|
||||
|
||||
When package code should keep the same library-facing async value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="lib.async.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.async.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Async Context Without Changing The Visible Facade Type
|
||||
|
||||
When package code should attach stable metadata but still keep the same visible `LibraryAsyncLogger[S]` shape:
|
||||
```moonbit
|
||||
let request_logger = logger.bind([@bitlogger.field("request_id", "req-42")])
|
||||
```
|
||||
|
||||
In this example, later writes automatically prepend `request_id`.
|
||||
|
||||
And unlike synchronous `LibraryLogger`, the returned async facade still has the visible type `LibraryAsyncLogger[S]` rather than a different sink wrapper spelling.
|
||||
|
||||
#### When Need To Recover The Full Async Logger Later
|
||||
|
||||
When a library-facing async facade should later be adapted back into the ordinary async logger surface:
|
||||
@@ -69,8 +103,22 @@ e.g.:
|
||||
|
||||
- Runtime behavior still depends on the wrapped sink `S` and async runtime support, so sink-specific or target-specific limitations remain unchanged behind the facade.
|
||||
|
||||
- If `S` is `RuntimeSink` because the value came from `build_library_async_logger(...)` or `parse_and_build_library_async_logger(...)`, facade narrowing also does not undo the underlying sync-first sink selection; queued and file-backed runtime behavior still depend on the already-built `RuntimeSink` variant.
|
||||
|
||||
- If `S` is `FormattedConsoleSink` because the value came from `build_library_async_text_logger(...)`, config fields like `logger.sink.kind=File` still do not make the wrapped logger file-backed; that builder preserved the text-console formatter route before the facade was applied.
|
||||
|
||||
- Post-close logging behavior, queue state, and failure-state behavior remain those of the wrapped `AsyncLogger[S]`; the facade only narrows what is directly exposed.
|
||||
|
||||
- If the wrapped async logger later ends in a mixed diagnostic state such as `has_failed=true` with retained backlog, unwrapping exposes that exact state instead of a library-specific translation.
|
||||
|
||||
- Shutdown through the facade keeps the wrapped logger's runtime-dependent failure cleanup behavior: after a worker-side failure, unwrapped pending versus dropped cleanup can still differ between native-worker and compatibility runtimes.
|
||||
|
||||
- Helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, `is_running()`, `has_failed()`, `last_error()`, `flush_policy()`, `state()`, and `wait_idle()` remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `LibraryAsyncLogger::new(...)`, `build_library_async_logger(...)`, or `parse_and_build_library_async_logger(...)` when you need a value of this type.
|
||||
|
||||
2. Use `AsyncLogger[S]` directly when exposing the full async lifecycle and state surface is acceptable.
|
||||
|
||||
3. Use `to_async_logger()` when internal code later needs the broader lifecycle/state helpers without changing the facade type exposed at the package boundary.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-bind
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Attach reusable structured fields to a LibraryLogger facade through the bind alias.
|
||||
description: Attach reusable structured fields to a LibraryLogger facade through the bind alias while extending the visible sink type to ContextSink.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -38,8 +38,11 @@ pub fn[S] LibraryLogger::bind(
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `bind(...)` delegates directly to `with_context_fields(...)`.
|
||||
- The original facade value is not mutated; a wrapped facade is returned.
|
||||
- Shared fields are applied to every later log call emitted through the returned facade.
|
||||
- The returned facade changes visible type from `LibraryLogger[S]` to `LibraryLogger[ContextSink[S]]` because the sink pipeline is extended with context-field merging behavior.
|
||||
- Minimum level, target, and timestamp behavior are preserved while the sink pipeline changes.
|
||||
- Broader composition helpers remain hidden behind the narrower facade after rewrapping; use `to_logger()` if later code needs them.
|
||||
- The original facade value is not mutated; a wrapped facade is returned.
|
||||
- This alias is useful when you prefer shorter chaining syntax in library code.
|
||||
|
||||
### How to Use
|
||||
@@ -68,15 +71,21 @@ let worker = LibraryLogger::new(console_sink(), target="app")
|
||||
|
||||
In this example, `bind(...)` communicates intent without changing underlying behavior.
|
||||
|
||||
And because it is only an alias, the resulting facade behaves the same as calling `with_context_fields(...)` directly.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `fields` is empty, the returned facade remains valid and simply adds no extra metadata.
|
||||
- If `fields` is empty, the returned facade remains valid and simply stores an empty shared field set in `ContextSink[S]`.
|
||||
|
||||
- If duplicate field keys are bound, all copies are preserved for downstream formatting or inspection.
|
||||
|
||||
- If callers want event-specific fields without changing the shared bound set, they should pass those through `log(..., fields=...)` on the returned facade.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `bind(...)` and `with_context_fields(...)` interchangeably; choose the one that reads better in context.
|
||||
|
||||
2. This alias exists for ergonomics, not for different semantics.
|
||||
|
||||
3. Use `with_context_fields(...)` when the longer name makes the shared-field sink-type change clearer at the call site.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-child
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Derive a child LibraryLogger facade by composing the current target with a child segment.
|
||||
description: Derive a child LibraryLogger facade by composing the current target with a child segment while rewrapping the same underlying logger state.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -38,6 +38,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
- If the parent target is empty, the child target becomes the full target.
|
||||
- If the child target is empty, the parent target is preserved.
|
||||
- If both are non-empty, they are joined with `.`.
|
||||
- Sink type, minimum level, timestamp behavior, and any existing sink wrappers remain the same because only the derived target changes.
|
||||
- Broader composition helpers remain hidden behind the narrower facade after rewrapping; use `to_logger()` if later code needs them.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,6 +67,8 @@ let client = LibraryLogger::new(console_sink(), target="sdk")
|
||||
|
||||
In this example, the final facade emits under `sdk.http.client`.
|
||||
|
||||
And the target derivation does not rebuild or reset the wrapped logger pipeline.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -77,3 +81,5 @@ e.g.:
|
||||
1. This is the preferred library-facing API for hierarchical target naming.
|
||||
|
||||
2. Composition uses `.` as the separator between parent and child segments.
|
||||
|
||||
3. Use `with_target(...)` instead when the new target should replace the current target rather than extend it.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-error
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Emit an error-level record through a LibraryLogger facade using the highest built-in severity shortcut.
|
||||
description: Emit an error-level record through a LibraryLogger facade by delegating to the wrapped logger's error shortcut.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -39,10 +39,11 @@ pub fn[S : Sink] LibraryLogger::error(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Error, ...)` on the wrapped logger.
|
||||
- This helper delegates to `error(...)` on the wrapped logger, which in turn uses `log(Level::Error, ...)`.
|
||||
- `Error` is the highest built-in severity in this sync facade API.
|
||||
- Per-call target override is not exposed here; use `log(...)` when explicit target control is required.
|
||||
- This helper does not accept a per-call target override. It uses the facade's stored target unless the facade was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Sink composition, filtering, patching, and queue wrappers still apply normally.
|
||||
- Broader composition helpers remain on the underlying `Logger[S]` and require `to_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,6 +67,10 @@ logger.error("dispatch failed", fields=[field("job_id", "42")])
|
||||
|
||||
In this example, the record carries machine-readable context without dropping to the generic `log(...)` form.
|
||||
|
||||
And any shared context already carried by the facade still participates through the wrapped logger pipeline.
|
||||
|
||||
And the write still uses the facade's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -73,8 +78,12 @@ e.g.:
|
||||
|
||||
- If a target override is required, use `log(...)` instead of this severity shortcut.
|
||||
|
||||
- If callers need broader composition helpers after logging, they must unwrap first with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper for high-severity library failures.
|
||||
|
||||
2. Emitting an error record is separate from throwing or handling program exceptions.
|
||||
|
||||
3. Use `log(...)` instead when the call site needs a per-call target override or a dynamic level.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-info
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Emit an info-level record through a LibraryLogger facade using the informational severity shortcut.
|
||||
description: Emit an info-level record through a LibraryLogger facade by delegating to the wrapped logger's info shortcut.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -39,10 +39,12 @@ pub fn[S : Sink] LibraryLogger::info(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Info, ...)` on the wrapped logger.
|
||||
- This helper delegates to `info(...)` on the wrapped logger, which in turn uses `log(Level::Info, ...)`.
|
||||
- `Info` is the default minimum logger threshold unless changed explicitly.
|
||||
- Per-call target override is not exposed here; use `log(...)` if needed.
|
||||
- This helper does not accept a per-call target override. It uses the facade's stored target unless the facade was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Any existing sink wrappers inside the logger pipeline still participate normally in the write path.
|
||||
- The library facade keeps the same write semantics while exposing a smaller public surface.
|
||||
- Broader composition helpers remain on the underlying `Logger[S]` and require `to_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,6 +68,10 @@ logger.info("request completed", fields=[field("status", "200")])
|
||||
|
||||
In this example, the event remains concise while still carrying useful fields.
|
||||
|
||||
And any shared context already carried by the facade still participates through the wrapped logger pipeline.
|
||||
|
||||
And the write still uses the facade's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -73,8 +79,12 @@ e.g.:
|
||||
|
||||
- If the event needs an explicit alternate target, use `log(...)` instead of this shortcut.
|
||||
|
||||
- If callers need broader composition helpers after logging, they must unwrap first with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is the common convenience method for normal library-facing events.
|
||||
|
||||
2. Because `Info` is often the default threshold, this helper is a natural baseline for facade examples.
|
||||
|
||||
3. Use `log(...)` instead when the call site needs a per-call target override or a dynamic level.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-is-enabled
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Check whether a LibraryLogger facade would accept a record at a given level.
|
||||
description: Check whether a LibraryLogger facade passes the wrapped logger's min-level gate for a given severity.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -35,8 +35,9 @@ pub fn[S] LibraryLogger::is_enabled(self : LibraryLogger[S], level : Level) -> B
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped logger's `is_enabled(...)` behavior.
|
||||
- It only checks logger-level severity gating; it does not evaluate sink filters or patch logic.
|
||||
- The result reflects the current facade value, so derived facades with different thresholds can return different answers.
|
||||
- It only checks logger-level severity gating through `min_level`; it does not evaluate later sink filters, patch logic, or any sink-side buffering/wrapping behavior.
|
||||
- The result reflects the current wrapped logger state, so derived facades with different thresholds can return different answers.
|
||||
- This check does not require unwrapping because it is part of the narrower library-facing surface.
|
||||
- Use it when message construction or field gathering is expensive enough to justify a pre-check.
|
||||
|
||||
### How to Use
|
||||
@@ -65,6 +66,8 @@ if logger.is_enabled(Level::Info) {
|
||||
|
||||
In this example, the check mirrors the same severity gate used by later write calls.
|
||||
|
||||
And a `true` result still means only that the level gate passed, not that the record is guaranteed to survive later sink-side filtering or routing.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -72,8 +75,12 @@ e.g.:
|
||||
|
||||
- A `true` result does not guarantee final sink output if later filter logic rejects the record.
|
||||
|
||||
- If callers need broader composition helpers rather than just the level gate, they must unwrap first with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a cheap severity check, not a full end-to-end delivery guarantee.
|
||||
|
||||
2. Prefer using it only when precomputing the log payload is meaningfully expensive.
|
||||
|
||||
3. Use `log(...)` or the severity shortcuts directly when no expensive precomputation needs to be avoided.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-log
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Emit a record through a LibraryLogger facade with explicit level, message, fields, and optional target override.
|
||||
description: Emit a record through a LibraryLogger facade with explicit level, message, fields, and optional target override by delegating to the wrapped logger.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -46,7 +46,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This method delegates directly to the wrapped logger's `log(...)` behavior.
|
||||
- The logger checks `is_enabled(level)` before building and writing the record.
|
||||
- If `target` is empty, the facade's stored default target is used.
|
||||
- Any existing sink wrappers already inside the wrapped logger, such as `ContextSink`, `FilterSink`, `PatchSink`, or queued/runtime sink variants, still participate normally in the write path.
|
||||
- The narrower library facade does not change record construction semantics; it only limits the visible API surface.
|
||||
- Broader composition helpers remain on the underlying `Logger[S]` and require `to_logger()` first.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -77,6 +79,20 @@ fn log_retry(logger : LibraryLogger[ConsoleSink], attempt : Int) -> Unit {
|
||||
|
||||
In this example, `log(...)` acts as the shared primitive under custom library-facing wrappers.
|
||||
|
||||
#### When Need Shared Context Plus Event-specific Fields
|
||||
|
||||
When a facade already carries shared context but one write needs extra detail:
|
||||
```moonbit
|
||||
let logger = base.with_context_fields([field("service", "cache")])
|
||||
logger.log(
|
||||
Level::Info,
|
||||
"entry refreshed",
|
||||
fields=[field("key", "user:42")],
|
||||
)
|
||||
```
|
||||
|
||||
In this example, both the stored shared context and the per-call fields still flow through the wrapped logger pipeline.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -84,8 +100,12 @@ e.g.:
|
||||
|
||||
- If `target` is empty and the logger default target is also empty, the emitted record simply has no target label.
|
||||
|
||||
- If callers need broader composition helpers after writing, they must unwrap first with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when the call site needs full control instead of a fixed-severity helper.
|
||||
|
||||
2. The facade remains synchronous; any buffering behavior only comes from the wrapped sink type.
|
||||
|
||||
3. Use the optional `target` argument when one write should temporarily override the facade's default target without rebuilding or rewrapping it.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: library-logger-new
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Create a narrower sync library logger facade from any sink implementation.
|
||||
description: Create a narrower sync library logger facade by building a regular logger and wrapping the same underlying state.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -27,7 +27,7 @@ pub fn[S] LibraryLogger::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `sink : S` - Any sink value implementing `Sink`, such as `console_sink()` or a composed sink.
|
||||
- `sink : S` - Underlying sink value that should receive writes, such as `console_sink()` or a composed sink.
|
||||
- `min_level : Level` - Minimum enabled level. Messages below this threshold are ignored.
|
||||
- `target : String` - Default target attached to emitted records unless later overridden.
|
||||
|
||||
@@ -39,9 +39,10 @@ pub fn[S] LibraryLogger::new(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds a regular `Logger::new(...)` internally and then wraps it as `LibraryLogger`.
|
||||
- This API builds a regular `Logger::new(...)` internally and then wraps that same value as `LibraryLogger`.
|
||||
- The returned facade intentionally exposes a smaller method set than the full `Logger[S]` type.
|
||||
- The original sink type is still preserved in `LibraryLogger[S]`.
|
||||
- This constructor does not add timestamps, filters, patches, or extra sink wrappers by itself; it only creates the base sync logger and narrows its public surface.
|
||||
- Call `to_logger()` if later code must recover the full sync logger surface.
|
||||
|
||||
### How to Use
|
||||
@@ -67,6 +68,16 @@ let logger = LibraryLogger::new(text_console_sink(text_formatter()), target="wor
|
||||
|
||||
In this example, sink typing remains explicit while the consumer only sees the library facade.
|
||||
|
||||
#### When Need Full Logger Composition Later
|
||||
|
||||
When library-facing construction should still allow later access to broader sync composition helpers:
|
||||
```moonbit
|
||||
let logger = LibraryLogger::new(console_sink(), target="lib")
|
||||
let full = logger.to_logger().with_timestamp()
|
||||
```
|
||||
|
||||
In this example, construction starts from the narrower facade, but the same underlying logger can still be recovered and extended later.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -74,8 +85,12 @@ e.g.:
|
||||
|
||||
- If later code needs methods outside the facade, it must unwrap with `to_logger()`.
|
||||
|
||||
- If callers want additional wrappers such as timestamps, filters, patches, or queued/context sink layers immediately, they must apply those through the full `Logger[S]` surface or the dedicated facade helpers.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is the direct constructor counterpart to `Logger::new(...)` for library-oriented APIs.
|
||||
|
||||
2. Prefer this when public package boundaries should stay narrower than `Logger[S]`.
|
||||
|
||||
3. Use `Logger::new(...)` directly when the full sync composition surface should remain public from the start.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-logger-to-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Recover the underlying full sync logger from the library-facing sync facade.
|
||||
update-time: 20260614
|
||||
description: Recover the underlying full sync logger from the library-facing sync facade without rebuilding, copying, or detaching the wrapped logger state.
|
||||
key-word:
|
||||
- logger
|
||||
- library
|
||||
@@ -34,8 +34,12 @@ pub fn[S] LibraryLogger::to_logger(self : LibraryLogger[S]) -> Logger[S] {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This conversion unwraps the existing logger instead of rebuilding it.
|
||||
- Sink wiring, target, min level, and attached wrappers remain the same.
|
||||
- Sink wiring, target, min level, timestamp behavior, and attached wrappers remain the same.
|
||||
- The returned `Logger[S]` is still that same live wrapped value, so later facade writes or later unwraps continue observing and mutating the same logger instance.
|
||||
- Use this when code needs full-surface APIs such as `with_timestamp(...)`, `with_filter(...)`, or `with_patch(...)`.
|
||||
- When the wrapped sink is `RuntimeSink`, unwrapping preserves that runtime sink value, but the result type is still `Logger[RuntimeSink]` rather than the `ConfiguredLogger` alias. Runtime-specific operations remain available through `full.sink` or by keeping a `ConfiguredLogger` value directly.
|
||||
- That same unwrap also preserves queued runtime state and file-backed helper behavior exactly as they existed behind the facade, including drain or flush results, file state snapshots, and file control methods.
|
||||
- Runtime and file helper mutations are still live too. Draining queued records, flushing, or changing file helper state through the unwrapped logger changes the same runtime-backed logger that the facade already wrapped.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -51,6 +55,18 @@ let full_logger = library_logger.to_logger().with_timestamp()
|
||||
|
||||
In this example, the facade is unwrapped so the caller can access full logger composition APIs again.
|
||||
|
||||
#### When Need Runtime Helpers After Library-oriented Config Build
|
||||
|
||||
When a library-facing runtime logger should later expose configured runtime controls internally:
|
||||
```moonbit
|
||||
let full = logger.to_logger()
|
||||
ignore(full.sink.pending_count())
|
||||
```
|
||||
|
||||
In this example, unwrapping preserves the same `RuntimeSink` pipeline, and callers reach runtime-specific helpers through that preserved sink value.
|
||||
|
||||
The same handle keeps reflecting later facade writes instead of becoming a detached snapshot.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -58,8 +74,16 @@ e.g.:
|
||||
|
||||
- Unwrapping does not change the current target or sink behavior by itself.
|
||||
|
||||
- Recovering the full logger does not rebuild or reset the existing sink wrappers.
|
||||
|
||||
- Recovering the full logger does not translate configured runtime state into a simpler snapshot; queued counts, file availability, file failure counters, and runtime file controls stay exactly as they were on the wrapped logger.
|
||||
|
||||
- Recovering the full logger also does not isolate later runtime-helper mutations. If unwrapped code drains queued records or changes file helper state, later facade writes still run against that same updated wrapped logger.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this only when the narrower facade is no longer sufficient.
|
||||
|
||||
2. This is the inverse projection of `Logger::to_library_logger()`.
|
||||
|
||||
3. Use `LibraryLogger[S]` directly when the narrower package-facing surface is still sufficient.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user