mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-05-30 15:42:25 +00:00
📝 Add async logger composition API docs
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-child
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Derive a child async logger by composing the current target with a child target segment.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- target
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-child
|
||||||
|
|
||||||
|
Create a child async logger by composing the current target with another target segment. This is the standard API for hierarchical async logger naming such as `app.worker` or `service.http.client`.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::child(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Parent async logger whose target should be extended.
|
||||||
|
- `target : String` - Child target segment or suffix.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger whose default target is the composed child path.
|
||||||
|
|
||||||
|
### 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 `.`.
|
||||||
|
- Queue settings, sink wiring, and runtime behavior are preserved in the returned logger.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Hierarchical Async Targets
|
||||||
|
|
||||||
|
When subsystem logs should stay grouped under one async namespace:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink(), target="service")
|
||||||
|
let worker = logger.child("worker")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, `worker` emits under `service.worker` while keeping the same async queue behavior.
|
||||||
|
|
||||||
|
#### When Build Deep Async Scopes Step By Step
|
||||||
|
|
||||||
|
When deeper target composition should remain readable:
|
||||||
|
```moonbit
|
||||||
|
let http = async_logger(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
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-context-fields
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Attach reusable structured fields to an async logger so every queued record inherits them.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- fields
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-context-fields
|
||||||
|
|
||||||
|
Bind shared structured fields to an async logger. This is the standard way to attach stable metadata such as service name, component, region, or subsystem identity without repeating them for every async log call.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_context_fields(
|
||||||
|
self : AsyncLogger[S],
|
||||||
|
fields : Array[@bitlogger.Field],
|
||||||
|
) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger that should gain shared fields.
|
||||||
|
- `fields : Array[Field]` - Structured fields that will be prepended to each emitted record.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger carrying the shared field set.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
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.
|
||||||
|
- Unlike synchronous `Logger::with_context_fields(...)`, this async variant stores fields directly on `AsyncLogger` instead of changing the visible sink type.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Stable Async Service Metadata
|
||||||
|
|
||||||
|
When every queued record should carry service-level metadata:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink(), target="billing")
|
||||||
|
.with_context_fields([
|
||||||
|
@bitlogger.field("service", "billing"),
|
||||||
|
@bitlogger.field("region", "cn"),
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, both fields are attached before each record enters the queue.
|
||||||
|
|
||||||
|
#### When Build Child Async Loggers For Subsystems
|
||||||
|
|
||||||
|
When a subsystem has both a target and fixed fields:
|
||||||
|
```moonbit
|
||||||
|
let worker = async_logger(console_sink(), target="app")
|
||||||
|
.child("worker")
|
||||||
|
.with_context_fields([@bitlogger.field("component", "worker")])
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, target composition and field binding stay separate but work together cleanly.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `fields` is empty, the logger remains valid and just adds no extra metadata.
|
||||||
|
|
||||||
|
- If duplicate field keys are provided, all fields are still emitted; conflict handling is left to the consumer side.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-filter
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Attach predicate-based filtering to an async logger so only matching records reach the queue.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- filter
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-filter
|
||||||
|
|
||||||
|
Attach a record predicate to an async logger so only matching records are enqueued. This is the main API for async routing-by-predicate without rewriting sink implementations.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_filter(
|
||||||
|
self : AsyncLogger[S],
|
||||||
|
predicate : (@bitlogger.Record) -> Bool,
|
||||||
|
) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger to constrain.
|
||||||
|
- `predicate : (Record) -> Bool` - Record predicate that decides whether a record should be enqueued.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger that only enqueues matching records.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- Filtering happens after record construction and patch application but before enqueue.
|
||||||
|
- Existing filter logic is preserved and combined with the new predicate using logical `and`.
|
||||||
|
- The original async logger is not mutated.
|
||||||
|
- Filtering avoids unnecessary queue pressure for records that should never be delivered.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Keep Only One Async Target Namespace
|
||||||
|
|
||||||
|
When an async logger should only enqueue records from a specific subsystem:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink(), target="service")
|
||||||
|
.with_filter(@bitlogger.target_has_prefix("service.api"))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, non-matching records are dropped before they reach the async queue.
|
||||||
|
|
||||||
|
#### When Combine Several Async Filter Rules
|
||||||
|
|
||||||
|
When filtering depends on multiple conditions:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink(), target="api")
|
||||||
|
.with_filter(fn(rec) {
|
||||||
|
rec.level.enabled(@bitlogger.Level::Warn) && rec.message.contains("timeout")
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, only warning-or-higher timeout records are enqueued.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If the predicate always returns `false`, the logger silently drops every record before enqueue.
|
||||||
|
|
||||||
|
- If the predicate always returns `true`, the wrapper behaves like a pass-through filter.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
1. Use this API for selection logic, not record mutation.
|
||||||
|
|
||||||
|
2. Async filtering is especially useful when queue capacity should be reserved for high-value records.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-min-level
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Replace the async logger minimum enabled level so lower-severity records are skipped before enqueue.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- level
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-min-level
|
||||||
|
|
||||||
|
Replace the async logger's minimum enabled level. This API controls the first gate checked before record creation and queue insertion.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_min_level(
|
||||||
|
self : AsyncLogger[S],
|
||||||
|
min_level : @bitlogger.Level,
|
||||||
|
) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger whose level threshold should change.
|
||||||
|
- `min_level : Level` - New minimum enabled level.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger value carrying the updated threshold.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Raise Async Noise Floor In Production
|
||||||
|
|
||||||
|
When only warning and error records should reach the async queue:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_min_level(@bitlogger.Level::Warn)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, lower-severity records are skipped before queue pressure increases.
|
||||||
|
|
||||||
|
#### When Derive A More Verbose Async Branch
|
||||||
|
|
||||||
|
When one branch of code should keep a different threshold:
|
||||||
|
```moonbit
|
||||||
|
let base = async_logger(console_sink(), min_level=@bitlogger.Level::Info)
|
||||||
|
let debug_logger = base.with_min_level(@bitlogger.Level::Debug)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the async sink and queue settings are 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 before they ever enter the queue.
|
||||||
|
|
||||||
|
- If callers need richer predicate logic than a simple threshold, `with_filter(...)` should be used instead.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
1. This API reduces async queue pressure by dropping disabled levels before enqueue.
|
||||||
|
|
||||||
|
2. Use it before adding more complex async filtering rules.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-patch
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Attach record transformation logic to an async logger before records reach the queue.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- patch
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-patch
|
||||||
|
|
||||||
|
Attach a record patch to an async logger so each record is transformed before filtering and enqueue. This is the main API for async record rewriting without changing the sink implementation.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_patch(
|
||||||
|
self : AsyncLogger[S],
|
||||||
|
patch : @bitlogger.RecordPatch,
|
||||||
|
) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger to wrap.
|
||||||
|
- `patch : RecordPatch` - Transformation applied to each record before enqueue.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger that rewrites each record before filtering and queue insertion.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
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.
|
||||||
|
- Patching can normalize, redact, or enrich records before they consume queue capacity.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Async Record Enrichment
|
||||||
|
|
||||||
|
When records should gain stable extra data before enqueue:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_patch(@bitlogger.append_fields([
|
||||||
|
@bitlogger.field("channel", "async"),
|
||||||
|
]))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the added fields are part of the record before filtering and queue insertion.
|
||||||
|
|
||||||
|
#### When Need Redaction Before Queueing
|
||||||
|
|
||||||
|
When sensitive fields should be removed early:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_patch(@bitlogger.redact_fields(["token", "password"]))
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, sensitive values are rewritten before they enter the async pipeline.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If the patch removes or rewrites important data incorrectly, later filters and sinks will only see the patched version.
|
||||||
|
|
||||||
|
- If callers need selection logic rather than transformation, `with_filter(...)` should be used instead.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
1. Use patches for transformation, not filtering decisions.
|
||||||
|
|
||||||
|
2. Redaction before enqueue helps keep sensitive data out of the queued pipeline.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-target
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Replace the default target carried by an async logger so later records inherit a new target namespace.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- target
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-target
|
||||||
|
|
||||||
|
Replace the async logger's default target. This API retargets later enqueue operations without changing the queue, overflow policy, or sink wiring.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_target(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger whose default target should be replaced.
|
||||||
|
- `target : String` - New default target namespace.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger value carrying the updated target.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Detailed rules explaining key parameters and behaviors
|
||||||
|
|
||||||
|
- The returned logger keeps the same sink, queue state, overflow policy, flush policy, and lifecycle flags.
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need A Stable Async Target Namespace
|
||||||
|
|
||||||
|
When one async logger should always emit under a fixed target:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_target("service.worker")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, later async log calls inherit `service.worker` unless a call overrides the target explicitly.
|
||||||
|
|
||||||
|
#### When Reuse One Async Setup Across Namespaces
|
||||||
|
|
||||||
|
When multiple subsystem loggers should share the same async queue behavior:
|
||||||
|
```moonbit
|
||||||
|
let base = async_logger(console_sink(), config=AsyncLoggerConfig::new(max_pending=64))
|
||||||
|
let api = base.with_target("api")
|
||||||
|
let jobs = base.with_target("jobs")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, target routing changes without rebuilding the async runtime configuration.
|
||||||
|
|
||||||
|
### Error Case
|
||||||
|
|
||||||
|
e.g.:
|
||||||
|
- If `target` is empty, the logger remains valid and later records default to an empty target.
|
||||||
|
|
||||||
|
- If callers need hierarchical target composition rather than replacement, `child(...)` is the better API.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
1. Use this API for replacement, not parent-child target composition.
|
||||||
|
|
||||||
|
2. It is useful when several subsystems should share one async queue policy.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
name: async-logger-with-timestamp
|
||||||
|
group: api
|
||||||
|
category: async
|
||||||
|
update-time: 20260512
|
||||||
|
description: Enable or disable automatic timestamp capture for records emitted by an async logger.
|
||||||
|
key-word:
|
||||||
|
- async
|
||||||
|
- logger
|
||||||
|
- timestamp
|
||||||
|
- public
|
||||||
|
---
|
||||||
|
|
||||||
|
## Async-logger-with-timestamp
|
||||||
|
|
||||||
|
Enable or disable automatic timestamp capture on async log emission. This API controls whether `AsyncLogger::log(...)` records the current time before enqueue or leaves the timestamp at `0UL`.
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
|
||||||
|
```moonbit
|
||||||
|
pub fn[S] AsyncLogger::with_timestamp(self : AsyncLogger[S], enabled~ : Bool = true) -> AsyncLogger[S] {}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### input
|
||||||
|
|
||||||
|
- `self : AsyncLogger[S]` - Base async logger whose timestamp behavior should change.
|
||||||
|
- `enabled : Bool` - Whether emitted records should capture current time automatically.
|
||||||
|
|
||||||
|
#### output
|
||||||
|
|
||||||
|
- `AsyncLogger[S]` - A new async logger value with updated timestamp behavior.
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### How to Use
|
||||||
|
|
||||||
|
Here are some specific examples provided.
|
||||||
|
|
||||||
|
#### When Need Real Event Time Before Queueing
|
||||||
|
|
||||||
|
When downstream formatting or JSON output should include event time:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_timestamp()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, each record captures its timestamp before entering the async queue.
|
||||||
|
|
||||||
|
#### When Need Deterministic Async Records
|
||||||
|
|
||||||
|
When timestamps should be disabled for tests or reduced output:
|
||||||
|
```moonbit
|
||||||
|
let logger = async_logger(console_sink())
|
||||||
|
.with_timestamp(enabled=false)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, queued 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, a separate logger value is usually clearer than toggling behavior repeatedly.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
1. This API controls record creation before enqueue, not formatter display policy.
|
||||||
|
|
||||||
|
2. It is useful for tests, deterministic snapshots, and production timing.
|
||||||
Reference in New Issue
Block a user