Library-async-logger-with-context-fields
Bind shared structured fields to a LibraryAsyncLogger[S]. This is the library-facing API for attaching stable metadata such as component name, plugin id, or request scope without repeating those fields on every async log call.
Interface
pub fn[S] LibraryAsyncLogger::with_context_fields(
self : LibraryAsyncLogger[S],
fields : Array[@bitlogger.Field],
) -> LibraryAsyncLogger[S] {input
self : LibraryAsyncLogger[S]- Base facade that should gain shared fields.fields : Array[@bitlogger.Field]- Structured fields stored on the facade and prepended to each emitted record.
output
LibraryAsyncLogger[S]- New library-facing async facade carrying the shared field set.
Explanation
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 toLibraryAsyncLogger. - The provided
fieldsarray 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 visibleLibraryAsyncLogger[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
Here are some specific examples provided.
When Need Stable Library Metadata On Every Async Record
When a package should attach fixed metadata to all queued records:
let logger = LibraryAsyncLogger::new(@bitlogger.console_sink())
.with_target("plugin")
.with_context_fields([
@bitlogger.field("plugin", "alpha"),
@bitlogger.field("region", "cn"),
])In this example, both fields are automatically attached before every record enters the queue.
When Combine Async Target Scoping And Shared Fields
When a subsystem facade should carry both a target and stable context:
let worker = LibraryAsyncLogger::new(@bitlogger.console_sink(), target="sdk")
.child("worker")
.with_context_fields([@bitlogger.field("component", "worker")])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
fieldsis 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
Use this for stable shared metadata, not highly dynamic event-specific values.
The narrower
LibraryAsyncLoggersurface is preserved after field binding.Use
bind(...)when the shorter alias reads better in chained library code.