mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-05-30 15:42:25 +00:00
📝 Document logger composition APIs
This commit is contained in:
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
name: logger-child
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Derive a child logger by composing the current target with a child target segment.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- target
|
||||||
|
- child
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-child
|
||||||
|
|
||||||
|
Create a child logger by composing the current target with another target segment. This is the standard API for hierarchical target naming such as `app.worker` or `service.http.client`.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::child(self : Logger[S], target : String) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Parent logger whose target should be extended.
|
||||||
|
- `target : String` - Child target segment or suffix.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - A new logger whose default target is the composed child path.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::child(self : Logger[S], target : String) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Parent logger.
|
||||||
|
- `target : String` - Child suffix.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - Logger with combined target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
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, min level, and timestamp settings are preserved in the returned logger.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Hierarchical Target Naming
|
||||||
|
|
||||||
|
When subsystem logs should stay grouped under a shared namespace:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="service")
|
||||||
|
let worker = logger.child("worker")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, `worker` emits records under `service.worker`.
|
||||||
|
|
||||||
|
#### When Build Deeply Scoped Loggers Step By Step
|
||||||
|
|
||||||
|
When deeper target composition should remain readable:
|
||||||
|
```moonbit
|
||||||
|
let http = Logger::new(console_sink(), target="app")
|
||||||
|
.child("http")
|
||||||
|
.child("client")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the final logger target becomes `app.http.client`.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `target` is empty, the returned logger keeps the original parent target.
|
||||||
|
|
||||||
|
- If callers need complete replacement rather than composition, `with_target(...)` should be used instead.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. This is the preferred API for hierarchical logger naming.
|
||||||
|
|
||||||
|
2. Composition uses `.` as the separator between parent and child segments.
|
||||||
|
|
||||||
|
3. The original logger is not mutated.
|
||||||
|
|
||||||
|
4. This API works well with `with_context_fields(...)` to align target and metadata scopes.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-filter
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Wrap a logger with predicate-based filtering so only matching records reach the sink.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- filter
|
||||||
|
- predicate
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-filter
|
||||||
|
|
||||||
|
Attach a `RecordPredicate` to a logger so only matching records are forwarded to the wrapped sink. This is the main API for routing-by-predicate at the logger layer without rewriting sink implementations.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_filter(self : Logger[S], predicate : (Record) -> Bool) -> Logger[FilterSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger to wrap.
|
||||||
|
- `predicate : (Record) -> Bool` - Record predicate that decides whether a record should pass.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[FilterSink[S]]` - A new logger that only forwards matching records.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_filter(self : Logger[S], predicate : (Record) -> Bool) -> Logger[FilterSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Logger to filter.
|
||||||
|
- `predicate : (Record) -> Bool` - Acceptance rule.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[FilterSink[S]]` - Logger with predicate filtering.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- Filtering happens after the record is constructed, so predicates can inspect target, message, level, timestamp, and fields.
|
||||||
|
- The original logger is not mutated; a wrapped logger is returned.
|
||||||
|
- Filter logic composes naturally with helper predicates such as `target_has_prefix(...)`, `message_contains(...)`, and `all_of(...)`.
|
||||||
|
- Filtering is a drop/no-drop decision and does not transform the record itself.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Keep Only One Target Namespace
|
||||||
|
|
||||||
|
When a logger should only emit records from a specific subsystem:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="service")
|
||||||
|
.with_filter(target_has_prefix("service"))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, records outside the `service` target prefix are filtered out.
|
||||||
|
|
||||||
|
And target-based rules stay reusable across different loggers.
|
||||||
|
|
||||||
|
#### When Combine Several Filter Rules
|
||||||
|
|
||||||
|
When filtering depends on multiple conditions:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="api")
|
||||||
|
.with_filter(all_of([
|
||||||
|
level_at_least(Level::Warn),
|
||||||
|
message_contains("timeout"),
|
||||||
|
]))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, only warning-or-higher timeout records are forwarded.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If the predicate always returns `false`, the logger silently drops every record.
|
||||||
|
|
||||||
|
- If the predicate always returns `true`, the wrapper behaves like a pass-through filter.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. Use this API for selection logic, not record mutation.
|
||||||
|
|
||||||
|
2. Prefer helper predicates for readability and reuse.
|
||||||
|
|
||||||
|
3. Filtering occurs before sink write, so it is cheaper than post-processing output.
|
||||||
|
|
||||||
|
4. If you need record modification too, combine this API with `with_patch(...)`.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-min-level
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Replace the logger minimum enabled level so lower-severity records are skipped earlier.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- level
|
||||||
|
- filter
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-min-level
|
||||||
|
|
||||||
|
Replace the logger's minimum enabled level. This API controls the first gate checked by `log(...)` and the convenience level methods.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_min_level(self : Logger[S], min_level : Level) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger whose level threshold should change.
|
||||||
|
- `min_level : Level` - New minimum enabled level.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - A new logger value carrying the updated threshold.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_min_level(self : Logger[S], min_level : Level) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Current logger.
|
||||||
|
- `min_level : Level` - New threshold.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - Level-updated logger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- `log(...)` checks `is_enabled(level)` before constructing and writing a record.
|
||||||
|
- Lower-severity records below `min_level` are skipped without reaching the sink.
|
||||||
|
- This API replaces the logger threshold and does not add a wrapper sink.
|
||||||
|
- The returned logger keeps the same sink, target, and timestamp settings.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Raise Noise Floor In Production
|
||||||
|
|
||||||
|
When only warning and error records should be emitted:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink())
|
||||||
|
.with_min_level(Level::Warn)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, `trace`, `debug`, and `info` calls are skipped.
|
||||||
|
|
||||||
|
#### When Derive A More Verbose Local Logger
|
||||||
|
|
||||||
|
When one branch of code should keep a different threshold:
|
||||||
|
```moonbit
|
||||||
|
let base = Logger::new(console_sink(), min_level=Level::Info)
|
||||||
|
let debug_logger = base.with_min_level(Level::Debug)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the sink is reused while the threshold changes per logger value.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `min_level` is set too high, expected lower-severity diagnostics may disappear.
|
||||||
|
|
||||||
|
- If callers need richer predicate logic than a simple threshold, `with_filter(...)` should be used instead.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. This API is the cheapest built-in severity gate.
|
||||||
|
|
||||||
|
2. Use it before adding more complex filtering rules.
|
||||||
|
|
||||||
|
3. It composes cleanly with target, patch, and context-field helpers.
|
||||||
|
|
||||||
|
4. The original logger is unchanged because a new value is returned.
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-patch
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Wrap a logger with record transformation logic before the record reaches the sink.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- patch
|
||||||
|
- transform
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-patch
|
||||||
|
|
||||||
|
Attach a `RecordPatch` to a logger so each record is transformed before it is written to the sink. This API is the standard way to rewrite target, message, or fields without changing caller code.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_patch(self : Logger[S], patch : RecordPatch) -> Logger[PatchSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger to wrap.
|
||||||
|
- `patch : RecordPatch` - Record-to-record transformer applied before writing.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[PatchSink[S]]` - A new logger that rewrites emitted records.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_patch(self : Logger[S], patch : RecordPatch) -> Logger[PatchSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Logger to transform.
|
||||||
|
- `patch : RecordPatch` - Rewrite logic.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[PatchSink[S]]` - Logger with transformation behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- Patches can rewrite `target`, `message`, and `fields` because they receive the full `Record`.
|
||||||
|
- This API does not change logging level gating; level checks still happen at the logger level.
|
||||||
|
- `compose_patches(...)` can be used to build ordered patch pipelines.
|
||||||
|
- This is the correct place for redaction, enrichment, and message prefixing.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Redaction Before Output
|
||||||
|
|
||||||
|
When sensitive data must be rewritten before reaching any sink:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="auth")
|
||||||
|
.with_patch(redact_fields(["token", "password"]))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, matching field values are replaced before the sink sees them.
|
||||||
|
|
||||||
|
And caller code does not need custom redaction logic per log call.
|
||||||
|
|
||||||
|
#### When Need Combined Enrichment And Message Rewrite
|
||||||
|
|
||||||
|
When you want several transformations in a stable order:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="svc")
|
||||||
|
.with_patch(compose_patches([
|
||||||
|
prefix_message("[safe] "),
|
||||||
|
append_fields([field("service", "svc")]),
|
||||||
|
]))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, patch order is explicit and deterministic.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If a patch returns the original record unchanged, the wrapper behaves like a pass-through transformer.
|
||||||
|
|
||||||
|
- If multiple patches rewrite the same field or property, the later patch wins according to composition order.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. Use patches for transformation, not filtering decisions.
|
||||||
|
|
||||||
|
2. Prefer helper patches such as `prefix_message(...)`, `append_fields(...)`, and `redact_fields(...)` for common cases.
|
||||||
|
|
||||||
|
3. Patch composition order matters.
|
||||||
|
|
||||||
|
4. Combine with `with_filter(...)` when both selection and rewriting are required.
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-queue
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Wrap a synchronous logger with an explicit bounded queue and overflow policy.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- queue
|
||||||
|
- buffering
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-queue
|
||||||
|
|
||||||
|
Wrap a logger with a `QueuedSink[S]` so records are first stored in an explicit queue and later drained to the wrapped sink. This API is useful when you want bounded backlog behavior without introducing the async runtime adapter package.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_queue(
|
||||||
|
self : Logger[S],
|
||||||
|
max_pending~ : Int = 0,
|
||||||
|
overflow~ : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
|
||||||
|
) -> Logger[QueuedSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger to wrap.
|
||||||
|
- `max_pending : Int` - Maximum queued records before overflow behavior is applied.
|
||||||
|
- `overflow : QueueOverflowPolicy` - Queue overflow strategy such as `DropNewest` or `DropOldest`.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[QueuedSink[S]]` - A logger using an explicit synchronous queue wrapper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_queue(self : Logger[S], max_pending~ : Int = 0, overflow~ : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest) -> Logger[QueuedSink[S]] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `max_pending : Int` - Queue bound.
|
||||||
|
- `overflow : QueueOverflowPolicy` - Backlog overflow behavior.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[QueuedSink[S]]` - Queue-wrapped logger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- This queue wrapper is synchronous and explicit. It is not the same as `bitlogger_async` runtime scheduling.
|
||||||
|
- Callers can inspect `pending_count()` and `dropped_count()` through the wrapped sink.
|
||||||
|
- `flush()` or `drain(...)` is required to move queued data to the underlying sink.
|
||||||
|
- Overflow policy determines whether new or old records are discarded when the queue is full.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Explicit Manual Drain
|
||||||
|
|
||||||
|
When records should accumulate and flush at controlled points:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink(), target="queue")
|
||||||
|
.with_queue(max_pending=2, overflow=QueueOverflowPolicy::DropOldest)
|
||||||
|
|
||||||
|
logger.info("one")
|
||||||
|
logger.info("two")
|
||||||
|
ignore(logger.sink.flush())
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, records stay queued until explicitly flushed.
|
||||||
|
|
||||||
|
And backlog size stays bounded.
|
||||||
|
|
||||||
|
#### When Need Bounded Overload Behavior
|
||||||
|
|
||||||
|
When you need defined behavior under burst load:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink())
|
||||||
|
.with_queue(max_pending=100, overflow=QueueOverflowPolicy::DropNewest)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, queue pressure is limited instead of growing without bound.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `max_pending` is too small, records may be dropped frequently under bursts.
|
||||||
|
|
||||||
|
- If callers never flush or drain the queue, queued records remain pending and do not reach the sink.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. Use this API when you want explicit bounded buffering without `bitlogger_async`.
|
||||||
|
|
||||||
|
2. `with_queue(...)` preserves the normal synchronous logger call style.
|
||||||
|
|
||||||
|
3. Always define when and where the queue will be drained.
|
||||||
|
|
||||||
|
4. For background queue draining, prefer `bitlogger_async` instead.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-target
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Replace the default target carried by a logger so later records inherit a new target namespace.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- target
|
||||||
|
- namespace
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-target
|
||||||
|
|
||||||
|
Replace the logger's default target. This API is the simplest way to retarget a logger instance so later log calls inherit a new namespace without passing `target?` on every call.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_target(self : Logger[S], target : String) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger whose default target should be replaced.
|
||||||
|
- `target : String` - New default target namespace.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - A new logger value carrying the updated target.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_target(self : Logger[S], target : String) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Current logger.
|
||||||
|
- `target : String` - Replacement target.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - Retargeted logger.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- The returned logger keeps the same sink, min level, and timestamp settings.
|
||||||
|
- This API replaces the default target instead of composing it.
|
||||||
|
- Per-call `target?` arguments can still override the default target on individual log calls.
|
||||||
|
- The original logger value is not mutated.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need A Stable Namespace For One Subsystem
|
||||||
|
|
||||||
|
When one logger should always emit under a fixed target:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink())
|
||||||
|
.with_target("service.auth")
|
||||||
|
|
||||||
|
logger.info("started")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, later records inherit `service.auth` unless a call overrides the target explicitly.
|
||||||
|
|
||||||
|
#### When Reuse One Sink Across Different Namespaces
|
||||||
|
|
||||||
|
When multiple subsystem loggers share the same sink:
|
||||||
|
```moonbit
|
||||||
|
let base = Logger::new(console_sink())
|
||||||
|
let worker = base.with_target("worker")
|
||||||
|
let api = base.with_target("api")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, target routing stays explicit without duplicating sink construction.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `target` is empty, the logger remains valid and later records default to an empty target.
|
||||||
|
|
||||||
|
- If callers actually need parent-child target composition, `child(...)` is the better API.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. Use this API for replacement, not hierarchical composition.
|
||||||
|
|
||||||
|
2. It is useful when one sink serves several target namespaces.
|
||||||
|
|
||||||
|
3. Per-call `target?` still has higher priority for that one log record.
|
||||||
|
|
||||||
|
4. Pair it with `child(...)` when both replacement and composition are needed in different stages.
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
name: logger-with-timestamp
|
||||||
|
group: api
|
||||||
|
category: logging
|
||||||
|
update-time: 20260512
|
||||||
|
description: Enable or disable automatic timestamp capture for records emitted by a logger.
|
||||||
|
key-word:
|
||||||
|
- logger
|
||||||
|
- timestamp
|
||||||
|
- record
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Logger-with-timestamp
|
||||||
|
|
||||||
|
Enable or disable automatic timestamp capture on log emission. This API controls whether `Logger::log(...)` records the current time through `@env.now()` or leaves the timestamp at `0UL`.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_timestamp(self : Logger[S], enabled~ : Bool = true) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Base logger whose timestamp behavior should change.
|
||||||
|
- `enabled : Bool` - Whether emitted records should capture current time automatically.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - A new logger value with updated timestamp behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] Logger::with_timestamp(self : Logger[S], enabled~ : Bool = true) -> Logger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : Logger[S]` - Current logger.
|
||||||
|
- `enabled : Bool` - Timestamp toggle.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `Logger[S]` - Logger with updated timestamp behavior.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- When enabled, `log(...)` captures `@env.now()` and stores it in the record.
|
||||||
|
- When disabled, emitted records use `0UL` as the timestamp value.
|
||||||
|
- This setting affects later emitted records only.
|
||||||
|
- Formatter or sink behavior stays unchanged; they just receive records with or without real timestamps.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Real Event Time In Records
|
||||||
|
|
||||||
|
When downstream formatting or JSON output should include event time:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink())
|
||||||
|
.with_timestamp()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, each emitted record captures current time automatically.
|
||||||
|
|
||||||
|
#### When Need Deterministic Or Minimal Records
|
||||||
|
|
||||||
|
When timestamps should be disabled for tests or reduced output:
|
||||||
|
```moonbit
|
||||||
|
let logger = Logger::new(console_sink())
|
||||||
|
.with_timestamp(enabled=false)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, records are emitted without runtime time capture.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If timestamps are disabled, formatters that expect meaningful time values may show empty or zero-like timestamp output.
|
||||||
|
|
||||||
|
- If callers need timestamps only for one record, per-call customization is not provided here and a separate logger value may be clearer.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Notes are here.
|
||||||
|
|
||||||
|
1. This API controls record creation, not formatter display policy.
|
||||||
|
|
||||||
|
2. It works well together with text formatters that optionally show timestamps.
|
||||||
|
|
||||||
|
3. The helper is useful for tests, deterministic snapshots, and production timing.
|
||||||
|
|
||||||
|
4. The returned logger preserves the same sink, target, and min level settings.
|
||||||
Reference in New Issue
Block a user