68 Commits

Author SHA1 Message Date
Nanaloveyuki d4f83f5ccb 🩹 FIx Github Action No such directory 2026-05-20 19:56:10 +08:00
Nanaloveyuki 73bcf78d89 🔖 Prepare 0.5.0 release metadata 2026-05-20 11:40:52 +08:00
Nanaloveyuki 1ca5ab0835 Add coverage for release helper APIs 2026-05-20 11:40:37 +08:00
Nanaloveyuki e019db11d6 📝 Polish onboarding and add feature examples 2026-05-20 11:40:23 +08:00
Nanaloveyuki 5f12991592 ♻️ Extract file sink implementation 2026-05-20 11:40:02 +08:00
Nanaloveyuki 25a6a973d2 📝 Update More API Document 2026-05-20 11:37:49 +08:00
Nanaloveyuki 55af0b664f 🙈 Update Vibecoding gitignore 2026-05-20 11:37:03 +08:00
Nanaloveyuki 0a098915af 📝 Add agent handoff note templates 2026-05-20 10:08:11 +08:00
Nanaloveyuki 4860d1e08b 📝 Document logger config presets 2026-05-20 10:07:52 +08:00
Nanaloveyuki dca34bc114 ♻️ Split global logger emitters 2026-05-20 10:07:08 +08:00
Nanaloveyuki 4c25a81b03 Add logger config presets 2026-05-20 10:06:51 +08:00
Nanaloveyuki 3c15d8ed13 Add logger config presets 2026-05-20 09:40:21 +08:00
Nanaloveyuki 6998df0ee4 ♻️ Split logger emission helpers 2026-05-20 09:40:15 +08:00
Nanaloveyuki 414d6a0ee8 ♻️ Split runtime file control helpers 2026-05-20 09:34:07 +08:00
Nanaloveyuki 2823a67d53 ♻️ Extract async config and state models 2026-05-20 09:22:56 +08:00
Nanaloveyuki a766dd09ac ♻️ Extract runtime file state helpers 2026-05-20 09:12:02 +08:00
Nanaloveyuki d0989d6308 ♻️ Extract config into utils subpackage 2026-05-20 09:02:44 +08:00
Nanaloveyuki 8b752a4b4d ♻️ Extract sink models into utils subpackage 2026-05-20 08:51:48 +08:00
Nanaloveyuki ac45ec2b03 ♻️ Extract file backend into utils subpackage 2026-05-20 08:44:54 +08:00
Nanaloveyuki 41d221af46 ♻️ Extract formatter into utils subpackage 2026-05-20 08:31:55 +08:00
Nanaloveyuki d6e47d4bb8 ♻️ Extract core and utils subpackages 2026-05-20 08:15:31 +08:00
Nanaloveyuki 91096a9e0d ⬆️ Update MoonBit Version to v0.9.2 and Fix warns 2026-05-20 07:55:14 +08:00
Nanaloveyuki 78423384ea Clarify application and library logger entries 2026-05-15 11:42:46 +08:00
Nanaloveyuki 1b56e1e20a Add library logger facades 2026-05-15 11:15:34 +08:00
Nanaloveyuki 91d778d92e ♻️ Split runtime and async shared layers 2026-05-15 11:15:20 +08:00
Nanaloveyuki 1c75c98e3c 🚚 Move bitlogger&bitlogger-async to src& src-async 2026-05-15 10:13:36 +08:00
Nanaloveyuki 02c40f26f9 ⬆️ 升级 Moonbit 版本和 Async 版本 2026-05-15 09:55:29 +08:00
Nanaloveyuki 640b717c70 📝 Add record and level API docs 2026-05-12 16:13:50 +08:00
Nanaloveyuki bd1ef99189 📝 Add global logger API docs 2026-05-12 16:10:24 +08:00
Nanaloveyuki 661c17e093 📝 Add logger write API docs 2026-05-12 16:06:00 +08:00
Nanaloveyuki 474a1931c2 📝 Add record patch API docs 2026-05-12 16:00:17 +08:00
Nanaloveyuki dd895ac211 📝 Add filter predicate API docs 2026-05-12 15:56:30 +08:00
Nanaloveyuki 9248135b9c 📝 Expand API index coverage 2026-05-12 15:51:54 +08:00
Nanaloveyuki 694414e068 📝 Add file state export API docs 2026-05-12 15:05:58 +08:00
Nanaloveyuki bab9864f1f 📝 Add configured logger file state API docs 2026-05-12 14:59:59 +08:00
Nanaloveyuki d1260c5deb 📝 Add configured logger file policy API docs 2026-05-12 14:58:22 +08:00
Nanaloveyuki cfde700526 📝 Add configured logger file control API docs 2026-05-12 14:52:23 +08:00
Nanaloveyuki 87cf651c4c 📝 Update README with remove async summary 2026-05-12 14:49:18 +08:00
Nanaloveyuki 1e487f2ffc 📝 Add API index and simplify README links 2026-05-12 14:30:49 +08:00
Nanaloveyuki f21aca670a 📝 Refocus English README on entry points 2026-05-12 14:26:38 +08:00
Nanaloveyuki 6f1d6086d6 📝 Refocus READMEs on features and entry points 2026-05-12 14:25:11 +08:00
Nanaloveyuki 0ab3b95959 📝 Add configured logger file runtime API docs 2026-05-12 14:18:54 +08:00
Nanaloveyuki bd3a1c24d0 📝 Add configured logger runtime control API docs 2026-05-12 14:17:19 +08:00
Nanaloveyuki d7f93e9f9c 📝 Add async logger write API docs 2026-05-12 14:04:44 +08:00
Nanaloveyuki dad55a241f 📝 Add async logger lifecycle API docs 2026-05-12 13:55:05 +08:00
Nanaloveyuki f172305453 📝 Add async logger composition API docs 2026-05-12 13:51:08 +08:00
Nanaloveyuki 80f15cd455 📝 Polish API doc consistency and scope 2026-05-12 13:47:04 +08:00
Nanaloveyuki c6c153cf53 📝 Refine API doc notes and guidance 2026-05-12 13:32:15 +08:00
Nanaloveyuki 4d913aa642 📝 Align API docs with updated interface template 2026-05-12 13:20:36 +08:00
Nanaloveyuki 8dbadd5938 📝 Add async state and config export API docs 2026-05-12 13:08:27 +08:00
Nanaloveyuki 3dbf848a53 📝 Document logger composition APIs 2026-05-12 13:08:21 +08:00
Nanaloveyuki 0b93af9261 📝 Add sync config export API docs 2026-05-12 13:08:15 +08:00
Nanaloveyuki 6e6c14d28c 📝 Document async logger state diagnostics 2026-05-12 10:57:38 +08:00
Nanaloveyuki f1b7d1d21c Add async logger runtime snapshots 2026-05-12 10:50:42 +08:00
Nanaloveyuki 8584fc7e01 💚 Expand cross-target CI coverage 2026-05-12 10:49:06 +08:00
Nanaloveyuki a328414087 Prepare 0.4.1 release documentation 2026-05-12 10:37:43 +08:00
Nanaloveyuki f609b02377 Add cross-target async compatibility runtime 2026-05-12 10:37:15 +08:00
Nanaloveyuki 3124d7a445 🔖 Release 0.4.0 2026-05-10 16:02:19 +08:00
Nanaloveyuki 4b54005401 Add ANSI color support fallback modes 2026-05-10 15:32:48 +08:00
Nanaloveyuki 2d2388c79f Support named style closing tags 2026-05-10 15:22:56 +08:00
Nanaloveyuki e78183d267 Add scoped target and field style markup 2026-05-10 15:17:13 +08:00
Nanaloveyuki 4be861acce Add builtin semantic style tags 2026-05-10 15:10:29 +08:00
Nanaloveyuki 20f79bbe2a Add configurable style markup modes 2026-05-10 15:06:20 +08:00
Nanaloveyuki f3e903b578 Add formatter style_tags config support 2026-05-10 15:00:37 +08:00
Nanaloveyuki f1b223f203 📝 Document styled formatter usage 2026-05-10 14:52:17 +08:00
Nanaloveyuki 3b6536f980 Add style tag registry overrides 2026-05-10 14:50:06 +08:00
Nanaloveyuki b1b2235160 Add inline style markup 2026-05-10 14:41:57 +08:00
Nanaloveyuki 90af009e93 Add ANSI color mode 2026-05-10 14:33:49 +08:00
269 changed files with 20638 additions and 3395 deletions
+28 -4
View File
@@ -29,19 +29,43 @@ jobs:
- name: Check bitlogger
run: |
moon check bitlogger
moon check
- name: Test bitlogger
run: |
moon test bitlogger
moon test
- name: Check bitlogger native
run: |
moon check bitlogger --target native
moon check --target native
- name: Check bitlogger wasm-gc
run: |
moon check --target wasm-gc
- name: Check bitlogger js
run: |
moon check --target js
- name: Check bitlogger_async native
run: |
moon check bitlogger_async --target native
moon check src-async --target native
- name: Check bitlogger_async wasm-gc
run: |
moon check src-async --target wasm-gc
- name: Check bitlogger_async js
run: |
moon check src-async --target js
- name: Test bitlogger_async wasm-gc
run: |
moon test src-async --target wasm-gc
- name: Test bitlogger_async js
run: |
moon test src-async --target js
- name: Run basic example
run: |
+3
View File
@@ -19,3 +19,6 @@ desktop.ini
# Dev Documentations
docs/dev/*
# vibecoding
AGENTS/
+54 -250
View File
@@ -12,275 +12,79 @@
## 📖 介绍
BitLogger 是一个使用 MoonBit 编写的结构化日志库
BitLogger 是一个使用 MoonBit 编写的结构化日志库,目标是提供可组合、可配置、可跨端编译的日志基础设施。
## ❇️ 特点
## 🧭 后端兼容
- 🧩 基础能力: 支持 level, formatter, sink, context field 和全局 logger.
- 🏗️ 结构清晰: 按 `level / record / formatter / sinks / logger / global` 拆分文件, 便于维护.
- 🔌 可扩展: 支持 `fanout_sink(...)``callback_sink(...)`, 方便接入文件, 指标或外部系统.
- 🔀 可分流: 支持 `split_sink(...)` / `split_by_level(...)`, 可按谓词或 level 将日志路由到不同 sink.
- 🧱 可组合: 支持 `buffered_sink(...)``filter_sink(...)`, 可以组合不同输出策略.
- 🔎 可复用过滤: 提供 `target_has_prefix(...)`, `message_contains(...)`, `level_at_least(...)`, `field_equals(...)` 等过滤辅助函数.
- 🩹 可变换 Record: 支持 `with_patch(...)`, `patch_sink(...)` 以及脱敏, 补字段, message 变换 helper.
- 🧷 可绑定上下文: 支持 `bind(...)``fields(...)`, 便于复用上下文字段.
- 📮 显式队列: 支持 `queued_sink(...)` / `with_queue(...)`, 支持有界积压和溢出策略.
- 🧾 可配置文本格式: 支持 `text_formatter(...)`, `format_text(...)`, `text_console_sink(...)`, `formatted_callback_sink(...)` 和模板化 `template` 输出.
- 💾 Native 文件输出: 支持 `file_sink(...)`, 基础 size rotation / backup retention, 显式 `reopen()` / `reopen_with_current_policy()` / `reopen_append()` / `reopen_truncate()` 与失败计数, 仅在 `native/llvm` backend 可用.
- 📦 MoonBit 适配: API 和工程结构与 MoonBit 的 package / visibility / toolchain 模型保持一致.
| 模块 / 能力 | native / llvm | js / wasm / wasm-gc |
| --- | --- | --- |
| `src` 主包 | 支持 | 支持 |
| `file_sink(...)` | 支持 | 不支持, `native_files_supported()` 返回 `false` |
| `src-async` | 支持原生 worker 语义 | 支持兼容实现 |
| `examples/async_basic` | 支持 | 受 `async fn main` 入口限制, 当前不提供 |
## ❇️ 关键特性
- 结构化日志基础能力:level、record、formatter、sink、target、context field。
- 组合式设计:支持 filter、patch、fanout、split、callback、queued sink 等组合能力。
- 配置驱动:支持 JSON 配置解析、导出与 `build_logger(...)` / `build_async_logger(...)` 运行时组装。
- 文本格式化:支持 `text_formatter(...)`、template、style tag、`color_mode` / `style_markup`
- Native 文件输出:支持 file sink、基础 size rotation、reopen、failure counter 与运行时状态读取。
- 异步层:提供独立 `src-async` package,支持 queue、worker lifecycle、runtime state 和跨端兼容实现。
## 🚀 快速开始
```moonbit
let logger = Logger::new(console_sink(), min_level=Level::Info, target="demo")
.with_timestamp()
.with_context_fields([field("service", "bitlogger")])
let logger = build_logger(
with_queue(
text_console(
min_level=Level::Info,
target="demo",
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
),
max_pending=32,
overflow=QueueOverflowPolicy::DropOldest,
),
)
logger.info("starting", fields=[field("port", "8080")])
```
<details><summary>层级 target 示例</summary>
```moonbit
let worker = Logger::new(console_sink(), target="app").child("worker")
worker.info("job ready")
```
</details>
<details><summary>自定义 callback sink 示例</summary>
```moonbit
let hook = Logger::new(
callback_sink(fn(rec) {
println("callback saw [\{rec.target}] \{rec.message}")
}),
target="hook",
)
hook.info("hello")
```
</details>
<details><summary>基础 buffered sink 示例</summary>
```moonbit
let sink = buffered_sink(console_sink(), flush_limit=2)
let logger = Logger::new(sink, target="buffered")
logger.info("one")
logger.info("two")
sink.flush()
```
</details>
<details><summary>基础 filter sink 示例</summary>
```moonbit
let sink = filter_sink(console_sink(), fn(rec) {
rec.target == "kept"
})
let kept = Logger::new(sink, target="kept")
let dropped = Logger::new(sink, target="dropped")
kept.info("visible")
dropped.info("hidden")
```
</details>
<details><summary>Logger 链式 filter 示例</summary>
```moonbit
let logger = Logger::new(console_sink(), target="service")
.with_filter(all_of([
target_has_prefix("service"),
message_contains("visible"),
]))
logger.info("hidden")
logger.child("api").info("visible")
```
</details>
<details><summary>Record patch 示例</summary>
```moonbit
let logger = Logger::new(console_sink(), target="auth")
.with_patch(compose_patches([
prefix_message("[safe] "),
redact_fields(["token"]),
append_fields([field("service", "bitlogger")]),
]))
logger.info("login", fields=[field("user", "alice"), field("token", "secret")])
```
</details>
<details><summary>bind 上下文示例</summary>
```moonbit
let logger = Logger::new(console_sink(), target="audit")
.bind(fields([("service", "bitlogger"), ("scope", "login")]))
logger.info("accepted", fields=[field("user", "alice")])
```
</details>
<details><summary>显式 queue sink 示例</summary>
```moonbit
let logger = Logger::new(console_sink(), target="queue")
.with_queue(max_pending=2, overflow=QueueOverflowPolicy::DropOldest)
logger.info("one")
logger.info("two")
logger.info("three")
ignore(logger.sink.flush())
```
</details>
<details><summary>按 level 分流 sink 示例</summary>
```moonbit
let logger = Logger::new(
split_by_level(
callback_sink(fn(rec) {
println("high priority: \{rec.level.label()} \{rec.message}")
}),
console_sink(),
min_level=Level::Warn,
),
min_level=Level::Trace,
target="split",
)
logger.info("normal output")
logger.warn("warning output")
```
</details>
<details><summary>自定义文本 formatter 示例</summary>
```moonbit
let formatter = text_formatter(
show_timestamp=false,
field_separator=",",
template="[{level}] {target} {message} :: {fields}",
)
let logger = Logger::new(text_console_sink(formatter), target="pretty")
logger.info("hello", fields=[field("mode", "pretty")])
```
</details>
<details><summary>JSON 配置加载示例</summary>
```moonbit
let config = parse_logger_config_text(
"{\"min_level\":\"debug\",\"target\":\"config.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"field_separator\":\",\",\"template\":\"[{level}] {target} {message} :: {fields}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}",
)
let logger = build_logger(config)
logger.info("configured from json")
ignore(logger.flush())
```
</details>
推荐先从 `presets + build_logger(...)` 路径开始:`console(...)` / `json_console(...)` / `text_console(...)` / `file(...)` 负责拼装 `LoggerConfig``with_queue(...)` / `with_file_rotation(...)` 负责做小范围组合,然后再交给 `build_logger(...)` 构建运行时 logger。
<details><summary>native 文件 sink 示例</summary>
当你需要 `fanout``split``callback``patch` 这类自定义 sink 图组合时,再优先使用 `Logger::new(...)` 直接进行运行时拼装。
异步入口示例:
```moonbit
if native_files_supported() {
let logger = Logger::new(
file_sink("bitlogger.log", rotation=Some(file_rotation(128, max_backups=2))),
target="file",
)
logger.info("hello", fields=[field("kind", "file")])
ignore(logger.sink.flush())
ignore(logger.sink.close())
}
let logger = async_logger(console_sink(), target="async.demo")
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("started")
logger.shutdown()
})
```
</details>
<details><summary>file runtime state dump 示例</summary>
```moonbit
let logger = build_logger(
LoggerConfig::new(
sink=SinkConfig::new(kind=SinkKind::File, path="bitlogger-runtime.log"),
queue=Some(QueueConfig::new(16)),
),
)
logger.info("queued hello")
match logger.file_runtime_state() {
Some(snapshot) => println(stringify_runtime_file_state(snapshot, pretty=true))
None => ()
}
```
</details>
## 📂 仓库结构
- `bitlogger/`: MoonBit 库 package, 包含本体实现, 测试与 Mooncake README
- `examples/basic/`: 最小可运行示例
- `examples/async_basic/`: 基于 `moonbitlang/async` 的异步 logger 示例
- `src/`: 主日志库 package。
- `src-async/`: 基于 `moonbitlang/async` 的异步日志层。
- `docs/api/`: 单接口粒度 API 文档。
- `examples/basic/`: breadth 示例;文件开头就是推荐的 presets + `build_logger(...)` 同步入口,后续继续覆盖更广能力面。
- `examples/console_basic/`: console 与 json console 最小输出示例。
- `examples/text_formatter/`: text formatter / template 示例。
- `examples/style_tags/`: style tag / colored formatter 示例。
- `examples/file_rotation/`: file sink / rotation 示例。
- `examples/config_build/`: `build_logger(...)` 配置驱动示例。
- `examples/presets/`: presets + `build_logger(...)` 示例。
- `examples/async_basic/`: 异步 logger 示例。
## 🔗 相关文档
## 🔗 文档入口
- [Mooncake 文档页](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
- [English README](./docs/README-en.md)
## 📝 配置说明
- 提供 JSON 配置层: `parse_logger_config_text(...)`, `stringify_logger_config(...)`, `build_logger(...)`
- `QueueConfig`, `TextFormatterConfig`, `SinkConfig` 可分别通过 `queue_config_to_json(...)` / `stringify_queue_config(...)`, `text_formatter_config_to_json(...)` / `stringify_text_formatter_config(...)`, `sink_config_to_json(...)` / `stringify_sink_config(...)` 单独导出 JSON
- 支持字段: `min_level`, `target`, `timestamp`, `sink.kind`, `sink.path`, `sink.append`, `sink.auto_flush`, `sink.rotation`, `sink.text_formatter`, `queue`
- `sink.rotation` 支持 `max_bytes``max_backups`, 用于基础 size-based rotation 和 backup retention
- `file_sink(...)` 提供 `reopen()`, `reopen_with_current_policy()`, `reopen_append()`, `reopen_truncate()`, `open_failures()`, `write_failures()`, `flush_failures()`, `rotation_failures()`, 用于基础可观测性
- `file_sink(...)` 提供 `append_mode()`. 显式传入 `reopen(append=...)` 时, 会更新后续 reopen 使用的 append 策略. `reopen_with_current_policy()` 使用当前保存的策略重开文件, `reopen_append()` / `reopen_truncate()` 提供常见的 append 和 truncate 模式
- `file_sink(...)` 支持 `set_append_mode(...)`, 用于修改后续 reopen 使用的 append 策略
- `file_sink(...)` 可读取 `path()``auto_flush_enabled()` 等基础 file 策略状态
- `file_sink(...)` 提供 `rotation_enabled()``rotation_config()`, 可查询 rotation 是否启用及其参数
- `file_sink(...)` 提供 `state()`, 可一次性读取 path, available, append, auto_flush, rotation 与各类 failure counter 快照
- `file_sink(...)` 提供 `policy()``default_policy()`, 可分别读取当前策略与创建时默认策略
- `file_sink(...)` 提供 `policy_matches_default()`, 可判断当前运行期策略是否偏离默认策略
- `file_sink(...)` 支持 `set_policy(...)`, 可一次性写回 append / auto_flush / rotation 三项策略
- `file_sink(...)` 提供 `reset_failure_counters()`, 可清空 open / write / flush / rotation 失败计数
- `file_sink(...)` 提供 `reset_policy()`, 可将 append / auto_flush / rotation 恢复到创建 sink 时的默认策略
- `file_sink(...)` 支持 `set_auto_flush(...)`, `set_rotation(...)`, `clear_rotation()`, 可在运行期调整写出策略
- `build_logger(...)` 产出的 `ConfiguredLogger` 也提供 `file_reopen()`, `file_reopen_with_current_policy()`, `file_reopen_append()`, `file_reopen_truncate()`, `file_flush()`, `file_close()`, `file_append_mode()`, `file_path()`, `file_auto_flush()`, `file_rotation_enabled()`, `file_rotation_config()`, `file_state()`, 以及 `file_set_append_mode(...)`, `file_set_auto_flush(...)`, `file_set_rotation(...)`, `file_clear_rotation()` 和对应的 file failure 计数访问器
- `ConfiguredLogger` 提供 `file_runtime_state()`, 可在 file sink 被 queue 包装时同时读取底层 file 快照, queue 状态, pending 计数与 dropped 计数
- `ConfiguredLogger` 提供 `file_policy()``file_default_policy()`, 可分别读取当前 file 策略与初始配置策略
- `ConfiguredLogger` 提供 `file_policy_matches_default()`, 可判断当前配置式 file 策略是否偏离默认值
- `ConfiguredLogger` 支持 `file_set_policy(...)`, 可一次性改写配置式 file sink 的当前运行期策略
- `ConfiguredLogger` 支持 `file_reset_failure_counters()`, 可在配置式 file sink 上统一清空失败计数
- `ConfiguredLogger` 支持 `file_reset_policy()`, 可将配置式 file sink 的运行期策略恢复到初始配置
- `file_sink_policy_to_json(...)`, `stringify_file_sink_policy(...)` 可将独立 file policy 直接导出为 JSON, 便于策略快照, 配置对比或诊断上报
- `file_sink_state_to_json(...)`, `stringify_file_sink_state(...)`, `runtime_file_state_to_json(...)`, `stringify_runtime_file_state(...)` 可直接把 file / queued-file 快照导出为 JSON, 便于排障或上报
- `sink.text_formatter.template` 支持固定 token: `{timestamp}`, `{timestamp_ms}`, `{level}`, `{target}`, `{message}`, `{fields}`
- 可由配置直接组装的 sink 类型: `console`, `json_console`, `text_console`, `file`
- `queue` 作为显式包装层附着在最终 sink 外侧. 这仍然是同步 drain 模型, 不是 async runtime
## 🧵 异步层
- 提供独立 package: `bitlogger_async/`
- 异步层基于 `moonbitlang/async`, 提供 `AsyncLogger`, `async_logger(...)`, 后台 `run()` worker 与有界 async queue
- async API 支持 `with_context_fields(...)`, `with_filter(...)`, `with_patch(...)`, `with_target(...)`, `child(...)`
- 建议使用 `shutdown()` 停止 worker. 默认模式下, 它会先等待队列清空, 再关闭 queue, 最后等待 worker 退出
- 提供基础生命周期观测: `is_closed()`, `is_running()`, `has_failed()`, `last_error()`
- async worker 支持 `max_batch` 批量消费, 以及 `flush=Never|Batch|Shutdown` 的基础 flush 策略
- 示例见 [examples/async_basic/main.mbt](/E:/repo/MooLiteyukiBot/examples/async_basic/main.mbt:1)
- 仅支持 `native/llvm` backend, 与同步 core 分开维护
### Async Config
- 提供 `parse_async_logger_config_text(...)`, `stringify_async_logger_config(...)`, `parse_async_logger_build_config_text(...)`, `build_async_logger(...)`
- JSON 顶层结构分为两个字段: `logger``async_config`
- `logger` 复用同步 `LoggerConfig` 的 schema, `async_config` 支持 `max_pending`, `overflow`, `max_batch`, `flush`
- 用法可参考 [examples/async_basic/main.mbt](/E:/repo/MooLiteyukiBot/examples/async_basic/main.mbt:1)
- [src package README](./src/README.mbt.md)
- [API 索引](./docs/api/index.md)
- 推荐起步 API: `text_console(...)` / `file(...)` / `with_queue(...)` / `build_logger(...)`
- facade API: `build_application_logger(...)` / `build_library_logger(...)` / `build_application_async_logger(...)`
-263
View File
@@ -1,263 +0,0 @@
# BitLogger
BitLogger is a minimal structured logger for MoonBit.
BitLogger 是一个使用 MoonBit 编写的结构化日志库.
## Features / 特性
- log levels: `Trace`, `Debug`, `Info`, `Warn`, `Error`
- 日志级别: `Trace`, `Debug`, `Info`, `Warn`, `Error`
- structured key-value fields
- 结构化字段: `field("key", "value")`
- sink abstraction
- sink 抽象与组合接口
- default global console logger
- 默认全局 logger 辅助函数
- context fields via `with_context_fields(...)`
- 通过 `with_context_fields(...)` 添加上下文字段
- child target composition via `child(...)`
- 通过 `child(...)` 组合层级 target
- optional timestamps via `with_timestamp()`
- 通过 `with_timestamp()` 启用时间戳
- JSON console output via `json_console_sink()`
- `json_console_sink()` 提供 JSON 控制台输出
- sink composition via `fanout_sink(...)`
- `fanout_sink(...)` 支持多 sink 组合
- sink routing via `split_sink(...)` and `split_by_level(...)`
- `split_sink(...)`, `split_by_level(...)` 支持按谓词或 level 将日志路由到不同 sink
- custom callback sink via `callback_sink(...)`
- `callback_sink(...)` 支持自定义外部集成
- buffered sink via `buffered_sink(...)`
- `buffered_sink(...)` 支持内存缓冲与 flush
- filter sink via `filter_sink(...)`
- `filter_sink(...)` 支持按 `Record` 条件筛选输出
- reusable filter helpers such as `target_has_prefix(...)`, `message_contains(...)`, and `field_equals(...)`
- 提供 `target_has_prefix(...)`, `message_contains(...)`, `field_equals(...)` 等可复用过滤辅助函数
- record patching via `with_patch(...)` and `patch_sink(...)`
- 支持 `with_patch(...)`, `patch_sink(...)` 以及常见 record patch helper
- context binding via `bind(...)` and `fields(...)`
- 支持 `bind(...)`, `fields(...)`, 更方便封装上下文字段
- explicit queued delivery via `queued_sink(...)` and `with_queue(...)`
- 支持 `queued_sink(...)`, `with_queue(...)`, 有界积压与溢出策略
- configurable text formatting via `text_formatter(...)`, `format_text(...)`, `text_console_sink(...)`, and template-driven `template` output
- 支持 `text_formatter(...)`, `format_text(...)`, `text_console_sink(...)` 以及模板化 `template` 文本输出
- JSON config parsing via `parse_logger_config_text(...)` and `stringify_logger_config(...)`
- 支持 `parse_logger_config_text(...)`, `stringify_logger_config(...)` 进行最小 JSON 配置读写
- `QueueConfig` / `TextFormatterConfig` / `SinkConfig` can also be exported independently through dedicated JSON helpers
- `QueueConfig` / `TextFormatterConfig` / `SinkConfig` 也可分别通过专用 JSON helper 单独导出
- config-driven logger assembly via `build_logger(...)`
- 支持 `build_logger(...)` 将配置组装为可直接使用的 logger
- native-only file output via `file_sink(...)`, with basic size rotation, backup retention, explicit `reopen()` / `reopen_with_current_policy()` / `reopen_append()` / `reopen_truncate()`, and failure counters
- 支持 `file_sink(...)`, 基础 size rotation / backup retention, 显式 `reopen()` / `reopen_with_current_policy()` / `reopen_append()` / `reopen_truncate()` 与失败计数, 仅在 `native/llvm` backend 可用
## Example / 示例
```mbt check
test {
let logger = Logger::new(console_sink(), min_level=Level::Debug, target="demo")
.with_timestamp()
logger.info("starting", fields=[field("port", "8080")])
}
```
```mbt check
test {
let logger = Logger::new(console_sink(), target="app").child("worker")
logger.info("ready")
}
```
```mbt check
test {
let logger = Logger::new(
fanout_sink(console_sink(), json_console_sink()),
min_level=Level::Info,
target="demo",
)
logger.info("ready", fields=[field("mode", "fanout")])
}
```
```mbt check
test {
let logger = Logger::new(
callback_sink(fn(rec) {
println("callback saw [\{rec.target}] \{rec.message}")
}),
target="hook",
)
logger.info("hello")
}
```
```mbt check
test {
let sink = buffered_sink(console_sink(), flush_limit=2)
let logger = Logger::new(sink, target="buffered")
logger.info("one")
logger.info("two")
sink.flush()
}
```
```mbt check
test {
let sink = filter_sink(console_sink(), fn(rec) {
rec.target == "kept"
})
let kept = Logger::new(sink, target="kept")
let dropped = Logger::new(sink, target="dropped")
kept.info("visible")
dropped.info("hidden")
}
```
```mbt check
test {
let logger = Logger::new(console_sink(), target="service")
.with_filter(all_of([
target_has_prefix("service"),
message_contains("visible"),
]))
logger.info("hidden")
logger.child("api").info("visible")
}
```
```mbt check
test {
let logger = Logger::new(console_sink(), target="auth")
.with_patch(compose_patches([
prefix_message("[safe] "),
redact_fields(["token"]),
append_fields([field("service", "bitlogger")]),
]))
logger.info("login", fields=[field("user", "alice"), field("token", "secret")])
}
```
```mbt check
test {
let logger = Logger::new(console_sink(), target="audit")
.bind(fields([("service", "bitlogger"), ("scope", "login")]))
logger.info("accepted", fields=[field("user", "alice")])
}
```
```mbt check
test {
let logger = Logger::new(console_sink(), target="queue")
.with_queue(max_pending=2, overflow=QueueOverflowPolicy::DropOldest)
logger.info("one")
logger.info("two")
logger.info("three")
ignore(logger.sink.flush())
}
```
```mbt check
test {
let logger = Logger::new(
split_by_level(
callback_sink(fn(rec) {
println("high priority: \{rec.level.label()} \{rec.message}")
}),
console_sink(),
min_level=Level::Warn,
),
min_level=Level::Trace,
target="split",
)
logger.info("normal output")
logger.warn("warning output")
}
```
```mbt check
test {
let formatter = text_formatter(
show_timestamp=false,
field_separator=",",
template="[{level}] {target} {message} :: {fields}",
)
let logger = Logger::new(text_console_sink(formatter), target="pretty")
logger.info("hello", fields=[field("mode", "pretty")])
}
```
```mbt check
test {
let config = parse_logger_config_text(
"{\"min_level\":\"debug\",\"target\":\"config.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"field_separator\":\",\",\"template\":\"[{level}] {target} {message} :: {fields}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}",
)
let logger = build_logger(config)
logger.info("configured from json")
ignore(logger.flush())
}
```
## Formatter Template / 模板格式
- supported tokens / 支持的 token: `{timestamp}`, `{timestamp_ms}`, `{level}`, `{target}`, `{message}`, `{fields}`
- disabled or missing parts render as empty text / 被关闭或缺失的部分会渲染为空字符串
- `template` is intentionally a simple token replacement layer, not a full DSL / `template` 使用轻量 token 替换方式, 不是完整 DSL
```mbt check
test {
if native_files_supported() {
let logger = Logger::new(
file_sink("bitlogger.log", rotation=Some(file_rotation(128, max_backups=2))),
target="file",
)
logger.info("hello", fields=[field("kind", "file")])
ignore(logger.sink.flush())
ignore(logger.sink.close())
}
}
```
```mbt check
test {
let logger = build_logger(
LoggerConfig::new(
sink=SinkConfig::new(kind=SinkKind::File, path="bitlogger-runtime.log"),
queue=Some(QueueConfig::new(16)),
),
)
logger.info("queued hello")
match logger.file_runtime_state() {
Some(snapshot) => println(stringify_runtime_file_state(snapshot, pretty=true))
None => ()
}
}
```
## File Rotation / 文件轮转
- basic rotation is size-based / 基础轮转按文件大小触发
- `file_rotation(max_bytes, max_backups=...)` controls threshold and retained backups / `file_rotation(max_bytes, max_backups=...)` 控制触发阈值和保留备份数
- JSON config uses `sink.rotation.max_bytes` and `sink.rotation.max_backups` / JSON 配置使用 `sink.rotation.max_bytes` 与 `sink.rotation.max_backups`
- `FileSink::reopen()` can explicitly reopen the current file handle, `FileSink::reopen_with_current_policy()` makes the stored-policy reopen path explicit, and `FileSink::reopen_append()` / `FileSink::reopen_truncate()` cover the two common reopen modes directly / `FileSink::reopen()` 可显式重开当前文件句柄, `FileSink::reopen_with_current_policy()` 会按当前保存的策略重开文件, `FileSink::reopen_append()` / `FileSink::reopen_truncate()` 提供常见的 append 与 truncate 模式
- `append_mode()` exposes the current append policy, and `reopen(append=...)` updates that policy for later reopen calls / `append_mode()` 可读取当前 append 策略, `reopen(append=...)` 会更新后续 reopen 复用的 append 策略
- `set_append_mode(...)` updates the stored append policy without forcing an immediate reopen / `set_append_mode(...)` 可直接更新保存的 append 策略, 不会强制立即 reopen
- `path()` and `auto_flush_enabled()` expose core file sink policy state / `path()` 与 `auto_flush_enabled()` 可读取 file sink 的基础策略状态
- `rotation_enabled()` and `rotation_config()` expose whether rotation is active and which settings are currently applied / `rotation_enabled()` 与 `rotation_config()` 可读取 rotation 是否启用及当前配置参数
- `state()` exposes a single file-sink snapshot including path, availability, append policy, auto-flush, rotation config, and failure counters / `state()` 可一次性读取包含 path, availability, append, auto_flush, rotation 配置与失败计数的 file sink 快照
- `policy()` and `default_policy()` expose the current runtime policy and the sink's original defaults separately / `policy()` 与 `default_policy()` 可分别读取当前运行期策略与 sink 初始默认策略
- `policy_matches_default()` explicitly tells whether the current runtime policy has drifted from the defaults / `policy_matches_default()` 可显式判断当前运行期策略是否已偏离默认策略
- `set_policy(...)` applies append, auto-flush, and rotation as a bundled runtime update / `set_policy(...)` 可将 append, auto_flush, rotation 作为一组运行期策略一次性写回
- `reset_failure_counters()` clears the open/write/flush/rotation counters after diagnostics or recovery / `reset_failure_counters()` 可在排障或恢复后清空 open/write/flush/rotation 失败计数
- `reset_policy()` restores append, auto-flush, and rotation back to the sink's original defaults / `reset_policy()` 可将 append, auto_flush, rotation 恢复到 sink 初始默认策略
- `file_sink_policy_to_json(...)` / `stringify_file_sink_policy(...)` also export standalone file policy snapshots as JSON / `file_sink_policy_to_json(...)` / `stringify_file_sink_policy(...)` 也可将独立 file policy 快照直接导出为 JSON
- `file_sink_state_to_json(...)` / `stringify_file_sink_state(...)` and `runtime_file_state_to_json(...)` / `stringify_runtime_file_state(...)` export snapshots as JSON / `file_sink_state_to_json(...)` / `stringify_file_sink_state(...)` 与 `runtime_file_state_to_json(...)` / `stringify_runtime_file_state(...)` 可将快照直接导出为 JSON
- `set_auto_flush(...)`, `set_rotation(...)`, and `clear_rotation()` allow runtime tuning of core file sink policies / `set_auto_flush(...)`, `set_rotation(...)`, `clear_rotation()` 可在运行期调整 file sink 的基础策略
- `open_failures()`, `write_failures()`, `flush_failures()`, `rotation_failures()` expose basic sink health counters / `open_failures()`, `write_failures()`, `flush_failures()`, `rotation_failures()` 可用于观察基础 sink 健康状态
- `ConfiguredLogger` also forwards file reopen, flush, close, append-mode, path, auto-flush, rotation-config, state snapshot, append setter, policy setter, and failure-counter helpers for config-built file sinks / `ConfiguredLogger` 也会为配置生成的 file sink 转发 reopen, flush, close, append-mode, path, auto-flush, rotation 配置, state 快照, append setter, 策略 setter 与失败计数访问器
- `ConfiguredLogger::file_runtime_state()` also reports whether a file sink is queue-wrapped and exposes the outer queue pending/drop counters together with the inner file snapshot / `ConfiguredLogger::file_runtime_state()` 还可报告 file sink 是否被 queue 包装, 并将外层 queue 的 pending/drop 计数与内层 file 快照一起返回
- `ConfiguredLogger::file_policy()` and `ConfiguredLogger::file_default_policy()` also expose current runtime policy and initial config policy separately / `ConfiguredLogger::file_policy()` 与 `ConfiguredLogger::file_default_policy()` 也可分别读取当前运行期策略与初始配置策略
- `ConfiguredLogger::file_policy_matches_default()` also tells whether the current runtime file policy has drifted from the default config / `ConfiguredLogger::file_policy_matches_default()` 也可显式判断当前运行期 file 策略是否已偏离默认配置
- `ConfiguredLogger::file_set_policy()` also applies a bundled runtime file policy through the config-built control surface / `ConfiguredLogger::file_set_policy()` 也可通过配置式控制面一次性写回 bundled file 策略
- `ConfiguredLogger::file_reset_failure_counters()` also clears file failure counters through the config-built control surface / `ConfiguredLogger::file_reset_failure_counters()` 也可通过配置式控制面清空 file 失败计数
- `ConfiguredLogger::file_reset_policy()` also restores runtime file policy back to the initial config values / `ConfiguredLogger::file_reset_policy()` 也可将运行期 file 策略恢复到初始配置值
- current scope is observability-first, not a full self-healing sink runtime / 当前以可观测性为主, 不提供完整的自恢复 sink runtime.
-1054
View File
File diff suppressed because it is too large Load Diff
-51
View File
@@ -1,51 +0,0 @@
pub struct FileHandle {
path : String
}
fn open_file_handle_internal(path : String, append : Bool) -> FileHandle? {
ignore(append)
ignore(path)
let _unused : FileHandle = { path: "" }
ignore(_unused)
None
}
fn write_file_handle_internal(handle : FileHandle, content : String) -> Bool {
ignore(handle)
ignore(content)
false
}
fn flush_file_handle_internal(handle : FileHandle) -> Bool {
ignore(handle)
false
}
fn close_file_handle_internal(handle : FileHandle) -> Bool {
ignore(handle)
false
}
fn file_size_internal(handle : FileHandle) -> Int {
ignore(handle)
0
}
fn rename_file_internal(from_path : String, to_path : String) -> Bool {
ignore(from_path)
ignore(to_path)
false
}
fn remove_file_internal(path : String) -> Bool {
ignore(path)
false
}
fn string_byte_length_internal(content : String) -> Int {
content.length()
}
fn native_files_supported_internal() -> Bool {
false
}
-128
View File
@@ -1,128 +0,0 @@
pub type RecordFormatter = (Record) -> String
pub struct TextFormatter {
show_timestamp : Bool
show_level : Bool
show_target : Bool
show_fields : Bool
separator : String
field_separator : String
template : String
}
pub fn text_formatter(
show_timestamp~ : Bool = true,
show_level~ : Bool = true,
show_target~ : Bool = true,
show_fields~ : Bool = true,
separator~ : String = " ",
field_separator~ : String = " ",
template~ : String = "",
) -> TextFormatter {
{
show_timestamp,
show_level,
show_target,
show_fields,
separator,
field_separator,
template,
}
}
fn fields_to_json(fields : Array[Field]) -> Json {
let obj : Map[String, Json] = {}
for item in fields {
obj[item.key] = Json::string(item.value)
}
Json::object(obj)
}
fn format_fields(fields : Array[Field], separator : String) -> String {
fields.map(fn(f) { "\{f.key}=\{f.value}" }).join(separator)
}
fn timestamp_text(rec : Record, formatter : TextFormatter) -> String {
if formatter.show_timestamp && rec.timestamp_ms != 0UL {
rec.timestamp_ms.to_string()
} else {
""
}
}
fn level_text(rec : Record, formatter : TextFormatter) -> String {
if formatter.show_level {
rec.level.label()
} else {
""
}
}
fn target_text(rec : Record, formatter : TextFormatter) -> String {
if formatter.show_target && rec.target != "" {
rec.target
} else {
""
}
}
fn fields_text(rec : Record, formatter : TextFormatter) -> String {
if formatter.show_fields && rec.fields.length() != 0 {
format_fields(rec.fields, formatter.field_separator)
} else {
""
}
}
fn render_template(rec : Record, formatter : TextFormatter) -> String {
formatter.template
.replace_all(old="{timestamp}", new=timestamp_text(rec, formatter))
.replace_all(old="{timestamp_ms}", new=timestamp_text(rec, formatter))
.replace_all(old="{level}", new=level_text(rec, formatter))
.replace_all(old="{target}", new=target_text(rec, formatter))
.replace_all(old="{message}", new=rec.message)
.replace_all(old="{fields}", new=fields_text(rec, formatter))
.trim()
.to_owned()
}
pub fn format_text(rec : Record, formatter~ : TextFormatter = text_formatter()) -> String {
if formatter.template != "" {
return render_template(rec, formatter)
}
let parts : Array[String] = []
if formatter.show_timestamp && rec.timestamp_ms != 0UL {
parts.push("[\{rec.timestamp_ms.to_string()}]")
}
if formatter.show_level {
parts.push("[\{rec.level.label()}]")
}
if formatter.show_target && rec.target != "" {
parts.push("[\{rec.target}]")
}
parts.push(rec.message)
let base = parts.join(formatter.separator)
if !formatter.show_fields || rec.fields.length() == 0 {
base
} else {
let details = format_fields(rec.fields, formatter.field_separator)
"\{base}\{formatter.separator}\{details}"
}
}
pub fn format_json(rec : Record) -> String {
let obj : Map[String, Json] = {
"level": Json::string(rec.level.label()),
"message": Json::string(rec.message),
"fields": fields_to_json(rec.fields),
}
if rec.timestamp_ms != 0UL {
obj["timestamp_ms"] = rec.timestamp_ms.to_json()
}
if rec.target == "" {
Json::object(obj).stringify()
} else {
obj["target"] = Json::string(rec.target)
Json::object(obj).stringify()
}
}
-42
View File
@@ -1,42 +0,0 @@
pub struct Field {
key : String
value : String
}
pub fn field(key : String, value : String) -> Field {
{ key, value }
}
pub fn fields(entries : Array[(String, String)]) -> Array[Field] {
entries.map(fn(entry) {
field(entry.0, entry.1)
})
}
pub struct Record {
level : Level
timestamp_ms : UInt64
target : String
message : String
fields : Array[Field]
}
pub fn Record::new(
level : Level,
message : String,
timestamp_ms~ : UInt64 = 0UL,
target~ : String = "",
fields~ : Array[Field] = [],
) -> Record {
{ level, timestamp_ms, target, message, fields }
}
fn record(
level : Level,
message : String,
timestamp_ms~ : UInt64 = 0UL,
target~ : String = "",
fields~ : Array[Field] = [],
) -> Record {
Record::new(level, message, timestamp_ms=timestamp_ms, target=target, fields=fields)
}
-650
View File
@@ -1,650 +0,0 @@
pub trait Sink {
write(Self, Record) -> Unit
}
pub struct ConsoleSink {
_dummy : Unit
}
pub fn console_sink() -> ConsoleSink {
{ _dummy: () }
}
pub impl Sink for ConsoleSink with write(self, rec) {
ignore(self)
println(format_text(rec))
}
pub struct ContextSink[S] {
sink : S
context_fields : Array[Field]
}
pub impl[S : Sink] Sink for ContextSink[S] with write(self, rec) {
let merged = if self.context_fields.length() == 0 {
rec.fields
} else if rec.fields.length() == 0 {
self.context_fields
} else {
self.context_fields + rec.fields
}
self.sink.write({ ..rec, fields: merged })
}
pub struct JsonConsoleSink {
_dummy : Unit
}
pub fn json_console_sink() -> JsonConsoleSink {
{ _dummy: () }
}
pub impl Sink for JsonConsoleSink with write(self, rec) {
ignore(self)
println(format_json(rec))
}
pub struct FileSink {
path : String
append : Ref[Bool]
default_append : Bool
handle : Ref[FileHandle?]
formatter : RecordFormatter
auto_flush : Ref[Bool]
default_auto_flush : Bool
rotation : Ref[FileRotation?]
default_rotation : FileRotation?
open_failures : Ref[Int]
write_failures : Ref[Int]
flush_failures : Ref[Int]
rotation_failures : Ref[Int]
}
pub struct FileRotation {
max_bytes : Int
max_backups : Int
}
pub struct FileSinkState {
path : String
available : Bool
append : Bool
auto_flush : Bool
rotation : FileRotation?
open_failures : Int
write_failures : Int
flush_failures : Int
rotation_failures : Int
}
pub struct FileSinkPolicy {
append : Bool
auto_flush : Bool
rotation : FileRotation?
}
pub fn FileSinkPolicy::new(
append~ : Bool = true,
auto_flush~ : Bool = true,
rotation~ : FileRotation? = None,
) -> FileSinkPolicy {
{ append, auto_flush, rotation }
}
pub fn FileSinkState::new(
path : String,
available~ : Bool = false,
append~ : Bool = true,
auto_flush~ : Bool = true,
rotation~ : FileRotation? = None,
open_failures~ : Int = 0,
write_failures~ : Int = 0,
flush_failures~ : Int = 0,
rotation_failures~ : Int = 0,
) -> FileSinkState {
{
path,
available,
append,
auto_flush,
rotation,
open_failures,
write_failures,
flush_failures,
rotation_failures,
}
}
pub fn file_rotation(max_bytes : Int, max_backups~ : Int = 1) -> FileRotation {
{
max_bytes: if max_bytes <= 0 { 1 } else { max_bytes },
max_backups: if max_backups <= 0 { 1 } else { max_backups },
}
}
pub fn native_files_supported() -> Bool {
native_files_supported_internal()
}
pub fn file_sink(
path : String,
append~ : Bool = true,
auto_flush~ : Bool = true,
rotation~ : FileRotation? = None,
formatter~ : RecordFormatter = fn(rec) {
format_text(rec)
},
) -> FileSink {
let handle = open_file_handle_internal(path, append)
{
path,
append: Ref::new(append),
default_append: append,
handle: Ref::new(handle),
formatter,
auto_flush: Ref::new(auto_flush),
default_auto_flush: auto_flush,
rotation: Ref::new(rotation),
default_rotation: rotation,
open_failures: Ref::new(if handle is Some(_) { 0 } else { 1 }),
write_failures: Ref::new(0),
flush_failures: Ref::new(0),
rotation_failures: Ref::new(0),
}
}
pub fn FileSink::is_available(self : FileSink) -> Bool {
self.handle.val is Some(_)
}
pub fn FileSink::flush(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = flush_file_handle_internal(handle)
if !ok {
self.flush_failures.val += 1
}
ok
}
}
}
pub fn FileSink::append_mode(self : FileSink) -> Bool {
self.append.val
}
pub fn FileSink::set_append_mode(self : FileSink, append : Bool) -> Unit {
self.append.val = append
}
pub fn FileSink::path(self : FileSink) -> String {
self.path
}
pub fn FileSink::auto_flush_enabled(self : FileSink) -> Bool {
self.auto_flush.val
}
pub fn FileSink::rotation_enabled(self : FileSink) -> Bool {
self.rotation.val is Some(_)
}
pub fn FileSink::rotation_config(self : FileSink) -> FileRotation? {
self.rotation.val
}
pub fn FileSink::set_auto_flush(self : FileSink, enabled : Bool) -> Unit {
self.auto_flush.val = enabled
}
pub fn FileSink::set_policy(self : FileSink, policy : FileSinkPolicy) -> Unit {
self.append.val = policy.append
self.auto_flush.val = policy.auto_flush
self.rotation.val = policy.rotation
}
pub fn FileSink::set_rotation(self : FileSink, rotation : FileRotation?) -> Unit {
self.rotation.val = rotation
}
pub fn FileSink::clear_rotation(self : FileSink) -> Unit {
self.rotation.val = None
}
pub fn FileSink::close(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = close_file_handle_internal(handle)
self.handle.val = None
ok
}
}
}
pub fn FileSink::rotation_failures(self : FileSink) -> Int {
self.rotation_failures.val
}
pub fn FileSink::open_failures(self : FileSink) -> Int {
self.open_failures.val
}
pub fn FileSink::write_failures(self : FileSink) -> Int {
self.write_failures.val
}
pub fn FileSink::flush_failures(self : FileSink) -> Int {
self.flush_failures.val
}
pub fn FileSink::reset_failure_counters(self : FileSink) -> Unit {
self.open_failures.val = 0
self.write_failures.val = 0
self.flush_failures.val = 0
self.rotation_failures.val = 0
}
pub fn FileSink::reset_policy(self : FileSink) -> Unit {
self.append.val = self.default_append
self.auto_flush.val = self.default_auto_flush
self.rotation.val = self.default_rotation
}
pub fn FileSink::policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.append.val,
auto_flush=self.auto_flush.val,
rotation=self.rotation.val,
)
}
pub fn FileSink::default_policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.default_append,
auto_flush=self.default_auto_flush,
rotation=self.default_rotation,
)
}
pub fn FileSink::policy_matches_default(self : FileSink) -> Bool {
let current = self.policy()
let default = self.default_policy()
current.append == default.append &&
current.auto_flush == default.auto_flush &&
policy_rotation_equals_internal(current.rotation, default.rotation)
}
fn policy_rotation_equals_internal(left : FileRotation?, right : FileRotation?) -> Bool {
match (left, right) {
(None, None) => true
(Some(a), Some(b)) => a.max_bytes == b.max_bytes && a.max_backups == b.max_backups
_ => false
}
}
pub fn FileSink::state(self : FileSink) -> FileSinkState {
{
path: self.path,
available: self.is_available(),
append: self.append.val,
auto_flush: self.auto_flush.val,
rotation: self.rotation.val,
open_failures: self.open_failures.val,
write_failures: self.write_failures.val,
flush_failures: self.flush_failures.val,
rotation_failures: self.rotation_failures.val,
}
}
pub fn FileSink::reopen(self : FileSink, append~ : Bool? = None) -> Bool {
let append_mode = append.unwrap_or(self.append.val)
self.append.val = append_mode
match self.handle.val {
None => ()
Some(handle) => {
ignore(close_file_handle_internal(handle))
self.handle.val = None
}
}
let reopened = open_file_handle_internal(self.path, append_mode)
self.handle.val = reopened
if reopened is Some(_) {
true
} else {
self.open_failures.val += 1
false
}
}
pub fn FileSink::reopen_with_current_policy(self : FileSink) -> Bool {
self.reopen()
}
pub fn FileSink::reopen_append(self : FileSink) -> Bool {
self.reopen(append=Some(true))
}
pub fn FileSink::reopen_truncate(self : FileSink) -> Bool {
self.reopen(append=Some(false))
}
fn rotated_file_path(path : String, index : Int) -> String {
"\{path}.\{index}"
}
fn rotate_file_sink_internal(sink : FileSink, rotation : FileRotation) -> Bool {
let closed = match sink.handle.val {
None => true
Some(handle) => {
let ok = close_file_handle_internal(handle)
sink.handle.val = None
ok
}
}
if !closed {
return false
}
if rotation.max_backups > 0 {
ignore(remove_file_internal(rotated_file_path(sink.path, rotation.max_backups)))
for index = rotation.max_backups - 1; index >= 1; {
let from_path = rotated_file_path(sink.path, index)
let to_path = rotated_file_path(sink.path, index + 1)
ignore(rename_file_internal(from_path, to_path))
continue index - 1
}
ignore(rename_file_internal(sink.path, rotated_file_path(sink.path, 1)))
} else {
ignore(remove_file_internal(sink.path))
}
sink.handle.val = open_file_handle_internal(sink.path, false)
sink.handle.val is Some(_)
}
fn rotate_if_needed_internal(sink : FileSink, next_line_bytes : Int) -> Bool {
match sink.rotation.val {
None => true
Some(rotation) => match sink.handle.val {
None => false
Some(handle) => {
let size = file_size_internal(handle)
if size + next_line_bytes <= rotation.max_bytes {
true
} else {
let rotated = rotate_file_sink_internal(sink, rotation)
if !rotated {
sink.rotation_failures.val += 1
}
rotated
}
}
}
}
}
pub impl Sink for FileSink with write(self, rec) {
match self.handle.val {
None => {
self.write_failures.val += 1
}
Some(_) => {
let line = "\{(self.formatter)(rec)}\n"
let can_write = rotate_if_needed_internal(self, string_byte_length_internal(line))
if can_write {
match self.handle.val {
None => {
self.write_failures.val += 1
}
Some(active) => {
let wrote = write_file_handle_internal(active, line)
if wrote {
if self.auto_flush.val {
let flushed = flush_file_handle_internal(active)
if !flushed {
self.flush_failures.val += 1
}
}
} else {
self.write_failures.val += 1
}
}
}
} else {
self.write_failures.val += 1
}
}
}
}
pub struct FormattedConsoleSink {
formatter : RecordFormatter
}
pub fn formatted_console_sink(formatter : RecordFormatter) -> FormattedConsoleSink {
{ formatter, }
}
pub fn text_console_sink(formatter : TextFormatter) -> FormattedConsoleSink {
formatted_console_sink(fn(rec) {
format_text(rec, formatter=formatter)
})
}
pub impl Sink for FormattedConsoleSink with write(self, rec) {
println((self.formatter)(rec))
}
pub struct FormattedCallbackSink {
formatter : RecordFormatter
callback : (String) -> Unit
}
pub fn formatted_callback_sink(
formatter : RecordFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
{ formatter, callback }
}
pub fn text_callback_sink(
formatter : TextFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
formatted_callback_sink(fn(rec) {
format_text(rec, formatter=formatter)
}, callback)
}
pub impl Sink for FormattedCallbackSink with write(self, rec) {
(self.callback)((self.formatter)(rec))
}
pub struct FanoutSink[A, B] {
left : A
right : B
}
pub fn[A, B] fanout_sink(left : A, right : B) -> FanoutSink[A, B] {
{ left, right }
}
pub impl[A : Sink, B : Sink] Sink for FanoutSink[A, B] with write(self, rec) {
self.left.write(rec)
self.right.write({ ..rec })
}
pub struct SplitSink[A, B] {
left : A
right : B
predicate : (Record) -> Bool
}
pub fn[A, B] split_sink(left : A, right : B, predicate : (Record) -> Bool) -> SplitSink[A, B] {
{ left, right, predicate }
}
pub fn[A, B] split_by_level(
left : A,
right : B,
min_level~ : Level = Level::Warn,
) -> SplitSink[A, B] {
split_sink(left, right, fn(rec) {
rec.level.enabled(min_level)
})
}
pub impl[A : Sink, B : Sink] Sink for SplitSink[A, B] with write(self, rec) {
if (self.predicate)(rec) {
self.left.write(rec)
} else {
self.right.write(rec)
}
}
pub struct CallbackSink {
callback : (Record) -> Unit
}
pub fn callback_sink(callback : (Record) -> Unit) -> CallbackSink {
{ callback, }
}
pub impl Sink for CallbackSink with write(self, rec) {
(self.callback)(rec)
}
pub struct BufferedSink[S] {
sink : S
buffer : Ref[Array[Record]]
flush_limit : Int
}
pub fn[S] buffered_sink(sink : S, flush_limit~ : Int = 1) -> BufferedSink[S] {
let actual_limit = if flush_limit <= 0 { 1 } else { flush_limit }
{ sink, buffer: Ref::new([]), flush_limit: actual_limit }
}
pub fn[S] BufferedSink::pending_count(self : BufferedSink[S]) -> Int {
self.buffer.val.length()
}
pub fn[S : Sink] BufferedSink::flush(self : BufferedSink[S]) -> Unit {
if self.buffer.val.length() == 0 {
()
} else {
let pending = self.buffer.val
self.buffer.val = []
for rec in pending {
self.sink.write(rec)
}
}
}
pub impl[S : Sink] Sink for BufferedSink[S] with write(self, rec) {
self.buffer.val.push(rec)
if self.buffer.val.length() >= self.flush_limit {
self.flush()
}
}
pub(all) enum QueueOverflowPolicy {
DropNewest
DropOldest
}
pub struct QueuedSink[S] {
sink : S
queue : @queue.Queue[Record]
max_pending : Int
overflow : QueueOverflowPolicy
dropped_count : Ref[Int]
}
pub fn[S] queued_sink(
sink : S,
max_pending~ : Int = 0,
overflow~ : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
) -> QueuedSink[S] {
{
sink,
queue: @queue.Queue::new(),
max_pending,
overflow,
dropped_count: Ref::new(0),
}
}
pub fn[S] QueuedSink::pending_count(self : QueuedSink[S]) -> Int {
self.queue.length()
}
pub fn[S] QueuedSink::dropped_count(self : QueuedSink[S]) -> Int {
self.dropped_count.val
}
pub fn[S : Sink] QueuedSink::drain(self : QueuedSink[S], max_items~ : Int = -1) -> Int {
if max_items == 0 {
return 0
}
let limit = if max_items < 0 { self.pending_count() } else { max_items }
for drained = 0; drained < limit; {
match self.queue.pop() {
None => break drained
Some(rec) => {
self.sink.write(rec)
continue drained + 1
}
}
} nobreak {
limit
}
}
pub fn[S : Sink] QueuedSink::flush(self : QueuedSink[S]) -> Int {
self.drain()
}
pub impl[S] Sink for QueuedSink[S] with write(self, rec) {
let full = self.max_pending > 0 && self.pending_count() >= self.max_pending
if !full {
self.queue.push(rec)
} else {
self.dropped_count.val += 1
match self.overflow {
QueueOverflowPolicy::DropNewest => ()
QueueOverflowPolicy::DropOldest => {
ignore(self.queue.pop())
self.queue.push(rec)
}
}
}
}
pub struct FilterSink[S] {
sink : S
predicate : (Record) -> Bool
}
pub fn[S] filter_sink(sink : S, predicate : (Record) -> Bool) -> FilterSink[S] {
{ sink, predicate }
}
pub impl[S : Sink] Sink for FilterSink[S] with write(self, rec) {
if (self.predicate)(rec) {
self.sink.write(rec)
}
}
pub struct PatchSink[S] {
sink : S
patch : RecordPatch
}
pub fn[S] patch_sink(sink : S, patch : RecordPatch) -> PatchSink[S] {
{ sink, patch }
}
pub impl[S : Sink] Sink for PatchSink[S] with write(self, rec) {
self.sink.write((self.patch)(rec))
}
-150
View File
@@ -1,150 +0,0 @@
async test "shutdown drains pending records" {
let written : Ref[Array[String]] = Ref::new([])
let flushes : Ref[Int] = Ref::new(0)
let logger = async_logger(
@bitlogger.callback_sink(fn(rec) {
written.val.push(rec.message)
}),
config=AsyncLoggerConfig::new(
max_pending=4,
overflow=AsyncOverflowPolicy::Blocking,
max_batch=4,
linger_ms=10,
flush=AsyncFlushPolicy::Batch,
),
min_level=@bitlogger.Level::Info,
target="async.test",
flush=fn(_) {
flushes.val += 1
1
},
)
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("one")
logger.info("two")
logger.shutdown()
})
inspect(logger.is_closed(), content="true")
inspect(logger.is_running(), content="false")
inspect(logger.has_failed(), content="false")
inspect(logger.pending_count(), content="0")
inspect(match logger.flush_policy() {
AsyncFlushPolicy::Never => "Never"
AsyncFlushPolicy::Batch => "Batch"
AsyncFlushPolicy::Shutdown => "Shutdown"
}, content="Batch")
inspect(written.val.length(), content="2")
inspect(written.val[0], content="one")
inspect(written.val[1], content="two")
inspect(flushes.val, content="1")
}
async test "close clear counts abandoned records as dropped" {
let logger = async_logger(
@bitlogger.callback_sink(fn(_) {
}),
config=AsyncLoggerConfig::new(
max_pending=4,
overflow=AsyncOverflowPolicy::Blocking,
),
min_level=@bitlogger.Level::Info,
target="async.clear",
)
logger.info("one")
logger.info("two")
inspect(logger.pending_count(), content="2")
inspect(logger.dropped_count(), content="0")
logger.close(clear=true)
inspect(logger.is_closed(), content="true")
inspect(logger.pending_count(), content="0")
inspect(logger.dropped_count(), content="2")
}
async test "shutdown clear closes without worker startup" {
let logger = async_logger(
@bitlogger.callback_sink(fn(_) {
}),
config=AsyncLoggerConfig::new(
max_pending=2,
overflow=AsyncOverflowPolicy::Blocking,
),
min_level=@bitlogger.Level::Info,
target="async.noworker",
)
logger.info("one")
logger.shutdown(clear=true)
inspect(logger.is_closed(), content="true")
inspect(logger.is_running(), content="false")
inspect(logger.pending_count(), content="0")
inspect(logger.dropped_count(), content="1")
}
test "async logger config stringify roundtrips stable fields" {
let text = stringify_async_logger_config(
AsyncLoggerConfig::new(
max_pending=8,
overflow=AsyncOverflowPolicy::DropOldest,
max_batch=3,
linger_ms=25,
flush=AsyncFlushPolicy::Batch,
),
)
let config = parse_async_logger_config_text(text)
inspect(config.max_pending, content="8")
inspect(config.max_batch, content="3")
inspect(config.linger_ms, content="25")
inspect(match config.overflow {
AsyncOverflowPolicy::Blocking => "Blocking"
AsyncOverflowPolicy::DropOldest => "DropOldest"
AsyncOverflowPolicy::DropNewest => "DropNewest"
}, content="DropOldest")
inspect(match config.flush {
AsyncFlushPolicy::Never => "Never"
AsyncFlushPolicy::Batch => "Batch"
AsyncFlushPolicy::Shutdown => "Shutdown"
}, content="Batch")
}
test "async build config stringify roundtrips nested logger and async fields" {
let text = stringify_async_logger_build_config(
AsyncLoggerBuildConfig::new(
logger=@bitlogger.LoggerConfig::new(
min_level=@bitlogger.Level::Warn,
target="async.roundtrip",
timestamp=true,
sink=@bitlogger.SinkConfig::new(kind=@bitlogger.SinkKind::TextConsole),
),
async_config=AsyncLoggerConfig::new(
max_pending=2,
overflow=AsyncOverflowPolicy::DropNewest,
max_batch=5,
linger_ms=40,
flush=AsyncFlushPolicy::Shutdown,
),
),
)
let config = parse_async_logger_build_config_text(text)
inspect(config.logger.min_level.label(), content="WARN")
inspect(config.logger.target, content="async.roundtrip")
inspect(config.logger.timestamp, content="true")
inspect(config.async_config.max_pending, content="2")
inspect(config.async_config.max_batch, content="5")
inspect(config.async_config.linger_ms, content="40")
inspect(match config.async_config.overflow {
AsyncOverflowPolicy::Blocking => "Blocking"
AsyncOverflowPolicy::DropOldest => "DropOldest"
AsyncOverflowPolicy::DropNewest => "DropNewest"
}, content="DropNewest")
inspect(match config.async_config.flush {
AsyncFlushPolicy::Never => "Never"
AsyncFlushPolicy::Batch => "Batch"
AsyncFlushPolicy::Shutdown => "Shutdown"
}, content="Shutdown")
}
-234
View File
@@ -1,234 +0,0 @@
pub(all) suberror AsyncLoggerClosed {
AsyncLoggerClosed
}
pub(all) enum AsyncOverflowPolicy {
Blocking
DropOldest
DropNewest
}
pub(all) enum AsyncFlushPolicy {
Never
Batch
Shutdown
}
pub struct AsyncLoggerConfig {
max_pending : Int
overflow : AsyncOverflowPolicy
max_batch : Int
linger_ms : Int
flush : AsyncFlushPolicy
}
pub fn AsyncLoggerConfig::new(
max_pending~ : Int = 0,
overflow~ : AsyncOverflowPolicy = AsyncOverflowPolicy::Blocking,
max_batch~ : Int = 1,
linger_ms~ : Int = 0,
flush~ : AsyncFlushPolicy = AsyncFlushPolicy::Never,
) -> AsyncLoggerConfig {
{
max_pending,
overflow,
max_batch: if max_batch <= 1 { 1 } else { max_batch },
linger_ms: if linger_ms < 0 { 0 } else { linger_ms },
flush,
}
}
pub struct AsyncLogger[S] {}
pub struct AsyncLoggerBuildConfig {
logger : @bitlogger.LoggerConfig
async_config : AsyncLoggerConfig
}
pub fn AsyncLoggerBuildConfig::new(
logger~ : @bitlogger.LoggerConfig = @bitlogger.default_logger_config(),
async_config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
) -> AsyncLoggerBuildConfig {
{ logger, async_config }
}
pub fn parse_async_logger_build_config_text(input : String) -> AsyncLoggerBuildConfig raise {
ignore(input)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn parse_async_logger_config_text(input : String) -> AsyncLoggerConfig raise {
ignore(input)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> @json_parser.JsonValue {
ignore(config)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn stringify_async_logger_config(config : AsyncLoggerConfig, pretty~ : Bool = false) -> String {
ignore(config)
ignore(pretty)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn async_logger_build_config_to_json(
config : AsyncLoggerBuildConfig,
) -> @json_parser.JsonValue {
ignore(config)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn stringify_async_logger_build_config(
config : AsyncLoggerBuildConfig,
pretty~ : Bool = false,
) -> String {
ignore(config)
ignore(pretty)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn async_logger[S : @bitlogger.Sink](
sink : S,
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
target~ : String = "",
flush~ : (S) -> Int = fn(_) { 0 },
) -> AsyncLogger[S] {
ignore(sink)
ignore(config)
ignore(min_level)
ignore(target)
ignore(flush)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_timestamp(self : AsyncLogger[S], enabled~ : Bool = true) -> AsyncLogger[S] {
ignore(self)
ignore(enabled)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_target(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {
ignore(self)
ignore(target)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_context_fields(
self : AsyncLogger[S],
fields : Array[@bitlogger.Field],
) -> AsyncLogger[S] {
ignore(self)
ignore(fields)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_filter(
self : AsyncLogger[S],
predicate : (@bitlogger.Record) -> Bool,
) -> AsyncLogger[S] {
ignore(self)
ignore(predicate)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_patch(
self : AsyncLogger[S],
patch : @bitlogger.RecordPatch,
) -> AsyncLogger[S] {
ignore(self)
ignore(patch)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::with_min_level(
self : AsyncLogger[S],
min_level : @bitlogger.Level,
) -> AsyncLogger[S] {
ignore(self)
ignore(min_level)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::child(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {
ignore(self)
ignore(target)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::is_enabled(self : AsyncLogger[S], level : @bitlogger.Level) -> Bool {
ignore(self)
ignore(level)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::pending_count(self : AsyncLogger[S]) -> Int {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::dropped_count(self : AsyncLogger[S]) -> Int {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::is_closed(self : AsyncLogger[S]) -> Bool {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::is_running(self : AsyncLogger[S]) -> Bool {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::has_failed(self : AsyncLogger[S]) -> Bool {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::last_error(self : AsyncLogger[S]) -> String {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Unit {
ignore(self)
ignore(clear)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub async fn[S] AsyncLogger::wait_idle(self : AsyncLogger[S]) -> Unit {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub async fn[S] AsyncLogger::shutdown(self : AsyncLogger[S], clear? : Bool = false) -> Unit {
ignore(self)
ignore(clear)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub async fn[S : @bitlogger.Sink] AsyncLogger::run(self : AsyncLogger[S]) -> Unit {
ignore(self)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn build_async_logger(
config : AsyncLoggerBuildConfig,
) -> AsyncLogger[@bitlogger.RuntimeSink] {
ignore(config)
abort("bitlogger_async currently only supports native/llvm backends")
}
pub fn build_async_text_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@bitlogger.FormattedConsoleSink] {
ignore(config)
abort("bitlogger_async currently only supports native/llvm backends")
}
+68 -242
View File
@@ -2,269 +2,95 @@
BitLogger is a structured logging library written in MoonBit.
This README focuses on project positioning, core capabilities, and entry points. Detailed API coverage now lives in `docs/api/`.
## Overview
BitLogger currently provides:
BitLogger is designed to provide composable, configurable, and cross-target logging infrastructure for MoonBit projects.
- log levels: `Trace`, `Debug`, `Info`, `Warn`, `Error`
- structured key-value fields
- plain console output
- JSON console output
- child target composition via `child(...)`
- context fields via `with_context_fields(...)`
- optional timestamps via `with_timestamp()`
- sink fanout via `fanout_sink(...)`
- sink routing via `split_sink(...)` and `split_by_level(...)`
- custom integration via `callback_sink(...)`
- in-memory buffering via `buffered_sink(...)`
- record filtering via `filter_sink(...)`
- reusable filter helpers such as `target_has_prefix(...)`, `message_contains(...)`, `level_at_least(...)`, and `field_equals(...)`
- record patching via `with_patch(...)` and `patch_sink(...)`
- patch helpers such as `prefix_message(...)`, `append_fields(...)`, and `redact_fields(...)`
- context binding via `bind(...)` and `fields(...)`
- explicit queued delivery via `queued_sink(...)` and `with_queue(...)`
- bounded backlog with `QueueOverflowPolicy::DropNewest` and `QueueOverflowPolicy::DropOldest`
- configurable text formatting via `text_formatter(...)`, `format_text(...)`, `text_console_sink(...)`, and template-driven `template` output
- formatter-based callback integration via `formatted_callback_sink(...)`
- native-only file output via `file_sink(...)`, with basic size rotation, backup retention, explicit `reopen()` / `reopen_with_current_policy()` / `reopen_append()` / `reopen_truncate()`, and failure counters
- `native_files_supported()` for backend capability detection
- default global logger helpers
## Backend Compatibility
| Module / capability | native / llvm | js / wasm / wasm-gc |
| --- | --- | --- |
| `src` core package | Supported | Supported |
| `file_sink(...)` | Supported | Not available, `native_files_supported()` returns `false` |
| `src-async` | Native worker semantics | Compatibility implementation |
| `examples/async_basic` | Supported | Not shipped currently because `async fn main` entry support is still limited |
## Key Features
- Structured logging with levels, targets, and key-value fields.
- Composable sinks, filters, patches, fanout, routing, and queue wrappers.
- Config-driven runtime assembly through JSON parse/export helpers and `build_logger(...)` / `build_async_logger(...)`.
- Configurable text formatting with templates, style tags, and color control.
- Native file sink support with basic rotation, reopen helpers, and runtime observability.
- Separate async layer with bounded queueing, worker lifecycle control, runtime state, and cross-target compatibility.
## Quick Start
```moonbit
let logger = Logger::new(console_sink(), min_level=Level::Info, target="demo")
.with_timestamp()
.with_context_fields([field("service", "bitlogger")])
let logger = build_logger(
with_queue(
text_console(
min_level=Level::Info,
target="demo",
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
),
max_pending=32,
overflow=QueueOverflowPolicy::DropOldest,
),
)
logger.info("starting", fields=[field("port", "8080")])
```
Child target composition:
```moonbit
let worker = Logger::new(console_sink(), target="app").child("worker")
worker.info("job ready")
```
Custom callback sink:
```moonbit
let hook = Logger::new(
callback_sink(fn(rec) {
println("callback saw [\{rec.target}] \{rec.message}")
}),
target="hook",
)
hook.info("hello")
```
Basic buffered sink:
```moonbit
let sink = buffered_sink(console_sink(), flush_limit=2)
let logger = Logger::new(sink, target="buffered")
logger.info("one")
logger.info("two")
sink.flush()
```
Basic filter sink:
```moonbit
let sink = filter_sink(console_sink(), fn(rec) {
rec.target == "kept"
})
let kept = Logger::new(sink, target="kept")
let dropped = Logger::new(sink, target="dropped")
kept.info("visible")
dropped.info("hidden")
```
Chained logger filter:
```moonbit
let logger = Logger::new(console_sink(), target="service")
.with_filter(all_of([
target_has_prefix("service"),
message_contains("visible"),
]))
logger.info("hidden")
logger.child("api").info("visible")
```
Record patching:
```moonbit
let logger = Logger::new(console_sink(), target="auth")
.with_patch(compose_patches([
prefix_message("[safe] "),
redact_fields(["token"]),
append_fields([field("service", "bitlogger")]),
]))
logger.info("login", fields=[field("user", "alice"), field("token", "secret")])
```
Context binding:
```moonbit
let logger = Logger::new(console_sink(), target="audit")
.bind(fields([("service", "bitlogger"), ("scope", "login")]))
logger.info("accepted", fields=[field("user", "alice")])
```
Explicit queued sink:
```moonbit
let logger = Logger::new(console_sink(), target="queue")
.with_queue(max_pending=2, overflow=QueueOverflowPolicy::DropOldest)
logger.info("one")
logger.info("two")
logger.info("three")
ignore(logger.sink.flush())
```
Level-based split sink:
```moonbit
let logger = Logger::new(
split_by_level(
callback_sink(fn(rec) {
println("high priority: \{rec.level.label()} \{rec.message}")
}),
console_sink(),
min_level=Level::Warn,
),
min_level=Level::Trace,
target="split",
)
logger.info("normal output")
logger.warn("warning output")
```
Custom text formatter:
```moonbit
let formatter = text_formatter(
show_timestamp=false,
field_separator=",",
template="[{level}] {target} {message} :: {fields}",
)
let logger = Logger::new(text_console_sink(formatter), target="pretty")
logger.info("hello", fields=[field("mode", "pretty")])
```
JSON config loading:
```moonbit
let config = parse_logger_config_text(
"{\"min_level\":\"debug\",\"target\":\"config.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"field_separator\":\",\",\"template\":\"[{level}] {target} {message} :: {fields}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}",
)
let logger = build_logger(config)
logger.info("configured from json")
ignore(logger.flush())
```
Native file sink:
Start new synchronous code with `presets + build_logger(...)`: use `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)` to assemble `LoggerConfig`, optionally extend it with `with_queue(...)` or `with_file_rotation(...)`, then call `build_logger(...)`.
Switch to `Logger::new(...)` when you need direct runtime sink composition such as `fanout`, `split`, `callback`, or custom patch/filter graphs.
Async entry example:
```moonbit
if native_files_supported() {
let logger = Logger::new(
file_sink("bitlogger.log", rotation=Some(file_rotation(128, max_backups=2))),
target="file",
)
logger.info("hello", fields=[field("kind", "file")])
ignore(logger.sink.flush())
ignore(logger.sink.close())
}
```
File runtime state dump:
```moonbit
let logger = build_logger(
LoggerConfig::new(
sink=SinkConfig::new(kind=SinkKind::File, path="bitlogger-runtime.log"),
queue=Some(QueueConfig::new(16)),
),
)
logger.info("queued hello")
match logger.file_runtime_state() {
Some(snapshot) => println(stringify_runtime_file_state(snapshot, pretty=true))
None => ()
}
let logger = async_logger(console_sink(), target="async.demo")
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("started")
logger.shutdown()
})
```
## Repository Layout
- `bitlogger/`: MoonBit library package, tests, and Mooncake package README
- `examples/basic/`: runnable example package
- `examples/async_basic/`: runnable async logger example built on `moonbitlang/async`
- `src/`: core logging package.
- `src-async/`: async logging layer built on `moonbitlang/async`.
- `docs/api/`: one-file-per-interface API documentation.
- `examples/basic/`: breadth example; starts with the recommended presets-based sync path and then sweeps broader capabilities.
- `examples/console_basic/`: minimal console and JSON console output.
- `examples/text_formatter/`: text formatter and template example.
- `examples/style_tags/`: style tag and colored formatter example.
- `examples/file_rotation/`: file sink and rotation example.
- `examples/config_build/`: config-driven `build_logger(...)` example.
- `examples/presets/`: presets-based construction example.
- `examples/async_basic/`: async logger example.
## Links
## Documentation Entry Points
- [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
- [Chinese README](../README.md)
- [src package README](../src/README.mbt.md)
- Recommended sync starting APIs: `text_console(...)`, `file(...)`, `with_queue(...)`, `build_logger(...)`
- Selected API docs in `docs/api/`:
- [logger-new.md](./api/logger-new.md)
- [async-logger.md](./api/async-logger.md)
- [build-logger.md](./api/build-logger.md)
- [build-async-logger.md](./api/build-async-logger.md)
- [build-application-logger.md](./api/build-application-logger.md)
- [build-library-logger.md](./api/build-library-logger.md)
- [build-application-async-logger.md](./api/build-application-async-logger.md)
## Config Notes
## Notes
- BitLogger now includes a JSON config layer via `parse_logger_config_text(...)`, `stringify_logger_config(...)`, and `build_logger(...)`.
- `QueueConfig`, `TextFormatterConfig`, and `SinkConfig` can also be exported independently through `queue_config_to_json(...)` / `stringify_queue_config(...)`, `text_formatter_config_to_json(...)` / `stringify_text_formatter_config(...)`, and `sink_config_to_json(...)` / `stringify_sink_config(...)`.
- Supported keys include `min_level`, `target`, `timestamp`, `sink.kind`, `sink.path`, `sink.append`, `sink.auto_flush`, `sink.rotation`, `sink.text_formatter`, and `queue`.
- `sink.rotation` currently supports `max_bytes` and `max_backups` for basic size-based rotation and backup retention.
- `file_sink(...)` also exposes `reopen()`, `reopen_with_current_policy()`, `reopen_append()`, `reopen_truncate()`, `open_failures()`, `write_failures()`, `flush_failures()`, and `rotation_failures()` for basic observability.
- `file_sink(...)` also exposes `append_mode()`. Passing `append=...` to `reopen(...)` updates the current append policy used by later reopen calls, `reopen_with_current_policy()` makes that stored-policy reopen path explicit, and `reopen_append()` / `reopen_truncate()` cover the two common policy switches directly.
- `file_sink(...)` also supports `set_append_mode(...)` for explicitly changing the append policy that later reopen calls will use.
- `file_sink(...)` also exposes `path()` and `auto_flush_enabled()` for reading basic file-sink policy state.
- `file_sink(...)` also exposes `rotation_enabled()` and `rotation_config()` for reading whether rotation is active and which parameters are currently in effect.
- `file_sink(...)` also exposes `state()` for reading a single snapshot that includes path, availability, append policy, auto-flush flag, rotation config, and all current failure counters.
- `file_sink(...)` also exposes `policy()` and `default_policy()` for reading the current runtime policy and the sink's original default policy separately.
- `file_sink(...)` also exposes `policy_matches_default()` for explicitly checking whether the current runtime policy has drifted from the original defaults.
- `file_sink(...)` also exposes `set_policy(...)` for applying append, auto-flush, and rotation as a single bundled runtime policy update.
- `file_sink(...)` also exposes `reset_failure_counters()` so open/write/flush/rotation failure counters can be cleared after diagnostics or recovery handling.
- `file_sink(...)` also exposes `reset_policy()` so append, auto-flush, and rotation settings can be restored to the sink's original defaults.
- `file_sink(...)` also supports `set_auto_flush(...)`, `set_rotation(...)`, and `clear_rotation()` for runtime policy updates.
- `ConfiguredLogger` built through `build_logger(...)` also exposes `file_reopen()`, `file_reopen_with_current_policy()`, `file_reopen_append()`, `file_reopen_truncate()`, `file_flush()`, `file_close()`, `file_append_mode()`, `file_path()`, `file_auto_flush()`, `file_rotation_enabled()`, `file_rotation_config()`, `file_state()`, plus `file_set_append_mode(...)`, `file_set_auto_flush(...)`, `file_set_rotation(...)`, `file_clear_rotation()`, and the corresponding file failure counters, so config-driven file logging keeps a usable control surface.
- `ConfiguredLogger` also exposes `file_runtime_state()` so queued file loggers can report both the underlying file snapshot and the outer queue backlog/drop state in one read.
- `ConfiguredLogger` also exposes `file_policy()` and `file_default_policy()` for reading current runtime file policy and initial config policy separately.
- `ConfiguredLogger` also exposes `file_policy_matches_default()` for explicitly checking whether the current runtime file policy differs from its default config.
- `ConfiguredLogger` also exposes `file_set_policy(...)` for applying a bundled runtime file policy through the config-built control surface.
- `ConfiguredLogger` also exposes `file_reset_failure_counters()` for clearing file failure counters through the config-built control surface.
- `ConfiguredLogger` also exposes `file_reset_policy()` for restoring runtime file policy back to the initial config values.
- `file_sink_policy_to_json(...)` and `stringify_file_sink_policy(...)` can export standalone file-policy snapshots directly as JSON for policy diffing, diagnostics, or reporting.
- `file_sink_state_to_json(...)`, `stringify_file_sink_state(...)`, `runtime_file_state_to_json(...)`, and `stringify_runtime_file_state(...)` can export file and queued-file snapshots directly as JSON for diagnostics or reporting.
- `sink.text_formatter.template` currently supports fixed tokens: `{timestamp}`, `{timestamp_ms}`, `{level}`, `{target}`, `{message}`, and `{fields}`.
- Config-driven sink assembly currently supports `console`, `json_console`, `text_console`, and `file`.
- `queue` remains a synchronous bounded wrapper around the final sink, not an async runtime.
## Async Layer
- A separate `bitlogger_async/` package is now included.
- It uses `moonbitlang/async` and provides `AsyncLogger`, `async_logger(...)`, a background `run()` worker, and bounded async queue delivery.
- The current async API already supports `with_context_fields(...)`, `with_filter(...)`, `with_patch(...)`, `with_target(...)`, and `child(...)`.
- `shutdown()` is now the recommended way to stop the async worker. By default it waits for the queue to drain, closes the queue, and then waits for the worker to exit.
- Basic lifecycle observability is also available through `is_closed()`, `is_running()`, `has_failed()`, and `last_error()`.
- The async worker now supports batched queue draining via `max_batch` and basic flush policies through `flush=Never|Batch|Shutdown`.
- The recommended startup pattern is shown in [examples/async_basic/main.mbt](/E:/repo/MooLiteyukiBot/examples/async_basic/main.mbt:1).
- This layer currently targets `native/llvm` only and remains isolated from the synchronous logger core.
### Async Config
- `parse_async_logger_config_text(...)`, `stringify_async_logger_config(...)`, `parse_async_logger_build_config_text(...)`, and `build_async_logger(...)` are now available.
- The JSON root is split into `logger` and `async_config`.
- `logger` fully reuses the synchronous `LoggerConfig` schema, while `async_config` currently supports `max_pending`, `overflow`, `max_batch`, and `flush`.
- The recommended config-driven startup flow is shown in [examples/async_basic/main.mbt](/E:/repo/MooLiteyukiBot/examples/async_basic/main.mbt:1).
- `docs/README-en.md` no longer acts as an API catalog.
- Detailed API references, config fields, runtime control helpers, and lifecycle surfaces now live under `docs/api/`.
- For concrete runnable flows, prefer `examples/`.
+146
View File
@@ -0,0 +1,146 @@
---
name: example-api
group: dev
category: example-group
update-time: 20260512
description: An example API file to show how to write API doc.
key-word:
- example
- async
- sync
- public
- doc
---
**ONE API ONE FILE**
## Example-api-name
long discription.
### Interface
```moonbit
pub fn function_name(input) -> output {}
```
#### input
- `args : type` - Explain
#### output
- `output : type` - Explain
---
> `---` Just when interface has double or more write. Used to separate two different APIs.
<!--
e.g.:
```moonbit
pub fn target_is(target : String) -> RecordPredicate {}
```
#### input
- `target : String` - the expected target value
#### output
- `RecordPredicate` - a predicate used for filtering records
---
-->
> Use<! --The content packaged with -->does not actually need to be written in the official document, it is only used as an example for reference.
It is not necessary to write the complete function implementation.
### Explanation
Detailed rules explaining key parameters and behaviors
- ...
### How to Use
Here are some specific examples provided.
e.g.:
#### <What-Time-To-Use>
> title like: `#### When Need Colorful Formatter`
When sometime ...:
```moonbit
impl
```
In this example, <something> will <do-what>.
And <extra-info>.
#### <Next-Use-Method>
...
### Error Case
e.g.:
- If `target` is empty, returns a predicate that always evaluates to false.
- ...
...
### Notes
1. ...
2. ...
...
---
## API MARKDOWN YAML HEADER
> This just is an example, `---` in fact has yaml grammer error.
```yaml
---
name: example-api
group: dev
category: example-group
update-time: 20260512
description: An example API file to show how to write API doc.
key-word:
- example
- async
- sync
- public
- doc
---
```
It has 6 key:
- `name` - short and clear api name
- `group` - in static doc template site will use this key to render how to fold and group
- `category` - fastly search category in repo and will be used in template site
- `update-time` - full number use YYYYMMdd(year, month, day)
- `discription` - short discription
- `key-word` - use 2~5 key-words to help user fastly search
## Title Capitalization Standards
NO `# ...`
- `## ...` use `Abcd`
- `### ...` use `Abcd`
- `#### ...` use `abcd`
NO `##### ...`
+82
View File
@@ -0,0 +1,82 @@
---
name: all-of
group: api
category: filtering
update-time: 20260512
description: Create a reusable record predicate that requires every nested predicate to pass.
key-word:
- combine
- filter
- predicate
- public
---
## All-of
Create a `RecordPredicate` that returns `true` only when every predicate in the array returns `true`. This helper is the standard way to build strict multi-condition filters.
### Interface
```moonbit
pub fn all_of(predicates : Array[RecordPredicate]) -> RecordPredicate {}
```
#### input
- `predicates : Array[RecordPredicate]` - Predicates that must all succeed for a record to match.
#### output
- `RecordPredicate` - Predicate that returns `true` only when every nested predicate returns `true`.
### Explanation
Detailed rules explaining key parameters and behaviors
- Predicates are evaluated in array order.
- Evaluation stops early on the first predicate that returns `false`.
- If the array is empty, the combined predicate returns `true` because no condition failed.
- This helper is ideal for combining namespace, level, and field requirements into one reusable rule.
### How to Use
Here are some specific examples provided.
#### When Require Several Conditions
When routing should be both target- and level-aware:
```moonbit
let predicate = all_of([
target_has_prefix("service.api"),
level_at_least(Level::Warn),
])
```
In this example, records must satisfy both conditions before they pass.
#### When Add Field Constraints
When only contextual failures should remain:
```moonbit
let predicate = all_of([
message_contains("failed"),
has_field("request_id"),
not_(field_equals("tenant", "internal")),
])
```
In this example, the filter stays readable even though the rule has several parts.
### Error Case
e.g.:
- If `predicates` is empty, the returned predicate always evaluates to `true`.
- If one nested predicate is too strict, the whole combination may reject more records than expected.
### Notes
1. Put the cheapest or most selective predicates earlier when evaluation cost matters.
2. `all_of(...)` is usually easier to maintain than a custom inline predicate closure.
+82
View File
@@ -0,0 +1,82 @@
---
name: any-of
group: api
category: filtering
update-time: 20260512
description: Create a reusable record predicate that passes when any nested predicate matches.
key-word:
- combine
- filter
- predicate
- public
---
## Any-of
Create a `RecordPredicate` that returns `true` when at least one predicate in the array returns `true`. This helper is useful for routing several independent cases through the same path.
### Interface
```moonbit
pub fn any_of(predicates : Array[RecordPredicate]) -> RecordPredicate {}
```
#### input
- `predicates : Array[RecordPredicate]` - Predicates where any successful match should admit the record.
#### output
- `RecordPredicate` - Predicate that returns `true` when at least one nested predicate returns `true`.
### Explanation
Detailed rules explaining key parameters and behaviors
- Predicates are evaluated in array order.
- Evaluation stops early on the first predicate that returns `true`.
- If the array is empty, the combined predicate returns `false` because no predicate matched.
- This helper is useful when several targets, levels, or field signatures should share one sink.
### How to Use
Here are some specific examples provided.
#### When Accept Several Target Paths
When multiple subsystems should share one route:
```moonbit
let predicate = any_of([
target_is("audit"),
target_has_prefix("security"),
])
```
In this example, either matching branch is enough for the record to pass.
#### When Combine Different Diagnostic Conditions
When several independent signals are interesting:
```moonbit
let predicate = any_of([
level_at_least(Level::Error),
message_contains("timeout"),
field_equals("retryable", "true"),
])
```
In this example, one satisfied condition is enough to keep the record visible.
### Error Case
e.g.:
- If `predicates` is empty, the returned predicate always evaluates to `false`.
- If one nested predicate is too broad, it may shadow the intent of the other branches.
### Notes
1. Put the most common or cheapest success path earlier when evaluation cost matters.
2. Use `any_of(...)` when a single sink should accept multiple independent match patterns.
+82
View File
@@ -0,0 +1,82 @@
---
name: append-fields
group: api
category: patching
update-time: 20260512
description: Create a reusable record patch that appends extra fields to the record.
key-word:
- patch
- fields
- transform
- public
---
## Append-fields
Create a `RecordPatch` that appends extra fields to `rec.fields`. Use it when records should be enriched with stable metadata before reaching sinks.
### Interface
```moonbit
pub fn append_fields(extra_fields : Array[Field]) -> RecordPatch {}
```
#### input
- `extra_fields : Array[Field]` - Fields appended after the record's existing field list.
#### output
- `RecordPatch` - Patch that returns a record with appended fields.
### Explanation
Detailed rules explaining key parameters and behaviors
- If `extra_fields` is empty, the patch returns the original record unchanged.
- If the original record has no fields, the appended fields become the new field list.
- Otherwise, the original fields stay first and `extra_fields` are appended afterward.
- This helper is useful for environment tags, service metadata, and bridge-layer context.
### How to Use
Here are some specific examples provided.
#### When Add Service Metadata
When every record should carry shared context:
```moonbit
let logger = Logger::new(console_sink())
.with_patch(append_fields([
field("service", "billing"),
field("region", "cn"),
]))
```
In this example, the extra fields are added to every emitted record.
#### When Compose With Message Rewriting
When both visible and structured context are needed:
```moonbit
let patch = compose_patches([
prefix_message("[api] "),
append_fields([field("component", "gateway")]),
])
```
In this example, the record gains both textual and structured enrichment.
### Error Case
e.g.:
- If `extra_fields` is empty, the patch behaves like a no-op.
- If appended field keys duplicate existing keys, both copies remain in the field list.
### Notes
1. This helper appends fields; it does not deduplicate or overwrite existing entries.
2. Field order can matter for downstream formatting or inspection, so keep appended context intentional.
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- build
- config
- public
---
## Async-logger-build-config-to-json
Convert `AsyncLoggerBuildConfig` into a `JsonValue`. This helper exports both the base synchronous logger config and the async runtime config as one structured payload.
### Interface
```moonbit
pub fn async_logger_build_config_to_json(
config : AsyncLoggerBuildConfig,
) -> @json_parser.JsonValue {}
```
#### input
- `config : AsyncLoggerBuildConfig` - Complete build config used by async logger builders.
#### output
- `JsonValue` - Structured JSON representation of the full async build config.
### Explanation
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(...)`.
- This helper is useful when generated setup should preserve both sink/logger behavior and async runtime behavior together.
### How to Use
Here are some specific examples provided.
#### When Need Structured Export Of Full Async Setup
When a tool or test needs one object describing the whole async logger build:
```moonbit
let payload = async_logger_build_config_to_json(
AsyncLoggerBuildConfig::new(
logger=@bitlogger.LoggerConfig::new(target="svc"),
async_config=AsyncLoggerConfig::new(max_pending=64),
),
)
```
In this example, both layers of configuration are exported together.
#### When Need Roundtrip-friendly Build Config Data
When generated build config should later be parsed again:
```moonbit
let value = async_logger_build_config_to_json(AsyncLoggerBuildConfig::new())
```
In this example, the resulting JSON matches the supported async build config shape.
### Error Case
e.g.:
- If callers only need the async runtime section, this API is broader than necessary and `async_logger_config_to_json(...)` should be used instead.
- If callers want direct text output, they should use `stringify_async_logger_build_config(...)` instead.
+78
View File
@@ -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.
+75
View File
@@ -0,0 +1,75 @@
---
name: async-logger-close
group: api
category: async
update-time: 20260512
description: Close the async logger queue and optionally clear pending records immediately.
key-word:
- async
- logger
- lifecycle
- public
---
## Async-logger-close
Close the async logger queue and stop treating the logger as an active enqueue target. This API is the low-level lifecycle close primitive behind shutdown flows.
### Interface
```moonbit
pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should be closed.
- `clear : Bool` - Whether pending records should be abandoned immediately instead of being left for drain behavior.
#### output
- `Unit` - No return value. The logger lifecycle state is updated in place.
### Explanation
Detailed rules explaining key parameters and behaviors
- `close(...)` marks the logger as closed immediately.
- `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.
### How to Use
Here are some specific examples provided.
#### When Need Immediate Queue Closure
When teardown should stop normal enqueue activity right away:
```moonbit
logger.close()
```
In this example, the logger enters closed state immediately.
#### When Need To Abandon Backlog Explicitly
When pending records should be discarded during fast shutdown:
```moonbit
logger.close(clear=true)
```
In this example, queued backlog is counted as dropped instead of waiting for further drain.
### Error Case
e.g.:
- If `clear=true`, pending records are intentionally discarded and contribute to `dropped_count()`.
- If callers need graceful waiting for drain completion, `shutdown()` is usually the better API.
### Notes
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.
+71
View File
@@ -0,0 +1,71 @@
---
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.
key-word:
- async
- config
- json
- public
---
## Async-logger-config-to-json
Convert a typed `AsyncLoggerConfig` into a `JsonValue`. This helper exports async queue capacity, overflow policy, batch sizing, linger timing, and flush behavior in a structured form.
### Interface
```moonbit
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> @json_parser.JsonValue {}
```
#### input
- `config : AsyncLoggerConfig` - Async logger runtime config to export.
#### output
- `JsonValue` - Structured JSON representation of the async config.
### Explanation
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.
- The JSON shape matches the `async_config` section used by async build config parsing.
### How to Use
Here are some specific examples provided.
#### When Need Structured Async Config Export
When async runtime policy should be embedded in a larger JSON payload:
```moonbit
let async_json = async_logger_config_to_json(
AsyncLoggerConfig::new(max_pending=128, max_batch=8),
)
```
In this example, callers receive a machine-readable config value.
#### When Need Roundtrip-friendly Async Settings
When code generates async policy and later persists it:
```moonbit
let value = async_logger_config_to_json(AsyncLoggerConfig::new(flush=AsyncFlushPolicy::Batch))
```
In this example, the exported JSON stays aligned with parser expectations.
### Error Case
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 want direct text output instead of a JSON value, they should use `stringify_async_logger_config(...)` instead.
+90
View File
@@ -0,0 +1,90 @@
---
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.
key-word:
- async
- config
- queue
- public
---
## Async-logger-config
Create an `AsyncLoggerConfig` value describing queue capacity, overflow behavior, batching size, linger timing, and flush policy for async logger construction.
### Interface
```moonbit
pub fn AsyncLoggerConfig::new(
max_pending~ : Int = 0,
overflow~ : AsyncOverflowPolicy = AsyncOverflowPolicy::Blocking,
max_batch~ : Int = 1,
linger_ms~ : Int = 0,
flush~ : AsyncFlushPolicy = AsyncFlushPolicy::Never,
) -> AsyncLoggerConfig {}
```
#### input
- `max_pending : Int` - Maximum queued records.
- `overflow : AsyncOverflowPolicy` - Queue overflow strategy.
- `max_batch : Int` - Maximum records drained per batch.
- `linger_ms : Int` - Optional wait window for batch accumulation.
- `flush : AsyncFlushPolicy` - Flush behavior for batch/shutdown phases.
#### output
- `AsyncLoggerConfig` - Async runtime config object used by `async_logger(...)` or async build helpers.
### Explanation
Detailed rules explaining key parameters and behaviors
- `max_batch <= 1` is normalized to `1`.
- `linger_ms < 0` is normalized to `0`.
- `overflow` and `flush` define the most important queue/runtime behavior tradeoffs.
- This type is used directly by `async_logger(...)` and embedded in `AsyncLoggerBuildConfig`.
### How to Use
Here are some specific examples provided.
#### When Tune Async Queue Backlog And Batch Size
When async behavior should be explicit in code:
```moonbit
let config = AsyncLoggerConfig::new(
max_pending=128,
overflow=AsyncOverflowPolicy::DropOldest,
max_batch=8,
linger_ms=10,
flush=AsyncFlushPolicy::Batch,
)
```
In this example, queue pressure, batch size, and flush timing are configured together.
#### When Export Async Config
When async policy should be serialized or logged:
```moonbit
println(stringify_async_logger_config(AsyncLoggerConfig::new(max_pending=8)))
```
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 `linger_ms` is negative, it is normalized 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.
+83
View File
@@ -0,0 +1,83 @@
---
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.
key-word:
- async
- logger
- debug
- public
---
## Async-logger-debug
Enqueue a debug-level record through the async logger. This is the convenience wrapper for `log(Level::Debug, ...)`.
### Interface
```moonbit
pub async fn[S] AsyncLogger::debug(
self : AsyncLogger[S],
message : String,
fields~ : Array[@bitlogger.Field] = [],
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the debug record.
- `message : String` - Debug message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
#### output
- `Unit` - No return value. The record is handled according to logger state and policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates to `log(Level::Debug, ...)`.
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
- Debug records are useful for development and targeted diagnostics.
- Use this helper when a named debug call is clearer than a raw `log(...)` call.
### How to Use
Here are some specific examples provided.
#### When Need Async Development Diagnostics
When intermediate async flow details should be visible during debugging:
```moonbit
await logger.debug("loaded worker config")
```
In this example, the call site communicates its intended diagnostic level directly.
#### When Attach Structured Debug Context
When a debug event should include extra fields:
```moonbit
await logger.debug(
"dispatch start",
fields=[@bitlogger.field("job_id", "42")],
)
```
In this example, the record carries structured context without needing the generic `log(...)` form.
### Error Case
e.g.:
- If the logger minimum level is above `Debug`, the record is skipped before enqueue.
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
### Notes
1. Prefer this helper when the event is semantically debug-level.
2. Use `log(...)` when the level must be chosen dynamically.
+77
View File
@@ -0,0 +1,77 @@
---
name: async-logger-dropped-count
group: api
category: async
update-time: 20260512
description: Read the number of records dropped by the async logger because of overflow or queue clearing.
key-word:
- async
- logger
- queue
- public
---
## Async-logger-dropped-count
Read the cumulative number of records dropped by an async logger. This metric is useful when overflow policy or forced shutdown behavior may discard queued data.
### Interface
```moonbit
pub fn[S] AsyncLogger::dropped_count(self : AsyncLogger[S]) -> Int {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose dropped-record counter should be inspected.
#### output
- `Int` - Number of records dropped so far.
### Explanation
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.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need To Detect Loss Under Pressure
When queue overflow may discard records:
```moonbit
if logger.dropped_count() > 0 {
println("async log loss detected")
}
```
In this example, the code checks whether the async logger has already discarded records.
#### When Validate Shutdown Behavior In Tests
When a test intentionally clears the queue:
```moonbit
logger.close(clear=true)
ignore(logger.dropped_count())
```
In this example, the drop counter helps confirm that abandoned backlog was tracked.
### Error Case
e.g.:
- If no records have been dropped, the method simply returns `0`.
- If callers need to know why records were lost, they must interpret this metric together with overflow policy and shutdown behavior.
### Notes
1. This helper reports record loss only; it does not explain the full logger state.
2. Pair it with `pending_count()` or `state()` when investigating backlog pressure.
+83
View File
@@ -0,0 +1,83 @@
---
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.
key-word:
- async
- logger
- error
- public
---
## Async-logger-error
Enqueue an error-level record through the async logger. This is the convenience wrapper for `log(Level::Error, ...)`.
### Interface
```moonbit
pub async fn[S] AsyncLogger::error(
self : AsyncLogger[S],
message : String,
fields~ : Array[@bitlogger.Field] = [],
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the error record.
- `message : String` - Error message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
#### output
- `Unit` - No return value. The record is handled according to logger state and policy.
### Explanation
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.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Async Failure Reporting
When an operation should emit a high-severity failure event:
```moonbit
await logger.error("worker execution failed")
```
In this example, failure intent is explicit at the call site.
#### When Attach Structured Error Context
When an error event should include diagnostic fields:
```moonbit
await logger.error(
"dispatch failed",
fields=[@bitlogger.field("job_id", "42")],
)
```
In this example, the error record carries structured context without falling back to the generic `log(...)` form.
### 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 callers need to inspect worker failure rather than emit an error record, `has_failed()` and `last_error()` are the relevant APIs.
### 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.
+74
View File
@@ -0,0 +1,74 @@
---
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.
key-word:
- async
- logger
- flush
- public
---
## Async-logger-flush-policy
Read the async logger flush policy. This helper exposes which flush mode currently controls batch and shutdown behavior.
### Interface
```moonbit
pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose flush policy should be inspected.
#### output
- `AsyncFlushPolicy` - Current flush policy used by the async worker logic.
### Explanation
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.
### How to Use
Here are some specific examples provided.
#### When Need To Inspect Runtime Flush Semantics
When diagnostics should show how the worker flushes:
```moonbit
ignore(logger.flush_policy())
```
In this example, the configured flush mode is exposed directly from the logger.
#### When Export Async Runtime Metadata
When code should include flush behavior in custom state reporting:
```moonbit
let flush = logger.flush_policy()
```
In this example, flush behavior can be surfaced without reading the full snapshot.
### Error Case
e.g.:
- This helper does not expose a normal runtime error path; it returns the configured policy.
- If callers need the policy together with backlog and failure state, `state()` is usually the better API.
### Notes
1. This helper exposes configuration-driven runtime behavior, not dynamic worker health.
2. Use `state()` when you want flush policy packaged with the rest of async logger diagnostics.
+76
View File
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- logger
- failure
- public
---
## Async-logger-has-failed
Read whether the async logger worker has encountered a failure. This helper is a compact health signal for async delivery problems.
### Interface
```moonbit
pub fn[S] AsyncLogger::has_failed(self : AsyncLogger[S]) -> Bool {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose failure flag should be inspected.
#### output
- `Bool` - Whether the worker has failed.
### Explanation
Detailed rules explaining key parameters and behaviors
- `run()` clears previous failure state at startup.
- If the worker loop raises an error, the logger records that failure and exposes it through this flag.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need A Fast Failure Signal
When runtime diagnostics should branch on worker health:
```moonbit
if logger.has_failed() {
println(logger.last_error())
}
```
In this example, the code checks failure state first, then reads the error detail.
#### When Inspect Async Runtime State In Tests
When a test needs to confirm that drain execution stayed healthy:
```moonbit
ignore(logger.has_failed())
```
In this example, the helper exposes a simple pass-fail runtime indicator.
### Error Case
e.g.:
- If `has_failed()` is `false`, queue pressure or dropped records may still exist for non-failure reasons.
- If `has_failed()` is `true`, callers should inspect `last_error()` or `state()` for more context.
### Notes
1. This helper reports worker failure, not general queue stress.
2. Pair it with `last_error()` when you need actionable detail.
+83
View File
@@ -0,0 +1,83 @@
---
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.
key-word:
- async
- logger
- info
- public
---
## Async-logger-info
Enqueue an info-level record through the async logger. This is the convenience wrapper for `log(Level::Info, ...)`.
### Interface
```moonbit
pub async fn[S] AsyncLogger::info(
self : AsyncLogger[S],
message : String,
fields~ : Array[@bitlogger.Field] = [],
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the info record.
- `message : String` - Info message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
#### output
- `Unit` - No return value. The record is handled according to logger state and policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates to `log(Level::Info, ...)`.
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Normal Operational Async Events
When async code should report routine progress or lifecycle events:
```moonbit
await logger.info("worker started")
```
In this example, the event is expressed at the most common operational logging level.
#### When Add Structured Operational Metadata
When an info event should include stable structured detail:
```moonbit
await logger.info(
"job queued",
fields=[@bitlogger.field("queue", "sync")],
)
```
In this example, the record remains concise while still carrying useful metadata.
### Error Case
e.g.:
- If the logger minimum level is above `Info`, the record is skipped before enqueue.
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
### Notes
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.
+77
View File
@@ -0,0 +1,77 @@
---
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.
key-word:
- async
- logger
- lifecycle
- public
---
## Async-logger-is-closed
Read whether the async logger has been closed. This helper is useful for lifecycle checks around shutdown and queue finalization.
### Interface
```moonbit
pub fn[S] AsyncLogger::is_closed(self : AsyncLogger[S]) -> Bool {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose closure state should be inspected.
#### output
- `Bool` - Whether the logger has already been closed.
### Explanation
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 reflects lifecycle state only and does not indicate whether the worker is still draining.
### How to Use
Here are some specific examples provided.
#### When Guard Late-stage Logging
When code should avoid treating a logger as fully active during teardown:
```moonbit
if logger.is_closed() {
println("logger already closed")
}
```
In this example, teardown logic can branch on the closure state.
#### When Verify Shutdown Progress
When tests or diagnostics want to inspect lifecycle state:
```moonbit
logger.close()
ignore(logger.is_closed())
```
In this example, the helper confirms that close state was entered.
### Error Case
e.g.:
- If `is_closed()` returns `true`, pending records may still exist until drain or clear behavior completes.
- If callers need to know whether the worker is still active, they should also inspect `is_running()`.
### Notes
1. Closed state and running state are related but not identical.
2. Use this helper when lifecycle control matters more than queue counters.
+76
View File
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- logger
- lifecycle
- public
---
## Async-logger-is-running
Read whether the async logger worker is currently active. This helper is useful for runtime diagnostics and shutdown coordination.
### Interface
```moonbit
pub fn[S] AsyncLogger::is_running(self : AsyncLogger[S]) -> Bool {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose worker state should be inspected.
#### output
- `Bool` - Whether the async worker loop is currently running.
### Explanation
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.
- This helper focuses on worker activity rather than queue size or failure details.
### How to Use
Here are some specific examples provided.
#### When Need Worker Activity Diagnostics
When runtime output should show whether the worker loop is active:
```moonbit
println(logger.is_running().to_string())
```
In this example, diagnostics can distinguish an idle configured logger from one with an active worker.
#### When Coordinate Shutdown Waiting
When code should poll worker completion explicitly:
```moonbit
while logger.is_running() {
@async.pause()
}
```
In this example, callers watch the worker lifecycle directly.
### Error Case
e.g.:
- If `is_running()` is `false`, pending records may still exist if the worker was never started or has already failed.
- If callers need a one-shot lifecycle flow, `shutdown()` is usually better than manual polling.
### 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.
+76
View File
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- logger
- failure
- public
---
## Async-logger-last-error
Read the last error string recorded by the async logger worker. This helper gives the textual detail behind `has_failed()`.
### Interface
```moonbit
pub fn[S] AsyncLogger::last_error(self : AsyncLogger[S]) -> String {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose last worker error should be inspected.
#### output
- `String` - Last recorded worker error message, or an empty string if no error was recorded.
### Explanation
Detailed rules explaining key parameters and behaviors
- `run()` resets the stored error string when the worker starts.
- If the worker loop fails, the error text is captured from the raised exception.
- An empty string normally means no failure has been recorded.
- This helper reports worker execution errors, not ordinary overflow or backpressure conditions.
### How to Use
Here are some specific examples provided.
#### When Need Failure Detail In Diagnostics
When a compact error message should be surfaced to operators:
```moonbit
if logger.has_failed() {
println(logger.last_error())
}
```
In this example, the error string is only read when failure is present.
#### When Export Full Async State
When custom diagnostics want to include the last error field directly:
```moonbit
let err = logger.last_error()
```
In this example, the helper provides the textual failure detail without building a full snapshot.
### Error Case
e.g.:
- If no runtime failure has occurred, the method returns an empty string.
- If callers need broader context than just the error text, they should use `state()`.
### 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.
+89
View File
@@ -0,0 +1,89 @@
---
name: async-logger-log
group: api
category: async
update-time: 20260512
description: Enqueue a record into the async logger with an explicit level, message, optional fields, and optional target override.
key-word:
- async
- logger
- log
- public
---
## 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.
### Interface
```moonbit
pub async fn[S] AsyncLogger::log(
self : AsyncLogger[S],
level : @bitlogger.Level,
message : String,
fields~ : Array[@bitlogger.Field] = [],
target? : String = "",
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the record.
- `level : Level` - Severity level for the record.
- `message : String` - Log message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
- `target : String` - Optional per-call target override.
#### output
- `Unit` - No return value. The record is either skipped, enqueued, or dropped according to logger state and policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- The logger checks `is_enabled(level)` before building a record.
- 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`.
### How to Use
Here are some specific examples provided.
#### When Need A Fully Explicit Async Log Call
When code should choose level, fields, and target per event:
```moonbit
await logger.log(
@bitlogger.Level::Info,
"worker started",
fields=[@bitlogger.field("job", "sync")],
target="service.worker",
)
```
In this example, all per-record inputs are supplied explicitly.
#### 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")
```
In this example, `log(...)` acts as the common primitive under custom wrappers or convenience methods.
### Error Case
e.g.:
- If the level is below the current minimum threshold, the record is skipped before queue insertion.
- If the logger is closed or overflow policy rejects the record, enqueue may not proceed as a normal accepted 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.
+76
View File
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- logger
- queue
- public
---
## Async-logger-pending-count
Read the current number of queued records that are still waiting to be processed. This API is the most direct backlog metric for an async logger instance.
### Interface
```moonbit
pub fn[S] AsyncLogger::pending_count(self : AsyncLogger[S]) -> Int {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose current backlog should be inspected.
#### output
- `Int` - Current number of pending records still in the async pipeline.
### Explanation
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.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need A Fast Backlog Check
When code should observe queue pressure directly:
```moonbit
let pending = logger.pending_count()
```
In this example, callers get the current queue backlog without building a full diagnostics snapshot.
#### When Wait For Near-idle Conditions
When operators or tests want to inspect drain progress:
```moonbit
if logger.pending_count() == 0 {
println("queue idle")
}
```
In this example, the queue backlog is checked directly.
### Error Case
e.g.:
- If the worker is not running, `pending_count()` may stay above `0` until records are drained or cleared.
- If the queue is empty, the method simply returns `0`.
### Notes
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.
+79
View File
@@ -0,0 +1,79 @@
---
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.
key-word:
- async
- logger
- worker
- public
---
## 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.
### Interface
```moonbit
pub async fn[S : @bitlogger.Sink] AsyncLogger::run(self : AsyncLogger[S]) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose queue should be drained by the worker loop.
#### output
- `Unit` - No return value. The method runs until the queue is closed or a worker failure occurs.
### Explanation
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.
### How to Use
Here are some specific examples provided.
#### When Need Background Queue Drain
When async logging should be processed by a worker task:
```moonbit
let logger = async_logger(console_sink())
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("started")
logger.shutdown()
})
```
In this example, `run()` is the worker loop that makes the async logger actually deliver queued records.
#### When Need Explicit Worker Lifetime Control
When worker execution should be started under application control:
```moonbit
group.spawn_bg(() => logger.run())
```
In this example, the application decides when the worker begins instead of hiding that lifecycle step.
### Error Case
e.g.:
- If the worker loop fails, `has_failed()` becomes `true` and `last_error()` stores the error text.
- If `run()` is never started, accepted records may remain queued and not reach the sink.
### 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.
+75
View File
@@ -0,0 +1,75 @@
---
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.
key-word:
- async
- logger
- lifecycle
- public
---
## Async-logger-shutdown
Gracefully stop an async logger. This is the main high-level shutdown API for async logging because it coordinates drain behavior, closure, and worker completion.
### Interface
```moonbit
pub async fn[S] AsyncLogger::shutdown(self : AsyncLogger[S], clear? : Bool = false) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should be shut down.
- `clear : Bool` - Whether pending records should be abandoned immediately instead of waiting for idle first.
#### output
- `Unit` - No return value. The method completes after shutdown coordination finishes.
### Explanation
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.
### How to Use
Here are some specific examples provided.
#### When Need Graceful Service Shutdown
When a service should stop logging only after queued records are drained:
```moonbit
await logger.shutdown()
```
In this example, the logger waits for normal drain behavior before final closure.
#### When Need Fast Shutdown Under Pressure
When teardown should prefer speed over preserving backlog:
```moonbit
await logger.shutdown(clear=true)
```
In this example, pending work is abandoned intentionally so shutdown can complete sooner.
### Error Case
e.g.:
- If `clear=true`, pending records are intentionally dropped rather than drained.
- 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.
+76
View File
@@ -0,0 +1,76 @@
---
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.
key-word:
- async
- state
- json
- public
---
## 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.
### Interface
```moonbit
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.JsonValue {}
```
#### input
- `state : AsyncLoggerState` - Snapshot produced by `AsyncLogger::state()`.
#### output
- `JsonValue` - Structured JSON representation of the async logger snapshot.
### Explanation
Detailed rules explaining key parameters and behaviors
- The JSON includes runtime mode, worker support, queue counters, lifecycle flags, last error, and flush policy.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Machine-readable Diagnostics
When the snapshot should be embedded into a JSON payload:
```moonbit
let state_json = async_logger_state_to_json(logger.state())
```
In this example, callers receive a structured value that can be composed into larger JSON objects.
#### When Need A Snapshot Before Custom Stringify
When another serializer or pipeline expects a JSON value:
```moonbit
let payload = async_logger_state_to_json(logger.state())
println(@json_parser.stringify(payload))
```
In this example, the helper stays useful even outside the built-in stringify wrapper.
### Error Case
e.g.:
- If the snapshot contains no error, `last_error` is serialized as an empty string.
- If the queue is empty, `pending_count` and `dropped_count` are still serialized normally as numeric values.
### Notes
1. Use this API when downstream code wants a JSON value rather than a ready-made string.
2. Pair it with `AsyncLogger::state()` to capture the snapshot first.
+80
View File
@@ -0,0 +1,80 @@
---
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.
key-word:
- async
- state
- diagnostics
- public
---
## Async-logger-state
Read a complete async logger runtime snapshot for diagnostics. This API is the preferred way to inspect queue backlog, dropped counts, lifecycle status, and runtime mode before exporting them through JSON helpers when needed.
### Interface
```moonbit
pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState {}
```
#### input
- `self : AsyncLogger[S]` - The async logger whose runtime snapshot should be read.
#### output
- `AsyncLoggerState` - A snapshot containing runtime mode, worker support, queue counts, lifecycle flags, last error, and flush policy.
### Explanation
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.
- `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.
### How to Use
Here are some specific examples provided.
#### When Need Startup Diagnostics
When you want to expose current async logger mode and queue state at startup:
```moonbit
let logger = build_async_logger(config)
println(stringify_async_logger_state(logger.state(), pretty=true))
```
In this example, the snapshot can be printed directly without extra manual formatting.
And downstream operators can see both runtime mode and queue-related status together.
#### When Need Failure Investigation Data
When diagnosing async delivery issues:
```moonbit
let state = logger.state()
if state.has_failed {
println(stringify_async_logger_state(state, pretty=true))
}
```
In this example, the same snapshot object works for conditional diagnostics and serialization.
### Error Case
e.g.:
- If no error has occurred, `last_error` is just an empty string.
- If the queue is empty, `pending_count` is `0`; this is normal and not a special error condition.
### 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.
+83
View File
@@ -0,0 +1,83 @@
---
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.
key-word:
- async
- logger
- trace
- public
---
## Async-logger-trace
Enqueue a trace-level record through the async logger. This is the convenience wrapper for `log(Level::Trace, ...)`.
### Interface
```moonbit
pub async fn[S] AsyncLogger::trace(
self : AsyncLogger[S],
message : String,
fields~ : Array[@bitlogger.Field] = [],
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the trace record.
- `message : String` - Trace message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
#### output
- `Unit` - No return value. The record is handled according to logger state and policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates to `log(Level::Trace, ...)`.
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Fine-grained Async Diagnostics
When low-level execution flow should be observable during debugging:
```moonbit
await logger.trace("entered reconciliation step")
```
In this example, the call site makes trace intent explicit.
#### When Attach Structured Trace Data
When a trace event should carry extra fields:
```moonbit
await logger.trace(
"cache probe",
fields=[@bitlogger.field("key", "user:42")],
)
```
In this example, the record stays lightweight while still carrying structured detail.
### Error Case
e.g.:
- If the logger minimum level is above `Trace`, the record is skipped before enqueue.
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
### Notes
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.
+75
View File
@@ -0,0 +1,75 @@
---
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.
key-word:
- async
- logger
- queue
- public
---
## Async-logger-wait-idle
Wait until the async logger backlog drains to zero. This helper is useful when callers want to observe idle state without fully shutting down the logger.
### Interface
```moonbit
pub async fn[S] AsyncLogger::wait_idle(self : AsyncLogger[S]) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger whose backlog should be waited on.
#### output
- `Unit` - No return value. The method completes when the logger becomes idle or failure prevents normal progress.
### Explanation
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.
- This API does not close the logger or stop the worker.
- It is narrower than `shutdown()` and is useful when the logger should continue to be used later.
### How to Use
Here are some specific examples provided.
#### When Need A Drain Barrier Without Shutdown
When code should wait for queued work to flush before continuing:
```moonbit
await logger.wait_idle()
```
In this example, the caller waits for backlog drain but leaves the logger usable afterward.
#### When Need Phase Boundaries In Tests
When a test wants to ensure earlier async logs were processed:
```moonbit
await logger.wait_idle()
println("phase complete")
```
In this example, the wait acts as a barrier between test phases.
### Error Case
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.
### 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.
+83
View File
@@ -0,0 +1,83 @@
---
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.
key-word:
- async
- logger
- warn
- public
---
## Async-logger-warn
Enqueue a warning-level record through the async logger. This is the convenience wrapper for `log(Level::Warn, ...)`.
### Interface
```moonbit
pub async fn[S] AsyncLogger::warn(
self : AsyncLogger[S],
message : String,
fields~ : Array[@bitlogger.Field] = [],
) -> Unit {}
```
#### input
- `self : AsyncLogger[S]` - Async logger that should receive the warning record.
- `message : String` - Warning message text.
- `fields : Array[Field]` - Optional structured fields added to the record.
#### output
- `Unit` - No return value. The record is handled according to logger state and policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates to `log(Level::Warn, ...)`.
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Async Degradation Signals
When the system should report a non-fatal problem:
```moonbit
await logger.warn("retry budget running low")
```
In this example, the event is surfaced at warning severity without using the generic `log(...)` form.
#### When Attach Structured Warning Detail
When a warning event should include context:
```moonbit
await logger.warn(
"queue near capacity",
fields=[@bitlogger.field("pending", logger.pending_count().to_string())],
)
```
In this example, the warning carries structured operational detail.
### Error Case
e.g.:
- If the logger minimum level is above `Warn`, the record is skipped before enqueue.
- If the logger is closed or overflow policy prevents acceptance, the write may not become a normal queued record.
### Notes
1. Use this helper for notable but non-fatal async runtime conditions.
2. Pair warnings with structured fields when operators need quick context.
@@ -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.
+82
View File
@@ -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.
+80
View File
@@ -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.
+82
View File
@@ -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.
+78
View File
@@ -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.
+77
View File
@@ -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.
+100
View File
@@ -0,0 +1,100 @@
---
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.
key-word:
- async
- logger
- queue
- public
---
## Async-logger
Create an `AsyncLogger[S]` on top of a sink and async queue configuration. This API is the main entry for queue-backed async logging, including overflow policy, batching, lifecycle control, and runtime observability.
### Interface
```moonbit
pub fn[S] async_logger(
sink : S,
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
target~ : String = "",
flush~ : (S) -> Int = fn(_) { 0 },
) -> AsyncLogger[S] {}
```
#### input
- `sink : S` - Underlying sink used after queue drain.
- `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.
#### output
- `AsyncLogger[S]` - A queue-backed async logger with lifecycle and state helpers.
### Explanation
Detailed rules explaining key parameters and behaviors
- `async_logger(...)` only builds the logger. Actual background draining is started by `run()`.
- In non-native targets, the implementation uses compatibility behavior while keeping the same public surface.
- `flush` is used only when batch or shutdown policy wants explicit flushing.
- Queue overflow behavior depends on `AsyncOverflowPolicy`.
### How to Use
Here are some specific examples provided.
#### When Need Background Queue Drain
When your sink should not be written directly on the caller path:
```moonbit
let logger = async_logger(callback_sink(fn(rec) { println(rec.message) }))
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("hello")
logger.shutdown()
})
```
In this example, the worker drains queued records in the background and `shutdown()` waits for completion.
And the logging call path stays queue-oriented rather than direct-sink oriented.
#### When Need Configurable Overflow And Flush Behavior
When queue semantics matter for service durability and load:
```moonbit
let logger = async_logger(
console_sink(),
config=AsyncLoggerConfig::new(
max_pending=128,
overflow=AsyncOverflowPolicy::DropOldest,
max_batch=8,
flush=AsyncFlushPolicy::Batch,
),
)
```
In this example, queue pressure and flush timing are both explicit.
### Error Case
e.g.:
- If the logger is closed, further enqueue attempts stop being normal active logging operations.
- If queue drain fails internally, runtime state can reflect that through `has_failed()` and `last_error()`.
### Notes
1. `async_logger(...)` is the async counterpart to `Logger::new(...)`.
2. Use `state()`, `pending_count()`, and `dropped_count()` for runtime diagnostics.
+69
View File
@@ -0,0 +1,69 @@
---
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.
key-word:
- async
- runtime
- label
- public
---
## Async-runtime-mode-label
Convert `AsyncRuntimeMode` into a stable string label. This helper is useful when runtime mode should be logged, serialized, or exposed through human-readable diagnostics.
### Interface
```moonbit
pub fn async_runtime_mode_label(mode : AsyncRuntimeMode) -> String {}
```
#### input
- `mode : AsyncRuntimeMode` - Runtime mode enum value.
#### output
- `String` - Stable mode label such as `native_worker` or `compatibility`.
### Explanation
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.
- Labels are more stable for telemetry and docs than ad hoc manual matching at call sites.
### How to Use
Here are some specific examples provided.
#### When Need A Stable Log Label
When the mode should be included in structured or plain logs:
```moonbit
println(async_runtime_mode_label(async_runtime_mode()))
```
In this example, the label is directly usable in diagnostics.
#### When Build Custom Serialization
When callers want to embed the mode into their own payloads:
```moonbit
let label = async_runtime_mode_label(async_runtime_mode())
```
In this example, code gets a stable string without duplicating enum matching logic.
### Error Case
e.g.:
- This API assumes a valid `AsyncRuntimeMode` input and does not expose a normal runtime error path.
- If callers need the whole runtime object rather than a string label, use `async_runtime_state()`.
+78
View File
@@ -0,0 +1,78 @@
---
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.
key-word:
- async
- runtime
- mode
- public
---
## Async-runtime-mode
Read the current backend-specific async runtime mode. This API is the narrowest capability probe when you only care whether the current build is running native worker semantics or compatibility behavior.
### Interface
```moonbit
pub fn async_runtime_mode() -> AsyncRuntimeMode {}
```
#### input
- `none` - No arguments are required.
#### output
- `AsyncRuntimeMode` - Either `NativeWorker` or `Compatibility`.
### Explanation
Detailed rules explaining key parameters and behaviors
- The return value is determined by the active backend implementation.
- `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.
- This API is intentionally small and useful for lightweight branching.
### How to Use
Here are some specific examples provided.
#### When Need A Small Capability Branch
When behavior should differ between native worker mode and compatibility mode:
```moonbit
match async_runtime_mode() {
AsyncRuntimeMode::NativeWorker => println("native worker")
AsyncRuntimeMode::Compatibility => println("compat")
}
```
In this example, the branch is explicit and readable.
#### When Need Stable String Output
When the mode should be included in logs or telemetry labels:
```moonbit
println(async_runtime_mode_label(async_runtime_mode()))
```
In this example, the output becomes a stable string instead of an enum pattern-match requirement.
### Error Case
e.g.:
- This API does not have a normal runtime failure mode; it reflects the compiled backend behavior.
- If you need worker support as a direct boolean, use `async_runtime_supports_background_worker()` instead.
### Notes
1. Use this API for minimal mode branching.
2. Use `async_runtime_state()` when you also want worker support packaged into one object.
+69
View File
@@ -0,0 +1,69 @@
---
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.
key-word:
- async
- state
- json
- public
---
## Async-runtime-state-to-json
Convert `AsyncRuntimeState` into a `JsonValue`. This helper exports the async runtime mode and background worker capability in a structured form.
### Interface
```moonbit
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> @json_parser.JsonValue {}
```
#### input
- `state : AsyncRuntimeState` - Runtime capability snapshot, usually produced by `async_runtime_state()`.
#### output
- `JsonValue` - Structured JSON representation of the runtime state.
### Explanation
Detailed rules explaining key parameters and behaviors
- The output includes `mode` and `background_worker`.
- `mode` is serialized through `async_runtime_mode_label(...)`.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need Structured Runtime Capability Checks
When startup diagnostics should include async runtime capability data:
```moonbit
let runtime_json = async_runtime_state_to_json(async_runtime_state())
```
In this example, the runtime snapshot becomes a reusable JSON value.
#### When Need To Embed Runtime State In A Larger Payload
When async support should be one field in a bigger diagnostics object:
```moonbit
let payload = async_runtime_state_to_json(state)
```
In this example, callers can reuse the exported object directly.
### Error Case
e.g.:
- If callers expect logger queue counters or failure status, this API is too narrow and `async_logger_state_to_json(...)` should be used instead.
- If the runtime is in compatibility mode, the helper still serializes normally using the matching mode label.
+77
View File
@@ -0,0 +1,77 @@
---
name: async-runtime-state
group: api
category: async
update-time: 20260512
description: Read the current backend-specific async runtime mode and worker capability.
key-word:
- async
- runtime
- diagnostics
- public
---
## Async-runtime-state
Read the current backend-specific async runtime state for diagnostics. This API focuses on the environment-level async mode rather than any one logger instance.
### Interface
```moonbit
pub fn async_runtime_state() -> AsyncRuntimeState {}
```
#### input
- `none` - This API reads the current backend mode and does not require logger input.
#### output
- `AsyncRuntimeState` - Runtime snapshot containing `mode` and `background_worker` capability.
### Explanation
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.
- `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.
### How to Use
Here are some specific examples provided.
#### When Need Startup Diagnostics
When startup logs should reveal async backend behavior:
```moonbit
println(stringify_async_runtime_state(async_runtime_state(), pretty=true))
```
In this example, backend mode is exposed before any logger is started.
#### When Branch Behavior By Runtime Capability
When code should react differently depending on worker support:
```moonbit
let runtime = async_runtime_state()
if runtime.background_worker {
println("native worker path")
}
```
In this example, branch decisions are based on actual runtime capability instead of assumptions.
### Error Case
e.g.:
- This API does not normally expose a dynamic error path; it reports the currently compiled backend behavior.
- If callers need richer runtime state, they should use `AsyncLogger::state()` on a logger instance instead.
### Notes
1. Use this API for environment-level diagnostics.
2. Use `AsyncLogger::state()` for logger-instance diagnostics.
@@ -0,0 +1,77 @@
---
name: async-runtime-supports-background-worker
group: api
category: async
update-time: 20260512
description: Return whether the current backend provides native async background worker support.
key-word:
- async
- runtime
- worker
- public
---
## Async-runtime-supports-background-worker
Return a boolean telling callers whether the current backend provides native background worker semantics for `bitlogger_async`. This is the narrowest capability probe when only worker support matters.
### Interface
```moonbit
pub fn async_runtime_supports_background_worker() -> Bool {}
```
#### input
- `none` - No explicit arguments are required.
#### output
- `Bool` - `true` when native background worker semantics are available, otherwise `false`.
### Explanation
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.
- Use it when an enum branch is unnecessary and a boolean capability check is enough.
### How to Use
Here are some specific examples provided.
#### When Need A Simple Capability Branch
When logic only depends on worker availability:
```moonbit
if async_runtime_supports_background_worker() {
println("native worker available")
}
```
In this example, callers avoid a more verbose mode match.
#### When Add Diagnostics Labels
When capability should be surfaced in diagnostics:
```moonbit
println(if async_runtime_supports_background_worker() { "worker" } else { "compat" })
```
In this example, a simple boolean can drive compact status output.
### Error Case
e.g.:
- This API does not normally fail at runtime; it reflects compiled backend behavior.
- If you need the exact mode name rather than a boolean, use `async_runtime_mode()` or `async_runtime_state()`.
### Notes
1. Use this helper for minimal capability checks.
2. Prefer `async_runtime_state()` when you want the same information in a richer object.
+66
View File
@@ -0,0 +1,66 @@
---
name: buffered-sink
group: api
category: sink
update-time: 20260520
description: Create a sink that buffers records and flushes them manually or at a threshold.
key-word:
- sink
- buffer
- flush
- public
---
## Buffered-sink
Create a sink that buffers records before forwarding them to another sink. This helper is useful when callers want explicit or threshold-based sync batching without using the queue wrapper API.
### Interface
```moonbit
pub fn[S] buffered_sink(sink : S, flush_limit~ : Int = 1) -> BufferedSink[S] {
```
#### input
- `sink : S` - Wrapped sink that receives flushed records.
- `flush_limit : Int` - Buffer length threshold that triggers automatic flush.
#### output
- `BufferedSink[S]` - Buffering sink with `pending_count()` and `flush()` helpers.
### Explanation
Detailed rules explaining key parameters and behaviors
- Records are stored in an in-memory buffer until flushed.
- `flush_limit <= 0` is normalized to `1`.
- Flushing forwards the buffered records in order to the wrapped sink.
### How to Use
Here are some specific examples provided.
#### When Need Manual Or Threshold-based Batch Delivery
When writes should accumulate before they reach the destination:
```moonbit
let sink = buffered_sink(console_sink(), flush_limit=2)
let logger = Logger::new(sink, target="buffered")
```
In this example, records stay buffered until the threshold is reached or `flush()` is called.
### Error Case
e.g.:
- If callers never flush a buffer whose threshold is not reached, records remain pending.
- If bounded dropping behavior is required instead of simple buffering, use `queued_sink(...)` or `Logger::with_queue(...)`.
### Notes
1. This helper is simpler than explicit queue overflow management.
2. It is useful for synchronous batching scenarios and tests.
@@ -0,0 +1,71 @@
---
name: build-application-async-logger
group: api
category: facade
update-time: 20260520
description: Build the application-facing async logger facade from an AsyncLoggerBuildConfig.
key-word:
- application
- async
- facade
- public
---
## Build-application-async-logger
Build an `ApplicationAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-facing async facade over `build_async_logger(...)`.
### Interface
```moonbit
pub fn build_application_async_logger(
config : AsyncLoggerBuildConfig,
) -> ApplicationAsyncLogger {
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined sync logger config and async queue/runtime config.
#### output
- `ApplicationAsyncLogger` - Application-facing async runtime logger.
### Explanation
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.
### How to Use
Here are some specific examples provided.
#### When Need Config-driven App Async Boot
When both sync sink shape and async queue policy are assembled as typed config:
```moonbit
let logger = build_application_async_logger(
AsyncLoggerBuildConfig::new(
logger=LoggerConfig::new(target="app.async"),
async_config=AsyncLoggerConfig::new(max_pending=8),
),
)
```
In this example, the app-facing async facade is built directly from typed config.
### Error Case
e.g.:
- If file output is selected on a backend without native file support, backend behavior still applies when the worker drains records.
- If the logger is never `run()`, enqueue behavior and lifecycle state still follow the normal async logger rules.
### 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.
+66
View File
@@ -0,0 +1,66 @@
---
name: build-application-logger
group: api
category: facade
update-time: 20260520
description: Build the application-facing configured logger facade from a LoggerConfig.
key-word:
- application
- facade
- logger
- public
---
## Build-application-logger
Build an `ApplicationLogger` from `LoggerConfig`. This facade is the application-oriented sync entry point and currently aliases the configured runtime logger shape returned by `build_logger(...)`.
### Interface
```moonbit
pub fn build_application_logger(config : LoggerConfig) -> ApplicationLogger {
```
#### input
- `config : LoggerConfig` - Fully assembled sync logger config.
#### output
- `ApplicationLogger` - Application-facing configured runtime logger.
### Explanation
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.
### How to Use
Here are some specific examples provided.
#### When Need An App-level Sync Builder Entry
When boot code assembles config values before runtime construction:
```moonbit
let logger = build_application_logger(
LoggerConfig::new(target="app", sink=SinkConfig::new(kind=SinkKind::Console)),
)
```
In this example, the application facade builds the same configured runtime logger shape as `build_logger(...)`.
### Error Case
e.g.:
- If the config uses file output on a backend without native file support, backend runtime limitations still apply after construction.
- If queueing is not configured, queue helper values simply reflect the non-queued runtime shape.
### Notes
1. This is a facade API, not a separate runtime implementation.
2. Use `parse_and_build_application_logger(...)` when starting from JSON text.
@@ -0,0 +1,71 @@
---
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.
key-word:
- application
- async
- text
- public
---
## 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(...)`.
### Interface
```moonbit
pub fn build_application_text_async_logger(
config : AsyncLoggerBuildConfig,
) -> ApplicationTextAsyncLogger {
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined sync logger config and async queue/runtime config.
#### output
- `ApplicationTextAsyncLogger` - Application-facing async logger backed by `FormattedConsoleSink`.
### Explanation
Detailed rules explaining key parameters and behaviors
- This API delegates to `build_async_text_logger(...)`.
- 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.
### How to Use
Here are some specific examples provided.
#### When Need An App Async Builder With Text Sink Shape
When async output should stay on text console formatting:
```moonbit
let logger = build_application_text_async_logger(
AsyncLoggerBuildConfig::new(
logger=text_console(target="app.text.async"),
async_config=AsyncLoggerConfig::new(max_pending=4),
),
)
```
In this example, the async logger is built for text-console output specifically.
### 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 runtime draining is never started, records still follow the normal async queue lifecycle rules.
### 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.
+81
View File
@@ -0,0 +1,81 @@
---
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.
key-word:
- async
- config
- builder
- public
---
## Build-async-logger
Build an async logger directly from `AsyncLoggerBuildConfig`. This is the config-driven async entry point that bridges synchronous logger config, sink creation, and async queue setup in one call.
### Interface
```moonbit
pub fn build_async_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@bitlogger.RuntimeSink] {}
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined synchronous logger config plus async queue/flush config.
#### output
- `AsyncLogger[RuntimeSink]` - A config-built async logger with runtime sink control preserved.
### Explanation
Detailed rules explaining key parameters and behaviors
- The `logger` section is built through the same config machinery used by synchronous configured loggers.
- 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.
- The returned sink type is `RuntimeSink`, which keeps configured control helpers available where relevant.
### How to Use
Here are some specific examples provided.
#### When Need Fully Config-driven Async Bootstrapping
When your application should build async logging entirely from configuration:
```moonbit
let config = parse_async_logger_build_config_text(raw) catch {
err => return
}
let logger = build_async_logger(config)
```
In this example, parsing and async runtime wiring are separated cleanly.
And the returned logger can immediately be started with `run()`.
#### When Need Runtime Sink Features After Async Build
When the sink shape is configured but runtime features still matter:
```moonbit
let logger = build_async_logger(config)
println(stringify_async_logger_state(logger.state(), pretty=true))
```
In this example, the built async logger remains introspectable even though construction was config-driven.
### Error Case
e.g.:
- If the config text was invalid, error handling should happen earlier in `parse_async_logger_build_config_text(...)`.
- 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.
### Notes
1. Prefer this API when applications externalize both sync sink choice and async queue behavior.
2. Use `async_logger(...)` directly when you want explicit code-defined sink wiring.
+69
View File
@@ -0,0 +1,69 @@
---
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.
key-word:
- async
- text
- builder
- public
---
## Build-async-text-logger
Build an async logger directly from `AsyncLoggerBuildConfig`, but keep the concrete sink type as `FormattedConsoleSink` instead of the broader runtime sink wrapper. This helper is the text-console specific counterpart to `build_async_logger(...)`.
### Interface
```moonbit
pub fn build_async_text_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@bitlogger.FormattedConsoleSink] {
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined sync logger config plus async queue and flush config.
#### output
- `AsyncLogger[FormattedConsoleSink]` - Config-built async logger backed by a concrete text console sink.
### Explanation
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(...)`.
- The returned logger inherits `min_level`, `target`, and timestamp behavior from `config.logger`.
- This helper is best suited to text-console output paths where callers want the concrete formatted sink type instead of `RuntimeSink`.
### How to Use
Here are some specific examples provided.
#### When Need Config-built Async Text Console Output
When async queue behavior is config-driven and output should stay on text console formatting:
```moonbit
let logger = build_async_text_logger(
AsyncLoggerBuildConfig::new(
logger=text_console(target="async.text"),
async_config=AsyncLoggerConfig::new(max_pending=4),
),
)
```
In this example, the async logger is built around a text console sink rather than the generic runtime sink enum.
### 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 the logger is never `run()`, pending records still follow the normal async queue lifecycle rules.
### Notes
1. This API is narrower than `build_async_logger(...)` because it preserves a concrete text sink type.
2. It is the base builder used by the application and library async text facades.
+71
View File
@@ -0,0 +1,71 @@
---
name: build-library-async-logger
group: api
category: facade
update-time: 20260520
description: Build the library-facing async logger facade from an AsyncLoggerBuildConfig.
key-word:
- library
- async
- facade
- public
---
## Build-library-async-logger
Build a `LibraryAsyncLogger[RuntimeSink]` from `AsyncLoggerBuildConfig`. This is the library-facing async facade over the general config-driven async builder.
### Interface
```moonbit
pub fn build_library_async_logger(
config : AsyncLoggerBuildConfig,
) -> LibraryAsyncLogger[RuntimeSink] {
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined sync logger config and async queue/runtime config.
#### output
- `LibraryAsyncLogger[RuntimeSink]` - Library-facing async logger wrapper.
### Explanation
Detailed rules explaining key parameters and behaviors
- This API builds the general async runtime logger and then wraps it in the narrower `LibraryAsyncLogger` facade.
- The result keeps async lifecycle operations such as `run()` and `shutdown()` while narrowing the public shape.
- `to_async_logger()` can be used to recover the underlying full async logger.
### How to Use
Here are some specific examples provided.
#### When Need A Narrower Async Type For Libraries
When a reusable package should expose async logging but not the full runtime type directly:
```moonbit
let logger = build_library_async_logger(
AsyncLoggerBuildConfig::new(
logger=LoggerConfig::new(target="lib.async"),
async_config=AsyncLoggerConfig::new(max_pending=4),
),
)
```
In this example, async runtime construction is hidden behind the library facade.
### 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()`.
### Notes
1. Prefer this API when library boundaries should stay narrow.
2. Use `parse_and_build_library_async_logger(...)` when starting from JSON text.
@@ -0,0 +1,71 @@
---
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.
key-word:
- library
- async
- text
- public
---
## Build-library-async-text-logger
Build a `LibraryAsyncLogger[FormattedConsoleSink]` from `AsyncLoggerBuildConfig`. This facade is the library-oriented async builder for text-console runtime output.
### Interface
```moonbit
pub fn build_library_async_text_logger(
config : AsyncLoggerBuildConfig,
) -> LibraryAsyncLogger[FormattedConsoleSink] {
```
#### input
- `config : AsyncLoggerBuildConfig` - Combined sync logger config and async queue/runtime config.
#### output
- `LibraryAsyncLogger[FormattedConsoleSink]` - Library-facing async logger backed by formatted console output.
### Explanation
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.
- `to_async_logger()` can recover the underlying full async logger if needed.
### How to Use
Here are some specific examples provided.
#### When Need A Narrow Async Text Logger For Libraries
When a library wants text-console async output and a narrower public type:
```moonbit
let logger = build_library_async_text_logger(
AsyncLoggerBuildConfig::new(
logger=text_console(target="lib.text.async"),
async_config=AsyncLoggerConfig::new(max_pending=4),
),
)
```
In this example, the async text sink shape is preserved under the library facade.
### Error Case
e.g.:
- If the embedded logger config does not describe text-console output, the caller should use the broader async facade instead.
- Normal async lifecycle expectations still apply if the logger is never run.
### Notes
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.
+66
View File
@@ -0,0 +1,66 @@
---
name: build-library-logger
group: api
category: facade
update-time: 20260520
description: Build the library-facing sync logger facade from a LoggerConfig.
key-word:
- library
- facade
- logger
- public
---
## Build-library-logger
Build a `LibraryLogger[RuntimeSink]` from `LoggerConfig`. This facade keeps a smaller library-oriented sync surface while still using config-driven runtime assembly underneath.
### Interface
```moonbit
pub fn build_library_logger(config : LoggerConfig) -> LibraryLogger[RuntimeSink] {
```
#### input
- `config : LoggerConfig` - Fully assembled sync logger config.
#### output
- `LibraryLogger[RuntimeSink]` - Library-facing logger wrapper over the configured runtime sink.
### Explanation
Detailed rules explaining key parameters and behaviors
- This API builds a configured runtime logger first and then wraps it as `LibraryLogger`.
- The facade intentionally exposes a smaller logging surface than the full configured runtime logger.
- Call `to_logger()` if a caller must recover the underlying full logger object.
### How to Use
Here are some specific examples provided.
#### When Need A Smaller Library-facing Logging Type
When package code should accept or produce a narrower logger facade:
```moonbit
let logger = build_library_logger(
LoggerConfig::new(target="lib", sink=SinkConfig::new(kind=SinkKind::Console)),
)
```
In this example, the logger is built from config and then narrowed to the library facade.
### 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()`.
### 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.
+85
View File
@@ -0,0 +1,85 @@
---
name: build-logger
group: api
category: config
update-time: 20260512
description: Build a configured runtime logger from a LoggerConfig while preserving queue and file control helpers.
key-word:
- logger
- config
- runtime
- public
---
## Build-logger
Build a `ConfiguredLogger` from `LoggerConfig`. This is the main config-to-runtime bridge for synchronous logging and is the builder used before async wrapping in config-driven async flows.
### Interface
```moonbit
pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {}
```
#### input
- `config : LoggerConfig` - Fully assembled logger config including level, target, timestamp, sink, and optional queue wrapper.
#### output
- `ConfiguredLogger` - A runtime logger backed by `RuntimeSink`, with queue and file control helpers preserved.
### Explanation
Detailed rules explaining key parameters and behaviors
- `build_logger(...)` constructs the runtime sink shape based on `SinkConfig` and optional queue wrapper.
- The returned logger still supports normal logging methods because `ConfiguredLogger` is `Logger[RuntimeSink]`.
- Queue metrics and file controls remain available through forwarding helpers on the configured logger.
- This API is deterministic and data-driven, making it suitable for bootstrapping from parsed config.
### How to Use
Here are some specific examples provided.
#### When Need Structured Config-first Bootstrapping
When config is already assembled as typed values:
```moonbit
let logger = build_logger(
LoggerConfig::new(
min_level=Level::Info,
target="svc",
sink=SinkConfig::new(kind=SinkKind::TextConsole),
),
)
```
In this example, no JSON parsing is required because config objects were built directly.
And the runtime logger is ready immediately.
#### When Need Config-built Queue Or File Runtime Helpers
When the sink shape comes from config but runtime controls still matter:
```moonbit
let logger = build_logger(config)
ignore(logger.pending_count())
ignore(logger.file_runtime_state())
```
In this example, config-driven construction does not remove observability or control helpers.
### Error Case
e.g.:
- If config contains a file sink on a non-native backend, callers must still respect backend capability behavior.
- If queue is not configured, queue-related counters simply reflect the non-queued runtime shape.
### Notes
1. Use this API when config is already typed as `LoggerConfig`.
2. Use `parse_and_build_logger(...)` when the starting point is raw JSON text.
+69
View File
@@ -0,0 +1,69 @@
---
name: callback-sink
group: api
category: sink
update-time: 20260520
description: Create a sink that forwards records to a user callback.
key-word:
- sink
- callback
- record
- public
---
## Callback-sink
Create a sink that forwards each `Record` to a callback. This is the most direct built-in integration hook for tests, adapters, and custom side effects.
### Interface
```moonbit
pub fn callback_sink(callback : (Record) -> Unit) -> CallbackSink {
```
#### input
- `callback : (Record) -> Unit` - Function called for each emitted record.
#### output
- `CallbackSink` - Sink that forwards records to the callback.
### Explanation
Detailed rules explaining key parameters and behaviors
- The callback receives the full structured record.
- This sink is useful for tests, custom bridges, or integration code that wants raw record access.
- Formatting is not applied automatically because the callback works on `Record` values directly.
### How to Use
Here are some specific examples provided.
#### When Need To Capture Structured Records
When tests or adapters want direct access to target, message, and fields:
```moonbit
let logger = Logger::new(
callback_sink(fn(rec) {
println(rec.target)
}),
target="hook",
)
```
In this example, the callback sees the structured record rather than pre-rendered text.
### Error Case
e.g.:
- If text output is needed instead of raw records, use `text_callback_sink(...)`.
- Callback behavior is fully user-defined, so failures inside the callback are outside the sink's own API contract.
### Notes
1. This sink is commonly useful in tests and adapters.
2. It composes naturally with filter, patch, fanout, and queue wrappers.
+64
View File
@@ -0,0 +1,64 @@
---
name: color-mode-label
group: api
category: formatter
update-time: 20260520
description: Convert a ColorMode value into its stable string label.
key-word:
- color
- formatter
- label
- public
---
## Color-mode-label
Convert `ColorMode` into its stable string label. This helper is useful for diagnostics, tests, and config inspection output that should mirror the built-in color mode names.
### Interface
```moonbit
pub fn color_mode_label(mode : ColorMode) -> String {
```
#### input
- `mode : ColorMode` - Color mode enum value to label.
#### output
- `String` - Stable label such as `never`, `auto`, or `always`.
### Explanation
Detailed rules explaining key parameters and behaviors
- The returned strings match the built-in color mode vocabulary.
- This helper is presentation-oriented and does not by itself enable or disable color rendering.
- It is useful when tests or diagnostics should expose the configured color policy clearly.
### How to Use
Here are some specific examples provided.
#### When Need A Readable Color Mode Name
When config or test output should include the current color policy:
```moonbit
let label = color_mode_label(ColorMode::Always)
```
In this example, `label` becomes `"always"`.
### Error Case
e.g.:
- There is no failure path for valid `ColorMode` values.
- If code needs rendering behavior rather than display text, the enum value itself is usually more useful than the label string.
### Notes
1. This helper is mostly useful for readable inspection and assertions.
2. It is a natural companion to `color_support_label(...)`.
+64
View File
@@ -0,0 +1,64 @@
---
name: color-support-label
group: api
category: formatter
update-time: 20260520
description: Convert a ColorSupport value into its stable string label.
key-word:
- color
- formatter
- label
- public
---
## Color-support-label
Convert `ColorSupport` into its stable string label. This helper is useful for diagnostics, tests, and config-oriented output that should mirror the built-in color support names.
### Interface
```moonbit
pub fn color_support_label(support : ColorSupport) -> String {
```
#### input
- `support : ColorSupport` - Color support enum value to label.
#### output
- `String` - Stable label such as `basic` or `truecolor`.
### Explanation
Detailed rules explaining key parameters and behaviors
- The returned strings are stable enum labels used by config and tests.
- This helper is presentation-oriented and does not change formatter behavior by itself.
- It is useful when code should display or assert a readable color support mode.
### How to Use
Here are some specific examples provided.
#### When Need A Readable Color Support Name
When diagnostics or tests should show the selected color capability:
```moonbit
let label = color_support_label(ColorSupport::Basic)
```
In this example, `label` becomes `"basic"`.
### Error Case
e.g.:
- There is no failure path for valid `ColorSupport` values.
- If code needs to choose rendering behavior, the enum value itself is usually more useful than its label string.
### Notes
1. This helper is mostly useful for readable output and assertions.
2. It pairs naturally with config parsing and formatter inspection tests.
+82
View File
@@ -0,0 +1,82 @@
---
name: compose-patches
group: api
category: patching
update-time: 20260512
description: Create a reusable record patch pipeline from several ordered patches.
key-word:
- patch
- compose
- pipeline
- public
---
## Compose-patches
Create a `RecordPatch` that applies several patches in order. This helper is the standard way to build deterministic record transformation pipelines.
### Interface
```moonbit
pub fn compose_patches(patches : Array[RecordPatch]) -> RecordPatch {}
```
#### input
- `patches : Array[RecordPatch]` - Patches applied sequentially from first to last.
#### output
- `RecordPatch` - Patch that returns the final record after every nested patch has run.
### Explanation
Detailed rules explaining key parameters and behaviors
- Composition is ordered: each patch receives the result of the previous patch.
- Later patches can overwrite changes made by earlier patches.
- If the patch array is empty, the returned patch behaves like `identity_patch()`.
- This helper is useful for combining normalization, enrichment, and redaction into one reusable unit.
### How to Use
Here are some specific examples provided.
#### When Build A Stable Rewrite Pipeline
When a logger needs several transformations in one place:
```moonbit
let patch = compose_patches([
set_target("api.gateway"),
prefix_message("[safe] "),
redact_fields(["token", "password"]),
])
```
In this example, target rewrite, message prefixing, and field redaction happen in fixed order.
#### When Reuse The Same Policy Across Loggers
When several sinks should share one transformation bundle:
```moonbit
let shared_patch = compose_patches([
append_fields([field("service", "billing")]),
redact_field("secret"),
])
```
In this example, the composed patch can be attached to multiple loggers without repeating inline closures.
### Error Case
e.g.:
- If `patches` is empty, the returned patch leaves records unchanged.
- If several patches rewrite the same property, the last patch in the array determines the final value.
### Notes
1. Keep patch order explicit because composition semantics are intentionally sequential.
2. Prefer one composed patch over many ad hoc inline wrappers when the transformation policy is shared.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-close
group: api
category: runtime
update-time: 20260512
description: Close the configured runtime logger sink and return whether the underlying sink reported a close action.
key-word:
- logger
- runtime
- lifecycle
- public
---
## Configured-logger-close
Close the sink behind a `ConfiguredLogger`. This helper is the config-driven runtime close surface for queue-backed or file-backed sinks.
### Interface
```moonbit
pub fn ConfiguredLogger::close(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose sink should be closed.
#### output
- `Bool` - Whether the underlying runtime sink reported a successful close action.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-backed file sinks close the wrapped file sink.
- Plain file sinks forward directly to file close behavior.
- Console-style sinks usually report `true` because they do not have a meaningful close step.
- This helper delegates to `RuntimeSink::close(...)` through the configured logger wrapper.
### How to Use
Here are some specific examples provided.
#### When Need Config-driven Runtime Teardown
When a config-built logger should release its sink resources:
```moonbit
ignore(logger.close())
```
In this example, sink teardown happens through the configured logger facade.
#### When Need To Observe Close Outcome
When application code wants a success flag from runtime close behavior:
```moonbit
let closed = logger.close()
```
In this example, callers can branch on the reported close result.
### Error Case
e.g.:
- If the runtime sink has no real close action, the helper may still return `true` as a no-op success.
- 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 sink shape is known to be file-backed.
+75
View File
@@ -0,0 +1,75 @@
---
name: configured-logger-drain
group: api
category: runtime
update-time: 20260512
description: Drain queued work from a configured runtime logger with optional item limits.
key-word:
- logger
- runtime
- queue
- public
---
## Configured-logger-drain
Drain queued work from a `ConfiguredLogger`. This helper is useful when config-driven queue wrapping should be advanced in a controlled, bounded way.
### Interface
```moonbit
pub fn ConfiguredLogger::drain(self : ConfiguredLogger, max_items~ : Int = -1) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose queued work should be drained.
- `max_items : Int` - Optional upper bound on how many queued items to drain. Negative values mean no explicit bound.
#### output
- `Int` - Number of drained items.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-wrapped sinks may drain up to `max_items` records.
- For plain file sinks, the runtime falls back to file flush behavior instead of queue draining.
- For sinks without queue semantics, the result is typically `0`.
- This helper delegates to `RuntimeSink::drain(...)` through the configured logger wrapper.
### How to Use
Here are some specific examples provided.
#### When Need Bounded Queue Progress
When queued output should be advanced in chunks:
```moonbit
let drained = logger.drain(max_items=16)
```
In this example, callers limit how much queued work is processed in one step.
#### When Need Full Manual Drain
When the configured queue should be emptied explicitly:
```moonbit
ignore(logger.drain())
```
In this example, the configured runtime logger drains without imposing an item cap.
### Error Case
e.g.:
- If the runtime sink is not queue-backed, draining may return `0` or follow fallback flush behavior.
- If callers only need generic flush semantics, `flush()` may be the simpler API.
### Notes
1. Prefer this helper when queue progress should be bounded or observable.
2. Use `pending_count()` to inspect remaining backlog after the drain call.
@@ -0,0 +1,76 @@
---
name: configured-logger-dropped-count
group: api
category: runtime
update-time: 20260512
description: Read the cumulative dropped-record count from a configured runtime logger.
key-word:
- logger
- runtime
- queue
- public
---
## Configured-logger-dropped-count
Read the cumulative dropped-record count from a `ConfiguredLogger`. This helper is useful when config-driven queue wrapping may discard records under pressure.
### Interface
```moonbit
pub fn ConfiguredLogger::dropped_count(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose dropped-record metric should be inspected.
#### output
- `Int` - Number of dropped records reported by the runtime sink.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-backed sinks return their live dropped-count metric.
- Non-queued sinks report `0`.
- The counter is cumulative for the runtime sink lifetime.
- This helper delegates to `RuntimeSink::dropped_count(...)` through the configured logger wrapper.
### How to Use
Here are some specific examples provided.
#### When Need Loss Visibility On Config-built Queues
When a config-driven queue may discard records:
```moonbit
if logger.dropped_count() > 0 {
println("configured logger dropped records")
}
```
In this example, the runtime logger exposes queue loss without manual sink inspection.
#### When Compare Queue Tuning Changes
When queue overflow policy should be validated operationally:
```moonbit
ignore(logger.dropped_count())
```
In this example, the helper exposes the metric needed to compare runtime queue tuning.
### Error Case
e.g.:
- If the logger is not queue-backed, the method simply returns `0`.
- If callers need queue shape and file status together, `file_runtime_state()` may carry more useful context for file sinks.
### Notes
1. This helper reports cumulative loss, not the reason for that loss.
2. Pair it with `pending_count()` and queue configuration when investigating pressure.
@@ -0,0 +1,75 @@
---
name: configured-logger-file-append-mode
group: api
category: runtime
update-time: 20260512
description: Read the current append-mode policy used by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-append-mode
Read the current append-mode policy used by a `ConfiguredLogger` file sink. This helper exposes whether future reopen behavior is currently append-oriented.
### Interface
```moonbit
pub fn ConfiguredLogger::file_append_mode(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose append-mode policy should be inspected.
#### output
- `Bool` - Current append-mode policy for the file sink.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their current append policy.
- Queued file sinks forward the policy from the wrapped file sink.
- Non-file sinks return `false`.
- This helper reports runtime file policy, not whether a file is currently writable.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Append-policy Visibility
When diagnostics should show how future reopen behavior is configured:
```moonbit
let append = logger.file_append_mode()
```
In this example, the configured logger exposes current reopen policy directly.
#### When Validate Runtime Policy Changes
When policy mutation should be observable after a setter call:
```moonbit
ignore(logger.file_set_append_mode(true))
ignore(logger.file_append_mode())
```
In this example, callers verify the updated append-mode policy.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need the full current file policy rather than just append mode, `file_policy()` is the better API.
### Notes
1. Use this helper when append policy is the only file setting you need to inspect.
2. Pair it with reopen helpers when debugging runtime file behavior.
@@ -0,0 +1,75 @@
---
name: configured-logger-file-auto-flush
group: api
category: runtime
update-time: 20260512
description: Read whether the configured runtime file sink currently has auto-flush enabled.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-auto-flush
Read whether auto-flush is currently enabled on a `ConfiguredLogger` file sink. This helper exposes one important runtime durability policy flag.
### Interface
```moonbit
pub fn ConfiguredLogger::file_auto_flush(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose auto-flush policy should be inspected.
#### output
- `Bool` - Whether auto-flush is currently enabled.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their current auto-flush policy.
- Queued file sinks forward the policy from the wrapped file sink.
- Non-file sinks return `false`.
- This helper exposes policy state only and does not force any flush action.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Durability Visibility
When diagnostics should expose whether each write auto-flushes:
```moonbit
let enabled = logger.file_auto_flush()
```
In this example, runtime file durability policy is surfaced directly.
#### When Validate Policy Updates
When code should observe auto-flush after a setter call:
```moonbit
ignore(logger.file_set_auto_flush(true))
ignore(logger.file_auto_flush())
```
In this example, callers verify the updated policy.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need to actually flush the file, `file_flush()` is the operational API.
### Notes
1. Use this helper to inspect runtime durability policy.
2. It complements `file_set_auto_flush(...)` rather than replacing real flush actions.
@@ -0,0 +1,76 @@
---
name: configured-logger-file-available
group: api
category: runtime
update-time: 20260512
description: Read whether the configured runtime logger currently has an available file sink behind its runtime sink shape.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-available
Read whether a `ConfiguredLogger` currently has an available file sink. This helper is useful for runtime diagnostics and recovery checks after config-driven file logger construction.
### Interface
```moonbit
pub fn ConfiguredLogger::file_available(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink availability should be inspected.
#### output
- `Bool` - Whether an underlying file sink is available.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed runtime sinks report actual file availability.
- Non-file sinks report `false`.
- Queued file sinks still expose the availability of their wrapped file sink.
- This helper delegates to the runtime sink and does not mutate logger state.
### How to Use
Here are some specific examples provided.
#### When Need Runtime File Health Checks
When operators or code should check file sink readiness:
```moonbit
if !logger.file_available() {
println("file sink unavailable")
}
```
In this example, the configured logger exposes file health directly.
#### When Gate Recovery Logic
When reopen or fallback behavior depends on file availability:
```moonbit
let ok = logger.file_available()
```
In this example, callers can decide whether a recovery action is needed.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need detailed failure counters rather than a simple availability flag, `file_state()` or `file_runtime_state()` is the better API.
### Notes
1. Use this helper for lightweight file sink health checks.
2. Pair it with reopen and failure-counter APIs when diagnosing file sink problems.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-clear-rotation
group: api
category: runtime
update-time: 20260512
description: Disable runtime rotation on the configured file-backed logger sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-clear-rotation
Disable runtime rotation on a `ConfiguredLogger` file sink. This helper is the direct shortcut for clearing rotation policy.
### Interface
```moonbit
pub fn ConfiguredLogger::file_clear_rotation(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation policy should be cleared.
#### output
- `Bool` - Whether the policy update was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks clear their runtime rotation policy.
- Queued file sinks forward the update to the wrapped file sink.
- Non-file sinks return `false`.
- This helper is equivalent in intent to setting rotation to `None`, but is clearer at the call site.
### How to Use
Here are some specific examples provided.
#### When Need To Disable Rotation Explicitly
When runtime file rotation should be turned off:
```moonbit
ignore(logger.file_clear_rotation())
```
In this example, rotation policy is removed directly.
#### When Need A Clear Intent Shortcut
When code should make the disable-rotation intent obvious:
```moonbit
let ok = logger.file_clear_rotation()
```
In this example, the call site reads more clearly than a generic config setter.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need to switch to another rotation config rather than disable rotation, `file_set_rotation(...)` is the better API.
### Notes
1. Use this helper when the desired effect is simply “rotation off”.
2. It is clearer than passing `None` through a broader setter when intent matters.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-file-close
group: api
category: runtime
update-time: 20260512
description: Close the file sink behind a configured runtime logger when one is present.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-close
Close the file sink behind a `ConfiguredLogger`. This helper is the file-specific runtime close surface for config-built file loggers.
### Interface
```moonbit
pub fn ConfiguredLogger::file_close(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be closed.
#### output
- `Bool` - Whether the underlying file close succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks forward directly to file close behavior.
- Queued file sinks flush queue work before closing the wrapped file sink.
- Non-file sinks return `false`.
- This helper is narrower than generic `close()` because it specifically targets file sink shutdown.
### How to Use
Here are some specific examples provided.
#### When Need File-specific Runtime Teardown
When a config-built file logger should close its file handle explicitly:
```moonbit
ignore(logger.file_close())
```
In this example, file teardown happens through the configured logger facade.
#### When Need A File-specific Close Result
When application code wants the file close outcome directly:
```moonbit
let closed = logger.file_close()
```
In this example, the result describes file close behavior rather than generic sink close behavior.
### Error Case
e.g.:
- If the configured sink is not file-backed, 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.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-default-policy
group: api
category: runtime
update-time: 20260512
description: Read the initial default file policy associated with a configured file-backed logger.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-default-policy
Read the initial default file policy associated with a `ConfiguredLogger`. This helper exposes the baseline file policy captured when the runtime sink was created.
### Interface
```moonbit
pub fn ConfiguredLogger::file_default_policy(self : ConfiguredLogger) -> FileSinkPolicy {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose default file policy should be inspected.
#### output
- `FileSinkPolicy` - Initial default file policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return the default policy captured at creation time.
- Queued file sinks forward the default policy from the wrapped file sink.
- Non-file sinks return a neutral fallback policy value.
- This helper is useful when callers need to compare runtime drift or restore defaults later.
### How to Use
Here are some specific examples provided.
#### When Need Baseline Policy Visibility
When diagnostics should show the original file policy separately from the live one:
```moonbit
let defaults = logger.file_default_policy()
```
In this example, the configured logger exposes its original file policy snapshot.
#### When Prepare For Policy Reset Logic
When tooling should capture or compare default settings explicitly:
```moonbit
let original = logger.file_default_policy()
```
In this example, callers can reason about “factory” file policy separately from runtime changes.
### Error Case
e.g.:
- If the configured sink is not file-backed, the return value is a neutral fallback policy.
- If callers only need to know whether runtime drift exists, `file_policy_matches_default()` is the simpler API.
### Notes
1. Use this helper when the original file policy matters operationally.
2. It complements `file_policy()` and `file_reset_policy()`.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-flush-failures
group: api
category: runtime
update-time: 20260512
description: Read the number of flush failures recorded by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-flush-failures
Read the number of flush failures recorded by a `ConfiguredLogger` file sink. This helper is useful for diagnosing durability-path problems on file-backed loggers.
### Interface
```moonbit
pub fn ConfiguredLogger::file_flush_failures(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose flush-failure counter should be inspected.
#### output
- `Int` - Number of recorded flush failures.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their recorded flush-failure count.
- Queued file sinks forward the metric from the wrapped file sink.
- Non-file sinks return `0`.
- The counter is cumulative until reset.
### How to Use
Here are some specific examples provided.
#### When Need Durability-path Diagnostics
When support output should show flush-path instability:
```moonbit
let count = logger.file_flush_failures()
```
In this example, the configured logger exposes whether flush attempts have been failing.
#### When Inspect Effects Of Auto-flush Policy
When runtime durability tuning should be inspected operationally:
```moonbit
ignore(logger.file_flush_failures())
```
In this example, callers can correlate flush failures with runtime file policy choices.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `0`.
- If callers need current policy and counters together, `file_state()` is the better API.
### Notes
1. Use this helper when flush-path reliability matters.
2. Pair it with `file_auto_flush()` and `file_flush()` when diagnosing durability behavior.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-file-flush
group: api
category: runtime
update-time: 20260512
description: Flush the file sink behind a configured runtime logger when one is present.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-flush
Flush the file sink behind a `ConfiguredLogger`. This helper is the file-specific runtime flush surface for config-built file loggers.
### Interface
```moonbit
pub fn ConfiguredLogger::file_flush(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be flushed.
#### output
- `Bool` - Whether the underlying file flush succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks forward directly to file flush behavior.
- Queued file sinks first flush queued records, then flush the wrapped file sink.
- Non-file sinks return `false`.
- This helper is narrower than generic `flush()` because it targets file sink behavior specifically.
### How to Use
Here are some specific examples provided.
#### When Need Explicit File Durability Steps
When a config-built file logger should flush to disk explicitly:
```moonbit
ignore(logger.file_flush())
```
In this example, the file-backed runtime sink is flushed directly through the configured logger.
#### When Need A File-specific Success Flag
When code should branch on the outcome of a file flush:
```moonbit
let ok = logger.file_flush()
```
In this example, callers can distinguish file flush success from a non-file sink shape.
### Error Case
e.g.:
- If the configured sink is not file-backed, 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.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-open-failures
group: api
category: runtime
update-time: 20260512
description: Read the number of open failures recorded by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-open-failures
Read the number of open failures recorded by a `ConfiguredLogger` file sink. This helper is useful for diagnosing file-availability or reopen problems.
### Interface
```moonbit
pub fn ConfiguredLogger::file_open_failures(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose open-failure counter should be inspected.
#### output
- `Int` - Number of recorded open failures.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their recorded open-failure count.
- Queued file sinks forward the metric from the wrapped file sink.
- Non-file sinks return `0`.
- The counter is cumulative until reset.
### How to Use
Here are some specific examples provided.
#### When Need File Availability Diagnostics
When operators should see whether file open or reopen failed:
```moonbit
let count = logger.file_open_failures()
```
In this example, the configured logger exposes the open-failure metric directly.
#### When Validate Recovery Logic
When tests or support code should inspect whether reopen attempts failed:
```moonbit
ignore(logger.file_open_failures())
```
In this example, the counter helps confirm whether file-open problems occurred during runtime.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `0`.
- If callers need the full file status snapshot rather than one counter, `file_state()` is the better API.
### Notes
1. Use this helper for focused file-open diagnostics.
2. Pair it with `file_reopen(...)` and `file_available()` during recovery analysis.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-file-path
group: api
category: runtime
update-time: 20260512
description: Read the effective file path used by the configured runtime logger when it is file-backed.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-path
Read the effective file path used by a `ConfiguredLogger`. This helper is useful for diagnostics, support output, and confirming which file-backed runtime sink is active.
### Interface
```moonbit
pub fn ConfiguredLogger::file_path(self : ConfiguredLogger) -> String {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file path should be inspected.
#### output
- `String` - Effective runtime file path, or an empty string for non-file sinks.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current file path.
- Queued file sinks forward the wrapped file sink path.
- Non-file sinks return an empty string.
- This helper is observation-only and does not modify file state.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Path Diagnostics
When diagnostics should show which file is active:
```moonbit
println(logger.file_path())
```
In this example, operators can verify the effective destination path directly.
#### When Build Support Output
When application state dumps should include file destination info:
```moonbit
let path = logger.file_path()
```
In this example, the path can be surfaced without reading broader runtime state.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns an empty string.
- If callers need richer file status than just the path, `file_state()` or `file_runtime_state()` is the better API.
### Notes
1. Use this helper for direct runtime path visibility.
2. Empty string usually means the configured sink is not file-backed.
@@ -0,0 +1,76 @@
---
name: configured-logger-file-policy-matches-default
group: api
category: runtime
update-time: 20260512
description: Read whether the current runtime file policy still matches the configured logger default policy.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-policy-matches-default
Read whether the current runtime file policy still matches the default policy of a `ConfiguredLogger`. This helper is useful for detecting operational drift.
### Interface
```moonbit
pub fn ConfiguredLogger::file_policy_matches_default(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose runtime policy drift should be checked.
#### output
- `Bool` - Whether the current runtime file policy still matches the default.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks compare current runtime file policy against their stored defaults.
- Queued file sinks forward the comparison from the wrapped file sink.
- Non-file sinks return `false`.
- This helper is a compact drift signal when callers do not need to compare full policy objects directly.
### How to Use
Here are some specific examples provided.
#### When Need Drift Detection
When diagnostics should report whether file policy changed after startup:
```moonbit
let unchanged = logger.file_policy_matches_default()
```
In this example, the configured logger exposes whether runtime file policy still matches the baseline.
#### When Gate Reset Logic
When code should only reset policy if drift exists:
```moonbit
if !logger.file_policy_matches_default() {
ignore(logger.file_reset_policy())
}
```
In this example, policy reset only happens when runtime state diverged from defaults.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need the exact differences instead of a boolean drift signal, they should inspect both `file_policy()` and `file_default_policy()`.
### Notes
1. Use this helper for compact runtime policy drift checks.
2. It is especially useful before calling reset-style operations.
+75
View File
@@ -0,0 +1,75 @@
---
name: configured-logger-file-policy
group: api
category: runtime
update-time: 20260512
description: Read the current runtime file policy from a configured file-backed logger.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-policy
Read the current runtime file policy from a `ConfiguredLogger`. This helper exposes the active append, auto-flush, and rotation settings as one policy object.
### Interface
```moonbit
pub fn ConfiguredLogger::file_policy(self : ConfiguredLogger) -> FileSinkPolicy {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose current file policy should be inspected.
#### output
- `FileSinkPolicy` - Current runtime file policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current runtime file policy.
- Queued file sinks forward the policy from the wrapped file sink.
- Non-file sinks return a neutral fallback policy value.
- This helper is broader than `file_append_mode()` or `file_auto_flush()` because it returns the whole policy object.
### How to Use
Here are some specific examples provided.
#### When Need Full Runtime Policy Visibility
When diagnostics should inspect the active file policy as one object:
```moonbit
let policy = logger.file_policy()
```
In this example, append, flush, and rotation settings are read together.
#### When Compare Current And Default Policy
When runtime drift from defaults should be inspected explicitly:
```moonbit
let current = logger.file_policy()
let defaults = logger.file_default_policy()
```
In this example, callers can compare current runtime settings with the initial policy snapshot.
### Error Case
e.g.:
- If the configured sink is not file-backed, the return value is a neutral fallback policy rather than a real active file policy.
- If callers only need one field from the policy, a narrower helper may be simpler.
### Notes
1. Use this helper when file policy should be handled as one object.
2. Pair it with `file_set_policy(...)` for roundtrip-style policy management.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-reopen-append
group: api
category: runtime
update-time: 20260512
description: Reopen the file sink behind a configured runtime logger in append mode.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reopen-append
Reopen the file sink behind a `ConfiguredLogger` in append mode. This helper is the explicit append-oriented recovery shortcut.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reopen_append(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be reopened in append mode.
#### output
- `Bool` - Whether reopen succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks reopen in append mode.
- Queued file sinks forward reopen behavior to the wrapped file sink.
- This helper is a specialized shortcut for a common reopen mode.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need Append-preserving Recovery
When file logging should continue appending after a reopen:
```moonbit
ignore(logger.file_reopen_append())
```
In this example, reopen behavior is fixed to append mode.
#### When Want An Explicit Append Shortcut
When code should avoid manually setting append overrides:
```moonbit
let ok = logger.file_reopen_append()
```
In this example, the call site states append intent directly.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need truncate behavior instead, `file_reopen_truncate()` is the correct API.
### Notes
1. Use this helper for explicit append-mode reopen flows.
2. It is especially useful after transient file availability issues.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-reopen-truncate
group: api
category: runtime
update-time: 20260512
description: Reopen the file sink behind a configured runtime logger in truncate mode.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reopen-truncate
Reopen the file sink behind a `ConfiguredLogger` in truncate mode. This helper is the explicit truncate-oriented recovery or reset shortcut.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reopen_truncate(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be reopened in truncate mode.
#### output
- `Bool` - Whether reopen succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks reopen in truncate mode.
- Queued file sinks forward reopen behavior to the wrapped file sink.
- This helper is a specialized shortcut for a common reset-style reopen mode.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need A Fresh Output File
When a runtime file should be reopened from an empty state:
```moonbit
ignore(logger.file_reopen_truncate())
```
In this example, reopen behavior truncates the file before future writes continue.
#### When Want An Explicit Truncate Shortcut
When code should make destructive reopen intent obvious:
```moonbit
let ok = logger.file_reopen_truncate()
```
In this example, the call site expresses reset-style reopen behavior directly.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers want to preserve existing file content, `file_reopen_append()` is the correct API.
### Notes
1. Use this helper when starting from a fresh file is intentional.
2. Truncate-mode reopen is a stronger action than generic reopen recovery.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-reopen-with-current-policy
group: api
category: runtime
update-time: 20260512
description: Reopen the file sink behind a configured runtime logger using its currently stored runtime policy.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reopen-with-current-policy
Reopen the file sink behind a `ConfiguredLogger` using the currently stored runtime policy. This helper is useful when callers want recovery behavior without supplying a one-off append override.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reopen_with_current_policy(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be reopened with current policy.
#### output
- `Bool` - Whether reopen succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks reuse their current stored reopen policy.
- Queued file sinks forward reopen behavior to the wrapped file sink.
- This helper differs from `file_reopen(...)` because it does not accept a per-call append override.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need Policy-preserving Recovery
When a file sink should be reopened without changing runtime append behavior:
```moonbit
ignore(logger.file_reopen_with_current_policy())
```
In this example, recovery reuses the runtime policy already stored on the sink.
#### When Separate Recovery From Policy Mutation
When append mode should be controlled elsewhere:
```moonbit
let ok = logger.file_reopen_with_current_policy()
```
In this example, reopen is explicit while policy mutation stays separate.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers want to change append behavior during reopen, `file_reopen(...)` or `file_reopen_append()` / `file_reopen_truncate()` are better APIs.
### Notes
1. Use this helper when recovery should respect the currently stored runtime policy.
2. It is clearer than passing no override through a more general reopen API.
+75
View File
@@ -0,0 +1,75 @@
---
name: configured-logger-file-reopen
group: api
category: runtime
update-time: 20260512
description: Reopen the file sink behind a configured runtime logger with an optional append-mode override.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reopen
Reopen the file sink behind a `ConfiguredLogger`. This helper is useful for recovery flows after file unavailability or policy changes.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reopen(self : ConfiguredLogger, append~ : Bool? = None) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file sink should be reopened.
- `append : Bool?` - Optional append-mode override used for reopen behavior.
#### output
- `Bool` - Whether reopen succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks reopen directly.
- Queued file sinks forward reopen behavior to the wrapped file sink.
- `append=None` preserves current reopen policy, while `Some(true/false)` overrides append mode.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need Recovery After File Failure
When application code should attempt to restore file logging:
```moonbit
ignore(logger.file_reopen())
```
In this example, the configured logger tries to reopen its runtime file sink using current policy.
#### When Need Explicit Append-mode Reopen
When recovery should choose append or truncate behavior explicitly:
```moonbit
let ok = logger.file_reopen(append=Some(true))
```
In this example, reopen behavior is directed by the call site.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers only need the current configured policy, `file_reopen_with_current_policy()` may be the clearer API.
### Notes
1. Use this helper for explicit recovery flows.
2. Pair it with `file_available()` and failure counters when diagnosing reopen behavior.
@@ -0,0 +1,75 @@
---
name: configured-logger-file-reset-failure-counters
group: api
category: runtime
update-time: 20260512
description: Reset the file failure counters recorded by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reset-failure-counters
Reset the file failure counters recorded by a `ConfiguredLogger`. This helper clears open, write, flush, and rotation failure metrics together.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reset_failure_counters(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file failure counters should be cleared.
#### output
- `Bool` - Whether the reset was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks clear all file failure counters together.
- Queued file sinks forward the reset behavior to the wrapped file sink.
- Non-file sinks return `false`.
- This helper is useful after diagnostics, recovery, or controlled tests.
### How to Use
Here are some specific examples provided.
#### When Need A Fresh Diagnostics Baseline
When previous failure history should be cleared before a new observation window:
```moonbit
ignore(logger.file_reset_failure_counters())
```
In this example, future failures can be measured from a clean baseline.
#### When Validate Post-recovery Behavior
When recovery logic should clear old counters before rechecking health:
```moonbit
ignore(logger.file_reset_failure_counters())
ignore(logger.file_open_failures())
```
In this example, the configured logger starts a new diagnostics window after reset.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need the current values before clearing them, they should read the counters or `file_state()` first.
### Notes
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.
@@ -0,0 +1,76 @@
---
name: configured-logger-file-reset-policy
group: api
category: runtime
update-time: 20260512
description: Reset the runtime file policy of a configured file-backed logger back to its original defaults.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-reset-policy
Reset the runtime file policy of a `ConfiguredLogger` back to its original defaults. This helper restores append, auto-flush, and rotation policy together.
### Interface
```moonbit
pub fn ConfiguredLogger::file_reset_policy(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file policy should be restored.
#### output
- `Bool` - Whether the reset was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks restore their stored default file policy.
- Queued file sinks forward the reset behavior to the wrapped file sink.
- Non-file sinks return `false`.
- This helper is the inverse of runtime policy drift, not a generic reopen or flush action.
### How to Use
Here are some specific examples provided.
#### When Need To Undo Runtime Policy Changes
When file policy should return to the original configured defaults:
```moonbit
ignore(logger.file_reset_policy())
```
In this example, append, auto-flush, and rotation settings are restored together.
#### When Use Drift-aware Recovery
When reset should only happen after runtime changes:
```moonbit
if !logger.file_policy_matches_default() {
ignore(logger.file_reset_policy())
}
```
In this example, reset is only applied when runtime policy has diverged.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need to restore only one setting, a narrower setter may be more appropriate than a full policy reset.
### Notes
1. Use this helper when the original bundled file policy should be restored intact.
2. It pairs naturally with `file_policy_matches_default()` and `file_default_policy()`.
@@ -0,0 +1,77 @@
---
name: configured-logger-file-rotation-config
group: api
category: runtime
update-time: 20260512
description: Read the current rotation configuration used by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-rotation-config
Read the current rotation configuration used by a `ConfiguredLogger` file sink. This helper exposes the active runtime rotation parameters when rotation is enabled.
### Interface
```moonbit
pub fn ConfiguredLogger::file_rotation_config(self : ConfiguredLogger) -> FileRotation? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation config should be inspected.
#### output
- `FileRotation?` - Current rotation config, or `None` if rotation is disabled or the sink is not file-backed.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current rotation configuration when enabled.
- Queued file sinks forward the config from the wrapped file sink.
- Non-file sinks return `None`.
- This helper is useful when callers need active runtime rotation parameters rather than only a boolean flag.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Rotation Parameters
When support output should include the active rotation policy:
```moonbit
let rotation = logger.file_rotation_config()
```
In this example, the configured logger exposes live runtime rotation parameters directly.
#### When Branch On Optional Rotation Presence
When code should react differently for rotating file sinks:
```moonbit
match logger.file_rotation_config() {
Some(cfg) => ignore(cfg)
None => ()
}
```
In this example, optional return shape reflects whether rotation is active.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers only need to know whether rotation is enabled, `file_rotation_enabled()` is the simpler API.
### Notes
1. Use this helper when current runtime rotation parameters matter.
2. It is useful after policy updates or recovery flows.
@@ -0,0 +1,76 @@
---
name: configured-logger-file-rotation-enabled
group: api
category: runtime
update-time: 20260512
description: Read whether rotation is currently enabled on the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-rotation-enabled
Read whether rotation is currently enabled on a `ConfiguredLogger` file sink. This helper exposes whether runtime rotation policy is active.
### Interface
```moonbit
pub fn ConfiguredLogger::file_rotation_enabled(self : ConfiguredLogger) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation state should be inspected.
#### output
- `Bool` - Whether rotation is currently enabled.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report whether a rotation config is active.
- Queued file sinks forward the state from the wrapped file sink.
- Non-file sinks return `false`.
- This helper is narrower than `file_rotation_config()` when only a yes/no check is needed.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Rotation Visibility
When support output should expose whether rotation is active:
```moonbit
let rotating = logger.file_rotation_enabled()
```
In this example, callers get a direct boolean answer without inspecting config objects.
#### When Guard Rotation-specific Behavior
When logic should only run for rotating file sinks:
```moonbit
if logger.file_rotation_enabled() {
println("rotation active")
}
```
In this example, runtime policy can drive control flow.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need the actual rotation parameters, `file_rotation_config()` is the better API.
### Notes
1. Use this helper for simple runtime rotation checks.
2. Pair it with `file_rotation_config()` when parameters also matter.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-rotation-failures
group: api
category: runtime
update-time: 20260512
description: Read the number of rotation failures recorded by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-rotation-failures
Read the number of rotation failures recorded by a `ConfiguredLogger` file sink. This helper is useful when runtime rotation behavior should be observed operationally.
### Interface
```moonbit
pub fn ConfiguredLogger::file_rotation_failures(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation-failure counter should be inspected.
#### output
- `Int` - Number of recorded rotation failures.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their recorded rotation-failure count.
- Queued file sinks forward the metric from the wrapped file sink.
- Non-file sinks return `0`.
- The counter is cumulative until reset.
### How to Use
Here are some specific examples provided.
#### When Need Rotation-specific Diagnostics
When support output should reveal whether rotation is failing:
```moonbit
let count = logger.file_rotation_failures()
```
In this example, the configured logger exposes a focused metric for rotation-path health.
#### When Validate Runtime Rotation Tuning
When changed rotation settings should be checked in operation:
```moonbit
ignore(logger.file_rotation_failures())
```
In this example, callers can observe whether runtime rotation changes introduced problems.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `0`.
- If callers need both rotation config and failure metrics, they should combine this helper with `file_rotation_config()` or `file_state()`.
### Notes
1. Use this helper when diagnosing file rotation reliability.
2. It is especially relevant when runtime rotation policy can change after startup.
@@ -0,0 +1,77 @@
---
name: configured-logger-file-runtime-state
group: api
category: runtime
update-time: 20260512
description: Read combined file and queue runtime state from a configured logger when it is backed by a file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-runtime-state
Read combined file and queue runtime state from a `ConfiguredLogger`. This helper is the richest file-specific diagnostics API on config-built runtime loggers.
### Interface
```moonbit
pub fn ConfiguredLogger::file_runtime_state(self : ConfiguredLogger) -> RuntimeFileState? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file runtime state should be inspected.
#### output
- `RuntimeFileState?` - Combined file and queue diagnostics when the runtime sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(RuntimeFileState)`.
- Queued file sinks include both file status and queue metrics in the returned state.
- Non-file sinks return `None`.
- This helper is richer than `file_state()` because it can also surface queued backlog and dropped counts.
### How to Use
Here are some specific examples provided.
#### When Need Rich File Runtime Diagnostics
When support output should include both file and queue state:
```moonbit
let state = logger.file_runtime_state()
```
In this example, callers can inspect availability, failure counters, and queue metrics together.
#### When Need To Branch On File-backed Runtime Shape
When code should behave differently for file-backed config-built loggers:
```moonbit
match logger.file_runtime_state() {
Some(state) => ignore(state)
None => ()
}
```
In this example, the optional return value reflects whether the runtime sink is file-backed.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers only need raw file status without queue context, `file_state()` may be the simpler API.
### Notes
1. Use this helper for the richest file-backed runtime diagnostics on config-built loggers.
2. It is especially useful for queued file sinks where file health and queue state both matter.
@@ -0,0 +1,76 @@
---
name: configured-logger-file-set-append-mode
group: api
category: runtime
update-time: 20260512
description: Update the append-mode policy used by the configured runtime file sink for later reopen behavior.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-set-append-mode
Update the append-mode policy used by a `ConfiguredLogger` file sink. This helper changes future reopen behavior without forcing an immediate reopen.
### Interface
```moonbit
pub fn ConfiguredLogger::file_set_append_mode(self : ConfiguredLogger, append : Bool) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose append-mode policy should change.
- `append : Bool` - New append-mode policy.
#### output
- `Bool` - Whether the policy update was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their stored append policy.
- Queued file sinks forward the policy update to the wrapped file sink.
- This helper updates policy only; it does not force immediate reopen.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need To Change Future Reopen Behavior
When runtime recovery should later prefer append mode explicitly:
```moonbit
ignore(logger.file_set_append_mode(true))
```
In this example, later reopen operations will use the updated append policy.
#### When Want To Separate Policy Mutation From Reopen Timing
When code should update policy now and reopen later:
```moonbit
ignore(logger.file_set_append_mode(false))
ignore(logger.file_reopen_with_current_policy())
```
In this example, policy mutation and reopen timing remain separate decisions.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need an immediate reopen as part of the same operation, one of the reopen helpers should be used afterward.
### Notes
1. This helper changes stored policy, not live file-handle state by itself.
2. It is useful when recovery logic wants to stage reopen behavior in advance.
@@ -0,0 +1,75 @@
---
name: configured-logger-file-set-auto-flush
group: api
category: runtime
update-time: 20260512
description: Update the auto-flush policy used by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-set-auto-flush
Update the auto-flush policy used by a `ConfiguredLogger` file sink. This helper changes runtime durability behavior without rebuilding the logger.
### Interface
```moonbit
pub fn ConfiguredLogger::file_set_auto_flush(self : ConfiguredLogger, enabled : Bool) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose auto-flush policy should change.
- `enabled : Bool` - New auto-flush setting.
#### output
- `Bool` - Whether the policy update was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their runtime auto-flush policy.
- Queued file sinks forward the update to the wrapped file sink.
- Non-file sinks return `false`.
- This helper changes policy only; it does not itself flush pending data.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Durability Tuning
When a file sink should start flushing each write automatically:
```moonbit
ignore(logger.file_set_auto_flush(true))
```
In this example, runtime policy is updated without rebuilding the configured logger.
#### When Need To Relax Flush Pressure
When a file sink should stop auto-flushing for throughput reasons:
```moonbit
let ok = logger.file_set_auto_flush(false)
```
In this example, the call site updates policy explicitly and can inspect the result.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers need an immediate flush action, `file_flush()` is the operational API.
### Notes
1. Use this helper for runtime durability tuning.
2. Pair it with `file_auto_flush()` to verify the active policy.
@@ -0,0 +1,79 @@
---
name: configured-logger-file-set-policy
group: api
category: runtime
update-time: 20260512
description: Apply a bundled runtime file policy update to a configured file-backed logger.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-set-policy
Apply a bundled runtime file policy update to a `ConfiguredLogger`. This helper updates append mode, auto-flush, and rotation together through one runtime policy object.
### Interface
```moonbit
pub fn ConfiguredLogger::file_set_policy(self : ConfiguredLogger, policy : FileSinkPolicy) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file policy should change.
- `policy : FileSinkPolicy` - Bundled runtime file policy to apply.
#### output
- `Bool` - Whether the policy update was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks update append, auto-flush, and rotation together.
- Queued file sinks forward the policy update to the wrapped file sink.
- Non-file sinks return `false`.
- This helper is broader than the single-setting setters because it updates the whole file policy in one call.
### How to Use
Here are some specific examples provided.
#### When Need Bundled Runtime Policy Changes
When append, flush, and rotation should change together:
```moonbit
ignore(logger.file_set_policy(FileSinkPolicy::new(
append=true,
auto_flush=false,
rotation=Some(file_rotation(2048, max_backups=2)),
)))
```
In this example, runtime file behavior is updated as one policy change.
#### When Apply A Policy Snapshot
When a previously captured or computed policy should be restored:
```moonbit
let ok = logger.file_set_policy(policy)
```
In this example, callers can reapply a whole policy object without splitting it into separate setter calls.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers only need to change one setting, a narrower setter such as `file_set_auto_flush(...)` or `file_set_rotation(...)` may be clearer.
### Notes
1. Use this helper when the runtime policy should be treated as one cohesive object.
2. It pairs naturally with `file_policy()` and `file_default_policy()`.
@@ -0,0 +1,78 @@
---
name: configured-logger-file-set-rotation
group: api
category: runtime
update-time: 20260512
description: Update the rotation configuration used by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-set-rotation
Update the rotation configuration used by a `ConfiguredLogger` file sink. This helper changes runtime file rotation behavior without rebuilding the logger.
### Interface
```moonbit
pub fn ConfiguredLogger::file_set_rotation(
self : ConfiguredLogger,
rotation : FileRotation?,
) -> Bool {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation policy should change.
- `rotation : FileRotation?` - New rotation config, or `None` to disable rotation.
#### output
- `Bool` - Whether the policy update was applied.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their runtime rotation policy.
- Queued file sinks forward the update to the wrapped file sink.
- Passing `None` disables rotation.
- Non-file sinks return `false`.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Rotation Tuning
When a file sink should enable or change rotation dynamically:
```moonbit
ignore(logger.file_set_rotation(Some(file_rotation(1024, max_backups=3))))
```
In this example, runtime rotation behavior is updated without rebuilding the logger.
#### When Need To Disable Rotation
When the file sink should stop rotating:
```moonbit
let ok = logger.file_set_rotation(None)
```
In this example, the runtime file sink has its rotation policy cleared explicitly.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `false`.
- If callers only want to remove rotation, `file_clear_rotation()` is the more direct API.
### Notes
1. Use this helper when setting a full runtime rotation config.
2. It is useful for operational tuning without rebuilding the logger.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-file-state
group: api
category: runtime
update-time: 20260512
description: Read the current file sink snapshot from a configured runtime logger.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-state
Read the current file sink snapshot from a `ConfiguredLogger`. This helper exposes path, availability, policy flags, rotation config, and failure counters as one object.
### Interface
```moonbit
pub fn ConfiguredLogger::file_state(self : ConfiguredLogger) -> FileSinkState {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file state snapshot should be inspected.
#### output
- `FileSinkState` - Current file sink snapshot.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return a live snapshot of file state.
- Queued file sinks forward the snapshot from the wrapped file sink.
- Non-file sinks return a fallback empty-state snapshot.
- This helper is broader than individual file counters or policy accessors because it aggregates core file status into one read.
### How to Use
Here are some specific examples provided.
#### When Need A Full File Health Snapshot
When diagnostics should inspect runtime file state as one object:
```moonbit
let state = logger.file_state()
```
In this example, callers receive a single file-state snapshot instead of querying each property separately.
#### When Need To Export File Runtime Diagnostics
When a support path should serialize current file state:
```moonbit
println(stringify_file_sink_state(logger.file_state(), pretty=true))
```
In this example, the configured logger snapshot can be exported directly through existing JSON helpers.
### Error Case
e.g.:
- If the configured sink is not file-backed, the returned snapshot is a fallback empty-style state rather than a live file view.
- If callers also need queue context for queued file sinks, `file_runtime_state()` is the richer API.
### Notes
1. Use this helper for the main one-shot file status snapshot.
2. Prefer it over individual counters when broader file diagnostics are needed.
@@ -0,0 +1,74 @@
---
name: configured-logger-file-write-failures
group: api
category: runtime
update-time: 20260512
description: Read the number of write failures recorded by the configured runtime file sink.
key-word:
- logger
- runtime
- file
- public
---
## Configured-logger-file-write-failures
Read the number of write failures recorded by a `ConfiguredLogger` file sink. This helper is useful for diagnosing runtime write-path problems.
### Interface
```moonbit
pub fn ConfiguredLogger::file_write_failures(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose write-failure counter should be inspected.
#### output
- `Int` - Number of recorded write failures.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks report their recorded write-failure count.
- Queued file sinks forward the metric from the wrapped file sink.
- Non-file sinks return `0`.
- The counter is cumulative until reset.
### How to Use
Here are some specific examples provided.
#### When Need Runtime Write-path Diagnostics
When support output should show whether writes have been failing:
```moonbit
let count = logger.file_write_failures()
```
In this example, callers get a focused signal for file write-path health.
#### When Compare Sink Health After Recovery
When recovery logic should verify whether new failures still occur:
```moonbit
ignore(logger.file_write_failures())
```
In this example, the metric helps measure whether runtime writes improved after intervention.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `0`.
- If callers need a fuller view of file health, `file_state()` or `file_runtime_state()` may be better APIs.
### Notes
1. Use this helper for focused write-failure visibility.
2. Pair it with `file_flush_failures()` when diagnosing output-path instability.
+74
View File
@@ -0,0 +1,74 @@
---
name: configured-logger-flush
group: api
category: runtime
update-time: 20260512
description: Flush a configured runtime logger and return how many queued or file-backed operations were advanced.
key-word:
- logger
- runtime
- flush
- public
---
## Configured-logger-flush
Flush a `ConfiguredLogger` and return how much work was advanced. This is the main runtime helper for forcing queued or file-backed logger output to move forward after config-driven construction.
### Interface
```moonbit
pub fn ConfiguredLogger::flush(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose sink should be flushed.
#### output
- `Int` - Count of flushed or drained items as reported by the runtime sink.
### Explanation
Detailed rules explaining key parameters and behaviors
- For queue-wrapped sinks, this forwards to the queue drain/flush behavior.
- For plain file sinks, the return value reflects whether a file flush happened.
- For plain console-style sinks, the result is typically `0` because there is no meaningful buffered flush step.
- This helper delegates to `RuntimeSink::flush(...)` through the configured logger wrapper.
### How to Use
Here are some specific examples provided.
#### When Need Explicit Queue Progress
When config-built queue output should be advanced manually:
```moonbit
ignore(logger.flush())
```
In this example, the configured runtime logger is flushed without reaching into the sink directly.
#### When Need A Post-write Flush Barrier
When a service wants stronger delivery behavior after a burst of writes:
```moonbit
let flushed = logger.flush()
```
In this example, callers can observe how much work was advanced by the flush request.
### Error Case
e.g.:
- If the configured sink has no flushable buffering, the method may simply return `0`.
- If callers need bounded manual draining rather than generic flush behavior, `drain(...)` is the better API.
### Notes
1. Use this helper after config-driven logger construction when explicit runtime flushing matters.
2. The exact return value depends on the underlying runtime sink shape.
@@ -0,0 +1,75 @@
---
name: configured-logger-pending-count
group: api
category: runtime
update-time: 20260512
description: Read the current queued backlog count from a configured runtime logger.
key-word:
- logger
- runtime
- queue
- public
---
## Configured-logger-pending-count
Read the current queued backlog count from a `ConfiguredLogger`. This is the direct runtime metric for config-driven queue wrapping.
### Interface
```moonbit
pub fn ConfiguredLogger::pending_count(self : ConfiguredLogger) -> Int {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose queue backlog should be inspected.
#### output
- `Int` - Current number of pending queued records.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-backed sinks return their live pending count.
- Non-queued sinks report `0`.
- This is a point-in-time metric and may change immediately after it is read.
- This helper delegates to `RuntimeSink::pending_count(...)` through the configured logger wrapper.
### How to Use
Here are some specific examples provided.
#### When Need Configured Queue Backlog Visibility
When a config-built logger should expose queue pressure:
```moonbit
let pending = logger.pending_count()
```
In this example, queue backlog is observed without reaching into the sink internals.
#### When Verify Manual Drain Progress
When drain loops should observe remaining backlog:
```moonbit
ignore(logger.drain(max_items=8))
ignore(logger.pending_count())
```
In this example, the queue metric helps verify manual drain progress.
### Error Case
e.g.:
- If the logger is not queue-backed, the method simply returns `0`.
- If callers need richer file-plus-queue state for file sinks, `file_runtime_state()` is the better API.
### Notes
1. Use this helper for simple queue visibility on config-built loggers.
2. Pair it with `dropped_count()` when investigating loss behavior.
+61
View File
@@ -0,0 +1,61 @@
---
name: console-sink
group: api
category: sink
update-time: 20260520
description: Create the built-in plain console sink for synchronous loggers.
key-word:
- sink
- console
- sync
- public
---
## Console-sink
Create the built-in plain console sink. This is the minimal terminal output sink commonly passed to `Logger::new(...)`.
### Interface
```moonbit
pub fn console_sink() -> ConsoleSink {
```
#### output
- `ConsoleSink` - Sink that writes records to the console using default text formatting.
### Explanation
Detailed rules explaining key parameters and behaviors
- This sink writes formatted text to the console.
- It is the simplest built-in sink for direct synchronous logging.
- Use other sink constructors when JSON, custom text formatting, callbacks, routing, or buffering are needed.
### How to Use
Here are some specific examples provided.
#### When Need Minimal Console Output
When a logger only needs default terminal output:
```moonbit
let logger = Logger::new(console_sink(), target="app")
logger.info("ready")
```
In this example, the logger writes directly to the console sink.
### Error Case
e.g.:
- If richer formatting or routing is required, this sink may be too minimal and another sink constructor should be used.
- Console output behavior still depends on the current runtime environment.
### Notes
1. This is the most common sink constructor for simple sync examples.
2. Use `json_console_sink()` or `text_console_sink(...)` when output shape matters more explicitly.
+73
View File
@@ -0,0 +1,73 @@
---
name: console
group: api
category: config
update-time: 20260520
description: Create a minimal logger config preset for the built-in console sink.
key-word:
- preset
- console
- config
- public
---
## Console
Create a `LoggerConfig` preset for the built-in plain console sink. This helper is the shortest way to assemble a config-driven logger that writes directly to the terminal without text formatter customization.
### Interface
```moonbit
pub fn console(
min_level~ : Level = Level::Info,
target~ : String = "",
timestamp~ : Bool = false,
) -> LoggerConfig {
```
#### input
- `min_level : Level` - Minimum enabled level for the preset.
- `target : String` - Default target stored in the returned config.
- `timestamp : Bool` - Whether the built logger should emit timestamps.
#### output
- `LoggerConfig` - Config using `SinkKind::Console` with no queue wrapper by default.
### Explanation
Detailed rules explaining key parameters and behaviors
- This preset always returns `sink.kind=SinkKind::Console`.
- `queue=None` by default, so output remains unqueued unless `with_queue(...)` is applied later.
- This helper only selects the sink shape and top-level logger config fields; it does not build a runtime logger by itself.
### How to Use
Here are some specific examples provided.
#### When Need A Minimal Terminal Config
When application startup wants a simple console config:
```moonbit
let config = console(min_level=Level::Debug, target="cli")
let logger = build_logger(config)
```
In this example, the logger uses the built-in console sink.
And no extra queue or file policy is configured.
### Error Case
e.g.:
- If `target` is empty, the config is still valid.
- If later runtime assembly targets an environment with sink-specific limitations, those runtime rules still apply outside this preset.
### Notes
1. Use this preset when you want the shortest path to console output.
2. Apply `with_queue(...)` separately if bounded synchronous buffering is needed.

Some files were not shown because too many files have changed in this diff Show More