mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-05-30 15:42:25 +00:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4f83f5ccb | |||
| 73bcf78d89 | |||
| 1ca5ab0835 | |||
| e019db11d6 | |||
| 5f12991592 | |||
| 25a6a973d2 | |||
| 55af0b664f | |||
| 0a098915af | |||
| 4860d1e08b | |||
| dca34bc114 | |||
| 4c25a81b03 | |||
| 3c15d8ed13 | |||
| 6998df0ee4 | |||
| 414d6a0ee8 | |||
| 2823a67d53 | |||
| a766dd09ac | |||
| d0989d6308 | |||
| 8b752a4b4d | |||
| ac45ec2b03 | |||
| 41d221af46 | |||
| d6e47d4bb8 | |||
| 91096a9e0d | |||
| 78423384ea | |||
| 1b56e1e20a | |||
| 91d778d92e | |||
| 1c75c98e3c | |||
| 02c40f26f9 | |||
| 640b717c70 | |||
| bd1ef99189 | |||
| 661c17e093 | |||
| 474a1931c2 | |||
| dd895ac211 | |||
| 9248135b9c | |||
| 694414e068 | |||
| bab9864f1f | |||
| d1260c5deb | |||
| cfde700526 | |||
| 87cf651c4c | |||
| 1e487f2ffc | |||
| f21aca670a | |||
| 6f1d6086d6 | |||
| 0ab3b95959 | |||
| bd3a1c24d0 | |||
| d7f93e9f9c | |||
| dad55a241f | |||
| f172305453 | |||
| 80f15cd455 | |||
| c6c153cf53 | |||
| 4d913aa642 | |||
| 8dbadd5938 | |||
| 3dbf848a53 | |||
| 0b93af9261 | |||
| 6e6c14d28c | |||
| f1b7d1d21c | |||
| 8584fc7e01 | |||
| a328414087 | |||
| f609b02377 | |||
| 3124d7a445 | |||
| 4b54005401 | |||
| 2d2388c79f | |||
| e78183d267 | |||
| 4be861acce | |||
| 20f79bbe2a | |||
| f3e903b578 | |||
| f1b223f203 | |||
| 3b6536f980 | |||
| b1b2235160 | |||
| 90af009e93 |
@@ -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: |
|
||||
|
||||
@@ -19,3 +19,6 @@ desktop.ini
|
||||
|
||||
# Dev Documentations
|
||||
docs/dev/*
|
||||
|
||||
# vibecoding
|
||||
AGENTS/
|
||||
@@ -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(...)`
|
||||
|
||||
@@ -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.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
@@ -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/`.
|
||||
|
||||
@@ -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 `##### ...`
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: async-logger-child
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Derive a child async logger by composing the current target with a child target segment.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- target
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-child
|
||||
|
||||
Create a child async logger by composing the current target with another target segment. This is the standard API for hierarchical async logger naming such as `app.worker` or `service.http.client`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::child(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Parent async logger whose target should be extended.
|
||||
- `target : String` - Child target segment or suffix.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger whose default target is the composed child path.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- If the parent target is empty, the child target becomes the full target.
|
||||
- If the child target is empty, the parent target is preserved.
|
||||
- If both are non-empty, they are joined with `.`.
|
||||
- Queue settings, sink wiring, and runtime behavior are preserved in the returned logger.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Need Hierarchical Async Targets
|
||||
|
||||
When subsystem logs should stay grouped under one async namespace:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink(), target="service")
|
||||
let worker = logger.child("worker")
|
||||
```
|
||||
|
||||
In this example, `worker` emits under `service.worker` while keeping the same async queue behavior.
|
||||
|
||||
#### When Build Deep Async Scopes Step By Step
|
||||
|
||||
When deeper target composition should remain readable:
|
||||
```moonbit
|
||||
let http = async_logger(console_sink(), target="app")
|
||||
.child("http")
|
||||
.child("client")
|
||||
```
|
||||
|
||||
In this example, the final logger target becomes `app.http.client`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `target` is empty, the returned logger keeps the original parent target.
|
||||
|
||||
- If callers need complete replacement rather than composition, `with_target(...)` should be used instead.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is the preferred API for hierarchical async logger naming.
|
||||
|
||||
2. Composition changes the target only and does not rebuild the queue or sink.
|
||||
@@ -0,0 +1,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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: async-logger-with-filter
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Attach predicate-based filtering to an async logger so only matching records reach the queue.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- filter
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-with-filter
|
||||
|
||||
Attach a record predicate to an async logger so only matching records are enqueued. This is the main API for async routing-by-predicate without rewriting sink implementations.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::with_filter(
|
||||
self : AsyncLogger[S],
|
||||
predicate : (@bitlogger.Record) -> Bool,
|
||||
) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Base async logger to constrain.
|
||||
- `predicate : (Record) -> Bool` - Record predicate that decides whether a record should be enqueued.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that only enqueues matching records.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Filtering happens after record construction and patch application but before enqueue.
|
||||
- Existing filter logic is preserved and combined with the new predicate using logical `and`.
|
||||
- The original async logger is not mutated.
|
||||
- Filtering avoids unnecessary queue pressure for records that should never be delivered.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Keep Only One Async Target Namespace
|
||||
|
||||
When an async logger should only enqueue records from a specific subsystem:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink(), target="service")
|
||||
.with_filter(@bitlogger.target_has_prefix("service.api"))
|
||||
```
|
||||
|
||||
In this example, non-matching records are dropped before they reach the async queue.
|
||||
|
||||
#### When Combine Several Async Filter Rules
|
||||
|
||||
When filtering depends on multiple conditions:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink(), target="api")
|
||||
.with_filter(fn(rec) {
|
||||
rec.level.enabled(@bitlogger.Level::Warn) && rec.message.contains("timeout")
|
||||
})
|
||||
```
|
||||
|
||||
In this example, only warning-or-higher timeout records are enqueued.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the predicate always returns `false`, the logger silently drops every record before enqueue.
|
||||
|
||||
- If the predicate always returns `true`, the wrapper behaves like a pass-through filter.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API for selection logic, not record mutation.
|
||||
|
||||
2. Async filtering is especially useful when queue capacity should be reserved for high-value records.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: async-logger-with-min-level
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Replace the async logger minimum enabled level so lower-severity records are skipped before enqueue.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- level
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-with-min-level
|
||||
|
||||
Replace the async logger's minimum enabled level. This API controls the first gate checked before record creation and queue insertion.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::with_min_level(
|
||||
self : AsyncLogger[S],
|
||||
min_level : @bitlogger.Level,
|
||||
) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Base async logger whose level threshold should change.
|
||||
- `min_level : Level` - New minimum enabled level.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger value carrying the updated threshold.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `log(...)` checks `is_enabled(level)` before creating a record or touching the queue.
|
||||
- Lower-severity records below `min_level` are skipped before enqueue.
|
||||
- This API replaces the logger threshold and does not alter queue configuration.
|
||||
- The returned logger keeps the same sink, target, and timestamp settings.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Raise Async Noise Floor In Production
|
||||
|
||||
When only warning and error records should reach the async queue:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_min_level(@bitlogger.Level::Warn)
|
||||
```
|
||||
|
||||
In this example, lower-severity records are skipped before queue pressure increases.
|
||||
|
||||
#### When Derive A More Verbose Async Branch
|
||||
|
||||
When one branch of code should keep a different threshold:
|
||||
```moonbit
|
||||
let base = async_logger(console_sink(), min_level=@bitlogger.Level::Info)
|
||||
let debug_logger = base.with_min_level(@bitlogger.Level::Debug)
|
||||
```
|
||||
|
||||
In this example, the async sink and queue settings are reused while the threshold changes per logger value.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `min_level` is set too high, expected lower-severity diagnostics may disappear before they ever enter the queue.
|
||||
|
||||
- If callers need richer predicate logic than a simple threshold, `with_filter(...)` should be used instead.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This API reduces async queue pressure by dropping disabled levels before enqueue.
|
||||
|
||||
2. Use it before adding more complex async filtering rules.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: async-logger-with-patch
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Attach record transformation logic to an async logger before records reach the queue.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- patch
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-with-patch
|
||||
|
||||
Attach a record patch to an async logger so each record is transformed before filtering and enqueue. This is the main API for async record rewriting without changing the sink implementation.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::with_patch(
|
||||
self : AsyncLogger[S],
|
||||
patch : @bitlogger.RecordPatch,
|
||||
) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Base async logger to wrap.
|
||||
- `patch : RecordPatch` - Transformation applied to each record before enqueue.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that rewrites each record before filtering and queue insertion.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Patch logic runs after record creation and field merging but before filtering and enqueue.
|
||||
- Existing patch logic is preserved and composed so the new patch wraps the current one.
|
||||
- The original async logger is not mutated.
|
||||
- Patching can normalize, redact, or enrich records before they consume queue capacity.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Need Async Record Enrichment
|
||||
|
||||
When records should gain stable extra data before enqueue:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_patch(@bitlogger.append_fields([
|
||||
@bitlogger.field("channel", "async"),
|
||||
]))
|
||||
```
|
||||
|
||||
In this example, the added fields are part of the record before filtering and queue insertion.
|
||||
|
||||
#### When Need Redaction Before Queueing
|
||||
|
||||
When sensitive fields should be removed early:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_patch(@bitlogger.redact_fields(["token", "password"]))
|
||||
```
|
||||
|
||||
In this example, sensitive values are rewritten before they enter the async pipeline.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the patch removes or rewrites important data incorrectly, later filters and sinks will only see the patched version.
|
||||
|
||||
- If callers need selection logic rather than transformation, `with_filter(...)` should be used instead.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use patches for transformation, not filtering decisions.
|
||||
|
||||
2. Redaction before enqueue helps keep sensitive data out of the queued pipeline.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: async-logger-with-target
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Replace the default target carried by an async logger so later records inherit a new target namespace.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- target
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-with-target
|
||||
|
||||
Replace the async logger's default target. This API retargets later enqueue operations without changing the queue, overflow policy, or sink wiring.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::with_target(self : AsyncLogger[S], target : String) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Base async logger whose default target should be replaced.
|
||||
- `target : String` - New default target namespace.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger value carrying the updated target.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned logger keeps the same sink, queue state, overflow policy, flush policy, and lifecycle flags.
|
||||
- This API replaces the default target instead of composing it.
|
||||
- Per-call `target?` arguments on `log(...)` can still override the default target.
|
||||
- The original logger value is not mutated.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Need A Stable Async Target Namespace
|
||||
|
||||
When one async logger should always emit under a fixed target:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_target("service.worker")
|
||||
```
|
||||
|
||||
In this example, later async log calls inherit `service.worker` unless a call overrides the target explicitly.
|
||||
|
||||
#### When Reuse One Async Setup Across Namespaces
|
||||
|
||||
When multiple subsystem loggers should share the same async queue behavior:
|
||||
```moonbit
|
||||
let base = async_logger(console_sink(), config=AsyncLoggerConfig::new(max_pending=64))
|
||||
let api = base.with_target("api")
|
||||
let jobs = base.with_target("jobs")
|
||||
```
|
||||
|
||||
In this example, target routing changes without rebuilding the async runtime configuration.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `target` is empty, the logger remains valid and later records default to an empty target.
|
||||
|
||||
- If callers need hierarchical target composition rather than replacement, `child(...)` is the better API.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API for replacement, not parent-child target composition.
|
||||
|
||||
2. It is useful when several subsystems should share one async queue policy.
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: async-logger-with-timestamp
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enable or disable automatic timestamp capture for records emitted by an async logger.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
- timestamp
|
||||
- public
|
||||
---
|
||||
|
||||
## Async-logger-with-timestamp
|
||||
|
||||
Enable or disable automatic timestamp capture on async log emission. This API controls whether `AsyncLogger::log(...)` records the current time before enqueue or leaves the timestamp at `0UL`.
|
||||
|
||||
### Interface
|
||||
|
||||
```moonbit
|
||||
pub fn[S] AsyncLogger::with_timestamp(self : AsyncLogger[S], enabled~ : Bool = true) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
#### input
|
||||
|
||||
- `self : AsyncLogger[S]` - Base async logger whose timestamp behavior should change.
|
||||
- `enabled : Bool` - Whether emitted records should capture current time automatically.
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger value with updated timestamp behavior.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- When enabled, `log(...)` captures `@env.now()` before placing the record into the queue.
|
||||
- When disabled, emitted records use `0UL` as the timestamp value.
|
||||
- This setting affects later emitted records only.
|
||||
- Queue, batching, and flush behavior are unchanged.
|
||||
|
||||
### How to Use
|
||||
|
||||
Here are some specific examples provided.
|
||||
|
||||
#### When Need Real Event Time Before Queueing
|
||||
|
||||
When downstream formatting or JSON output should include event time:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_timestamp()
|
||||
```
|
||||
|
||||
In this example, each record captures its timestamp before entering the async queue.
|
||||
|
||||
#### When Need Deterministic Async Records
|
||||
|
||||
When timestamps should be disabled for tests or reduced output:
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink())
|
||||
.with_timestamp(enabled=false)
|
||||
```
|
||||
|
||||
In this example, queued records are emitted without runtime time capture.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If timestamps are disabled, formatters that expect meaningful time values may show empty or zero-like timestamp output.
|
||||
|
||||
- If callers need timestamps only for one record, a separate logger value is usually clearer than toggling behavior repeatedly.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This API controls record creation before enqueue, not formatter display policy.
|
||||
|
||||
2. It is useful for tests, deterministic snapshots, and production timing.
|
||||
@@ -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.
|
||||
|
||||
@@ -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()`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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(...)`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
Reference in New Issue
Block a user