📝 Document logger composition APIs

This commit is contained in:
Nanaloveyuki
2026-05-12 13:08:21 +08:00
parent 0b93af9261
commit 3dbf848a53
7 changed files with 732 additions and 0 deletions
+106
View File
@@ -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(...)`.