36 Commits

Author SHA1 Message Date
Nanaloveyuki 0b05ee0c94 🔖 release 0.7.3 2026-07-19 17:12:02 +08:00
Nanaloveyuki f9227890bd add wasm coverage and trace context 2026-07-19 17:04:27 +08:00
Nanaloveyuki 06db039a03 add release version helper 2026-07-17 21:26:59 +08:00
Nanaloveyuki 5ed02ec563 🔖 release 0.7.2 2026-07-17 21:22:04 +08:00
Nanaloveyuki a58a0747bb ♻️ migrate to core json 2026-07-17 21:19:11 +08:00
Nanaloveyuki bcfb35d7ae 🩹 harden file rotation failures 2026-07-17 20:10:25 +08:00
Nanaloveyuki 55540f56a2 ⬆️ upgrade async to 0.20.2 2026-07-17 19:42:53 +08:00
Nanaloveyuki 0342603b9e 🔖 prepare 1.1.1(0.7.1) release 2026-07-17 19:16:00 +08:00
Nanaloveyuki ea6113354a 🔧 support MoonBit 0.10.4 2026-07-17 19:04:17 +08:00
Nanaloveyuki b18dcf972d 🧪 relocate test logs 2026-07-17 18:22:04 +08:00
Nanaloveyuki a745cd5172 📝 add Chinese API guides and locale detection 2026-07-17 18:06:30 +08:00
Nanaloveyuki b32de63d57 📝 add Chinese examples and extension docs 2026-07-17 16:31:25 +08:00
Nanaloveyuki 4e2ef69b11 📝 add example-first documentation paths 2026-07-17 16:24:06 +08:00
Nanaloveyuki c6962fe71a 📝 add generated package interface snapshots 2026-07-17 15:53:21 +08:00
Nanaloveyuki 74b9815bab 🧹 apply moon fmt and async mbti refresh 2026-07-17 15:53:21 +08:00
Nanaloveyuki 6ee0d97aac 💚 enhance CI matrix and coverage checks 2026-07-17 15:53:21 +08:00
Nanaloveyuki 465e5ecd5d fix async js shutdown test expectation 2026-07-17 15:53:21 +08:00
Nanaloveyuki 52b26b31f2 🔖 prepare 1.1.0(0.7.0) sync release notes 2026-07-17 15:53:21 +08:00
Nanaloveyuki 9405a04c13 📝 align sync API docs with owner packages 2026-07-17 15:53:21 +08:00
Nanaloveyuki e5a048090f split sync tests by behavior boundary 2026-07-17 15:53:21 +08:00
Nanaloveyuki e3097ba4fc ♻️ sync owner migration and facade thinning 2026-07-17 15:53:21 +08:00
Nanaloveyuki 5d9924026e 🐛 修复异常 Runtime Queue 2026-07-17 15:53:20 +08:00
Nanaloveyuki bdbb161865 添加 Logger build测试 2026-07-17 15:53:20 +08:00
Nanaloveyuki 83371bb4d5 📝 补充runtime文档 2026-07-17 15:53:20 +08:00
Nanaloveyuki 0e02f3d2cf 📝 Fix policy test and doc 2026-07-17 15:53:20 +08:00
Nanaloveyuki 46e87403bf 🔖 bump version to 0.6.1 2026-07-17 15:53:20 +08:00
Nanaloveyuki 3bdab60412 📝 Add 1.0.1(0.6.1) change notes for wide file rotation 2026-07-17 15:53:20 +08:00
Nanaloveyuki e001315319 Add Int64 file rotation path and JSON roundtrip 2026-07-17 15:53:20 +08:00
Nanaloveyuki 93e9bd966d 📝 添加Wiki并修改1.0.0文件名 2026-07-17 15:53:20 +08:00
Nanaloveyuki e8a2c76652 🩹 Fix the major version must be 0 2026-07-17 15:53:20 +08:00
Nanaloveyuki c8023b0ded 🔊 Update 1.0.0 2026-07-17 15:53:20 +08:00
Nanaloveyuki 7557e37cc8 ⬆️ Update Async and Mbt Version 2026-07-17 15:53:19 +08:00
Nanaloveyuki 637962a934 📝 add VitePress site and deployment workflow 2026-06-14 16:00:00 +08:00
Nanaloveyuki 265cd69ea9 📝 consolidate logger API and async lifecycle guidance 2026-06-14 14:00:00 +08:00
Nanaloveyuki 4f2ad097af consolidate async and native sink coverage 2026-06-14 10:00:00 +08:00
Nanaloveyuki 81385d5c0b consolidate async and native runtime updates 2026-06-14 09:00:00 +08:00
300 changed files with 16568 additions and 9739 deletions
+157 -33
View File
@@ -7,74 +7,198 @@ on:
pull_request:
jobs:
moonbit:
quality-matrix:
name: Quality (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup MoonBit (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: |
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> "$GITHUB_PATH"
- name: Setup MoonBit (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://cli.moonbitlang.com/install/powershell.ps1 | iex
"$HOME/.moon/bin" | Out-File -FilePath $env:GITHUB_PATH -Append
- name: Setup MSYS2 toolchain (Windows)
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-openssl
- name: Show tool versions
shell: bash
run: |
moon version --all
- name: Update Moon registry
shell: bash
run: |
moon update
- name: Check formatting and exported interfaces
shell: bash
run: |
moon fmt --check
moon info
git diff --exit-code
- name: Root checks and tests
shell: bash
run: |
moon check --deny-warn
moon test --deny-warn
moon check --target native --deny-warn
moon check --target wasm --deny-warn
moon check --target wasm-gc --deny-warn
moon check --target js --deny-warn
- name: Test wasm path
shell: bash
run: |
moon test --target wasm --deny-warn
- name: Run sync example
shell: bash
run: |
moon run examples/basic
- name: Check async example native
if: runner.os != 'Windows'
shell: bash
run: |
moon check examples/async_basic --target native --deny-warn
async-cross-targets:
name: Async cross-targets (Ubuntu)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install MoonBit
- name: Setup MoonBit
shell: bash
run: |
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> "$GITHUB_PATH"
- name: Show tool versions
shell: bash
run: |
moon version
moon version --all
- name: Update Moon registry
shell: bash
run: |
moon update
- name: Check bitlogger
- name: Check async package on wasm, wasm-gc and js
shell: bash
run: |
moon check
moon check src-async --target wasm --deny-warn
moon check src-async --target wasm-gc --deny-warn
moon check src-async --target js --deny-warn
- name: Test bitlogger
- name: Test async package on wasm, wasm-gc and js
shell: bash
run: |
moon test
moon test src-async --target wasm --deny-warn
moon test src-async --target wasm-gc --deny-warn
moon test src-async --target js --deny-warn
- name: Check bitlogger native
run: |
moon check --target native
async-native:
name: Async native (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
- name: Check bitlogger wasm-gc
run: |
moon check --target wasm-gc
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check bitlogger js
- name: Setup MoonBit
shell: bash
run: |
moon check --target js
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> "$GITHUB_PATH"
- name: Check bitlogger_async native
- name: Show tool versions
shell: bash
run: |
moon check src-async --target native
moon version --all
- name: Check bitlogger_async wasm-gc
- name: Update Moon registry
shell: bash
run: |
moon check src-async --target wasm-gc
moon update
- name: Check bitlogger_async js
- name: Check async native path
shell: bash
run: |
moon check src-async --target js
moon check src-async --target native --deny-warn
- name: Test bitlogger_async native
- name: Test async native path
shell: bash
run: |
moon test src-async --target native
moon test src-async --target native --deny-warn
- name: Test bitlogger_async wasm-gc
run: |
moon test src-async --target wasm-gc
coverage:
name: Coverage (Ubuntu)
runs-on: ubuntu-latest
- name: Test bitlogger_async js
run: |
moon test src-async --target js
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run basic example
- name: Setup MoonBit
shell: bash
run: |
moon run examples/basic
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> "$GITHUB_PATH"
- name: Check async example native
- name: Show tool versions
shell: bash
run: |
moon check examples/async_basic --target native
moon version --all
- name: Update Moon registry
shell: bash
run: |
moon update
- name: Root coverage (default target)
shell: bash
run: |
moon coverage clean
moon test --deny-warn --enable-coverage
moon coverage report -f summary
moon coverage analyze
- name: Root coverage (native target)
shell: bash
run: |
moon coverage clean
moon test --deny-warn --target native --enable-coverage
moon coverage report -f summary
moon coverage analyze
+3
View File
@@ -26,5 +26,8 @@ docs/.vitepress/cache/
docs/.vitepress/dist/
docs/.vitepress/generated/
# Benchmark package has no public API; moon info emits an empty snapshot.
benchmarks/pkg.generated.mbti
# vibecoding
AGENTS/
+26
View File
@@ -0,0 +1,26 @@
# Agent Notes
## Version Releases
Do not update package versions and installation snippets by hand. Run the
repository release helper from the root instead:
```powershell
.\scripts\update-version.ps1 <x.x.x>
```
For a normal patch release, the script updates `moon.mod`, all current
installation examples, creates the next `docs/changes/<release>(<version>).md`,
and adds it to `docs/changes/index.md`. Review and replace any generated `TODO`
entries in the release note before committing.
For a major or minor package-version change, supply the public release number
explicitly so it is not inferred:
```powershell
.\scripts\update-version.ps1 <x.x.x> -ReleaseVersion <x.x.x>
```
Use `-WhatIf` to preview the target files. Pass `-Change`, `-Verification`,
and `-Note` to populate release-note sections directly. Validate the edited
repository before committing with a `gitmoji + short desc` message.
+46 -30
View File
@@ -4,35 +4,58 @@ BitLogger 是一个使用 MoonBit 编写的结构化日志库,适合命令行
- [Mooncake 文档页](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
- [English README](./docs/README-en.md)
- [Wiki](https://bitlogger.naloveyuki.top)
## 介绍
## 从第一个日志到扩展能力
BitLogger 提供统一的日志级别、目标名、结构化字段和可定制格式,既可以直接输出到控制台,也可以在 native 环境写入文件,并提供异步日志版本
BitLogger 的文档按使用路径组织:先完成一个可运行的日志输出,再按需进入文件、配置、异步和组合能力。API 文档保留为精确参考,不需要从 API 文件名开始阅读
## 快速开始
### 1. 安装
```moonbit
let logger = build_logger(
text_console(
min_level=Level::Info,
target="demo",
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
),
)
从一个空项目开始:
logger.info("starting", fields=[field("port", "8080")])
ignore(logger.flush())
```bash
moon new log-demo
cd log-demo
moon add Nanaloveyuki/BitLogger@0.7.3
```
推荐从 `console(...)``json_console(...)``text_console(...)``file(...)` 这几个入口开始,再按需配合 `with_queue(...)``with_file_rotation(...)`
### 2. 写入第一条结构化日志
如果你需要自己组合 sink,比如 `fanout``split``callback`,再使用 `Logger::new(...)`
在应用 package 的 `moon.pkg` 中导入库:
```moonbit
import {
"Nanaloveyuki/BitLogger/src" @log,
}
```
然后在 `main.mbt` 中创建控制台 logger
```moonbit
fn main {
let logger = @log.build_logger(
@log.console(min_level=@log.Level::Info, target="app"),
)
logger.info("server started", fields=[
@log.field("port", "8080"),
@log.field("environment", "development"),
])
}
```
运行 `moon run` 后,记录会携带 level、target、message 和 fields。下一步按实际需求选择:
- [控制台与结构化字段](./docs/examples/console.md)
- [文件输出与轮转](./docs/examples/file-rotation.md)
- [JSON 配置构建](./docs/examples/config.md)
- [异步日志生命周期](./docs/examples/async.md)
## 支持情况
- `BitLogger` 当前在 CI 中检查/验证的目标是 `native``js``wasm-gc`
- `bitlogger_async` 当前在 CI 中检查 `native``js``wasm-gc`,测试覆盖 `native``js``wasm-gc`
- `wasm` 目标在源码 `moon.pkg` 中保留声明,但当前未纳入 CI 验证口径
- `BitLogger` 当前在 CI 中检查/验证的目标是 `native``wasm``js``wasm-gc`
- `bitlogger_async` 当前在 CI 中检查和测试 `native``wasm``js``wasm-gc`
- `llvm` 目前按实验性目标处理,当前环境未完成验证
- 文件输出是 native 能力;跨端代码里建议先判断 `native_files_supported()`
- `src-async` 可用,但示例 `examples/async_basic` 目前仍按 native 入口提供
@@ -45,20 +68,13 @@ ignore(logger.flush())
- 配置驱动构建:`build_logger(...)``build_async_logger(...)`
- 组合能力:queue、filter、patch、fanout、split、callback
- 异步日志:独立 `src-async` package
## 示例
- `examples/console_basic/`:最小 console / json console 示例
- `examples/text_formatter/`:文本格式与模板示例
- `examples/style_tags/`style tag 与彩色输出示例
- `examples/config_build/`:配置构建示例
- `examples/presets/`:常用预设组合示例
- `examples/file_rotation/`:文件输出与轮转示例,仅适用于 native
- `examples/async_basic/`:异步日志示例
- Trace context:标准 `trace_id``span_id``trace_flags``trace_state` 字段绑定
## 文档
- [API 索引](./docs/api/index.md)
- [Examples:完整使用流程](./docs/examples/index.md)
- [Extend:队列、组合、格式化和跨端边界](./docs/extend/index.md)
- [API 索引:按公开符号查询](./docs/api/index.md)
- [src package README](./src/README.mbt.md)
常用入口:`text_console(...)``file(...)``with_queue(...)``build_logger(...)``build_async_logger(...)`
仓库内的 `examples/` 目录与 Examples 文档一一对应;文档解释选择、前提、运行命令和后续扩展,源码保持为可执行参考。
+11
View File
@@ -0,0 +1,11 @@
# BitLogger Benchmarks
Run the native baseline from the repository root:
```powershell
moon bench benchmarks --target native --release --deny-warn
```
The suite measures callback emission, trace-context field binding, synchronous queue enqueue-and-flush, and native file writes. It intentionally has no pass/fail threshold: compare results only on the same machine, toolchain, target, and power profile.
Async throughput remains lifecycle-sensitive and is exercised by the native integration test rather than mixed into these synchronous microbenchmarks. Record its end-to-end measurements separately when profiling an application workload.
+61
View File
@@ -0,0 +1,61 @@
///|
let benchmark_record : @log.Record = @log.Record::new(
@log.Level::Info,
"benchmark record",
target="bench",
fields=[@log.field("component", "bitlogger")],
)
///|
test "bench callback logger emission" (it : @bench.T) {
let writes : Ref[Int] = Ref(0)
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 }))
it.bench(fn() {
logger.info("benchmark record", fields=[
@log.field("component", "bitlogger"),
])
it.keep(writes.val)
})
}
///|
test "bench trace context field binding" (it : @bench.T) {
let writes : Ref[Int] = Ref(0)
let context = @log.trace_context("trace-bench", "span-bench")
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 })).with_trace_context(
context,
)
it.bench(fn() {
logger.info("benchmark record", fields=[@log.field("event", "request")])
it.keep(writes.val)
})
}
///|
test "bench queued logger enqueue and flush" (it : @bench.T) {
let writes : Ref[Int] = Ref(0)
let logger = @log.Logger::new(@log.callback_sink(fn(_) { writes.val += 1 })).with_queue(
max_pending=8,
)
it.bench(fn() {
logger.info("benchmark record")
it.keep(logger.sink.flush())
})
}
///|
test "bench native file sink write" (it : @bench.T) {
let sink = @log.file_sink(
"logs/bitlogger-benchmark.log",
append=false,
auto_flush=false,
formatter=fn(rec) { rec.message },
)
if sink.is_available() {
it.bench(fn() {
sink.write(benchmark_record)
it.keep(sink.write_failures())
})
ignore(sink.close())
}
}
+4
View File
@@ -0,0 +1,4 @@
import {
"Nanaloveyuki/BitLogger/src" @log,
"moonbitlang/core/bench",
}
+124 -22
View File
@@ -104,6 +104,119 @@ function buildChangesSidebar(): DefaultTheme.SidebarItem[] {
]
}
function buildExamplesSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: 'Overview', link: '/examples/' },
{ text: 'Console And Fields', link: '/examples/console' },
{ text: 'File Rotation', link: '/examples/file-rotation' },
{ text: 'Configuration', link: '/examples/config' },
{ text: 'Async Lifecycle', link: '/examples/async' },
]
}
function buildExtendSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: 'Overview', link: '/extend/' },
{ text: 'Queueing', link: '/extend/queue' },
{ text: 'Sink Composition', link: '/extend/composition' },
{ text: 'Text Formatting', link: '/extend/formatting' },
{ text: 'Target Boundaries', link: '/extend/targets' },
{ text: 'Trace Context', link: '/extend/observability' },
]
}
function buildChineseExamplesSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: '概览', link: '/zh/examples/' },
{ text: '控制台与结构化字段', link: '/zh/examples/console' },
{ text: '文件输出与轮转', link: '/zh/examples/file-rotation' },
{ text: '配置驱动构建', link: '/zh/examples/config' },
{ text: '异步日志生命周期', link: '/zh/examples/async' },
]
}
function buildChineseExtendSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: '概览', link: '/zh/extend/' },
{ text: '队列与溢出策略', link: '/zh/extend/queue' },
{ text: 'Sink 组合', link: '/zh/extend/composition' },
{ text: '文本格式与样式', link: '/zh/extend/formatting' },
{ text: '目标平台边界', link: '/zh/extend/targets' },
{ text: 'Trace Context', link: '/zh/extend/observability' },
]
}
function buildChineseApiSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: '概览', link: '/zh/api/' },
{ text: '基础记录', link: '/zh/api/basics' },
{ text: '配置与队列', link: '/zh/api/configuration' },
{ text: '文件输出', link: '/zh/api/file-output' },
{ text: '文本格式', link: '/zh/api/formatting' },
{ text: '异步生命周期', link: '/zh/api/async' },
{ text: 'Sink 组合', link: '/zh/api/composition' },
]
}
const englishThemeConfig: DefaultTheme.Config = {
siteTitle: 'BitLogger',
nav: [
{ text: 'Home', link: '/' },
{ text: 'Examples', link: '/examples/' },
{ text: 'Extend', link: '/extend/' },
{ text: 'API', link: '/api/' },
{ text: 'Changes', link: '/changes/' },
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
],
search: {
provider: 'local',
},
socialLinks: [{ icon: 'github', link: repository }],
editLink: {
pattern: `${repository}/edit/main/docs/:path`,
text: 'Edit this page on GitHub',
},
sidebar: {
'/examples/': buildExamplesSidebar(),
'/extend/': buildExtendSidebar(),
'/api/': buildApiSidebar(),
'/changes/': buildChangesSidebar(),
},
footer: {
message: 'Published from the repository docs folder with VitePress.',
copyright: 'MIT',
},
}
const chineseThemeConfig: DefaultTheme.Config = {
siteTitle: 'BitLogger',
nav: [
{ text: '首页', link: '/zh/' },
{ text: '示例', link: '/zh/examples/' },
{ text: '扩展', link: '/zh/extend/' },
{ text: 'API', link: '/zh/api/' },
{ text: '更新记录(英文)', link: '/changes/' },
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
],
search: {
provider: 'local',
},
socialLinks: [{ icon: 'github', link: repository }],
editLink: {
pattern: `${repository}/edit/main/docs/:path`,
text: '在 GitHub 上编辑此页',
},
sidebar: {
'/zh/examples/': buildChineseExamplesSidebar(),
'/zh/extend/': buildChineseExtendSidebar(),
'/zh/api/': buildChineseApiSidebar(),
},
footer: {
message: '由仓库中的 VitePress 文档构建。',
copyright: 'MIT',
},
}
export default defineConfig({
title: 'BitLogger',
description: 'Structured logging library docs for MoonBit.',
@@ -114,29 +227,18 @@ export default defineConfig({
markdown: {
languages: [createMoonbitLanguageRegistration()],
},
themeConfig: {
siteTitle: 'BitLogger',
nav: [
{ text: 'Home', link: '/' },
{ text: 'API', link: '/api/' },
{ text: 'Changes', link: '/changes/' },
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
],
search: {
provider: 'local',
themeConfig: englishThemeConfig,
locales: {
root: {
label: 'English',
lang: 'en-US',
themeConfig: englishThemeConfig,
},
socialLinks: [{ icon: 'github', link: repository }],
editLink: {
pattern: `${repository}/edit/main/docs/:path`,
text: 'Edit this page on GitHub',
},
sidebar: {
'/api/': buildApiSidebar(),
'/changes/': buildChangesSidebar(),
},
footer: {
message: 'Published from the repository docs folder with VitePress.',
copyright: 'MIT',
zh: {
label: '简体中文',
lang: 'zh-CN',
link: '/zh/',
themeConfig: chineseThemeConfig,
},
},
})
+36 -1
View File
@@ -6,11 +6,46 @@ import HomeDocHub from './components/HomeDocHub.vue'
import './custom.css'
const localePreferenceKey = 'bitlogger-docs-locale'
function routePath(path: string): string {
return path.split(/[?#]/, 1)[0]
}
function isLocaleRoot(path: string): boolean {
const normalized = routePath(path)
return normalized === '/' || normalized === '/zh/'
}
function browserPrefersChinese(): boolean {
return navigator.languages.some(language => language.toLowerCase().startsWith('zh'))
}
const theme: Theme = {
extends: DefaultTheme,
enhanceApp({ app }) {
enhanceApp({ app, router }) {
app.component('ApiOverview', ApiOverview)
app.component('HomeDocHub', HomeDocHub)
if (typeof window === 'undefined') return
router.onAfterRouteChange = to => {
if (!isLocaleRoot(to)) return
window.localStorage.setItem(localePreferenceKey, routePath(to) === '/zh/' ? 'zh' : 'en')
}
if (router.route.path !== '/') return
const savedLocale = window.localStorage.getItem(localePreferenceKey)
const locale = savedLocale === 'zh' || savedLocale === 'en'
? savedLocale
: browserPrefersChinese()
? 'zh'
: 'en'
if (locale === 'zh') {
void router.go('/zh/')
}
},
}
+31 -24
View File
@@ -9,28 +9,42 @@ BitLogger is a structured logging library for MoonBit projects.
BitLogger gives you consistent levels, targets, structured fields, configurable text output, native file logging, and an async logging layer.
## Quick Start
## Start With A Working Flow
```moonbit
let logger = build_logger(
text_console(
min_level=Level::Info,
target="demo",
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
),
)
The documentation is organized around complete usage flows. Start with an executable logger, then move into file output, configuration, async lifecycle, and extensions. The API reference remains the precise symbol-level reference.
logger.info("starting", fields=[field("port", "8080")])
ignore(logger.flush())
### 1. Install
```bash
moon new log-demo
cd log-demo
moon add Nanaloveyuki/BitLogger@0.7.3
```
Start with `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)`, then add `with_queue(...)` or `with_file_rotation(...)` only when needed.
### 2. Import And Log
Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
Add the package import to your application's `moon.pkg`:
```moonbit
import {
"Nanaloveyuki/BitLogger/src" @log,
}
```
```moonbit
fn main {
let logger = @log.build_logger(
@log.console(min_level=@log.Level::Info, target="app"),
)
logger.info("server started", fields=[@log.field("port", "8080")])
}
```
Continue with the [Examples guide](./examples/index.md): console fields, file rotation, JSON configuration, and async lifecycle are each documented as a complete path.
## Support Status
- Current local verification covers `native`, `js`, `wasm`, and `wasm-gc` for the main `src` package and `src-async`
- CI checks and tests `native`, `wasm`, `js`, and `wasm-gc` for the main `src` package and `src-async`
- `llvm` is still treated as experimental in the current release context and was not locally re-verified in the current environment
- The source packages still declare `wasm` support alongside `native`, `llvm`, `js`, and `wasm-gc`; see [`target-verification.md`](./api/target-verification.md) for the current release-facing verification boundary
- File output is a native capability; check `native_files_supported()` in cross-target code
@@ -44,19 +58,12 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
- Config-based builders: `build_logger(...)` and `build_async_logger(...)`
- Composition helpers: queue, filter, patch, fanout, split, callback
- Separate async package under `src-async`
## Examples
- `examples/console_basic/`: minimal console and JSON console example
- `examples/text_formatter/`: text formatting and template example
- `examples/style_tags/`: style tags and colored output example
- `examples/config_build/`: config-based build example
- `examples/presets/`: common preset combinations
- `examples/file_rotation/`: native file logging and rotation example
- `examples/async_basic/`: async logging example
- Trace-context binding through standard `trace_id`, `span_id`, `trace_flags`, and `trace_state` fields
## Documentation
- [Examples](./examples/index.md): complete application-facing usage flows
- [Extend](./extend/index.md): queueing, sink composition, formatting, and target boundaries
- [API index](./api/index.md): canonical API reference, organized as one public API per file
- [src package README](https://github.com/Nanaloveyuki/BitLogger/blob/main/src/README.mbt.md): package-level usage notes and target reminders
- [`docs/changes/`](./changes/): versioned release notes and publish-facing change summaries
@@ -13,14 +13,14 @@ key-word:
## 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.
Convert `AsyncLoggerBuildConfig` into a `Json`. 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 {}
) -> Json {}
```
#### input
@@ -29,7 +29,7 @@ pub fn async_logger_build_config_to_json(
#### output
- `JsonValue` - Structured JSON representation of the full async build config.
- `Json` - Structured JSON representation of the full async build config.
### Explanation
+5 -4
View File
@@ -2,7 +2,7 @@
name: async-logger-close
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Close the async logger queue immediately and optionally convert current pending backlog into dropped records before queue closure.
key-word:
- async
@@ -35,13 +35,14 @@ pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Un
Detailed rules explaining key parameters and behaviors
- `close(...)` marks the logger as closed immediately.
- If a worker is still active when `close(...)` runs, the lifecycle phase moves into the closing path first and only settles into a terminal closed phase after worker exit.
- `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.
- `close(...)` also does not change `has_failed()` or `last_error()`; it is a closure primitive, not a failure reset API.
- Because this is a low-level close primitive, it does not first run `wait_idle()` or apply the runtime-dependent fallback logic used by `shutdown()`.
- After closure, later log attempts do not add new pending or dropped counts, but backend behavior can still differ before the closed queue rejects the record.
- In the current direct coverage, compatibility runtimes short-circuit before patch-path work on late log attempts, while native-worker runtimes may still build and patch the record before the closed queue rejects it.
- After closure, later log attempts are rejected in the shared async log path before record build, patch, or filter work starts.
- That means post-close late logs no longer add pending or dropped counts, and they no longer trigger patch/filter side effects on one backend but not another.
### How to Use
@@ -72,7 +73,7 @@ e.g.:
- If `clear=false`, pending records may still exist after closure until worker drain or later cleanup resolves them.
- If callers perform late log attempts after closure, backlog counters still stay unchanged, but patch-path side effects are runtime-dependent and should not be treated as a portable post-close contract.
- If callers perform late log attempts after closure, backlog counters still stay unchanged and patch/filter side effects stay skipped.
- If callers need graceful waiting for drain completion, `shutdown()` is usually the better API.
+3 -3
View File
@@ -13,12 +13,12 @@ key-word:
## 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.
Convert a typed `AsyncLoggerConfig` into a `Json`. 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 {}
pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> Json {}
```
#### input
@@ -27,7 +27,7 @@ pub fn async_logger_config_to_json(config : AsyncLoggerConfig) -> @json_parser.J
#### output
- `JsonValue` - Structured JSON representation of the async config.
- `Json` - Structured JSON representation of the async config.
### Explanation
+6 -4
View File
@@ -2,8 +2,8 @@
name: async-logger-flush-policy
group: api
category: async
update-time: 20260614
description: Read the async logger flush policy currently governing batch-end and shutdown-end flushing behavior.
update-time: 20260707
description: Read the async logger flush policy currently governing batch-end and shutdown-end flush-callback timing.
key-word:
- async
- logger
@@ -37,8 +37,10 @@ Detailed rules explaining key parameters and behaviors
- `Batch` means the worker invokes the stored async flush callback after each completed drained batch, not after every individual record write.
- `Shutdown` means the worker invokes the stored async flush callback once after the worker loop exits.
- `Never` leaves that explicit callback path unused and relies entirely on sink behavior or external control.
- This helper reports callback timing policy, not a guarantee that a particular sink type has a meaningful built-in flush effect on every constructor path.
- In particular, text-specific async builder paths keep the default no-op flush callback even when the visible policy is `Batch` or `Shutdown`, so the reported policy can describe when the callback would run without implying extra sink work actually happens on that path.
- This helper reports callback timing policy, not a progress count contract.
- The callback is now treated as an attempted flush action with optional failure, not as a meaningful returned item count.
- Builder routes that wrap `RuntimeSink` align with the sync/runtime facade by calling `RuntimeSink::flush_progress()` inside that callback and discarding the returned progress details.
- Text-specific async builder paths still keep the default no-op flush callback even when the visible policy is `Batch` or `Shutdown`, so the reported policy can describe when the callback would run without implying extra sink work actually happens on that path.
### How to Use
+4 -4
View File
@@ -2,7 +2,7 @@
name: async-logger-has-failed
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Read whether the async logger worker has recorded a runtime failure after run() startup reset and drain execution.
key-word:
- async
@@ -35,7 +35,7 @@ Detailed rules explaining key parameters and behaviors
- `run()` clears previous failure state at startup.
- `run()` also clears the stored `last_error()` string at startup before drain work begins.
- If the worker loop raises an error, the logger records that failure and exposes it through this flag.
- If the worker loop raises an error, the logger transitions its lifecycle phase to `failed` or `closed_failed` and exposes that failure through this flag.
- Once set by a failed run, the flag stays `true` until a later `run()` invocation actually starts and resets it.
- Failure-driven shutdown does not clear this flag by itself, so `has_failed()` can remain `true` even after the logger is already closed.
- This helper is intentionally compact and should usually be paired with `last_error()` for details.
@@ -71,9 +71,9 @@ e.g.:
- If `has_failed()` is `false`, queue pressure or dropped records may still exist for non-failure reasons.
- If `has_failed()` becomes `true`, `wait_idle()` may stop early while pending records still remain until a later close or clear path handles them.
- In the current regression coverage, that later handling can be an explicit `close(clear=true)` that resets pending backlog immediately or a runtime-dependent `shutdown(...)` path that either clears pending into dropped records or leaves the closed-queue remainder visible.
- In the current regression coverage, that later handling can be an explicit `close(clear=true)` that resets pending backlog immediately or a `shutdown(...)` path that clears retained pending backlog into dropped records before returning.
- If `has_failed()` is still `true` after shutdown, that does not by itself mean cleanup failed; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
- If `has_failed()` is still `true` after shutdown, that does not by itself mean cleanup failed; the logger may already be `phase=closed_failed` and `terminal=true` while the retained failure record is still intentionally visible.
- If `has_failed()` is `true`, callers should inspect `last_error()` or `state()` for more context.
+5 -5
View File
@@ -2,7 +2,7 @@
name: async-logger-is-closed
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Read whether the async logger has entered closed lifecycle state and should no longer be treated as a normal active enqueue target.
key-word:
- async
@@ -36,11 +36,11 @@ 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 is only a direct read of the current `is_closed` ref; it does not wait for drain completion or clear any other state.
- This helper is derived from the current lifecycle phase rather than from an independent closed flag ref.
- This helper reflects lifecycle state only and does not indicate whether the worker is still draining.
- `is_closed()` becoming `true` does not imply the logger reached a clean success state. Failure flags, retained `last_error()`, and remaining backlog-related counters can still reflect the earlier worker outcome.
- After shutdown on a worker-failure path, `is_closed()` can therefore be `true` while backlog cleanup remains runtime-dependent: native-worker runtimes can convert leftover pending records into dropped ones, while compatibility runtimes can still report the remaining queue entries as pending.
- Exact post-close logging behavior is runtime-dependent: compatibility runtimes can short-circuit before patch and enqueue work, while native-worker runtimes may still build and patch a record before the closed queue rejects it, so `is_closed()` should be read as a lifecycle signal rather than a full enqueue-policy contract.
- After shutdown on a worker-failure path, `is_closed()` can therefore be `true` while retained failure state still remains visible under `phase=closed_failed`.
- Late log attempts are rejected in the shared async log path after closure, so `is_closed()` now aligns with a stable post-close enqueue rejection contract.
### How to Use
@@ -74,7 +74,7 @@ e.g.:
- If callers need to know whether the worker is still active, they should also inspect `is_running()`.
- If callers need to know whether closure also prevented later log attempts on the current backend, they must interpret this together with the active runtime behavior rather than this flag alone.
- If callers need to know whether the logger is also terminal or rerunnable, they should inspect `state().terminal` or `state().can_rerun`.
- Closing a logger does not by itself reset `has_failed()` or `last_error()`.
+4 -4
View File
@@ -2,7 +2,7 @@
name: async-logger-is-running
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Read whether the async logger worker loop is currently running, regardless of queue backlog or recorded failure state.
key-word:
- async
@@ -36,8 +36,8 @@ 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.
- After a worker failure, the logger can already be `is_running=false` while still retaining `has_failed=true`, the recorded `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
- This helper is only a direct read of the current `is_running` ref; it does not wait, synchronize, or infer why the value is what it is.
- After a worker failure, the logger can already be `is_running=false` while still retaining `has_failed=true`, the recorded `last_error()`, and leftover dropped-count cleanup.
- This helper is derived from the current lifecycle phase rather than from an independent running flag ref; it does not wait, synchronize, or infer why the value is what it is.
- This helper focuses on worker activity rather than queue size or failure details.
- `is_running()` can be `false` even when `pending_count()` is still nonzero, for example if the worker was never started or if it exited after a recorded failure.
- A later `run()` attempt can flip `is_running()` back to `true` before that retained failure/backlog state has been fully drained, because the restarted worker clears stale failure state only once the new run has actually started.
@@ -71,7 +71,7 @@ In this example, callers watch the worker lifecycle directly.
e.g.:
- If `is_running()` is `false`, pending records may still exist if the worker was never started or has already failed.
- If `is_running()` is `false`, the logger may also already be closed while still retaining the previous failure record and a runtime-dependent pending-versus-dropped cleanup result from shutdown.
- If `is_running()` is `false`, the logger may also already be closed while still retaining the previous failure record and the cleanup result from shutdown.
- If callers need a one-shot lifecycle flow, `shutdown()` is usually better than manual polling.
+2 -2
View File
@@ -2,7 +2,7 @@
name: async-logger-last-error
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Read the last error string recorded by the async logger worker after run() resets and runtime failure capture.
key-word:
- async
@@ -76,7 +76,7 @@ e.g.:
- `close()` or `shutdown()` do not clear a previously recorded error string by themselves; the reset happens only after a later `run()` has already started.
- If the same error string is still present after shutdown, that does not by itself mean cleanup was skipped; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
- If the same error string is still present after shutdown, that does not by itself mean cleanup was skipped; the logger may already be `phase=closed_failed` and `terminal=true` while the retained failure record is still intentionally visible.
### Notes
+10 -6
View File
@@ -2,7 +2,7 @@
name: async-logger-run
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Start the async logger worker loop so queued records are drained to the underlying sink while lifecycle and failure state are reset and updated around execution.
key-word:
- async
@@ -34,13 +34,15 @@ pub async fn[S : @bitlogger.Sink] AsyncLogger::run(self : AsyncLogger[S]) -> Uni
Detailed rules explaining key parameters and behaviors
- `run()` sets `is_running` to `true` before worker execution begins.
- `run()` moves the logger lifecycle phase from `ready` or `failed` into `running`.
- `run()` now enforces a single-worker contract. If the same logger already has a running worker, the later call raises `AsyncLoggerAlreadyRunning` instead of silently starting another drain loop.
- If the logger is already closed, `run()` raises `AsyncLoggerClosed` instead of trying to restart a terminal logger.
- Every invocation clears previous failure state first by setting `has_failed=false` and `last_error()` to an empty string once that `run()` call has actually started executing.
- The method then keeps draining records until `queue.get()` stops with `AsyncLoggerClosed` or a worker error escapes.
- On a normal queue-close exit, `run()` clears `is_running` and returns normally.
- On failure, the logger records `has_failed=true`, stores the error text in `last_error`, clears `is_running`, and then raises the error back out of `run()`.
- On a normal queue-close exit, `run()` leaves the logger in `closed` if shutdown/close had already started, otherwise it returns to `ready`.
- On failure, the logger records `last_error`, transitions to `failed` or `closed_failed`, and then raises the error back out of `run()`.
- A worker failure does not guarantee the async backlog was fully drained first. If the failure happens after some records were already written, later queued records can remain pending when `run()` exits.
- A later `run()` invocation can resume draining that retained backlog, but the stale failure flag and stale `last_error()` value are only cleared after the new worker call actually begins running.
- This helper does not enforce a single-worker guard by itself, so the public contract should be treated as application-controlled worker startup rather than an API that deduplicates repeated `run()` calls.
### How to Use
@@ -80,7 +82,9 @@ e.g.:
- A later `run()` attempt starts from a fresh failure flag and empty `last_error()` string once that retrying worker has actually started, even if an earlier run failed.
- Starting more than one `run()` task for the same logger is not prevented by this method and can produce application-level worker coordination bugs.
- Starting more than one `run()` task for the same logger raises `AsyncLoggerAlreadyRunning` on the later attempt.
- Starting `run()` after a terminal close path raises `AsyncLoggerClosed`.
### Notes
@@ -90,4 +94,4 @@ e.g.:
3. Pair it with `has_failed()`, `last_error()`, or `state()` when tests need to inspect how a worker exit affected logger health.
4. Start one deliberate worker task per logger unless your own code is intentionally coordinating a different pattern.
4. Start one deliberate worker task per logger. A concurrent second startup now fails explicitly with `AsyncLoggerAlreadyRunning`.
+7 -14
View File
@@ -2,8 +2,8 @@
name: async-logger-shutdown
group: api
category: async
update-time: 20260614
description: Gracefully stop an async logger by waiting for idle or clearing queued work, with worker-wait behavior depending on the active async runtime.
update-time: 20260707
description: Gracefully stop an async logger by waiting for idle or clearing queued work, then waiting for any active worker to finish.
key-word:
- async
- logger
@@ -35,12 +35,10 @@ pub async fn[S] AsyncLogger::shutdown(self : AsyncLogger[S], clear? : Bool = fal
Detailed rules explaining key parameters and behaviors
- `clear=false` first waits for idle, then closes the logger.
- In runtimes where shutdown clearing after idle is enabled, remaining backlog after `wait_idle()` triggers a fallback `close(clear=true)`.
- If `wait_idle()` returned early because a worker failure left backlog behind, shutdown now converts that retained pending backlog into dropped records with `close(clear=true)` before returning.
- `clear=true` immediately closes and abandons pending records, even if no worker was ever started for that logger.
- In runtimes where shutdown waits for workers, the method then waits until `is_running()` becomes `false` before returning.
- In the current backend split, native-worker runtimes enable both the post-`wait_idle()` clear fallback and the final wait-for-worker phase, while compatibility runtimes skip both extra steps.
- That means a failure-short-circuited `wait_idle()` can still be followed by forced pending-to-dropped cleanup on native-worker runtimes, while compatibility runtimes close without that extra forced clear step.
- In the current tested failure path, native-worker shutdown turns the leftover pending item into one more dropped record, while compatibility shutdown leaves that leftover closed-queue count in `pending_count()` instead.
- After either shutdown branch starts closing, the helper waits until `is_running()` becomes `false` before returning.
- In lifecycle-phase terms, shutdown drives the logger into `closing` while a worker is still active, then settles into `closed` or `closed_failed` once that worker has exited.
- Shutdown itself does not clear retained worker failure state. If a previous `run()` already recorded `has_failed=true` and a non-empty `last_error()`, those diagnostics can remain visible after shutdown completes.
- Because `clear=false` delegates to `wait_idle()` first, shutdown can also wait indefinitely when pending records exist but no worker is making progress and no failure flag is raised.
@@ -71,15 +69,10 @@ In this example, pending work is abandoned intentionally so shutdown can complet
e.g.:
- If `clear=true`, pending records are intentionally dropped rather than drained.
- If `wait_idle()` returns early because the worker failed, shutdown behavior after that point still depends on the active runtime's fallback and worker-wait rules.
- After a worker failure, native-worker shutdown may convert the remaining backlog into dropped records, while compatibility shutdown can leave the pending counter reflecting that leftover closed queue state.
- In the current direct regression coverage, that split appears as `pending_count() == 0` and `dropped_count()` increasing on native-worker runtimes, versus `pending_count() > 0` and no extra dropped cleanup on compatibility runtimes.
- If `wait_idle()` returns early because the worker failed, shutdown still finishes by converting retained pending backlog into dropped records before it returns.
- Even after shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` from an earlier worker failure.
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
- If pending work exists but no worker was started, `shutdown(clear=false)` may never reach its later close step because it is still waiting inside `wait_idle()`.
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
@@ -88,7 +81,7 @@ e.g.:
1. Prefer this API over raw `close()` in normal application shutdown paths.
2. Exact post-close waiting behavior depends on the active async runtime mode.
2. `shutdown()` now always waits for an already-running worker to leave `is_running=true` before returning.
3. Choose `clear=true` only when loss of queued records is acceptable.
+9 -16
View File
@@ -2,8 +2,8 @@
name: async-logger-state-new
group: api
category: async
update-time: 20260614
description: Construct an AsyncLoggerState snapshot from explicit runtime, queue, lifecycle, failure, and flush-policy values without probing a live logger.
update-time: 20260707
description: Construct an AsyncLoggerState snapshot from explicit runtime, lifecycle phase, queue, failure text, and flush-policy values without probing a live logger.
key-word:
- async
- logger
@@ -20,11 +20,9 @@ Construct an `AsyncLoggerState` snapshot from explicit runtime, queue, lifecycle
```moonbit
pub fn AsyncLoggerState::new(
runtime : AsyncRuntimeState,
phase : AsyncLifecyclePhase,
pending_count : Int,
dropped_count : Int,
is_closed : Bool,
is_running : Bool,
has_failed : Bool,
last_error : String,
flush_policy : AsyncFlushPolicy,
) -> AsyncLoggerState {
@@ -33,11 +31,9 @@ pub fn AsyncLoggerState::new(
#### input
- `runtime : AsyncRuntimeState` - Embedded backend-level runtime snapshot.
- `phase : AsyncLifecyclePhase` - Primary lifecycle conclusion for the snapshot.
- `pending_count : Int` - Current async queue backlog.
- `dropped_count : Int` - Current dropped-record count.
- `is_closed : Bool` - Whether the logger has been closed.
- `is_running : Bool` - Whether the logger worker loop is currently running.
- `has_failed : Bool` - Whether the logger has recorded a runtime failure state.
- `last_error : String` - Latest error text, or an empty string when no failure has been recorded.
- `flush_policy : AsyncFlushPolicy` - Active async flush policy for the logger.
@@ -49,11 +45,12 @@ pub fn AsyncLoggerState::new(
Detailed rules explaining key parameters and behaviors
- This constructor simply packages the supplied fields into one public snapshot value.
- This constructor packages the supplied runtime, phase, counters, last error, and flush policy into one public snapshot value.
- It does not inspect a live logger instance by itself.
- `AsyncLogger::state()` is the higher-level API that reads these values from a concrete logger.
- It also does not validate whether the supplied fields represent a combination that could come from one real logger instant.
- The supplied `runtime : AsyncRuntimeState` is stored exactly as provided; this constructor does not recompute `mode` or `background_worker` from the active backend.
- The constructor derives `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, and `terminal` from the supplied `phase` and `pending_count`.
- The constructed value matches the same public shape used by async logger serializers.
- Because `AsyncLoggerState` is only a data snapshot type, this constructor is mainly useful for tests, adapters, and synthetic diagnostics rather than ordinary logger inspection.
- Serialization helpers accept any `AsyncLoggerState` value, including hand-built ones from this constructor.
@@ -69,11 +66,9 @@ When tests or adapters should construct a full async logger state explicitly:
```moonbit
let state = AsyncLoggerState::new(
runtime=AsyncRuntimeState::new(AsyncRuntimeMode::Compatibility, false),
phase=AsyncLifecyclePhase::Running,
pending_count=0,
dropped_count=0,
is_closed=false,
is_running=true,
has_failed=false,
last_error="",
flush_policy=AsyncFlushPolicy::Never,
)
@@ -87,11 +82,9 @@ When code should prepare a typed logger state value for later export:
```moonbit
let state = AsyncLoggerState::new(
runtime=async_runtime_state(),
phase=logger.phase(),
pending_count=logger.pending_count(),
dropped_count=logger.dropped_count(),
is_closed=logger.is_closed(),
is_running=logger.is_running(),
has_failed=logger.has_failed(),
last_error=logger.last_error(),
flush_policy=logger.flush_policy(),
)
@@ -110,7 +103,7 @@ e.g.:
- If callers want the current backend-derived runtime pair instead of a synthetic one, they must pass `async_runtime_state()` explicitly or use `AsyncLogger::state()`.
- This constructor does not apply cleanup semantics such as clearing `last_error` on restart or draining pending records; callers must provide those fields exactly as they want them represented.
- This constructor does not apply cleanup semantics such as clearing `last_error` on restart or draining pending records; callers must provide the phase and counters exactly as they want them represented.
### Notes
+8 -8
View File
@@ -2,7 +2,7 @@
name: async-logger-state-to-json
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Convert an AsyncLoggerState snapshot into a JSON value for diagnostics and transport using the canonical nested runtime shape and flush-policy labels.
key-word:
- async
@@ -13,12 +13,12 @@ key-word:
## Async-logger-state-to-json
Convert `AsyncLoggerState` into a `JsonValue`. This helper is the structured export path for async logger runtime snapshots or manually constructed state values when callers want machine-readable diagnostics instead of a plain string.
Convert `AsyncLoggerState` into a `Json`. This helper is the structured export path for async logger runtime snapshots or manually constructed state values 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 {}
pub fn async_logger_state_to_json(state : AsyncLoggerState) -> Json {}
```
#### input
@@ -27,20 +27,20 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
#### output
- `JsonValue` - Structured JSON representation of the async logger snapshot.
- `Json` - 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.
- The top-level fields are `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
- The JSON includes runtime mode, lifecycle phase, derived lifecycle conclusions, queue counters, last error, and flush policy.
- The top-level fields are `runtime`, `phase`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, `terminal`, `last_error`, and `flush_policy`.
- The nested `runtime` field reuses `async_runtime_state_to_json(...)`, and `flush_policy` is serialized with the canonical labels `Never`, `Batch`, or `Shutdown`.
- The public helper returns the same internal JSON snapshot shape used by `stringify_async_logger_state(...)`, so both export paths stay aligned without duplicate field assembly logic.
- 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 or constructed before serialization.
- Serialization preserves whatever snapshot combination it receives, including failure flags together with remaining backlog counts.
- Serialization preserves whatever snapshot combination it receives, including lifecycle phase and failure/backlog combinations together with remaining backlog counts.
- This helper never rereads a logger instance by itself. If callers pass an older or manually constructed `AsyncLoggerState`, the JSON reflects that provided value exactly rather than refreshing fields from live runtime state.
- It also does not normalize mixed diagnostic combinations. If the provided snapshot says `is_closed=true` together with retained `has_failed=true`, `last_error`, or non-zero backlog counters, those exact combinations are emitted unchanged.
@@ -62,7 +62,7 @@ In this example, callers receive a structured value that can be composed into la
When another serializer or pipeline expects a JSON value:
```moonbit
let payload = async_logger_state_to_json(logger.state())
println(@json_parser.stringify(payload))
println(payload.stringify())
```
In this example, the helper stays useful even outside the built-in stringify wrapper.
+7 -6
View File
@@ -2,8 +2,8 @@
name: async-logger-state-type
group: api
category: async
update-time: 20260614
description: Public async logger state alias used for queue, lifecycle, runtime, and flush-policy diagnostics.
update-time: 20260707
description: Public async logger state alias used for queue, lifecycle phase, derived lifecycle conclusions, runtime, and flush-policy diagnostics.
key-word:
- async
- logger
@@ -23,7 +23,7 @@ pub type AsyncLoggerState = @utils.AsyncLoggerState
#### output
- `AsyncLoggerState` - Public async logger snapshot containing `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
- `AsyncLoggerState` - Public async logger snapshot containing `runtime`, `phase`, queue counters, derived lifecycle conclusions, `last_error`, and `flush_policy`.
### Explanation
@@ -31,13 +31,14 @@ Detailed rules explaining key parameters and behaviors
- This is a type alias, not a live logger handle.
- The `runtime` field embeds an `AsyncRuntimeState` snapshot.
- The remaining fields capture queue counts, lifecycle status, failure state, last error text, and active flush policy.
- The remaining fields capture queue counts, lifecycle phase, derived lifecycle conclusions, failure state, last error text, and active flush policy.
- `AsyncLogger::state()` returns this type directly, while `async_logger_state_to_json(...)` and `stringify_async_logger_state(...)` export the same data shape for diagnostics.
- `AsyncLogger::state()` currently builds this snapshot from `async_runtime_state()` plus the logger's current counters, lifecycle flags, last error, and flush policy.
- `AsyncLogger::state()` currently builds this snapshot from `async_runtime_state()` plus the logger's current counters, lifecycle phase, last error, and flush policy.
- When the value comes from `AsyncLogger::state()`, the fields are read one by one rather than through a transactional snapshot primitive.
- `AsyncLoggerState::new(...)` can also construct this type manually, but manual construction is synthetic snapshot data and does not read one live logger instant by itself.
- The type itself does not distinguish live logger snapshots from hand-built ones; callers must track whether a given `AsyncLoggerState` value came from `AsyncLogger::state()` or from manual construction.
- When a live snapshot is taken after worker failure, `has_failed=true`, a retained `last_error`, and non-zero `pending_count` may legitimately coexist until later cleanup or restart.
- `phase` is the strongest lifecycle conclusion in the snapshot, while `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, and `terminal` are the main derived convenience reads.
- When a live snapshot is taken after worker failure, `phase=failed`, a retained `last_error`, `backlog_retained=true`, and non-zero `pending_count` may legitimately coexist until later cleanup or restart.
### How to Use
+13 -9
View File
@@ -2,8 +2,8 @@
name: async-logger-state
group: api
category: async
update-time: 20260614
description: Read a full async logger runtime snapshot including the embedded runtime snapshot, queue counters, lifecycle flags, last error, and flush policy.
update-time: 20260707
description: Read a full async logger runtime snapshot including runtime, lifecycle phase, derived lifecycle conclusions, queue counters, last error, and flush policy.
key-word:
- async
- state
@@ -27,22 +27,26 @@ pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState {}
#### output
- `AsyncLoggerState` - A snapshot containing runtime mode, worker support, queue counts, lifecycle flags, last error, and flush policy.
- `AsyncLoggerState` - A snapshot containing runtime mode, lifecycle phase, derived lifecycle conclusions, queue counts, 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`.
- `AsyncLoggerState` includes `runtime`, `phase`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, `terminal`, `last_error`, and `flush_policy`.
- `state()` returns a value snapshot rather than a live handle.
- This helper is equivalent to `AsyncLoggerState::new(async_runtime_state(), self.pending_count(), self.dropped_count(), self.is_closed(), self.is_running(), self.has_failed(), self.last_error(), self.flush_policy())`.
- This helper is equivalent to `AsyncLoggerState::new(async_runtime_state(), self.phase(), self.pending_count(), self.dropped_count(), self.last_error(), self.flush_policy())`.
- `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()` from the moment `state()` is called, so callers do not need to join separate helpers manually.
- The runtime portion is therefore recomputed from the active backend helper on each call, while the remaining fields come from the logger's current counters, flags, and flush policy at that same general moment.
- The runtime portion is therefore recomputed from the active backend helper on each call, while the remaining fields come from the logger's current counters, lifecycle phase, derived lifecycle conclusions, and flush policy at that same general moment.
- Because the snapshot is assembled field by field when `state()` is called, later logger changes require calling `state()` again rather than reusing an older `AsyncLoggerState` value as if it refreshed itself.
- That field-by-field assembly also means this helper is not an atomic freeze across all refs; under concurrent logger activity, neighboring fields can reflect slightly different instants.
- After a worker failure, `has_failed=true`, a non-empty `last_error`, and `pending_count>0` can legitimately appear together in one snapshot until later cleanup or a later started `run()` changes them.
- After shutdown cleanup on an already failed logger, snapshots can also legitimately show `is_closed=true` together with retained `has_failed=true` and the same `last_error()`, while `pending_count` versus `dropped_count` still reflects the active runtime's cleanup path.
- `phase` is the primary lifecycle conclusion. The current public phases are `ready`, `running`, `failed`, `closing`, `closed`, and `closed_failed`.
- `backlog_retained=true` means pending backlog still exists while the logger is no longer actively draining it.
- `can_rerun=true` means the current phase still allows a future `run()` call to restart drain work.
- `terminal=true` means the logger is closed, no worker is active, and no pending backlog remains.
- After a worker failure, snapshots can legitimately report `phase=failed`, `has_failed=true`, `backlog_retained=true`, a non-empty `last_error`, and `pending_count>0` together until later cleanup or restart changes them.
- After shutdown cleanup on an already failed logger, snapshots can also legitimately report `phase=closed_failed`, `is_closed=true`, `terminal=true`, retained `has_failed=true`, and the same `last_error()`.
- `state()` only reports the current field values; it does not clear failure state, drain backlog, or synchronize pending work by itself.
### How to Use
@@ -90,7 +94,7 @@ e.g.:
- A snapshot showing `has_failed=true` does not imply `pending_count` is already `0`; remaining queued records may still be visible until later cleanup or restart.
- A snapshot showing `is_closed=true` also does not imply failure state was cleared; after failure-driven shutdown, `has_failed=true` and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
- A snapshot showing `is_closed=true` also does not imply failure state was cleared; after failure-driven shutdown, `phase=closed_failed`, `has_failed=true`, and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
### Notes
@@ -2,7 +2,7 @@
name: async-logger-to-library-async-logger
group: api
category: facade
update-time: 20260614
update-time: 20260707
description: Convert a full async logger into the narrower library-facing async facade without rebuilding or detaching the underlying async state.
key-word:
- async
@@ -38,8 +38,8 @@ Detailed rules explaining key parameters and behaviors
- The original `AsyncLogger[S]` handle remains the same live logger. If caller code keeps that original value, later facade calls and later unwraps still observe the same shared queue, counters, sink helpers, and lifecycle mutations.
- The returned facade keeps library-facing async operations including `log(...)`, `run()`, and `shutdown(...)`.
- Async inspection helpers and broader composition APIs remain on the underlying `AsyncLogger[S]` and are intentionally hidden until `to_async_logger()` is used again.
- If later facade-level `run()` or `shutdown()` calls record worker failure, leave backlog behind, or follow runtime-dependent shutdown cleanup rules, unwrapping later still exposes that same post-call state instead of a translated facade copy.
- That includes states where delegated shutdown already finished with `is_closed=true` while retained `has_failed()` and `last_error()` remain on the wrapped logger, together with the same runtime-dependent pending-versus-dropped cleanup outcome.
- If later facade-level `run()` or `shutdown()` calls record worker failure or perform shutdown cleanup, unwrapping later still exposes that same post-call state instead of a translated facade copy.
- That includes states where delegated shutdown already finished with `is_closed=true` while retained `has_failed()` and `last_error()` remain on the wrapped logger, together with the same retained dropped-count cleanup outcome.
- When `S` itself exposes richer runtime helpers, projecting to the library facade does not strip those capabilities from the wrapped logger; they are still reachable after `to_async_logger()`.
- When `S` is `RuntimeSink`, projection also preserves queued runtime state and file-backed runtime helper behavior behind the facade instead of replacing them with a library-specific copy.
- Unwrapping later with `to_async_logger()` therefore exposes the same queue counters, failure snapshots, file state, and runtime file controls that the original async logger already carried.
@@ -83,7 +83,7 @@ e.g.:
- Projection does not normalize failure snapshots; a later unwrap can still show combinations such as retained `last_error()` with remaining `pending_count()` when the wrapped async logger really ended up in that state.
- Projection also does not normalize delegated shutdown results; a later unwrap can still show `is_closed=true` together with retained `has_failed()`, the same `last_error()`, and the same runtime-dependent leftover backlog or dropped-count cleanup that accumulated behind the facade.
- Projection also does not normalize delegated shutdown results; a later unwrap can still show `is_closed=true` together with retained `has_failed()`, the same `last_error()`, and the same retained dropped-count cleanup that accumulated behind the facade.
- Projection also does not normalize richer runtime sink state; if the original async logger already carried queued runtime data or file-backed helper state, a later unwrap still exposes that same live state.
+9 -10
View File
@@ -2,7 +2,7 @@
name: async-logger-type
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Public asynchronous logger root type used for queue-backed sink-preserving logging pipelines with explicit lifecycle, failure, and flush-callback state.
key-word:
- async
@@ -13,7 +13,7 @@ key-word:
## Async-logger-type
`AsyncLogger[S]` is the public asynchronous root logger type. It stores a concrete sink type `S` together with queue policy, runtime state counters, and lifecycle flags, then serves as the base value for async composition, worker control, and async write APIs.
`AsyncLogger[S]` is the public asynchronous root logger type. It stores a concrete sink type `S` together with queue policy, runtime state counters, and lifecycle state, then serves as the base value for async composition, worker control, and async write APIs.
### Interface
@@ -27,16 +27,14 @@ pub struct AsyncLogger[S] {
linger_ms : Int
flush_policy : AsyncFlushPolicy
sink : S
flush_sink : (S) -> Int raise
flush_callback : (S) -> Unit raise
context_fields : Array[@bitlogger.Field]
filter : (@bitlogger.Record) -> Bool
patch : @bitlogger.RecordPatch
queue : @async.Queue[@bitlogger.Record]
pending_count : Ref[Int]
dropped_count : Ref[Int]
is_closed : Ref[Bool]
is_running : Ref[Bool]
has_failed : Ref[Bool]
phase : Ref[AsyncLifecyclePhase]
last_error : Ref[String]
}
```
@@ -50,9 +48,10 @@ pub struct AsyncLogger[S] {
Detailed rules explaining key parameters and behaviors
- This is a public root struct, not a type alias.
- The current fields cover targeting, overflow policy, batching, linger timing, flush policy, the wrapped sink, context/filter/patch behavior, queue state, and worker lifecycle flags.
- `flush_sink : (S) -> Int raise` stores the raising flush callback used by batch and shutdown flush policies.
- `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, and `last_error` are mutable runtime refs that power the higher-level lifecycle and diagnostics helpers.
- The current fields cover targeting, overflow policy, batching, linger timing, flush policy, the wrapped sink, context/filter/patch behavior, queue state, and the internal lifecycle phase model.
- `flush_callback : (S) -> Unit raise` stores the raising flush callback used by batch and shutdown flush policies.
- `pending_count`, `dropped_count`, `phase`, and `last_error` are the mutable runtime refs that power the higher-level lifecycle and diagnostics helpers.
- `phase` is the stronger internal lifecycle source of truth; helper reads such as `is_closed()`, `is_running()`, `has_failed()`, and the richer `state()` snapshot are derived from it.
- The sink type parameter is preserved across async composition, which is why helpers such as `with_target(...)`, `with_context_fields(...)`, `with_filter(...)`, and `with_patch(...)` keep returning `AsyncLogger[S]`.
- `async_logger(...)` constructs this type as the main asynchronous entry point.
- This root type is also what sits underneath both async facade families: `ApplicationAsyncLogger` is a direct alias over the runtime-sink line, while `LibraryAsyncLogger[S]` is a narrowing wrapper around an `AsyncLogger[S]` value.
@@ -88,7 +87,7 @@ e.g.:
- Actual enqueue, worker, and flush behavior still depends on the wrapped sink `S`, async runtime support, and the configured queue policy.
- Post-close logging behavior is not a property of the struct shape alone; it also depends on the active runtime implementation.
- Post-close logging behavior now follows the shared close contract rather than backend-specific divergence, but lifecycle interpretation should still prefer `state()` over manual field reasoning.
### Notes
+3 -2
View File
@@ -2,7 +2,7 @@
name: async-logger-wait-idle
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Wait until the async logger backlog drains to zero or a worker failure interrupts normal progress, using the repo's direct async call style.
key-word:
- async
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- If `has_failed()` becomes `true`, waiting stops early instead of continuing to spin.
- This API does not close the logger or stop the worker.
- A return from `wait_idle()` therefore means either backlog reached `0` or failure interrupted normal drain progress.
- In the stronger lifecycle model, the common post-failure interpretation is `phase=failed` together with `backlog_retained=true` while `pending_count() > 0` remains visible.
- `wait_idle()` does not clear retained failure state by itself, so if pending records remain after a worker failure, later `wait_idle()` calls also short-circuit until another path changes that state.
- A later `run()` can make `wait_idle()` meaningful again for the retained backlog, but only after that new worker invocation has actually started and reset the stale failure flag.
- If no worker is draining the queue and no failure flag is raised, `wait_idle()` can wait indefinitely.
@@ -73,7 +74,7 @@ e.g.:
- If the worker was never started, or if nothing is making pending records decrease, `wait_idle()` can block indefinitely.
- If callers need backlog cleanup after a failure-short-circuit, they still need a later `close(clear=true)` or `shutdown(...)` path.
- In the current direct coverage, `wait_idle()` can return with `pending_count() > 0` after a worker failure, and the later cleanup path may either clear that backlog explicitly or follow the runtime-dependent shutdown split documented on `shutdown(...)`.
- In the current direct coverage, `wait_idle()` can return with `pending_count() > 0` after a worker failure, and a later `shutdown(...)` call will convert that retained backlog into dropped records before returning.
- If callers retry `wait_idle()` immediately after such a failure without restarting the worker or forcing cleanup first, the call can return again with the same retained pending backlog.
@@ -0,0 +1,21 @@
---
name: async-logger-with-trace-context
group: api
category: async
update-time: 20260719
description: Bind TraceContext fields before records enter an AsyncLogger queue.
key-word:
- async
- trace
- context
---
## Async-logger-with-trace-context
```moonbit
let contextual = logger.with_trace_context(
@bitlogger.trace_context("trace-1", "span-1"),
)
```
The helper stores the mapped fields on the returned async logger and preserves its queue, lifecycle, target, and threshold state. Context is added before records are enqueued.
+11 -5
View File
@@ -2,7 +2,7 @@
name: async-logger
group: api
category: async
update-time: 20260614
update-time: 20260707
description: Create an async logger with bounded queueing, overflow policy, lifecycle helpers, background run control, and a raising flush callback.
key-word:
- async
@@ -23,7 +23,7 @@ pub fn[S] async_logger(
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
target~ : String = "",
flush~ : (S) -> Int raise = fn(_) { 0 },
flush~ : (S) -> Unit raise = fn(_) { () },
) -> AsyncLogger[S] {}
```
@@ -33,7 +33,7 @@ pub fn[S] async_logger(
- `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 raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
- `flush : (S) -> Unit raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
#### output
@@ -45,7 +45,7 @@ Detailed rules explaining key parameters and behaviors
- `async_logger(...)` only builds the logger. Actual background draining is started by `run()`.
- `async_logger(...)` returns the full `AsyncLogger[S]` surface directly. It is therefore the underlying constructor used by both application-facing async aliases and the narrower `LibraryAsyncLogger[S]` wrapper line.
- The constructed logger starts with `is_closed=false`, `is_running=false`, `has_failed=false`, `last_error=""`, and zeroed pending/dropped counters.
- The constructed logger starts in lifecycle phase `ready`, with `last_error=""` and zeroed pending/dropped counters.
- The constructed logger also keeps the core async target contract unchanged: `log(..., target=...)` can override the target for one call, while fixed-level helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
- Unlike synchronous `Logger`, async `with_context_fields(...)` and `bind(...)` preserve the visible `AsyncLogger[S]` type because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
- `ApplicationAsyncLogger` and `ApplicationTextAsyncLogger` are only alias names over concrete `AsyncLogger[...]` shapes, so they keep the same lifecycle, queue, failure, and state helpers without adding a wrapper layer.
@@ -54,10 +54,15 @@ Detailed rules explaining key parameters and behaviors
- `src-async` is designed for `native / llvm / js / wasm / wasm-gc`, but current release-facing local verification is stronger for `native / js / wasm / wasm-gc` than for `llvm`.
- `llvm` should currently be read as experimental and locally unverified in this environment rather than as a stable checked target.
- `flush` is used only when batch or shutdown policy wants explicit flushing.
- The callback expresses attempted flush timing and failure, not a returned progress count contract.
- If the supplied flush callback raises, worker failure state is recorded through `has_failed()` and `last_error()`.
- `wait_idle()` is failure-aware rather than a pure backlog-to-zero guarantee. If a worker failure sets `has_failed=true`, waiting stops early and the logger can still report `pending_count() > 0` until later cleanup or restart work happens.
- A later `run()` attempt starts by clearing stale failure state back to `has_failed=false` and `last_error=""` before it resumes draining any backlog still left in the queue.
- The exact behavior of late log attempts after closure is runtime-dependent, so callers should use lifecycle helpers like `is_closed()` and `shutdown()` instead of assuming identical post-close enqueue semantics everywhere.
- `state()` exposes the stronger lifecycle conclusion through `phase`, plus derived reads such as `backlog_retained`, `can_rerun`, and `terminal` so callers do not have to infer these states from raw flag combinations.
- Late log attempts after closure are rejected in the shared async log path before patch/filter work runs, so post-close side effects no longer vary by backend.
- `run()` now enforces a single-worker contract and raises `AsyncLoggerAlreadyRunning` when a second worker startup is attempted for the same live logger.
- `run()` also rejects already-closed loggers with `AsyncLoggerClosed`, which keeps rerun semantics aligned with the stronger lifecycle model: rerun is for retained `failed` backlog, not for terminal closed phases.
- `shutdown()` now always waits for any active worker to finish and converts retained post-failure backlog into dropped records before returning.
- Queue overflow behavior depends on `AsyncOverflowPolicy`.
### How to Use
@@ -125,6 +130,7 @@ 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()`.
- If a second worker startup is attempted while `is_running()` is already true, `run()` raises `AsyncLoggerAlreadyRunning`.
### Notes
+3 -3
View File
@@ -13,12 +13,12 @@ key-word:
## 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.
Convert `AsyncRuntimeState` into a `Json`. 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 {}
pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> Json {}
```
#### input
@@ -27,7 +27,7 @@ pub fn async_runtime_state_to_json(state : AsyncRuntimeState) -> @json_parser.Js
#### output
- `JsonValue` - Structured JSON representation of the runtime state.
- `Json` - Structured JSON representation of the runtime state.
### Explanation
+3
View File
@@ -15,6 +15,8 @@ key-word:
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.
At the public `src` layer, this API is the entry facade that consumes the shared config model owned by `src/config_model` and produces a `ConfiguredLogger` backed by `src/runtime.RuntimeSink`.
### Interface
```moonbit
@@ -34,6 +36,7 @@ pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {}
Detailed rules explaining key parameters and behaviors
- `build_logger(...)` first constructs a base `RuntimeSink` from `config.sink`, then applies `config.queue` when present, and finally builds `Logger::new(...)` with `config.min_level`, `config.target`, and `config.timestamp`.
- The returned file-control and file-diagnostics helpers are still facade methods over `RuntimeSink`, which in turn delegates file behavior to `src/file_runtime` and file model values to `src/file_model`.
- The returned logger still supports normal logging methods because `ConfiguredLogger` is `Logger[RuntimeSink]`.
- The returned logger also keeps inherited logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
- That means `log(..., target=...)` can override the target for one write, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
+6 -5
View File
@@ -2,8 +2,8 @@
name: config-error
group: api
category: config
update-time: 20260613
description: Public config parsing error alias used by synchronous config-loading helpers.
update-time: 20260707
description: Public config parsing error re-exported from config_model for synchronous config-loading helpers.
key-word:
- config
- error
@@ -13,12 +13,12 @@ key-word:
## Config-error
`ConfigError` is the public error type raised by synchronous config parsing helpers. It is a direct alias to the internal config error definition and currently exposes a single structured error case for invalid input.
`ConfigError` is the public error type raised by synchronous config parsing helpers. On the root `src` facade, it is re-exported from `src/config_model`, which is the real owner of the structured config error definition.
### Interface
```moonbit
pub type ConfigError = @utils.ConfigError
pub using @config_model { type ConfigError }
```
#### output
@@ -29,7 +29,8 @@ pub type ConfigError = @utils.ConfigError
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a separate public wrapper.
- This root surface is a re-export, not the concrete owner definition.
- The concrete error lives in `@config_model.ConfigError`, not in `@utils`.
- The current public error case is `ConfigError::InvalidConfig(message)`.
- The alias is used by parsing helpers such as `parse_logger_config_text(...)` and by lower-level config parsing routines beneath it.
- Error messages describe concrete schema problems such as invalid JSON, wrong value types, unsupported enum text, or missing required values for a chosen sink kind.
+14 -8
View File
@@ -2,8 +2,8 @@
name: configured-logger-close
group: api
category: runtime
update-time: 20260613
description: Close the configured runtime logger sink and return whether the wrapped RuntimeSink reported a successful close action.
update-time: 20260707
description: Close the configured runtime logger sink and return whether the wrapped RuntimeSink close path succeeded.
key-word:
- logger
- runtime
@@ -27,7 +27,7 @@ pub fn ConfiguredLogger::close(self : ConfiguredLogger) -> Bool {}
#### output
- `Bool` - Whether the wrapped `RuntimeSink::close(...)` call reported a successful close action.
- `Bool` - Whether the wrapped `RuntimeSink::close(...)` call reported a successful close path.
### Explanation
@@ -35,9 +35,11 @@ Detailed rules explaining key parameters and behaviors
- This helper delegates directly to `self.sink.close()`.
- Plain file sinks forward to `FileSink::close()` through `RuntimeSink`.
- Queue-backed file sinks close the wrapped file sink instead of only the queue wrapper.
- Generic `close()` on queued file sinks does not drain pending queue entries first; it closes the wrapped file sink directly.
- Console-style sinks and queue-wrapped console-style sinks return `true` as a no-op success because they do not expose a meaningful close step here.
- Queue-backed file sinks now follow the same safe teardown path as `file_close()`: they first try to drain the queue, then flush the wrapped inner file sink, then close it.
- On queued file sinks, the returned `Bool` is `true` only when that whole teardown path succeeds without introducing new file failures.
- Plain console-style sinks return `true` as a no-op success because they do not expose a meaningful close step here.
- Queue-wrapped console-style sinks now use drain-first teardown before returning `true`.
- For `QueuedConsole`, `QueuedJsonConsole`, and `QueuedTextConsole`, success means the queued backlog was consumed to zero.
- For file-backed configured loggers, later `close()` calls return `false` after the wrapped file handle has already been cleared.
### How to Use
@@ -69,12 +71,16 @@ e.g.:
- If the configured logger shares the same wrapped runtime sink state with another facade and one path already closed that file-backed sink, a later close attempt returns `false`.
- If callers need a file-specific close path with queue flush nuances, `file_close()` may be the better API.
- If callers need a file-specific close path with queue flush nuances, `file_close()` may still be the clearer API.
- On queued file sinks, `true` still does not mean the records are guaranteed to be durable beyond what the current backend can report; it means the queue-drain, file-flush, and file-close path completed without a newly observed file failure.
- On queued non-file sinks, `true` means the queued backlog was consumed before teardown completed.
### Notes
1. This is the generic configured runtime close helper.
2. Prefer `file_close()` when the configured sink shape is file-backed and queued records should be flushed before teardown.
2. Generic `close()` is now safe for queue-backed runtime sinks and no longer skips queued records silently.
3. Converting the same configured logger through wrapper paths such as library projection still shares the wrapped runtime sink state, so close effects are visible across those facades.
@@ -0,0 +1,79 @@
---
name: configured-logger-drain-progress
group: api
category: runtime
update-time: 20260707
description: Return a structured generic drain progress snapshot for a ConfiguredLogger through RuntimeSink without collapsing queue and plain-file fallback semantics into one Int.
key-word:
- logger
- runtime
- drain
- public
---
## Configured-logger-drain-progress
Return a structured generic drain progress snapshot for a `ConfiguredLogger`. This is the recommended configured runtime wrapper over `RuntimeSink::drain_progress(...)` when code wants truthful generic progress instead of the compatibility `Int` returned by `drain()`.
### Interface
```moonbit
pub fn ConfiguredLogger::drain_progress(
self : ConfiguredLogger,
max_items~ : Int = -1,
) -> RuntimeSinkProgress {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose generic drain progress should be observed.
- `max_items : Int` - Optional upper bound on drained queued items. Negative values mean no explicit bound.
#### output
- `RuntimeSinkProgress` - Structured progress snapshot returned by the wrapped `RuntimeSink::drain_progress(...)` call.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates directly to `self.sink.drain_progress(max_items=max_items)`.
- Queue-wrapped sinks report drained queued items through `queue_advanced_count`.
- Plain file sinks report the generic fallback flush step through `file_flush_step_count`.
- Plain console-style sinks return a zeroed snapshot.
- This helper keeps configured-runtime drain behavior explicit without forcing callers to interpret one mixed `Int`.
### How to Use
Here are some specific examples provided.
#### When Need Recommended Bounded Generic Drain Progress On A Config-built Logger
When queued config-built output should be advanced in chunks with explicit semantics:
```moonbit
let progress = logger.drain_progress(max_items=16)
```
In this example, queue advancement and plain-file fallback stay separated.
#### When Need Full Manual Generic Drain Progress
When config-built support code should empty runtime queue work while keeping shape information:
```moonbit
let progress = logger.drain_progress()
```
In this example, the structured result still tells whether the logger was queue-backed or plain file-backed.
### Error Case
e.g.:
- If the configured runtime sink is not queue-backed, the snapshot may stay zeroed or use the plain-file fallback field.
- If callers need file-specific success semantics after queue delivery, `file_flush()` is the better API.
### Notes
1. Prefer `drain_progress()` over `drain()` in new configured-runtime code.
2. Use `pending_count()` together with this helper when remaining backlog should also be observed.
+21 -14
View File
@@ -2,8 +2,8 @@
name: configured-logger-drain
group: api
category: runtime
update-time: 20260613
description: Drain queued work from a configured runtime logger with optional item limits through RuntimeSink.
update-time: 20260707
description: Compatibility configured runtime drain helper that still returns one Int by collapsing generic queue progress and plain-file fallback steps.
key-word:
- logger
- runtime
@@ -13,7 +13,7 @@ key-word:
## Configured-logger-drain
Drain queued work from a `ConfiguredLogger`. This helper is the configured logger wrapper over `RuntimeSink::drain(...)` when config-driven queue wrapping should be advanced in a controlled, bounded way.
Drain queued work from a `ConfiguredLogger` and return one compatibility count. This helper is still available for older code, but new configured-runtime code should prefer `drain_progress()` because it exposes queue advancement and plain-file fallback steps separately.
### Interface
@@ -28,16 +28,17 @@ pub fn ConfiguredLogger::drain(self : ConfiguredLogger, max_items~ : Int = -1) -
#### output
- `Int` - Count returned by the wrapped `RuntimeSink::drain(...)` call.
- `Int` - Compatibility count returned by the wrapped `RuntimeSink::drain(...)` call.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates directly to `self.sink.drain(max_items=max_items)`.
- Queue-wrapped sinks forward to the concrete queue sink's `drain(...)` behavior and may drain up to `max_items` records.
- Plain file sinks fall back to `FileSink::flush()` behavior through `RuntimeSink` and return `1` or `0`.
- Plain console-style sinks return `0` because they do not own a drainable queue here.
- Under the hood, `RuntimeSink::drain()` now rebuilds one compatibility count from `drain_progress()`.
- Queue-wrapped sinks still report drained queued items through that compatibility count.
- Plain file sinks still report the generic fallback flush step as `1` or `0`.
- Plain console-style sinks still return `0`.
### How to Use
@@ -45,31 +46,37 @@ Here are some specific examples provided.
#### When Need Bounded Queue Progress
When queued output should be advanced in chunks:
When older code already expects one numeric drain count from a config-built logger:
```moonbit
let drained = logger.drain(max_items=16)
```
In this example, callers limit how much queued work is processed in one step.
In this example, callers still limit how much queued work is processed in one step while keeping the legacy return shape.
#### When Need Full Manual Drain
When the configured queue should be emptied explicitly:
When code should keep the older full-drain compatibility path:
```moonbit
ignore(logger.drain())
```
In this example, the configured runtime logger drains without imposing an item cap.
In this example, the configured runtime logger drains without changing the legacy return shape.
### Error Case
e.g.:
- If the configured runtime sink is not queue-backed, draining may return `0` or follow the plain-file flush fallback.
- If callers only need generic flush semantics, `flush()` may be the simpler API.
- If callers need truthful generic progress semantics, `drain_progress()` is the better API.
- If callers only need generic flush semantics, `flush_progress()` may be the simpler structured API.
- If callers need file-specific success semantics after draining a queued file logger, they should inspect failure counters or use `file_flush()` for the file-specific path.
### Notes
1. Prefer this helper when queue progress should be bounded or observable.
1. Treat this method as a compatibility helper for older numeric code.
2. Use `pending_count()` to inspect remaining backlog after the drain call when the configured sink is queue-backed.
2. Prefer `drain_progress()` in new configured-runtime code.
3. Use `pending_count()` to inspect remaining backlog after the drain call when the configured sink is queue-backed.
+7 -3
View File
@@ -2,7 +2,7 @@
name: configured-logger-file-available
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Read whether the configured runtime logger currently has an available file sink behind its runtime sink shape.
key-word:
- logger
@@ -36,6 +36,8 @@ Detailed rules explaining key parameters and behaviors
- File-backed runtime sinks report actual file availability through the wrapped `RuntimeSink`.
- Queued file sinks still expose the availability of their wrapped inner file sink.
- Non-file sinks report `false`.
- This means `false` currently merges two different situations: “not file-backed” and “file-backed but currently unavailable”.
- Use `file_path_or_none()`, `file_policy_or_none()`, `file_default_policy_or_none()`, `file_state_or_none()`, or `file_runtime_state()` when callers need truthful file-semantics detection.
- This helper delegates to the runtime sink and does not mutate logger state.
### How to Use
@@ -67,10 +69,12 @@ In this example, callers can decide whether a recovery action is needed.
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.
- If callers need to distinguish “not file-backed” from “file-backed but unavailable”, use one of the truthful `*_or_none()` helpers or `file_runtime_state()`.
- If callers need detailed failure counters rather than a simple availability flag, `file_state_or_none()` 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.
2. Pair it with reopen and failure-counter APIs when diagnosing file sink problems, but prefer the truthful `*_or_none()` helpers when file semantics themselves must be checked.
@@ -2,8 +2,8 @@
name: configured-logger-file-clear-rotation
group: api
category: runtime
update-time: 20260512
description: Disable runtime rotation on the configured file-backed logger sink.
update-time: 20260707
description: Clear the rotation policy used by the configured runtime file sink.
key-word:
- logger
- runtime
@@ -13,7 +13,7 @@ key-word:
## Configured-logger-file-clear-rotation
Disable runtime rotation on a `ConfiguredLogger` file sink. This helper is the direct shortcut for clearing rotation policy.
Clear the rotation policy used by a `ConfiguredLogger` file sink.
### Interface
@@ -23,7 +23,7 @@ pub fn ConfiguredLogger::file_clear_rotation(self : ConfiguredLogger) -> Bool {}
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose rotation policy should be cleared.
- `self : ConfiguredLogger` - Config-driven runtime logger whose file rotation policy should be cleared.
#### output
@@ -34,41 +34,12 @@ pub fn ConfiguredLogger::file_clear_rotation(self : ConfiguredLogger) -> Bool {}
Detailed rules explaining key parameters and behaviors
- File-backed sinks clear their runtime rotation policy through the wrapped `RuntimeSink`.
- Queued file sinks forward the update to the wrapped inner file sink.
- Queued file sinks clear the wrapped inner file sink rotation policy only when no queued records are pending.
- 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.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different rotation policy than the one they were queued under.
### Notes
1. Use this helper when the desired effect is simply “rotation off”.
1. Use this helper when runtime rotation policy should be removed without rebuilding the logger.
2. It is clearer than passing `None` through a broader setter when intent matters.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
+9 -6
View File
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Close the file sink behind a configured runtime logger when one is present, with queued-file success tied to safe drain-flush-close behavior.
key-word:
- logger
- runtime
@@ -27,14 +27,15 @@ pub fn ConfiguredLogger::file_close(self : ConfiguredLogger) -> Bool {}
#### output
- `Bool` - Whether the underlying file close succeeded.
- `Bool` - Whether the file-specific close path succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks forward directly to file close behavior through the wrapped `RuntimeSink`.
- Queued file sinks flush queue work before closing the wrapped inner file sink.
- Queued file sinks first drain the queue, then flush the wrapped inner file sink, then close it.
- For queued file sinks, the returned `Bool` is `true` only when that whole path succeeds and no new write, flush, or rotation failures are observed during the drain-flush-close step.
- Non-file sinks return `false`.
- This helper is narrower than generic `close()` because it specifically targets file sink shutdown.
- After a file-backed configured logger has already cleared its file handle, later `file_close()` calls return `false`.
@@ -59,7 +60,7 @@ When application code wants the file close outcome directly:
let closed = logger.file_close()
```
In this example, the result describes file close behavior rather than generic sink close behavior.
In this example, the result describes the file-specific close path rather than generic sink close behavior.
### Error Case
@@ -70,10 +71,12 @@ e.g.:
- If callers only need generic sink teardown, `close()` is the broader API.
- On queued file sinks, `true` still does not mean a stronger durability contract than the current backend can report; it means the safe drain-flush-close path completed without a newly observed file failure.
### Notes
1. Prefer this helper when file-backed runtime behavior matters specifically.
2. Queued file sinks flush pending records before closing the file, unlike generic `close()`.
2. Generic `close()` now uses the same safe queued-file teardown path.
3. Library or application facades derived from the same configured runtime logger still observe the same underlying file-close state.
@@ -0,0 +1,82 @@
---
name: configured-logger-file-default-policy-or-none
group: api
category: runtime
update-time: 20260707
description: Read the default runtime file policy from a ConfiguredLogger only when it is actually file-backed.
key-word:
- logger
- runtime
- file
- truthful
---
## Configured-logger-file-default-policy-or-none
Read the default runtime file policy from a `ConfiguredLogger` only when it is actually file-backed.
The returned `FileSinkPolicy` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and avoids fabricating that model on non-file variants.
This is the truthful companion to `file_default_policy()`. Prefer it when default-policy inspection should not fabricate a fallback object on non-file sinks.
### Interface
```moonbit
pub fn ConfiguredLogger::file_default_policy_or_none(self : ConfiguredLogger) -> FileSinkPolicy? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose default file policy should be inspected.
#### output
- `FileSinkPolicy?` - `Some(policy)` when the configured sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(default_policy)` through the wrapped `RuntimeSink`.
- Queued file sinks forward the wrapped inner file sink default policy as `Some(default_policy)`.
- The wrapped value is the shared `@file_model.FileSinkPolicy` model, not a configured-logger-owned concrete type.
- Non-file sinks return `None`.
- This helper is useful when callers need to compare runtime drift or restore defaults without fabricating file semantics.
### How to Use
Here are some specific examples provided.
#### When Need Truthful Baseline Policy Visibility
When tooling should inspect defaults only for real file-backed sinks:
```moonbit
let defaults = logger.file_default_policy_or_none()
```
In this example, `None` means the configured logger has no file default policy.
#### When Compare Current And Default Policy Truthfully
When diagnostics should avoid fallback policy objects:
```moonbit
match (logger.file_policy_or_none(), logger.file_default_policy_or_none()) {
(Some(current), Some(defaults)) => ignore((current, defaults))
_ => ()
}
```
In this example, comparisons happen only when real file semantics exist.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers only need a drift boolean, `file_policy_matches_default()` remains simpler.
### Notes
1. Prefer this helper over `file_default_policy()` for truthful runtime diagnostics.
2. `file_default_policy()` remains available as the compatibility fallback API.
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Read the initial default file policy associated with a configured logger, with a compatibility fallback for non-file sinks.
key-word:
- logger
- runtime
@@ -15,6 +15,10 @@ key-word:
Read the initial default file policy associated with a `ConfiguredLogger`. This helper exposes the baseline file policy captured when the runtime sink was created.
The returned `FileSinkPolicy` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates default-policy reads to the wrapped `RuntimeSink`.
`file_default_policy()` is the compatibility form. For truthful file-semantics detection, prefer `file_default_policy_or_none()`.
### Interface
```moonbit
@@ -35,7 +39,10 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks return the default policy captured at creation time through the wrapped `RuntimeSink`.
- Queued file sinks forward the default policy from the wrapped inner file sink.
- The returned policy object itself is the shared `@file_model.FileSinkPolicy` model, not a configured-logger-owned concrete type.
- Non-file sinks return the same neutral fallback policy value produced by `RuntimeSink::file_default_policy()`.
- This fallback keeps older callers source-compatible, but it is not a real file default policy.
- New diagnostic or recovery code should prefer `file_default_policy_or_none()`.
- This helper is useful when callers need to compare runtime drift or restore defaults later.
### How to Use
@@ -65,10 +72,12 @@ In this example, callers can reason about “factory” file policy separately f
e.g.:
- If the configured sink is not file-backed, the return value is a neutral fallback policy.
- If callers need a truthful file-semantics check, use `file_default_policy_or_none()`.
- 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()`.
2. It complements `file_policy()` and `file_reset_policy()`, while `file_default_policy_or_none()` is the truthful diagnostic form.
+10 -8
View File
@@ -2,8 +2,8 @@
name: configured-logger-file-flush
group: api
category: runtime
update-time: 20260614
description: Flush the file sink behind a configured runtime logger when one is present.
update-time: 20260707
description: Flush the file sink behind a configured runtime logger when one is present, with queued-file success tied to both queue drain and file flush outcome.
key-word:
- logger
- runtime
@@ -27,16 +27,16 @@ pub fn ConfiguredLogger::file_flush(self : ConfiguredLogger) -> Bool {}
#### output
- `Bool` - Whether the underlying file flush succeeded.
- `Bool` - Whether the file-specific flush path succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain file sinks forward directly to file flush behavior through the wrapped `RuntimeSink`.
- Queued file sinks first flush queued records, then flush the wrapped inner file sink.
- For queued file sinks, the returned `Bool` comes from the inner file flush rather than the queue flush step.
- For queued file sinks, this step may also be the point where queued records finally hit the inner file sink, so write-failure counters can change here even if earlier log calls only queued records.
- Queued file sinks first drain queued records, then flush the wrapped inner file sink.
- For queued file sinks, the returned `Bool` is `true` only when all queued records were consumed, the final file flush succeeded, and no new write, flush, or rotation failures were observed during that flush path.
- For queued file sinks, this step may also be the point where queued records finally hit the inner file sink, so failure counters can change here even if earlier log calls only queued records.
- Non-file sinks return `false`.
- This helper is narrower than generic `flush()` because it targets file sink behavior specifically.
- After a file-backed configured logger has already cleared its file handle, later `file_flush()` calls return `false`.
@@ -61,7 +61,7 @@ When code should branch on the outcome of a file flush:
let ok = logger.file_flush()
```
In this example, callers can distinguish file flush success from a non-file sink shape.
In this example, callers can distinguish file-specific flush success from a non-file sink shape.
### Error Case
@@ -72,10 +72,12 @@ e.g.:
- If callers want generic queue or sink advancement instead of file-specific behavior, `flush()` is the broader API.
- On queued file sinks, a `false` result may still mean queue consumption happened but file delivery observed a new failure.
### 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, including surfacing delayed write failures from previously queued records.
2. Queued file sinks may perform both queue drain and file flush work here, including surfacing delayed failures from previously queued records.
3. Library or application facades derived from the same configured runtime logger still observe the same underlying file-flush availability state.
@@ -0,0 +1,79 @@
---
name: configured-logger-file-path-or-none
group: api
category: runtime
update-time: 20260707
description: Read the effective file path from a ConfiguredLogger only when it is actually file-backed.
key-word:
- logger
- runtime
- file
- truthful
---
## Configured-logger-file-path-or-none
Read the effective file path from a `ConfiguredLogger` only when it is actually file-backed.
This is the truthful companion to `file_path()`. Prefer it for diagnostics, branching, and recovery logic that must distinguish “not file-backed” from compatibility fallbacks.
### Interface
```moonbit
pub fn ConfiguredLogger::file_path_or_none(self : ConfiguredLogger) -> String? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file path should be inspected.
#### output
- `String?` - `Some(path)` when the configured sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(path)` through the wrapped `RuntimeSink`.
- Queued file sinks forward the wrapped inner file sink path as `Some(path)`.
- Non-file sinks return `None`.
- This helper is observation-only and does not mutate logger state.
### How to Use
Here are some specific examples provided.
#### When Need Truthful File-backed Detection
When code should branch only if file semantics really exist:
```moonbit
match logger.file_path_or_none() {
Some(path) => println(path)
None => ()
}
```
In this example, `None` means the configured logger is not file-backed.
#### When Need Path Diagnostics Without Fallback Values
When support output should avoid compatibility placeholders:
```moonbit
let maybe_path = logger.file_path_or_none()
```
In this example, callers can keep “no file semantics” distinct from a real path.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers need the broader file snapshot or queue context, prefer `file_state_or_none()` or `file_runtime_state()`.
### Notes
1. Prefer this helper over `file_path()` for truthful runtime diagnostics.
2. `file_path()` remains available as the compatibility fallback API.
+10 -4
View File
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Read the effective file path used by the configured runtime logger, with a compatibility fallback for non-file sinks.
key-word:
- logger
- runtime
@@ -15,6 +15,8 @@ key-word:
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.
`file_path()` is the compatibility form. For truthful file-semantics detection, prefer `file_path_or_none()`.
### Interface
```moonbit
@@ -36,6 +38,8 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current file path through the wrapped `RuntimeSink`.
- Queued file sinks forward the wrapped inner file sink path.
- Non-file sinks return an empty string.
- This fallback keeps older callers source-compatible, but it does not truthfully express whether file semantics exist.
- New diagnostic code should prefer `file_path_or_none()`.
- This helper is observation-only and does not modify file state.
### How to Use
@@ -65,10 +69,12 @@ In this example, the path can be surfaced without reading broader runtime state.
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.
- If callers need a truthful file-semantics check, use `file_path_or_none()`.
- If callers need richer file status than just the path, `file_state_or_none()` 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.
2. Empty string is a compatibility fallback; `file_path_or_none()` is the recommended truthful API.
@@ -0,0 +1,79 @@
---
name: configured-logger-file-policy-or-none
group: api
category: runtime
update-time: 20260707
description: Read the current runtime file policy from a ConfiguredLogger only when it is actually file-backed.
key-word:
- logger
- runtime
- file
- truthful
---
## Configured-logger-file-policy-or-none
Read the current runtime file policy from a `ConfiguredLogger` only when it is actually file-backed.
This is the truthful companion to `file_policy()`. Prefer it for diagnostics and recovery logic that must avoid fallback policy objects.
### Interface
```moonbit
pub fn ConfiguredLogger::file_policy_or_none(self : ConfiguredLogger) -> FileSinkPolicy? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose current file policy should be inspected.
#### output
- `FileSinkPolicy?` - `Some(policy)` when the configured sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(current_policy)` through the wrapped `RuntimeSink`.
- Queued file sinks forward the wrapped inner file sink policy as `Some(current_policy)`.
- Non-file sinks return `None`.
- This helper is broader than `file_append_mode()` or `file_auto_flush()` because it returns the whole policy object without fallback synthesis.
### How to Use
Here are some specific examples provided.
#### When Need Truthful Policy Diagnostics
When diagnostics should inspect file policy only if file semantics exist:
```moonbit
match logger.file_policy_or_none() {
Some(policy) => ignore(policy)
None => ()
}
```
In this example, `None` means there is no real file policy to inspect.
#### When Need Compatibility-free Recovery Decisions
When recovery logic should not treat a fallback object as real state:
```moonbit
let maybe_policy = logger.file_policy_or_none()
```
In this example, callers can distinguish missing file semantics from a live policy snapshot.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers need default-policy comparison, pair it with `file_default_policy_or_none()`.
### Notes
1. Prefer this helper over `file_policy()` for truthful runtime diagnostics.
2. `file_policy()` remains available as the compatibility fallback API.
+12 -3
View File
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Read the current runtime file policy from a configured logger, with a compatibility fallback for non-file sinks.
key-word:
- logger
- runtime
@@ -15,6 +15,10 @@ key-word:
Read the current runtime file policy from a `ConfiguredLogger`. This helper exposes the active append, auto-flush, and rotation settings as one policy object.
The returned `FileSinkPolicy` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates policy reads to the wrapped `RuntimeSink`, which in turn reaches file-backed variants that ultimately use `src/file_runtime.FileSink`.
`file_policy()` is the compatibility form. For truthful file-semantics detection, prefer `file_policy_or_none()`.
### Interface
```moonbit
@@ -35,7 +39,10 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current runtime file policy through the wrapped `RuntimeSink`.
- Queued file sinks forward the policy from the wrapped inner file sink.
- The returned policy object itself is the shared `@file_model.FileSinkPolicy` model, not a configured-logger-owned concrete type.
- Non-file sinks return the same neutral fallback policy value produced by `RuntimeSink::file_policy()`.
- This fallback keeps older callers source-compatible, but it is not a real file policy.
- New diagnostic or recovery code should prefer `file_policy_or_none()`.
- This helper is broader than `file_append_mode()` or `file_auto_flush()` because it returns the whole policy object.
### How to Use
@@ -66,10 +73,12 @@ In this example, callers can compare current runtime settings with the initial p
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 need a truthful file-semantics check, use `file_policy_or_none()`.
- 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.
2. Pair it with `file_set_policy(...)` for roundtrip-style policy management, or prefer `file_policy_or_none()` for truthful diagnostics.
+6 -3
View File
@@ -2,7 +2,7 @@
name: configured-logger-file-reopen
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Reopen the file sink behind a configured runtime logger with an optional append-mode override.
key-word:
- logger
@@ -35,9 +35,10 @@ pub fn ConfiguredLogger::file_reopen(self : ConfiguredLogger, append~ : Bool? =
Detailed rules explaining key parameters and behaviors
- Plain file sinks reopen directly through the wrapped `RuntimeSink`.
- Queued file sinks forward reopen behavior to the wrapped inner file sink.
- Queued file sinks forward reopen behavior to the wrapped inner file sink only when no queued records are pending.
- `append=None` preserves current reopen policy, while `Some(true/false)` overrides append mode.
- Non-file sinks return `false`.
- If a queued file sink still has pending records, reopen is rejected and returns `false` so queued records are not later written under a different reopen mode than the one they were queued under.
### How to Use
@@ -68,8 +69,10 @@ e.g.:
- If callers only need the current configured policy, `file_reopen_with_current_policy()` may be the clearer API.
- If a queued file sink still has pending records, callers should flush or close it first before attempting reopen.
### Notes
1. Use this helper for explicit recovery flows.
2. Pair it with `file_available()` and failure counters when diagnosing reopen behavior.
2. On queued file sinks, clear pending records first so reopen semantics stay stable for already queued writes.
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Reset the runtime file policy of a configured logger back to its captured defaults.
key-word:
- logger
- runtime
@@ -13,7 +13,7 @@ key-word:
## 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.
Reset the runtime file policy of a `ConfiguredLogger` back to its captured defaults.
### Interface
@@ -21,56 +21,17 @@ Reset the runtime file policy of a `ConfiguredLogger` back to its original defau
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 through the wrapped `RuntimeSink`.
- Queued file sinks forward the reset behavior to the wrapped inner file sink.
- File-backed sinks restore their runtime default policy through the wrapped `RuntimeSink`.
- Queued file sinks restore the wrapped inner file sink default policy only when no queued records are pending.
- 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.
- If a queued file sink still has pending records, the reset is rejected and returns `false` so already queued records are not later written under a different policy than the one they were queued under.
### Notes
1. Use this helper when the original bundled file policy should be restored intact.
1. Use this helper when runtime file policy should return to its captured defaults.
2. It pairs naturally with `file_policy_matches_default()` and `file_default_policy()`.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
@@ -15,6 +15,8 @@ key-word:
Read the current rotation configuration used by a `ConfiguredLogger` file sink. This helper exposes the active runtime rotation parameters when rotation is enabled.
The returned `FileRotation` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates rotation reads to the wrapped `RuntimeSink`.
### Interface
```moonbit
@@ -35,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks return their current rotation configuration when enabled through the wrapped `RuntimeSink`.
- Queued file sinks forward the config from the wrapped inner file sink.
- The returned rotation object itself is the shared `@file_model.FileRotation` model, not a configured-logger-owned concrete type.
- Non-file sinks return `None`.
- This helper is useful when callers need active runtime rotation parameters rather than only a boolean flag.
@@ -2,7 +2,7 @@
name: configured-logger-file-rotation-failures
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Read the number of rotation failures recorded by the configured runtime file sink.
key-word:
- logger
@@ -13,7 +13,7 @@ key-word:
## 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.
Read the number of runtime file-rotation attempts whose critical file steps failed in a `ConfiguredLogger`.
### Interface
@@ -36,6 +36,7 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks report their recorded rotation-failure count through the wrapped `RuntimeSink`.
- Queued file sinks forward the metric from the wrapped inner file sink.
- Non-file sinks return `0`.
- For file-backed paths, the counter covers incomplete rotations caused by critical remove/rename/reopen failures, not every possible file-health issue.
- The counter is cumulative until reset.
### How to Use
@@ -49,7 +50,7 @@ When support output should reveal whether rotation is failing:
let count = logger.file_rotation_failures()
```
In this example, the configured logger exposes a focused metric for rotation-path health.
In this example, the configured logger exposes whether runtime file rotation stopped completing its critical steps.
#### When Validate Runtime Rotation Tuning
@@ -58,7 +59,7 @@ When changed rotation settings should be checked in operation:
ignore(logger.file_rotation_failures())
```
In this example, callers can observe whether runtime rotation changes introduced problems.
In this example, callers can observe whether runtime rotation changes introduced incomplete rotations.
### Error Case
@@ -67,8 +68,10 @@ e.g.:
- If callers need both rotation config and failure metrics, they should combine this helper with `file_rotation_config()` or `file_state()`.
- This counter does not identify the exact remove/rename/reopen step that failed.
### Notes
1. Use this helper when diagnosing file rotation reliability.
1. Use this helper when diagnosing incomplete file rotations.
2. It is especially relevant when runtime rotation policy can change after startup.
@@ -15,6 +15,8 @@ key-word:
Read combined file and queue runtime state from a `ConfiguredLogger`. This helper is the richest file-specific diagnostics API on config-built runtime loggers.
The returned `RuntimeFileState` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates runtime file-state reads to the wrapped `RuntimeSink`.
### Interface
```moonbit
@@ -35,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(RuntimeFileState)` through the wrapped `RuntimeSink`.
- Queued file sinks include both file status and queue metrics in the returned state.
- The returned snapshot object itself is the shared `@file_model.RuntimeFileState` model, not a configured-logger-owned concrete type.
- Non-file sinks return `None`.
- This helper is richer than `file_state()` because it can also surface queued backlog and dropped counts.
@@ -2,7 +2,7 @@
name: configured-logger-file-set-append-mode
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Update the append-mode policy used by the configured runtime file sink for later reopen behavior.
key-word:
- logger
@@ -35,9 +35,10 @@ pub fn ConfiguredLogger::file_set_append_mode(self : ConfiguredLogger, append :
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their stored append policy through the wrapped `RuntimeSink`.
- Queued file sinks forward the policy update to the wrapped inner file sink.
- Queued file sinks forward the policy update to the wrapped inner file sink only when no queued records are pending.
- This helper updates policy only; it does not force immediate reopen.
- Non-file sinks return `false`.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different append policy than the one they were queued under.
### How to Use
@@ -69,8 +70,10 @@ e.g.:
- If callers need an immediate reopen as part of the same operation, one of the reopen helpers should be used afterward.
- If a queued file sink still has pending records, callers should flush or close it first before changing append mode.
### 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.
2. On queued file sinks, clear pending records first so staged reopen policy does not retroactively affect already queued writes.
@@ -2,7 +2,7 @@
name: configured-logger-file-set-auto-flush
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Update the auto-flush policy used by the configured runtime file sink.
key-word:
- logger
@@ -35,9 +35,10 @@ pub fn ConfiguredLogger::file_set_auto_flush(self : ConfiguredLogger, enabled :
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their runtime auto-flush policy through the wrapped `RuntimeSink`.
- Queued file sinks forward the update to the wrapped inner file sink.
- Queued file sinks forward the update to the wrapped inner file sink only when no queued records are pending.
- Non-file sinks return `false`.
- This helper changes policy only; it does not itself flush pending data.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different auto-flush policy than the one they were queued under.
### How to Use
@@ -68,8 +69,10 @@ e.g.:
- If callers need an immediate flush action, `file_flush()` is the operational API.
- If a queued file sink still has pending records, callers should flush or close it first before changing auto-flush policy.
### Notes
1. Use this helper for runtime durability tuning.
2. Pair it with `file_auto_flush()` to verify the active policy.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
@@ -2,7 +2,7 @@
name: configured-logger-file-set-policy
group: api
category: runtime
update-time: 20260512
update-time: 20260707
description: Apply a bundled runtime file policy update to a configured file-backed logger.
key-word:
- logger
@@ -15,6 +15,8 @@ key-word:
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.
The input `FileSinkPolicy` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates policy mutation to the wrapped `RuntimeSink`.
### Interface
```moonbit
@@ -35,9 +37,11 @@ pub fn ConfiguredLogger::file_set_policy(self : ConfiguredLogger, policy : FileS
Detailed rules explaining key parameters and behaviors
- File-backed sinks update append, auto-flush, and rotation together through the wrapped `RuntimeSink`.
- Queued file sinks forward the policy update to the wrapped inner file sink.
- Queued file sinks forward the policy update to the wrapped inner file sink only when no queued records are pending.
- The accepted policy object itself is the shared `@file_model.FileSinkPolicy` model, not a configured-logger-owned concrete type.
- Non-file sinks return `false`.
- This helper is broader than the single-setting setters because it updates the whole file policy in one call.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different policy bundle than the one they were queued under.
### How to Use
@@ -72,8 +76,10 @@ e.g.:
- If callers only need to change one setting, a narrower setter such as `file_set_auto_flush(...)` or `file_set_rotation(...)` may be clearer.
- If a queued file sink still has pending records, callers should flush or close it first before changing policy.
### 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()`.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
+15 -36
View File
@@ -2,8 +2,8 @@
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.
update-time: 20260707
description: Update the rotation policy used by the configured runtime file sink.
key-word:
- logger
- runtime
@@ -13,21 +13,20 @@ key-word:
## 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.
Update the rotation policy used by a `ConfiguredLogger` file sink.
The input `FileRotation` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates rotation mutation to the wrapped `RuntimeSink`.
### Interface
```moonbit
pub fn ConfiguredLogger::file_set_rotation(
self : ConfiguredLogger,
rotation : FileRotation?,
) -> Bool {}
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.
- `self : ConfiguredLogger` - Config-driven runtime logger whose file rotation policy should change.
- `rotation : FileRotation?` - New runtime rotation policy.
#### output
@@ -38,41 +37,21 @@ pub fn ConfiguredLogger::file_set_rotation(
Detailed rules explaining key parameters and behaviors
- File-backed sinks update their runtime rotation policy through the wrapped `RuntimeSink`.
- Queued file sinks forward the update to the wrapped inner file sink.
- Passing `None` disables rotation.
- Queued file sinks forward the update to the wrapped inner file sink only when no queued records are pending.
- The accepted rotation object itself is the shared `@file_model.FileRotation` model, not a configured-logger-owned concrete type.
- 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.
- This helper changes policy only; it does not itself rotate or flush pending data.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different rotation policy than the one they were queued under.
### 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.
- If a queued file sink still has pending records, callers should flush or close it first before changing rotation policy.
### Notes
1. Use this helper when setting a full runtime rotation config.
1. Use this helper when runtime rotation policy should change without rebuilding the logger.
2. It is useful for operational tuning without rebuilding the logger.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
@@ -0,0 +1,79 @@
---
name: configured-logger-file-state-or-none
group: api
category: runtime
update-time: 20260707
description: Read the current file sink snapshot from a ConfiguredLogger only when it is actually file-backed.
key-word:
- logger
- runtime
- file
- truthful
---
## Configured-logger-file-state-or-none
Read the current file sink snapshot from a `ConfiguredLogger` only when it is actually file-backed.
This is the truthful companion to `file_state()`. Prefer it when diagnostics or recovery logic must not confuse fallback snapshots with real file state.
### Interface
```moonbit
pub fn ConfiguredLogger::file_state_or_none(self : ConfiguredLogger) -> FileSinkState? {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose file state snapshot should be inspected.
#### output
- `FileSinkState?` - `Some(snapshot)` when the configured sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- File-backed sinks return `Some(live_snapshot)` through the wrapped `RuntimeSink`.
- Queued file sinks forward the wrapped inner file sink snapshot as `Some(live_snapshot)`.
- Non-file sinks return `None`.
- This helper is broader than individual counters or policy reads because it returns the aggregated file snapshot without fallback synthesis.
### How to Use
Here are some specific examples provided.
#### When Need Truthful File Health Diagnostics
When support output should include file state only for real file-backed sinks:
```moonbit
match logger.file_state_or_none() {
Some(state) => println(stringify_file_sink_state(state, pretty=true))
None => ()
}
```
In this example, no compatibility snapshot is fabricated for non-file sinks.
#### When Need State Without Queue Expansion
When code only needs the file snapshot and not queue metadata:
```moonbit
let maybe_state = logger.file_state_or_none()
```
In this example, `None` cleanly signals missing file semantics.
### Error Case
e.g.:
- If the configured sink is not file-backed, the method returns `None`.
- If callers also need queue metrics for queued file sinks, prefer `file_runtime_state()`.
### Notes
1. Prefer this helper over `file_state()` for truthful runtime diagnostics.
2. `file_state()` remains available as the compatibility fallback API.
+12 -3
View File
@@ -2,8 +2,8 @@
name: configured-logger-file-state
group: api
category: runtime
update-time: 20260512
description: Read the current file sink snapshot from a configured runtime logger.
update-time: 20260707
description: Read the current file sink snapshot from a configured runtime logger, with a compatibility fallback for non-file sinks.
key-word:
- logger
- runtime
@@ -15,6 +15,10 @@ key-word:
Read the current file sink snapshot from a `ConfiguredLogger`. This helper exposes path, availability, policy flags, rotation config, and failure counters as one object.
The returned `FileSinkState` value is owned by `src/file_model`; this configured-logger surface is a facade over `Logger[@runtime.RuntimeSink]` and delegates file-state reads to the wrapped `RuntimeSink`.
`file_state()` is the compatibility form. For truthful file-semantics detection, prefer `file_state_or_none()`.
### Interface
```moonbit
@@ -35,7 +39,10 @@ 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 inner file sink.
- The returned snapshot object itself is the shared `@file_model.FileSinkState` model, not a configured-logger-owned concrete type.
- Non-file sinks return the same fallback empty-style state produced by `RuntimeSink::file_state()`, with an empty path, disabled policy flags, no rotation, and zeroed counters.
- This fallback keeps older callers source-compatible, but it is not a live file-backed snapshot.
- New diagnostic or recovery code should prefer `file_state_or_none()`.
- This helper is broader than individual file counters or policy accessors because it aggregates core file status into one read.
### How to Use
@@ -65,10 +72,12 @@ In this example, the configured logger snapshot can be exported directly through
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 need a truthful file-semantics check, use `file_state_or_none()`.
- 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.
2. Prefer `file_state_or_none()` or `file_runtime_state()` when broader truthful diagnostics are needed.
@@ -0,0 +1,78 @@
---
name: configured-logger-flush-progress
group: api
category: runtime
update-time: 20260707
description: Return a structured generic flush progress snapshot for a ConfiguredLogger through RuntimeSink without collapsing queue and plain-file fallback semantics into one Int.
key-word:
- logger
- runtime
- flush
- public
---
## Configured-logger-flush-progress
Return a structured generic flush progress snapshot for a `ConfiguredLogger`. This is the recommended configured runtime wrapper over `RuntimeSink::flush_progress(...)` when code wants truthful generic progress instead of the compatibility `Int` returned by `flush()`.
### Interface
```moonbit
pub fn ConfiguredLogger::flush_progress(self : ConfiguredLogger) -> RuntimeSinkProgress {}
```
#### input
- `self : ConfiguredLogger` - Config-driven runtime logger whose generic flush progress should be observed.
#### output
- `RuntimeSinkProgress` - Structured progress snapshot returned by the wrapped `RuntimeSink::flush_progress(...)` call.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates directly to `self.sink.flush_progress()`.
- Queue-wrapped sinks report queue advancement through `queue_advanced_count`.
- Plain file sinks report the generic fallback flush step through `file_flush_step_count`.
- Plain console-style sinks return a zeroed snapshot.
- This helper is the configured-logger entry point for the truthful generic progress surface introduced alongside the older compatibility `flush()` path.
### How to Use
Here are some specific examples provided.
#### When Need Recommended Generic Flush Progress On A Config-built Logger
When config-built runtime code should inspect flush progress explicitly:
```moonbit
let progress = logger.flush_progress()
```
In this example, the configured runtime logger reports structured progress without collapsing runtime-shape semantics.
#### When Need To Branch On Queue-backed Versus Plain-file Generic Progress
When application code should keep the config-built facade but still distinguish progress meaning:
```moonbit
let progress = logger.flush_progress()
if progress.queue_backed {
println(progress.queue_advanced_count)
}
```
In this example, queue advancement remains explicit even though the logger was built from config.
### Error Case
e.g.:
- If the configured runtime sink shape has no flushable state, the snapshot simply reports zero counts.
- If callers need file-specific success semantics on a file-backed logger, `file_flush()` is the better API.
### Notes
1. Prefer `flush_progress()` over `flush()` in new configured-runtime code.
2. `flush()` is still available for compatibility with older code that expects one numeric count.
+21 -14
View File
@@ -2,8 +2,8 @@
name: configured-logger-flush
group: api
category: runtime
update-time: 20260613
description: Flush a configured runtime logger and return how many queued or file-backed operations were advanced through RuntimeSink.
update-time: 20260707
description: Compatibility configured runtime flush helper that still returns one Int by collapsing generic queue progress and plain-file fallback steps.
key-word:
- logger
- runtime
@@ -13,7 +13,7 @@ key-word:
## Configured-logger-flush
Flush a `ConfiguredLogger` and return how much work was advanced. This is the configured logger wrapper over `RuntimeSink::flush(...)` for forcing queued or file-backed output to move forward after config-driven construction.
Flush a `ConfiguredLogger` and return one compatibility count. This helper is still available for older code, but new configured-runtime code should prefer `flush_progress()` because it exposes queue advancement and plain-file fallback steps separately.
### Interface
@@ -27,16 +27,17 @@ pub fn ConfiguredLogger::flush(self : ConfiguredLogger) -> Int {}
#### output
- `Int` - Count returned by the wrapped `RuntimeSink::flush(...)` call.
- `Int` - Compatibility count returned by the wrapped `RuntimeSink::flush(...)` call.
### Explanation
Detailed rules explaining key parameters and behaviors
- This helper delegates directly to `self.sink.flush()`.
- Queue-wrapped sinks forward to the concrete queue sink's `flush()` behavior.
- Plain file sinks call `FileSink::flush()` through `RuntimeSink` and convert the boolean result into `1` or `0`.
- Plain console-style sinks return `0` because they do not expose a meaningful buffered flush step here.
- Under the hood, `RuntimeSink::flush()` now rebuilds one compatibility count from `flush_progress()`.
- Queue-wrapped sinks still report queued-record consumption through that compatibility count.
- Plain file sinks still report the generic fallback flush step as `1` or `0`.
- Plain console-style sinks still return `0`.
### How to Use
@@ -44,31 +45,37 @@ Here are some specific examples provided.
#### When Need Explicit Queue Progress
When config-built queue output should be advanced manually:
When older code already expects one numeric flush count from a config-built logger:
```moonbit
ignore(logger.flush())
```
In this example, the configured runtime logger is flushed without reaching into the sink directly.
In this example, the configured runtime logger is flushed without changing the legacy return shape.
#### When Need A Post-write Flush Barrier
When a service wants stronger delivery behavior after a burst of writes:
When code only needs a coarse non-zero check and does not care which runtime dimension advanced:
```moonbit
let flushed = logger.flush()
```
In this example, callers can observe how much work was advanced by the flush request.
In this example, callers still get one compatibility count rather than a structured progress snapshot.
### Error Case
e.g.:
- If the configured runtime sink shape has no flushable state, the method may simply return `0`.
- If callers need bounded manual draining rather than generic flush behavior, `drain(...)` is the better API.
- If callers need truthful generic progress semantics, `flush_progress()` is the better API.
- If callers need bounded manual draining rather than generic flush behavior, `drain_progress(...)` is usually the better API.
- If callers need file-specific success semantics on a file-backed logger, `file_flush()` is the better API.
### Notes
1. Use this helper after config-driven logger construction when explicit runtime flushing matters.
1. Treat this method as a compatibility helper for older numeric code.
2. The exact return value depends on which `RuntimeSink` variant the configured logger owns.
2. Prefer `flush_progress()` in new configured-runtime code.
3. The exact return value depends on which `RuntimeSink` variant the configured logger owns.
+3 -2
View File
@@ -2,7 +2,7 @@
name: configured-logger
group: api
category: runtime
update-time: 20260613
update-time: 20260707
description: Public configured runtime logger alias used for config-built sync logging with queue and file controls.
key-word:
- logger
@@ -33,7 +33,8 @@ Detailed rules explaining key parameters and behaviors
- It preserves normal logger methods such as `info(...)`, `warn(...)`, `error(...)`, and the other `Logger` APIs.
- Because it is `Logger[RuntimeSink]`, it also keeps ordinary logger composition and target behavior such as `with_target(...)`, `child(...)`, and per-call `log(..., target=...)` overrides.
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
- It also exposes configured runtime helpers such as `flush()`, `drain()`, `pending_count()`, `dropped_count()`, and file-specific controls when the runtime sink supports them.
- It also exposes configured runtime helpers such as `flush_progress()`, `drain_progress()`, `flush()`, `drain()`, `pending_count()`, `dropped_count()`, and file-specific controls when the runtime sink supports them.
- `flush_progress()` and `drain_progress()` are the recommended truthful generic progress helpers, while `flush()` and `drain()` remain compatibility wrappers that collapse the structured result back into one `Int`.
- Because `ConfiguredLogger` is only an alias over `Logger[RuntimeSink]`, ordinary sync composition helpers such as `with_target(...)`, `child(...)`, `with_timestamp(...)`, `with_filter(...)`, and `with_patch(...)` keep the same visible runtime-sink logger line rather than stripping away the configured helper surface.
- In current direct builder coverage, derived configured loggers still preserve queue counters, drain or flush behavior, runtime sink variant choice, and file helper access instead of collapsing back to a narrower non-runtime shape.
- Builders such as `build_logger(...)` and `parse_and_build_logger(...)` return this alias as the main sync config-to-runtime result.
+20 -6
View File
@@ -13,12 +13,12 @@ key-word:
## File-rotation-config-to-json
Convert `FileRotation` into a `JsonValue`. This helper is the structured export path for file rotation policy when callers need machine-readable config data instead of a runtime policy object.
Convert `FileRotation` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
### Interface
```moonbit
pub fn file_rotation_config_to_json(config : FileRotation) -> @json_parser.JsonValue {}
pub fn file_rotation_config_to_json(config : FileRotation) -> Json {}
```
#### input
@@ -27,14 +27,16 @@ pub fn file_rotation_config_to_json(config : FileRotation) -> @json_parser.JsonV
#### output
- `JsonValue` - Structured JSON representation of the rotation policy.
- `Json` - Structured JSON representation of the rotation policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- The output includes `max_bytes` and `max_backups`.
- Both numeric fields are exported as JSON numbers.
- The output always includes `max_bytes` and `max_backups`.
- The root surface forwards to `@file_model.file_rotation_config_to_json(...)`, which is the real owner of this serialization logic.
- Both of those compatibility fields are exported as JSON numbers.
- If the policy carries a wide threshold from `file_rotation_i64(...)`, the output also includes `max_bytes_i64` as a JSON string for exact roundtrip transport.
- This helper serializes the rotation config object itself rather than sink availability, failure counters, or file state.
- The same JSON shape is reused by `sink_config_to_json(...)`, file sink state export helpers, and larger logger config serialization paths.
@@ -61,6 +63,16 @@ let rotation_json = file_rotation_config_to_json(rotation)
In this example, callers can carry the nested rotation shape without exporting a full sink config.
#### When Need Exact Wide-threshold Roundtrip
When a native large-file policy should preserve the original `Int64` threshold in JSON:
```moonbit
let rotation = file_rotation_i64(4294967296L, max_backups=2)
let rotation_json = file_rotation_config_to_json(rotation)
```
In this example, the JSON value includes the compatibility `max_bytes` view plus `max_bytes_i64` as a string.
### Error Case
e.g.:
@@ -70,6 +82,8 @@ e.g.:
### Notes
1. Use this helper when downstream code expects `JsonValue` rather than a typed `FileRotation` value.
1. Use this helper when downstream code expects `Json` rather than a typed `FileRotation` value.
2. Pair it with `file_rotation(...)` when building rotation policy in code before export.
3. For `file_rotation_i64(...)`, treat `max_bytes_i64` as the exact roundtrip field and `max_bytes` as the compatibility view.
+74
View File
@@ -0,0 +1,74 @@
---
name: file-rotation-i64
group: api
category: sink
update-time: 20260705
description: Create a native-focused large-file rotation policy using an Int64 byte threshold.
key-word:
- file
- rotation
- i64
- native
---
## File-rotation-i64
Create a size-based file rotation policy with an `Int64` byte threshold. This helper is the opt-in native-focused path for large-file rotation scenarios that exceed the default `Int`-based contract.
### Interface
```moonbit
pub fn file_rotation_i64(max_bytes : Int64, max_backups~ : Int = 1) -> FileRotation {}
```
#### input
- `max_bytes : Int64` - Maximum active file size threshold before rotation on the native-focused wide path.
- `max_backups : Int` - Number of retained backup files.
#### output
- `FileRotation` - Rotation policy value that preserves a native wide threshold while staying compatible with the existing file sink API surface.
### Explanation
- `max_bytes <= 0L` is normalized to `1L`.
- `max_backups <= 0` is normalized to `1`.
- Rotation is size-based only.
- This helper keeps the existing `FileRotation` type but stores an additional native wide threshold internally for runtime rotation checks.
- The helper is intended for native file backends and advanced large-file scenarios.
- JSON config export helpers preserve the exact threshold through `max_bytes_i64` while still exposing a compatibility `max_bytes` view.
### How to Use
#### When Need Native Large-file Rotation Thresholds
```moonbit
let sink = file_sink(
"app.log",
rotation=Some(file_rotation_i64(5368709120L, max_backups=4)),
)
```
In this example, the active rotation threshold is expressed with `Int64` rather than the default `Int` path.
#### When Need A Wide Policy Value First
```moonbit
let rotation = file_rotation_i64(3221225472L, max_backups=2)
let sink = file_sink("app.log", rotation=Some(rotation))
```
In this example, the wide threshold policy is assembled separately and then reused.
### Error Case
- If `max_bytes` is non-positive, it is clamped to `1L`.
- This API does not make non-native targets gain real file support; callers should still use `native_files_supported()` when portability matters.
### Notes
1. This API is additive and does not replace the default `file_rotation(...)` contract.
2. Prefer the default `Int` path unless a native large-file threshold is actually required.
3. String-based JSON roundtrip uses `max_bytes_i64` for the exact threshold and keeps `max_bytes` as the compatibility field.
4. This helper is designed for advanced use and target-aware code.
+13 -9
View File
@@ -2,8 +2,8 @@
name: file-rotation-type
group: api
category: sink
update-time: 20260613
description: Public file rotation alias used for native size-based file sink policy data.
update-time: 20260707
description: Public file rotation type re-exported from file_model for root file and config APIs.
key-word:
- file
- rotation
@@ -13,26 +13,28 @@ key-word:
## File-rotation-type
`FileRotation` is the public file sink rotation policy type used for native size-based log file rollover. It is a direct alias to the sink model that stores the byte limit and retained backup count.
`FileRotation` is the public file sink rotation policy type used for size-based log file rollover. On the root `src` facade, it is re-exported from `src/file_model`, which is the real owner of the concrete file rotation model.
### Interface
```moonbit
pub type FileRotation = @utils.FileRotation
pub using @file_model { type FileRotation }
```
#### output
- `FileRotation` - Public file rotation policy object containing `max_bytes` and `max_backups`.
- `FileRotation` - Public file rotation policy object containing the standard `max_bytes` view, `max_backups`, and additive native wide-threshold support for advanced rotation flows.
### Explanation
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a file sink or rotation trigger by itself.
- The current fields are `max_bytes : Int` and `max_backups : Int`.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@file_model.FileRotation`, not in `@utils` and not in `@file_runtime`.
- The current fields are `max_bytes : Int`, `max_backups : Int`, and additive native wide-threshold metadata used by the optional `file_rotation_i64(...)` path.
- `file_rotation(...)` constructs this type as the main public helper.
- The same value type is consumed by `file_sink(...)`, `FileSinkPolicy::new(...)`, `SinkConfig`, file runtime inspection APIs, and logger config serialization helpers.
- `file_runtime` owns the live `FileSink` behavior that consumes this type, while `runtime` owns higher-level runtime file projections.
### How to Use
@@ -65,6 +67,8 @@ e.g.:
### Notes
1. Use `file_rotation(...)` when you need a value of this type in code.
1. Use `file_rotation(...)` when you need the default `Int`-based value of this type in code.
2. Use `native_files_supported()` or the target verification guidance when portability matters across non-native targets.
2. Use `file_rotation_i64(...)` when a native large-file threshold must exceed the default `Int` contract.
3. Use `native_files_supported()` or the target verification guidance when portability matters across non-native targets.
+5
View File
@@ -38,6 +38,9 @@ Detailed rules explaining key parameters and behaviors
- `max_backups <= 0` is normalized to `1`.
- Rotation is size-based only.
- This policy is consumed by `file_sink(...)`, file policy helpers, and config-driven file sink assembly.
- The default `max_bytes : Int` path follows the current 32-bit `Int` contract used by the standard file rotation APIs.
- On native targets, that default path is intended for ordinary log-file ranges rather than explicit large-file guarantees beyond the 32-bit `Int` boundary.
- If a native large-file threshold is required, prefer `file_rotation_i64(...)`.
### How to Use
@@ -79,3 +82,5 @@ e.g.:
2. Rotation currently focuses on size thresholds rather than time schedules or compression.
3. The default API keeps the portable `Int`-based contract stable; the wider native-only path is exposed separately through `file_rotation_i64(...)`.
+8 -4
View File
@@ -13,12 +13,12 @@ key-word:
## File-sink-policy-to-json
Convert `FileSinkPolicy` into a `JsonValue`. This helper exports append mode, auto-flush, and optional rotation as a structured runtime policy snapshot.
Convert `FileSinkPolicy` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
### Interface
```moonbit
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> @json_parser.JsonValue {}
pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> Json {}
```
#### input
@@ -27,17 +27,19 @@ pub fn file_sink_policy_to_json(policy : FileSinkPolicy) -> @json_parser.JsonVal
#### output
- `JsonValue` - Structured JSON representation of the file policy.
- `Json` - Structured JSON representation of the file policy.
### Explanation
Detailed rules explaining key parameters and behaviors
- The output includes `append`, `auto_flush`, and `rotation`.
- The root surface forwards to `@file_model.file_sink_policy_to_json(...)`, which is the real owner of this serialization logic.
- `rotation` is exported as `null` when rotation is disabled.
- This helper exports runtime file policy, not current file health counters or availability.
- The JSON value is useful for policy snapshots, comparisons, and diagnostics payloads.
- Typical inputs come from `FileSink::policy()`, `RuntimeSink::file_policy()`, or `ConfiguredLogger::file_policy()`.
- If the active rotation policy originated from `file_rotation_i64(...)`, the nested rotation JSON includes both the compatibility `max_bytes` number and `max_bytes_i64` as the exact string form.
### How to Use
@@ -71,6 +73,8 @@ e.g.:
### Notes
1. Use this helper when downstream code expects `JsonValue` rather than text.
1. Use this helper when downstream code expects `Json` rather than text.
2. It pairs naturally with `FileSink::policy()`, `RuntimeSink::file_policy()`, and related default-policy accessors.
3. For wide native rotation policies, downstream code should read `rotation.max_bytes_i64` when exact threshold recovery matters.
+6 -5
View File
@@ -2,8 +2,8 @@
name: file-sink-policy
group: api
category: sink
update-time: 20260613
description: Public file policy alias used by file sinks and runtime file-control APIs.
update-time: 20260707
description: Public file policy type re-exported from file_model for file and runtime control APIs.
key-word:
- file
- policy
@@ -13,12 +13,12 @@ key-word:
## File-sink-policy
`FileSinkPolicy` is the public policy object used to describe file append mode, auto-flush behavior, and optional rotation settings together. It is a direct alias to the file policy model shared by `FileSink`, `RuntimeSink`, `ConfiguredLogger`, and file policy JSON helpers.
`FileSinkPolicy` is the public policy object used to describe file append mode, auto-flush behavior, and optional rotation settings together. On the root `src` facade, it is re-exported from `src/file_model`, which is the real owner of the concrete policy model.
### Interface
```moonbit
pub type FileSinkPolicy = @utils.FileSinkPolicy
pub using @file_model { type FileSinkPolicy }
```
#### output
@@ -29,7 +29,8 @@ pub type FileSinkPolicy = @utils.FileSinkPolicy
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a separate runtime wrapper.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@file_model.FileSinkPolicy`, not in `@utils`.
- The current policy fields are `append : Bool`, `auto_flush : Bool`, and `rotation : FileRotation?`.
- The same policy object is returned by `FileSink::policy()`, `RuntimeSink::file_policy()`, and `ConfiguredLogger::file_policy()`.
- It is also accepted by `FileSink::set_policy(...)`, `RuntimeSink::file_set_policy(...)`, and `ConfiguredLogger::file_set_policy(...)`.
+2 -1
View File
@@ -2,7 +2,7 @@
name: file-sink-reopen-append
group: api
category: sink
update-time: 20260613
update-time: 20260717
description: Reopen a FileSink in append mode.
key-word:
- file
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- Reopen behavior is fixed to append mode.
- The stored append policy is updated to `true` as part of the reopen path.
- On failure, the sink remains unavailable and `open_failures` is incremented.
- After a rotation failure, this is the explicit recovery path once the underlying remove, rename, or open condition has been resolved.
### How to Use
+12 -5
View File
@@ -2,7 +2,7 @@
name: file-sink-rotation-failures
group: api
category: sink
update-time: 20260613
update-time: 20260717
description: Read the number of rotation failures recorded by a FileSink.
key-word:
- file
@@ -13,7 +13,7 @@ key-word:
## File-sink-rotation-failures
Read the number of rotation failures recorded by a `FileSink`. This helper is useful when direct runtime rotation behavior should be observed operationally.
Read the number of rotation attempts whose critical file-rotation steps failed for a `FileSink`.
### Interface
@@ -34,6 +34,9 @@ pub fn FileSink::rotation_failures(self : FileSink) -> Int {
Detailed rules explaining key parameters and behaviors
- The sink reports its recorded rotation-failure count directly.
- The counter increases when a rotation attempt cannot complete its critical file steps, such as removing the active file with zero backups, renaming a live file into `.1`, shifting an existing backup to a higher slot, or reopening the fresh active file afterward.
- Missing optional backup slots do not count by themselves; only steps that fail while their source path still exists are treated as rotation failures.
- When a critical rotation step fails, the triggering record is not written and the sink remains unavailable until an explicit reopen succeeds.
- The counter is cumulative until reset.
- This helper is observation-only and does not mutate sink state.
@@ -48,7 +51,7 @@ When support output should reveal whether direct sink rotation is failing:
let count = sink.rotation_failures()
```
In this example, the concrete file sink exposes a focused metric for rotation-path health.
In this example, the concrete file sink exposes whether a rotation attempt hit a critical file-step failure.
#### When Validate Runtime Rotation Tuning
@@ -57,17 +60,21 @@ When changed rotation settings should be checked in operation:
ignore(sink.rotation_failures())
```
In this example, callers can observe whether runtime rotation changes introduced problems.
In this example, callers can observe whether runtime rotation changes started producing incomplete rotations.
### Error Case
e.g.:
- If callers need both rotation config and failure metrics, they should combine this helper with `rotation_config()` or `state()`.
- This counter alone does not identify which rename/remove/reopen step failed.
- This counter alone does not say whether the sink is currently available.
- After a recorded rotation failure, use `reopen_append()` only after the underlying filesystem condition has been resolved.
### Notes
1. Use this helper when diagnosing direct file rotation reliability.
1. Use this helper when diagnosing incomplete direct file rotations.
2. It is especially relevant when runtime rotation policy can change after startup.
+8 -4
View File
@@ -13,12 +13,12 @@ key-word:
## File-sink-state-to-json
Convert `FileSinkState` into a `JsonValue`. This helper exports file path, availability, policy flags, rotation config, and failure counters in a structured form.
Convert `FileSinkState` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
### Interface
```moonbit
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {}
pub fn file_sink_state_to_json(state : FileSinkState) -> Json {}
```
#### input
@@ -27,17 +27,19 @@ pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue
#### output
- `JsonValue` - Structured JSON representation of the file sink state.
- `Json` - Structured JSON representation of the file sink state.
### Explanation
Detailed rules explaining key parameters and behaviors
- The output includes `path`, `available`, `append`, `auto_flush`, failure counters, and `rotation`.
- The root surface forwards to `@file_model.file_sink_state_to_json(...)`, which is the real owner of this serialization logic.
- `rotation` is exported as `null` when rotation is disabled.
- This helper exports state snapshots, not mutable runtime control handles.
- It is useful when file sink state should be embedded into larger diagnostics payloads.
- Typical inputs come from `FileSink::state()`, `RuntimeSink::file_state()`, or `ConfiguredLogger::file_state()`.
- If the live rotation policy was created from `file_rotation_i64(...)`, the nested `rotation` JSON includes `max_bytes_i64` as a string alongside the compatibility `max_bytes` number.
### How to Use
@@ -70,6 +72,8 @@ e.g.:
### Notes
1. Use this helper when diagnostics consumers expect `JsonValue`.
1. Use this helper when diagnostics consumers expect `Json`.
2. It pairs naturally with `FileSink::state()`, `RuntimeSink::file_state()`, and `ConfiguredLogger::file_state()`.
3. For wide native rotation policies, `rotation.max_bytes_i64` is the exact roundtrip field.
+7 -5
View File
@@ -2,8 +2,8 @@
name: file-sink-state
group: api
category: runtime
update-time: 20260613
description: Public file state alias used for live file-sink snapshots and runtime diagnostics.
update-time: 20260707
description: Public file state type re-exported from file_model for live file-sink snapshots and runtime diagnostics.
key-word:
- file
- state
@@ -13,12 +13,12 @@ key-word:
## File-sink-state
`FileSinkState` is the public snapshot object used to describe the current state of a file sink. It is a direct alias to the file state model returned by `FileSink::state()` and by higher-level runtime file inspection helpers.
`FileSinkState` is the public snapshot object used to describe the current state of a file sink. On the root `src` facade, it is re-exported from `src/file_model`, which is the real owner of the concrete file state model.
### Interface
```moonbit
pub type FileSinkState = @utils.FileSinkState
pub using @file_model { type FileSinkState }
```
#### output
@@ -29,8 +29,10 @@ pub type FileSinkState = @utils.FileSinkState
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a live file handle wrapper.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@file_model.FileSinkState`, not in `@utils`.
- The current snapshot fields are `path`, `available`, `append`, `auto_flush`, `rotation`, `open_failures`, `write_failures`, `flush_failures`, and `rotation_failures`.
- `rotation_failures` records how many rotation attempts failed to complete critical remove/rename/reopen steps; it is not a full file-health or backup-integrity audit.
- `FileSink::state()` returns this object directly for a concrete file sink.
- `RuntimeSink::file_state()` also returns this type, including fallback snapshots for non-file runtime sinks.
- `ConfiguredLogger::file_state()` returns the same snapshot type through the config-built runtime wrapper.
+7 -20
View File
@@ -2,8 +2,8 @@
name: file-sink-type
group: api
category: sink
update-time: 20260613
description: Public native file sink type used for file-backed synchronous logging with runtime policy and failure tracking.
update-time: 20260707
description: Public file sink type re-exported from file_runtime for file-backed synchronous logging.
key-word:
- sink
- file
@@ -13,26 +13,12 @@ key-word:
## File-sink-type
`FileSink` is the public native file sink type used for file-backed synchronous logging. It is the concrete sink type returned by `file_sink(...)`, and it stores runtime file policy, availability state, formatter behavior, and failure counters.
`FileSink` is the public file sink type used for file-backed synchronous logging. On the root `src` facade, it is re-exported from `src/file_runtime`, which is the real owner of the concrete sink behavior type.
### Interface
```moonbit
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 using @file_runtime { type FileSink }
```
#### output
@@ -43,8 +29,9 @@ pub struct FileSink {
Detailed rules explaining key parameters and behaviors
- This is a public root struct, not a type alias.
- The current fields cover path, append mode, handle state, formatter, auto-flush policy, optional rotation policy, and failure counters.
- This root surface is a re-export of the concrete `@file_runtime.FileSink` type.
- The struct intentionally hides its fields; callers interact through methods such as `flush()`, `close()`, `reopen()`, `policy()`, and `state()`.
- File model companions such as `FileRotation`, `FileSinkPolicy`, `FileSinkState`, and `RuntimeFileState` are owned by `@file_model`, not by `FileSink` itself.
- `file_sink(...)` constructs this type directly from path and policy inputs.
- The type exposes runtime helpers such as `flush()`, `close()`, `reopen()`, `policy()`, `state()`, and failure-counter accessors.
+4 -2
View File
@@ -13,7 +13,7 @@ key-word:
## File-sink
Create a native file sink for text-oriented or custom-formatted file logging. This API includes lifecycle controls, rotation configuration, reopen behavior, policy inspection, and failure counters.
Create a native file sink for text-oriented or custom-formatted file logging. On the root `src` facade, this helper forwards to `src/file_runtime`, while the companion file model types it accepts and returns are owned by `src/file_model`.
### Interface
@@ -32,7 +32,7 @@ pub fn file_sink(
- `path : String` - Destination file path.
- `append : Bool` - Whether opening/reopening should append rather than truncate.
- `auto_flush : Bool` - Whether flush is attempted after each write.
- `rotation : FileRotation?` - Optional size-based rotation policy.
- `rotation : FileRotation?` - Optional size-based rotation policy. The default helper path is `file_rotation(...)`; advanced native large-file callers can also pass a policy created by `file_rotation_i64(...)`.
- `formatter : RecordFormatter` - Formatter used to render each record before writing.
#### output
@@ -44,6 +44,8 @@ pub fn file_sink(
Detailed rules explaining key parameters and behaviors
- This sink is designed for host-file capable backends, with current local verification centered on `native`.
- The root `file_sink(...)` entry is a facade over `@file_runtime.file_sink(...)`.
- The accepted `FileRotation` input and the related `FileSinkPolicy` / `FileSinkState` diagnostics types come from `@file_model`.
- `llvm` should be treated as experimental in the current release context and was not successfully re-verified in this environment.
- The `src` library is still designed for multi-target compilation, but file sink availability remains backend-sensitive inside that broader portable surface.
- `append` is persistent policy state and also affects later reopen behavior.
+6 -2
View File
@@ -13,7 +13,9 @@ key-word:
## File
Create a `LoggerConfig` preset for the built-in file sink. This helper packages file path, append policy, auto-flush behavior, optional rotation, and text formatter settings into a single config object for runtime logger assembly.
Create a `LoggerConfig` preset for the built-in file sink. On the root `src` facade, this helper forwards to the preset builder owned by `src/presets_pkg` and returns a `LoggerConfig` owned by `src/config_model`.
The optional `rotation` value accepted here is the shared `FileRotation` model owned by `src/file_model`.
### Interface
@@ -38,7 +40,7 @@ pub fn file(
- `timestamp : Bool` - Whether the built logger should emit timestamps.
- `append : Bool` - Whether file writes should append instead of truncate-on-open behavior.
- `auto_flush : Bool` - Whether writes should be flushed automatically.
- `rotation : FileRotation?` - Optional size-based file rotation policy.
- `rotation : FileRotation?` - Optional size-based file rotation policy. The default `file_rotation(...)` path uses the standard `Int`-based threshold contract, while advanced native large-file callers can opt into `file_rotation_i64(...)`.
- `text_formatter : TextFormatterConfig` - Formatter config used for file text rendering.
#### output
@@ -50,6 +52,8 @@ pub fn file(
Detailed rules explaining key parameters and behaviors
- This preset always returns `sink.kind=SinkKind::File`.
- The root `file(...)` entry is a facade over `@presets_pkg.file(...)`.
- The returned config shape is the shared `@config_model.LoggerConfig` / `@config_model.SinkConfig` model, not a preset-owned concrete type.
- `path` must be non-empty.
- `rotation=None` leaves file rotation disabled until a rotation policy is provided directly or through `with_file_rotation(...)`.
- `queue=None` by default, so buffering is opt-in through `with_queue(...)`.
+18
View File
@@ -31,6 +31,7 @@ BitLogger API navigation.
- [logger-with-timestamp.md](./logger-with-timestamp.md)
- [logger-with-context-fields.md](./logger-with-context-fields.md)
- [logger-bind.md](./logger-bind.md)
- [logger-with-trace-context.md](./logger-with-trace-context.md)
- [logger-with-filter.md](./logger-with-filter.md)
- [logger-with-patch.md](./logger-with-patch.md)
- [logger-with-queue.md](./logger-with-queue.md)
@@ -99,6 +100,10 @@ BitLogger API navigation.
- [fields.md](./fields.md)
- [text-formatter-config-type.md](./text-formatter-config-type.md)
## Observability
- [trace-context.md](./trace-context.md)
## Record and level
- [record.md](./record.md)
@@ -178,6 +183,7 @@ BitLogger API navigation.
- [file-sink-type.md](./file-sink-type.md)
- [native-files-supported.md](./native-files-supported.md)
- [file-rotation.md](./file-rotation.md)
- [file-rotation-i64.md](./file-rotation-i64.md)
- [file-rotation-type.md](./file-rotation-type.md)
- [file-sink-policy-to-json.md](./file-sink-policy-to-json.md)
- [file-sink-policy.md](./file-sink-policy.md)
@@ -244,6 +250,7 @@ BitLogger API navigation.
- [file.md](./file.md)
- [with-queue.md](./with-queue.md)
- [with-file-rotation.md](./with-file-rotation.md)
- [with-file-rotation-i64.md](./with-file-rotation-i64.md)
## Async logger
@@ -264,6 +271,7 @@ BitLogger API navigation.
- [async-logger-with-min-level.md](./async-logger-with-min-level.md)
- [async-logger-with-timestamp.md](./async-logger-with-timestamp.md)
- [async-logger-with-context-fields.md](./async-logger-with-context-fields.md)
- [async-logger-with-trace-context.md](./async-logger-with-trace-context.md)
- [async-logger-with-filter.md](./async-logger-with-filter.md)
- [async-logger-with-patch.md](./async-logger-with-patch.md)
@@ -320,6 +328,7 @@ BitLogger API navigation.
- [library-logger-child.md](./library-logger-child.md)
- [library-logger-with-context-fields.md](./library-logger-with-context-fields.md)
- [library-logger-bind.md](./library-logger-bind.md)
- [library-logger-with-trace-context.md](./library-logger-with-trace-context.md)
- [library-logger-is-enabled.md](./library-logger-is-enabled.md)
- [library-logger-log.md](./library-logger-log.md)
- [library-logger-info.md](./library-logger-info.md)
@@ -339,6 +348,7 @@ BitLogger API navigation.
- [library-async-logger-child.md](./library-async-logger-child.md)
- [library-async-logger-with-context-fields.md](./library-async-logger-with-context-fields.md)
- [library-async-logger-bind.md](./library-async-logger-bind.md)
- [library-async-logger-with-trace-context.md](./library-async-logger-with-trace-context.md)
- [library-async-logger-is-enabled.md](./library-async-logger-is-enabled.md)
- [library-async-logger-log.md](./library-async-logger-log.md)
- [library-async-logger-info.md](./library-async-logger-info.md)
@@ -364,6 +374,7 @@ BitLogger API navigation.
- [runtime-sink-pending-count.md](./runtime-sink-pending-count.md)
- [runtime-sink-dropped-count.md](./runtime-sink-dropped-count.md)
- [runtime-sink-file-path.md](./runtime-sink-file-path.md)
- [runtime-sink-file-path-or-none.md](./runtime-sink-file-path-or-none.md)
- [runtime-sink-file-available.md](./runtime-sink-file-available.md)
- [runtime-sink-file-reopen.md](./runtime-sink-file-reopen.md)
- [runtime-sink-file-reopen-with-current-policy.md](./runtime-sink-file-reopen-with-current-policy.md)
@@ -387,9 +398,12 @@ BitLogger API navigation.
- [runtime-sink-file-reset-failure-counters.md](./runtime-sink-file-reset-failure-counters.md)
- [runtime-sink-file-reset-policy.md](./runtime-sink-file-reset-policy.md)
- [runtime-sink-file-policy.md](./runtime-sink-file-policy.md)
- [runtime-sink-file-policy-or-none.md](./runtime-sink-file-policy-or-none.md)
- [runtime-sink-file-default-policy.md](./runtime-sink-file-default-policy.md)
- [runtime-sink-file-default-policy-or-none.md](./runtime-sink-file-default-policy-or-none.md)
- [runtime-sink-file-policy-matches-default.md](./runtime-sink-file-policy-matches-default.md)
- [runtime-sink-file-state.md](./runtime-sink-file-state.md)
- [runtime-sink-file-state-or-none.md](./runtime-sink-file-state-or-none.md)
- [runtime-sink-file-runtime-state.md](./runtime-sink-file-runtime-state.md)
- [configured-logger.md](./configured-logger.md)
- [configured-logger-flush.md](./configured-logger-flush.md)
@@ -402,10 +416,14 @@ BitLogger API navigation.
- [configured-logger-file-available.md](./configured-logger-file-available.md)
- [configured-logger-file-path.md](./configured-logger-file-path.md)
- [configured-logger-file-path-or-none.md](./configured-logger-file-path-or-none.md)
- [configured-logger-file-state.md](./configured-logger-file-state.md)
- [configured-logger-file-state-or-none.md](./configured-logger-file-state-or-none.md)
- [configured-logger-file-runtime-state.md](./configured-logger-file-runtime-state.md)
- [configured-logger-file-policy.md](./configured-logger-file-policy.md)
- [configured-logger-file-policy-or-none.md](./configured-logger-file-policy-or-none.md)
- [configured-logger-file-default-policy.md](./configured-logger-file-default-policy.md)
- [configured-logger-file-default-policy-or-none.md](./configured-logger-file-default-policy-or-none.md)
- [configured-logger-file-policy-matches-default.md](./configured-logger-file-policy-matches-default.md)
- [configured-logger-file-rotation-config.md](./configured-logger-file-rotation-config.md)
- [configured-logger-file-rotation-enabled.md](./configured-logger-file-rotation-enabled.md)
+4 -4
View File
@@ -2,7 +2,7 @@
name: library-async-logger-new
group: api
category: facade
update-time: 20260613
update-time: 20260707
description: Create a narrower library-facing async logger facade by building a regular async logger and wrapping the same underlying state.
key-word:
- async
@@ -23,7 +23,7 @@ pub fn[S] LibraryAsyncLogger::new(
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
target~ : String = "",
flush~ : (S) -> Int raise = fn(_) { 0 },
flush~ : (S) -> Unit raise = fn(_) { () },
) -> LibraryAsyncLogger[S] {
```
@@ -33,7 +33,7 @@ pub fn[S] LibraryAsyncLogger::new(
- `config : AsyncLoggerConfig` - Queue size, overflow behavior, batching, linger, and flush policy.
- `min_level : Level` - Minimum enabled level. Messages below this threshold are ignored before enqueue.
- `target : String` - Default target attached to emitted records unless later overridden.
- `flush : (S) -> Int raise` - Flush callback used when async batch or shutdown policy needs explicit flushing, and allowed to raise if flushing fails.
- `flush : (S) -> Unit raise` - Flush callback used when async batch or shutdown policy needs explicit flushing, and allowed to raise if flushing fails.
#### output
@@ -73,7 +73,7 @@ When the sink requires a flush callback for batch or shutdown policy:
```moonbit
let logger = LibraryAsyncLogger::new(
@bitlogger.console_sink(),
flush=fn(sink) { 0 },
flush=fn(sink) { () },
)
```
+3 -1
View File
@@ -2,7 +2,7 @@
name: library-async-logger-run
group: api
category: facade
update-time: 20260613
update-time: 20260707
description: Start the LibraryAsyncLogger worker loop by delegating to the wrapped async logger's run behavior.
key-word:
- async
@@ -35,6 +35,7 @@ Detailed rules explaining key parameters and behaviors
- This method delegates directly to the wrapped async logger's `run()` behavior.
- It starts the worker loop that makes accepted queued records reach the underlying sink.
- The delegated `run()` call now preserves the wrapped logger's single-worker guard, so a second concurrent facade-level startup raises `AsyncLoggerAlreadyRunning`.
- Failure and lifecycle state are still tracked by the wrapped async logger.
- Once a delegated `run()` call has actually started, it clears any previous `has_failed()` flag and stored `last_error()` string on that same wrapped logger before draining resumes.
- The narrower library facade does not hide the need to explicitly activate queue draining.
@@ -78,6 +79,7 @@ e.g.:
- If `run()` is never started, accepted records may remain queued and not reach the sink.
- A later delegated `run()` attempt starts from a fresh failure flag and empty `last_error()` string on the same wrapped logger, even if an earlier facade-level run failed.
- A concurrent delegated `run()` attempt while the wrapped logger is already active fails explicitly with `AsyncLoggerAlreadyRunning`.
- If callers need to inspect `is_running()`, `has_failed()`, or `last_error()` directly, they must unwrap first with `to_async_logger()`.
+7 -10
View File
@@ -2,8 +2,8 @@
name: library-async-logger-shutdown
group: api
category: facade
update-time: 20260614
description: Gracefully stop a LibraryAsyncLogger by delegating to the wrapped async logger's shutdown behavior, including runtime-dependent drain and worker-wait rules.
update-time: 20260707
description: Gracefully stop a LibraryAsyncLogger by delegating to the wrapped async logger's shutdown behavior, including drain and worker-wait rules.
key-word:
- async
- library
@@ -39,13 +39,12 @@ Detailed rules explaining key parameters and behaviors
- This method delegates directly to the wrapped async logger's `shutdown(...)` behavior.
- `clear=false` first waits for idle, then closes the logger.
- If the active async runtime uses shutdown clearing after idle and backlog still remains, the wrapped logger falls back to `close(clear=true)`.
- If backlog still remains after `wait_idle()` because failure stopped progress early, the wrapped logger falls back to `close(clear=true)`.
- `clear=true` immediately closes and abandons pending records, even if the facade never started a drain worker for the wrapped logger.
- In runtimes where shutdown waits for workers, the method then waits until the worker is no longer running before returning.
- After a worker-failure short-circuit, native-worker backends can still convert remaining backlog into dropped records, while compatibility backends skip that extra forced-clear step.
- In the current direct facade coverage, the wrapped logger ends that path with `pending_count() == 0` and one more dropped item on native-worker runtimes, versus a still-nonzero pending count and no extra dropped cleanup on compatibility runtimes.
- The method then waits until the worker is no longer running before returning.
- After a worker-failure short-circuit, delegated shutdown converts remaining backlog into dropped records before it returns.
- Delegated shutdown also does not clear retained worker failure state by itself. If the wrapped logger had already recorded a worker error, later inspection through `to_async_logger()` can still show `has_failed=true` and the same `last_error()` string after shutdown completes.
- The narrower library facade does not change any of these runtime-dependent shutdown rules; it only keeps the broader inspection helpers out of the direct public surface.
- The narrower library facade does not change any of these shutdown rules; it only keeps the broader inspection helpers out of the direct public surface.
- Inspecting the logger later through `to_async_logger()` reveals the same delegated shutdown result rather than a rebuilt or translated lifecycle snapshot.
### How to Use
@@ -79,8 +78,6 @@ e.g.:
- `clear=true` can therefore be used as the facade-level no-worker cleanup path when queued records were accepted but no `run()` task was ever started.
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
- Even when delegated shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` on the unwrapped logger if the worker had already failed before shutdown cleanup completed.
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
@@ -91,7 +88,7 @@ e.g.:
1. Prefer this API over low-level closure control in normal library shutdown paths.
2. Exact post-close waiting behavior depends on the active async runtime mode.
2. Delegated shutdown waits for an already-running worker to finish before returning.
3. Choose `clear=true` only when loss of queued records is acceptable.
@@ -2,7 +2,7 @@
name: library-async-logger-to-async-logger
group: api
category: facade
update-time: 20260614
update-time: 20260707
description: Recover the underlying full async logger from the library-facing async facade without rebuilding, copying, or detaching the wrapped async state.
key-word:
- async
@@ -40,9 +40,9 @@ Detailed rules explaining key parameters and behaviors
- Use this when code needs wider async logger APIs outside the library facade.
- This is the step required for helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, `last_error()`, or broader composition methods that are intentionally hidden by `LibraryAsyncLogger[S]`.
- It is also the step that exposes the real post-run or post-shutdown state after facade-level lifecycle calls, because those calls delegated to this same wrapped logger all along.
- That includes failure/backlog combinations and runtime-dependent shutdown results exactly as they accumulated behind the facade.
- That includes failure/backlog combinations and shutdown results exactly as they accumulated behind the facade.
- Because the same live value is shared, code can unwrap once, keep that `AsyncLogger[S]` handle, and then observe `pending_count()`, `state()`, `is_closed()`, or failure fields changing as later facade-level `info(...)`, `log(...)`, `run()`, or `shutdown(...)` calls execute.
- That also means the unwrapped logger can already be both `is_closed=true` and `has_failed=true`, with the same retained `last_error()` string and the same runtime-dependent pending-versus-dropped cleanup outcome that delegated shutdown left behind.
- That also means the unwrapped logger can already be both `is_closed=true` and `has_failed=true`, with the same retained `last_error()` string and the same dropped-count cleanup outcome that delegated shutdown left behind.
- If the wrapped sink type has richer helper APIs, unwrapping also restores access to that same sink helper surface through the original `AsyncLogger[S]` value.
- When the wrapped sink is `RuntimeSink`, that same unwrap also preserves queued runtime state and file-backed helper behavior exactly as they existed behind the facade, including runtime file state snapshots and file control methods.
- Runtime sink helper mutations are still live too. Changing file append mode, auto-flush, rotation, reopen state, or related helper-managed file state through the unwrapped logger changes the same wrapped runtime sink that later facade writes and shutdown behavior continue using.
@@ -87,7 +87,7 @@ e.g.:
- Unwrapping does not convert facade lifecycle history into a simplified snapshot; any retained `last_error()`, pending backlog, dropped counts, or sink runtime details stay exactly as they were on the wrapped logger.
- If facade-level `run()` or `shutdown()` already ended in a failure-retaining state, unwrapping can therefore expose combinations such as `is_closed=true` together with `has_failed=true`, the same `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
- If facade-level `run()` or `shutdown()` already ended in a failure-retaining state, unwrapping can therefore expose combinations such as `is_closed=true` together with `has_failed=true`, the same `last_error()`, and the same leftover dropped-count cleanup.
- Recovering the full async logger also does not translate runtime sink helper state into a simpler snapshot; queue counters, file availability, file failure counters, and runtime file controls stay exactly as they were on the wrapped logger.
@@ -0,0 +1,15 @@
---
name: library-async-logger-with-trace-context
group: api
category: facade
update-time: 20260719
description: Bind TraceContext fields through the restricted LibraryAsyncLogger facade.
key-word:
- library
- async
- trace
---
## Library-async-logger-with-trace-context
`LibraryAsyncLogger::with_trace_context(...)` returns a derived library facade with `trace_id`, `span_id`, `trace_flags`, and optional `trace_state` attached to each later record. It does not start a worker or change lifecycle state.
+4 -4
View File
@@ -2,7 +2,7 @@
name: library-async-logger
group: api
category: facade
update-time: 20260614
update-time: 20260707
description: Public library-facing async logger facade type used to expose a narrower surface than AsyncLogger while preserving sink typing.
key-word:
- library
@@ -34,13 +34,13 @@ Detailed rules explaining key parameters and behaviors
- This is a public facade struct, not a type alias.
- The wrapped sink type `S` is preserved, so sink-specific typing remains available through the facade type parameter.
- The facade keeps common library-safe async operations such as `new(...)`, `with_target(...)`, `child(...)`, `bind(...)`, `is_enabled(...)`, `run()`, `shutdown()`, and the main async write methods `log(...)`, `info(...)`, `warn(...)`, and `error(...)`.
- `LibraryAsyncLogger::new(...)` delegates to `async_logger(...)`, so the same async queue, raising flush callback, and runtime-dependent lifecycle behavior still exist behind the narrower facade.
- `LibraryAsyncLogger::new(...)` delegates to `async_logger(...)`, so the same async queue, raising flush callback, and lifecycle behavior still exist behind the narrower facade.
- Because `new(...)` delegates directly, `AsyncLoggerConfig` values such as pending limits, overflow mode, batch sizing, linger timing, and flush policy are preserved exactly rather than being translated into facade-specific defaults.
- The facade also preserves the wrapped async logger's target rules on its exposed write methods: `log(..., target=...)` can override the target for one call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derives another logger with `with_target(...)` or `child(...)`.
- Most facade reshaping helpers keep the same visible sink type, and unlike synchronous `LibraryLogger`, async `with_context_fields(...)` and `bind(...)` also preserve the visible `LibraryAsyncLogger[S]` type because they update stored async context metadata instead of changing the exposed sink wrapper.
- It does not expose the wider `AsyncLogger[S]` composition surface or the async state and lifecycle inspection helpers directly.
- Call `to_async_logger()` when later code must recover the full underlying `AsyncLogger[S]` surface.
- That recovered logger is the same live async value, so queue counters, retained failure state, runtime-dependent shutdown outcomes, and sink helper access are preserved rather than recomputed.
- That recovered logger is the same live async value, so queue counters, retained failure state, shutdown outcomes, and sink helper access are preserved rather than recomputed.
- If the delegated async flush callback fails, the facade preserves the same `has_failed()`, `last_error()`, `is_running()`, pending-backlog, and shutdown-cleanup semantics that the underlying `AsyncLogger[S]` would have exposed directly.
- If `S` is `RuntimeSink`, queued runtime state and file-backed helper behavior also stay on that same wrapped logger value; the facade only hides direct access until `to_async_logger()` is used.
- If `S` is `RuntimeSink` and the value came from `build_library_async_logger(...)` or `parse_and_build_library_async_logger(...)`, the wrapped logger also keeps that sync-first builder route unchanged: any optional `LoggerConfig.queue` was already applied before async wrapping, and `logger.sink.kind` had already decided the concrete `RuntimeSink` variant before the facade narrowed the surface.
@@ -111,7 +111,7 @@ e.g.:
- If the wrapped async logger later ends in a mixed diagnostic state such as `has_failed=true` with retained backlog, unwrapping exposes that exact state instead of a library-specific translation.
- Shutdown through the facade keeps the wrapped logger's runtime-dependent failure cleanup behavior: after a worker-side failure, unwrapped pending versus dropped cleanup can still differ between native-worker and compatibility runtimes.
- Shutdown through the facade keeps the wrapped logger's failure cleanup behavior: after a worker-side failure, unwrapped retained pending backlog is converted into dropped records before shutdown returns.
- Helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, `is_running()`, `has_failed()`, `last_error()`, `flush_policy()`, `state()`, and `wait_idle()` remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
@@ -0,0 +1,20 @@
---
name: library-logger-with-trace-context
group: api
category: facade
update-time: 20260719
description: Bind TraceContext fields through the restricted LibraryLogger facade.
key-word:
- library
- trace
- context
---
## Library-logger-with-trace-context
```moonbit
let logger = LibraryLogger::new(console_sink())
.with_trace_context(trace_context("trace-1", "span-1"))
```
The derived facade preserves its library-facing API while wrapping the sink with context fields. Use `to_logger()` only when broader composition is required.
+4 -4
View File
@@ -13,12 +13,12 @@ key-word:
## Logger-config-to-json
Convert a typed `LoggerConfig` into a `JsonValue`. This helper is the structured export path when config should be persisted, inspected, or embedded into larger JSON payloads.
Convert a typed `LoggerConfig` into a `Json`. This helper is the structured export path when config should be persisted, inspected, or embedded into larger JSON payloads.
### Interface
```moonbit
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {}
pub fn logger_config_to_json(config : LoggerConfig) -> Json {}
```
#### input
@@ -27,7 +27,7 @@ pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {}
#### output
- `JsonValue` - JSON representation of the logger config.
- `Json` - JSON representation of the logger config.
### Explanation
@@ -73,5 +73,5 @@ e.g.:
1. Use this helper when you need a reusable JSON value rather than a final JSON string.
2. Use `stringify_logger_config(...)` when the next consumer expects JSON text instead of `JsonValue`.
2. Use `stringify_logger_config(...)` when the next consumer expects JSON text instead of `Json`.
+6 -5
View File
@@ -2,8 +2,8 @@
name: logger-config-type
group: api
category: config
update-time: 20260613
description: Public logger config alias used for serializable top-level sync logger settings.
update-time: 20260707
description: Public logger config type re-exported from config_model for serializable top-level sync settings.
key-word:
- logger
- config
@@ -13,12 +13,12 @@ key-word:
## Logger-config-type
`LoggerConfig` is the public top-level serializable config type used to describe sync logger behavior. It is a direct alias to the logger config model used by config parsers, sync logger builders, and async build config embedding.
`LoggerConfig` is the public top-level serializable config type used to describe sync logger behavior. On the root `src` facade, it is re-exported from `src/config_model`, which is the real owner of the concrete logger config model.
### Interface
```moonbit
pub type LoggerConfig = @utils.LoggerConfig
pub using @config_model { type LoggerConfig }
```
#### output
@@ -29,7 +29,8 @@ pub type LoggerConfig = @utils.LoggerConfig
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a built logger instance.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@config_model.LoggerConfig`, not in `@utils`.
- The current fields are `min_level : Level`, `target : String`, `timestamp : Bool`, `sink : SinkConfig`, and `queue : QueueConfig?`.
- `LoggerConfig::new(...)` constructs this type as the main sync config entry point.
- `parse_logger_config_text(...)`, `logger_config_to_json(...)`, `stringify_logger_config(...)`, and `build_logger(...)` all consume or produce the same public config shape.
+22
View File
@@ -0,0 +1,22 @@
---
name: logger-with-trace-context
group: api
category: logger
update-time: 20260719
description: Bind a TraceContext to every record from a synchronous Logger.
key-word:
- logger
- trace
- context
---
## Logger-with-trace-context
```moonbit
pub fn[S] Logger::with_trace_context(
self : Logger[S],
context : TraceContext,
) -> Logger[ContextSink[S]]
```
Returns a derived logger that prepends the context fields to every record. The original logger is unchanged. This is equivalent to `with_context_fields(context.as_fields())`, while making trace intent explicit.
+3
View File
@@ -15,6 +15,8 @@ key-word:
Parse a JSON logger definition and build a ready-to-use `ConfiguredLogger`. This is the most direct API when configuration is loaded from files, environment-derived JSON, or external settings systems.
At the public `src` layer, this API is a facade composition of the root parser and root builder: it parses into the shared config model owned by `src/config_model`, then produces a `ConfiguredLogger` backed by `src/runtime.RuntimeSink`.
### Interface
```moonbit
@@ -34,6 +36,7 @@ pub fn parse_and_build_logger(input : String) -> ConfiguredLogger raise ConfigEr
Detailed rules explaining key parameters and behaviors
- Parsing and building are done in one step by calling `parse_logger_config_text(input)` first and then passing the resulting `LoggerConfig` into `build_logger(...)`.
- The resulting file-control and file-diagnostics helpers remain facade methods over `RuntimeSink`, which in turn delegates file behavior to `src/file_runtime` and file model values to `src/file_model`.
- The returned `ConfiguredLogger` is just `Logger[RuntimeSink]`, so it still supports regular logging calls.
- The returned logger also keeps inherited logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
- That means `log(..., target=...)` can override the target for one write, while severity helpers such as `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
+3
View File
@@ -15,6 +15,8 @@ key-word:
Parse JSON text into a typed `LoggerConfig`. This API is useful when you want validation and inspection of config data before turning it into a runtime logger.
At the public `src` layer, this parser is a facade over the concrete config parser owned by `src/config_model`, and the returned config object is the shared `@config_model.LoggerConfig` model.
### Interface
```moonbit
@@ -34,6 +36,7 @@ pub fn parse_logger_config_text(input : String) -> LoggerConfig raise ConfigErro
Detailed rules explaining key parameters and behaviors
- Parsing is separated from runtime construction.
- Any parsed file rotation field is stored as the shared `@file_model.FileRotation` model inside `SinkConfig.rotation`.
- This API is ideal for validating, editing, or inspecting config values before calling `build_logger(...)`.
- Supported keys are intentionally constrained to stable built-in sink shapes and formatter options.
- Omitted top-level keys fall back to the same defaults used by the typed config constructors: `min_level=Info`, `target=""`, `timestamp=false`, `sink=default_sink_config()`, and `queue=None`.
+4 -4
View File
@@ -13,12 +13,12 @@ key-word:
## Queue-config-to-json
Convert a typed `QueueConfig` into a `JsonValue`. This helper is the structured export path for synchronous queue wrapper configuration when callers want machine-readable config output.
Convert a typed `QueueConfig` into a `Json`. This helper is the structured export path for synchronous queue wrapper configuration when callers want machine-readable config output.
### Interface
```moonbit
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {}
pub fn queue_config_to_json(queue : QueueConfig) -> Json {}
```
#### input
@@ -27,7 +27,7 @@ pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {}
#### output
- `JsonValue` - Structured JSON representation of the queue config.
- `Json` - Structured JSON representation of the queue config.
### Explanation
@@ -74,5 +74,5 @@ e.g.:
1. Use this helper when you need a reusable JSON value rather than a final JSON string.
2. Use `stringify_queue_config(...)` when the next consumer expects text instead of `JsonValue`.
2. Use `stringify_queue_config(...)` when the next consumer expects text instead of `Json`.
+6 -5
View File
@@ -2,8 +2,8 @@
name: queue-config-type
group: api
category: config
update-time: 20260613
description: Public queue config alias used for serializable synchronous queue wrapping settings.
update-time: 20260707
description: Public queue config type re-exported from config_model for serializable sync queue settings.
key-word:
- queue
- config
@@ -13,12 +13,12 @@ key-word:
## Queue-config-type
`QueueConfig` is the public serializable config type used to describe synchronous queue wrapping. It is a direct alias to the queue config model used by sync logger configuration, parsers, and queue config serializers.
`QueueConfig` is the public serializable config type used to describe synchronous queue wrapping. On the root `src` facade, it is re-exported from `src/config_model`, which is the real owner of the concrete queue config model.
### Interface
```moonbit
pub type QueueConfig = @utils.QueueConfig
pub using @config_model { type QueueConfig }
```
#### output
@@ -29,7 +29,8 @@ pub type QueueConfig = @utils.QueueConfig
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a runtime queue instance.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@config_model.QueueConfig`, not in `@utils`.
- The current fields are `max_pending : Int` and `overflow : QueueOverflowPolicy`.
- `QueueConfig::new(...)` constructs this type as the normal handwritten entry point.
- `queue_config_to_json(...)` and `stringify_queue_config(...)` serialize the same public config shape for tooling or persistence.
+3 -2
View File
@@ -13,12 +13,12 @@ key-word:
## Queue-overflow-policy
`QueueOverflowPolicy` is the public enum that defines what a synchronous queue should do when it reaches `max_pending`. It is a direct alias to the queue model enum used by both `QueuedSink` and `QueueConfig`.
`QueueOverflowPolicy` is the public enum that defines what a synchronous queue should do when it reaches `max_pending`. It is a direct alias to the shared queue model enum used by both `QueuedSink` and `QueueConfig`.
### Interface
```moonbit
pub type QueueOverflowPolicy = @utils.QueueOverflowPolicy
pub type QueueOverflowPolicy = @queue_model.QueueOverflowPolicy
```
#### output
@@ -32,6 +32,7 @@ Detailed rules explaining key parameters and behaviors
- This is a type alias, not a separate policy wrapper.
- `QueueOverflowPolicy::DropNewest` keeps the existing queued records and drops the incoming one when the queue is full.
- `QueueOverflowPolicy::DropOldest` removes one pending record and enqueues the new record instead.
- The concrete owner is the shared `@queue_model` package, so config-side and sink-side queue APIs reference the same model without making config packages depend on sink runtime packages.
- The same enum is used by code-side queue composition through `queued_sink(...)` and config-driven queue composition through `QueueConfig::new(...)`.
### How to Use
+1
View File
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- This is a public root struct, not a type alias.
- The current fields are `sink : S`, `queue : @queue.Queue[Record]`, `max_pending : Int`, `overflow : QueueOverflowPolicy`, and `dropped_count : Ref[Int]`.
- The `overflow` field resolves to the shared `@queue_model.QueueOverflowPolicy` owner.
- `queued_sink(...)` constructs this type directly from a wrapped sink and queue policy.
- The sink preserves the wrapped sink type `S`, which is useful when typed composition still matters after queueing is introduced.
+7 -3
View File
@@ -13,12 +13,12 @@ key-word:
## Runtime-file-state-to-json
Convert `RuntimeFileState` into a `JsonValue`. This helper exports both file sink status and queue metrics for combined runtime file diagnostics.
Convert `RuntimeFileState` into a `Json`. On the root `src` facade, this helper forwards to the concrete serializer owned by `src/file_model`.
### Interface
```moonbit
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> @json_parser.JsonValue {}
pub fn runtime_file_state_to_json(state : RuntimeFileState) -> Json {}
```
#### input
@@ -27,16 +27,18 @@ pub fn runtime_file_state_to_json(state : RuntimeFileState) -> @json_parser.Json
#### output
- `JsonValue` - Structured JSON representation of the runtime file state.
- `Json` - Structured JSON representation of the runtime file state.
### Explanation
Detailed rules explaining key parameters and behaviors
- The output includes `file`, `queued`, `pending_count`, and `dropped_count`.
- The root surface forwards to `@file_model.runtime_file_state_to_json(...)`, which is the real owner of this serialization logic.
- `file` is itself exported as a nested file sink state object.
- This helper is richer than `file_sink_state_to_json(...)` because it also carries queue wrapping context.
- It is useful for `RuntimeSink::file_runtime_state()`, `ConfiguredLogger::file_runtime_state()`, and similar queued-file diagnostics flows.
- Any `file_rotation_i64(...)` policy nested under `file` includes both the compatibility `max_bytes` number and `max_bytes_i64` as a string.
### How to Use
@@ -78,3 +80,5 @@ e.g.:
1. Use this helper when file and queue runtime context should stay together.
2. It is especially useful for queued file runtime paths, whether accessed directly or through configured loggers.
3. For wide native rotation policies nested under `file.rotation`, use `max_bytes_i64` when exact threshold recovery matters.
+6 -5
View File
@@ -2,8 +2,8 @@
name: runtime-file-state
group: api
category: runtime
update-time: 20260613
description: Public combined file-and-queue runtime state alias used by runtime file diagnostics.
update-time: 20260707
description: Public combined file-and-queue runtime state re-exported from file_model for runtime diagnostics.
key-word:
- runtime
- file
@@ -13,12 +13,12 @@ key-word:
## Runtime-file-state
`RuntimeFileState` is the public snapshot object that combines file state with queue runtime context for file-backed runtime diagnostics. It is a direct alias to the runtime model used by `RuntimeSink`, `ConfiguredLogger`, and JSON export helpers.
`RuntimeFileState` is the public snapshot object that combines file state with queue runtime context for file-backed runtime diagnostics. On the root `src` facade, it is re-exported from `src/file_model`, which is the real owner of the concrete runtime file state model.
### Interface
```moonbit
pub type RuntimeFileState = @utils.RuntimeFileState
pub using @file_model { type RuntimeFileState }
```
#### output
@@ -29,7 +29,8 @@ pub type RuntimeFileState = @utils.RuntimeFileState
Detailed rules explaining key parameters and behaviors
- This is a type alias, not a live runtime controller.
- This root surface is a re-export, not the concrete owner definition.
- The concrete type lives in `@file_model.RuntimeFileState`, not in `@utils`.
- The current fields are `file : FileSinkState`, `queued : Bool`, `pending_count : Int`, and `dropped_count : Int`.
- This type is returned by `RuntimeSink::file_runtime_state()` and by `ConfiguredLogger::file_runtime_state()` when file runtime context is available.
- It is broader than `FileSinkState` because it also reports whether queue wrapping is involved and how the queue is behaving.
+13 -7
View File
@@ -2,8 +2,8 @@
name: runtime-sink-close
group: api
category: runtime
update-time: 20260613
description: Close a RuntimeSink and report whether the underlying sink performed a successful close action.
update-time: 20260707
description: Close a RuntimeSink and report whether the concrete close path succeeded.
key-word:
- runtime
- sink
@@ -27,7 +27,7 @@ pub fn RuntimeSink::close(self : RuntimeSink) -> Bool {
#### output
- `Bool` - Whether the concrete runtime sink reported a successful close action.
- `Bool` - Whether the concrete runtime sink reported a successful close path.
### Explanation
@@ -35,9 +35,11 @@ Detailed rules explaining key parameters and behaviors
- Plain console-style runtime sinks return `true` because they do not have a meaningful close step here.
- Plain file runtime sinks forward directly to `FileSink::close()`.
- Queued file runtime sinks close the wrapped file sink rather than only the queue wrapper.
- Generic `close()` on `QueuedFile` does not drain or flush queued records first; it closes the inner file sink directly.
- Queue-wrapped console-style runtime sinks return `true` as a no-op success.
- Queued file runtime sinks now follow the same safe teardown path as `file_close()`: they first try to drain the queue, then flush the wrapped file sink, then close it.
- On `QueuedFile`, the returned `Bool` is `true` only when that whole teardown path succeeds without introducing new file failures.
- Queue-wrapped console-style runtime sinks also use drain-first teardown.
- For `QueuedConsole`, `QueuedJsonConsole`, and `QueuedTextConsole`, `close()` consumes pending queue items before reporting success.
- Because these wrapped console-style sinks do not expose an additional close-failure channel, success for these variants means the backlog was drained to zero.
- This method is the direct sink-level API used by `ConfiguredLogger::close(...)`.
- For file-backed runtime sinks, later `close()` calls return `false` after the handle has already been cleared.
@@ -72,10 +74,14 @@ e.g.:
- If callers know they are working with a direct `FileSink`, `FileSink::close()` is the narrower API.
- On queued file sinks, `true` still does not mean the records are guaranteed to be durable beyond what the current backend can report; it means the queue-drain, file-flush, and file-close path completed without a newly observed file failure.
- On queued non-file sinks, `true` means the queued backlog was consumed before teardown completed.
### Notes
1. Use this helper when code is managing a `RuntimeSink` value directly.
2. Use `file_close()` instead of generic `close()` when queued file sinks should flush pending records before file teardown.
2. Generic `close()` is now safe for queue-backed runtime sinks and no longer skips queued records silently.
3. `ConfiguredLogger::close(...)` is the higher-level wrapper for config-built loggers.
+80
View File
@@ -0,0 +1,80 @@
---
name: runtime-sink-drain-progress
group: api
category: runtime
update-time: 20260707
description: Return a structured generic drain progress snapshot for a RuntimeSink without collapsing queue and plain-file fallback semantics into one Int.
key-word:
- runtime
- sink
- drain
- public
---
## Runtime-sink-drain-progress
Return a structured generic drain progress snapshot for a `RuntimeSink`. This is the recommended truthful generic runtime drain helper when callers need bounded or full queue progress without relying on the compatibility `Int` returned by `drain()`.
### Interface
```moonbit
pub fn RuntimeSink::drain_progress(
self : RuntimeSink,
max_items~ : Int = -1,
) -> RuntimeSinkProgress {
```
#### input
- `self : RuntimeSink` - Runtime sink whose generic drain progress should be observed.
- `max_items : Int` - Optional upper bound on drained queued items. Negative values mean no explicit bound.
#### output
- `RuntimeSinkProgress` - Structured progress snapshot separating queue advancement from plain-file flush fallback steps.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-wrapped runtime sinks report consumed queued items through `queue_advanced_count` and honor `max_items`.
- Plain file runtime sinks still follow the generic fallback path and report `FileSink::flush()` as `file_flush_step_count` `1` or `0`.
- Plain console-style runtime sinks return a zeroed snapshot because they do not own a drainable queue here.
- Queued file runtime sinks report queue advancement, while delayed file delivery failures still belong to file failure counters and `file_flush()`.
- This helper preserves the old runtime behavior but makes the meaning explicit in the result shape.
- `drain()` remains available as the legacy convenience wrapper that sums the fields back into one count.
### How to Use
Here are some specific examples provided.
#### When Need Recommended Bounded Generic Drain Progress
When code should observe one bounded drain step without mixing units:
```moonbit
let progress = sink.drain_progress(max_items=16)
```
In this example, queue progress and plain-file fallback stay separated.
#### When Need A Full Manual Generic Drain Snapshot
When support code should empty queued work and keep runtime-shape context:
```moonbit
let progress = sink.drain_progress()
```
In this example, the caller can inspect both the count and the runtime shape afterwards.
### Error Case
e.g.:
- If the runtime sink is not queue-backed, the snapshot may stay zeroed or use the plain-file fallback field.
- If callers want file-specific success semantics after queue delivery, `file_flush()` is the better API.
### Notes
1. Prefer `drain_progress()` over `drain()` in new generic runtime code.
2. Use `pending_count()` together with this helper when remaining backlog must also be observed.
+23 -15
View File
@@ -2,8 +2,8 @@
name: runtime-sink-drain
group: api
category: runtime
update-time: 20260613
description: Drain queued work from a RuntimeSink with an optional item limit.
update-time: 20260707
description: Compatibility RuntimeSink drain helper that still returns one Int by collapsing generic queue progress and plain-file fallback steps.
key-word:
- runtime
- sink
@@ -13,7 +13,7 @@ key-word:
## Runtime-sink-drain
Drain queued work from a `RuntimeSink`. This helper is useful when code owns a runtime sink directly and needs controlled queue progress.
Drain queued work from a `RuntimeSink` and return one compatibility count. This helper is still available for older code, but new generic runtime code should prefer `drain_progress()` because it exposes queue advancement and plain-file fallback steps separately.
### Interface
@@ -28,16 +28,18 @@ pub fn RuntimeSink::drain(self : RuntimeSink, max_items~ : Int = -1) -> Int {
#### output
- `Int` - Number of drained items, or the fallback flush count for plain file sinks.
- `Int` - Compatibility count produced from the structured `drain_progress()` result.
### Explanation
Detailed rules explaining key parameters and behaviors
- Queue-wrapped runtime sinks forward to the wrapped queue sink's `drain(...)` behavior.
- Plain file runtime sinks fall back to `FileSink::flush()` behavior and return `1` or `0`.
- Plain console-style runtime sinks return `0` because they do not own a drainable queue.
- This method is the direct sink-level API used by `ConfiguredLogger::drain(...)`.
- This method now delegates to `drain_progress()` and sums `queue_advanced_count + file_flush_step_count` back into one compatibility number.
- Queue-wrapped runtime sinks still report how many queued records were consumed during the drain.
- A queued file drain can still return a positive number even when file failure counters increase during the same queue-delivery step.
- Plain file runtime sinks still use the generic `FileSink::flush()` fallback and therefore still return `1` or `0`.
- Plain console-style runtime sinks still return `0` because the structured result stays zeroed.
- This method remains the direct sink-level API used by `ConfiguredLogger::drain(...)`.
### How to Use
@@ -45,31 +47,37 @@ Here are some specific examples provided.
#### When Need Bounded Queue Progress
When direct queue-backed runtime work should be advanced in chunks:
When older code already expects one numeric drain count:
```moonbit
let drained = sink.drain(max_items=16)
```
In this example, callers cap how much queued work is processed in one step.
In this example, callers still cap how much queued work is processed in one step while keeping the legacy return shape.
#### When Need Full Manual Drain
When a runtime queue should be emptied without an explicit item cap:
When code should keep the older full-drain compatibility path:
```moonbit
ignore(sink.drain())
```
In this example, the runtime sink drains as much queued work as its concrete variant allows.
In this example, the runtime sink drains as much queued work as its concrete variant allows while still returning one numeric count.
### Error Case
e.g.:
- If the runtime sink is not queue-backed, the result may be `0` or file-flush fallback behavior.
- If callers only need generic flush semantics, `flush()` may be the simpler API.
- If callers need truthful generic progress semantics, `drain_progress()` is the better API.
- If callers only need generic flush semantics, `flush_progress()` may be the simpler structured API.
- If callers need file-specific success semantics after draining a queued file sink, they should inspect failure counters or use `file_flush()` for the file-specific path.
### Notes
1. Prefer this helper when queue progress should be bounded or directly observed.
1. Treat this method as a compatibility helper for older numeric code.
2. `ConfiguredLogger::drain(...)` is the higher-level wrapper for config-built loggers.
2. Prefer `drain_progress()` in new generic runtime code.
3. `ConfiguredLogger::drain(...)` is the higher-level wrapper for config-built loggers.
+7 -3
View File
@@ -2,7 +2,7 @@
name: runtime-sink-file-available
group: api
category: runtime
update-time: 20260613
update-time: 20260707
description: Read whether a RuntimeSink currently has an available file sink behind its runtime sink shape.
key-word:
- runtime
@@ -36,6 +36,8 @@ Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants report actual file availability.
- `QueuedFile` runtime variants expose the availability of their wrapped inner file sink.
- Non-file runtime variants return `false`.
- This means `false` currently merges two different situations: “not file-backed” and “file-backed but currently unavailable”.
- Use `file_path_or_none()`, `file_policy_or_none()`, `file_default_policy_or_none()`, `file_state_or_none()`, or `file_runtime_state()` when callers need truthful file-semantics detection.
- This helper does not mutate runtime sink state.
### How to Use
@@ -67,10 +69,12 @@ In this example, callers can decide whether a recovery action is needed.
e.g.:
- If the runtime 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.
- If callers need to distinguish “not file-backed” from “file-backed but unavailable”, use one of the truthful `*_or_none()` helpers or `file_runtime_state()`.
- If callers need detailed failure counters rather than a simple availability flag, `file_state_or_none()` or `file_runtime_state()` is the better API.
### Notes
1. Use this helper for lightweight file sink health checks on direct `RuntimeSink` values.
2. Pair it with reopen and failure-counter APIs when diagnosing file sink problems.
2. Pair it with reopen and failure-counter APIs when diagnosing file sink problems, but prefer the truthful `*_or_none()` helpers when file semantics themselves must be checked.
+9 -38
View File
@@ -2,8 +2,8 @@
name: runtime-sink-file-clear-rotation
group: api
category: runtime
update-time: 20260613
description: Disable runtime rotation on a file-backed RuntimeSink.
update-time: 20260707
description: Clear the rotation policy used by a file-backed RuntimeSink.
key-word:
- runtime
- sink
@@ -13,7 +13,7 @@ key-word:
## Runtime-sink-file-clear-rotation
Disable runtime rotation on a file-backed `RuntimeSink`. This helper is the direct shortcut for clearing rotation policy from a runtime sink value.
Clear the rotation policy used by a file-backed `RuntimeSink`.
### Interface
@@ -23,7 +23,7 @@ pub fn RuntimeSink::file_clear_rotation(self : RuntimeSink) -> Bool {
#### input
- `self : RuntimeSink` - Runtime sink whose rotation policy should be cleared.
- `self : RuntimeSink` - Runtime sink whose file rotation policy should be cleared.
#### output
@@ -33,42 +33,13 @@ pub fn RuntimeSink::file_clear_rotation(self : RuntimeSink) -> Bool {
Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants clear rotation on the wrapped `FileSink` and return `true`.
- `QueuedFile` runtime variants forward the update to the wrapped inner `FileSink` and return `true`.
- Plain `File` runtime variants clear the wrapped `FileSink` rotation policy and return `true`.
- `QueuedFile` runtime variants clear the wrapped inner `FileSink` rotation policy only when no queued records are pending.
- Non-file runtime variants 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 on a direct sink value:
```moonbit
ignore(sink.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 = sink.file_clear_rotation()
```
In this example, the call site reads more clearly than a generic config setter.
### Error Case
e.g.:
- If the runtime 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.
- If a queued file sink still has pending records, the update is rejected and returns `false` so already queued records are not later written under a different rotation policy than the one they were queued under.
### Notes
1. Use this helper when the desired effect is simply `rotation off`.
1. Use this helper when runtime rotation policy should be removed without rebuilding the sink.
2. It is clearer than passing `None` through a broader setter when intent matters.
2. On queued file sinks, clear pending records first so policy mutation does not retroactively affect already queued writes.
+9 -7
View File
@@ -2,8 +2,8 @@
name: runtime-sink-file-close
group: api
category: runtime
update-time: 20260613
description: Close the file sink behind a RuntimeSink when one is present.
update-time: 20260707
description: Close the file sink behind a RuntimeSink when one is present, with queued-file success tied to safe drain-flush-close behavior.
key-word:
- runtime
- sink
@@ -27,15 +27,15 @@ pub fn RuntimeSink::file_close(self : RuntimeSink) -> Bool {
#### output
- `Bool` - Whether the underlying file close succeeded.
- `Bool` - Whether the file-specific close path succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants forward directly to `FileSink::close()`.
- `QueuedFile` runtime variants first attempt `sink.flush()` on the queue wrapper, then call `sink.sink.close()` on the wrapped file sink.
- For `QueuedFile`, the queue flush result is ignored and the returned `Bool` comes from the inner file close.
- `QueuedFile` runtime variants first drain the queue, then flush the wrapped file sink, then close it.
- For `QueuedFile`, the returned `Bool` is `true` only when that whole path succeeds and no new write, flush, or rotation failures are observed during the drain-flush-close step.
- Non-file runtime variants return `false`.
- After a file-backed runtime sink has already cleared its handle, later `file_close()` calls return `false`.
@@ -59,7 +59,7 @@ When application code wants the direct file close outcome:
let closed = sink.file_close()
```
In this example, the result describes file close behavior rather than generic sink close behavior.
In this example, the result describes the file-specific close path rather than generic sink close behavior.
### Error Case
@@ -70,8 +70,10 @@ e.g.:
- If callers only need generic sink teardown, `close()` is the broader API.
- On queued file sinks, `true` still does not mean a stronger durability contract than the current backend can report; it means the safe drain-flush-close path completed without a newly observed file failure.
### Notes
1. Prefer this helper when direct runtime code specifically cares about file-backed shutdown.
2. Queued file sinks flush pending records before closing the file, unlike generic `close()`.
2. Generic `close()` now uses the same safe queued-file teardown path.
@@ -0,0 +1,82 @@
---
name: runtime-sink-file-default-policy-or-none
group: api
category: runtime
update-time: 20260707
description: Read the default runtime file policy from a RuntimeSink only when it is actually file-backed.
key-word:
- runtime
- sink
- file
- truthful
---
## Runtime-sink-file-default-policy-or-none
Read the default runtime file policy from a `RuntimeSink` only when it is actually file-backed.
The returned `FileSinkPolicy` value is owned by `src/file_model`; this method belongs to the runtime facade layer and avoids fabricating that model on non-file variants.
This is the truthful companion to `file_default_policy()`. Prefer it when default-policy inspection should not fabricate a fallback object on non-file sinks.
### Interface
```moonbit
pub fn RuntimeSink::file_default_policy_or_none(self : RuntimeSink) -> FileSinkPolicy? {
```
#### input
- `self : RuntimeSink` - Runtime sink whose default file policy should be inspected.
#### output
- `FileSinkPolicy?` - `Some(policy)` when the runtime sink is file-backed, otherwise `None`.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants return `Some(default_policy)`.
- `QueuedFile` runtime variants forward the wrapped inner file sink default policy as `Some(default_policy)`.
- The wrapped value is the shared `@file_model.FileSinkPolicy` model, not a runtime-owned concrete type.
- Non-file runtime variants return `None`.
- This helper is useful when callers need to compare runtime drift or restore defaults without fabricating file semantics.
### How to Use
Here are some specific examples provided.
#### When Need Truthful Baseline Policy Visibility
When tooling should inspect defaults only for real file-backed sinks:
```moonbit
let defaults = sink.file_default_policy_or_none()
```
In this example, `None` means the runtime sink has no file default policy.
#### When Compare Current And Default Policy Truthfully
When diagnostics should avoid fallback policy objects:
```moonbit
match (sink.file_policy_or_none(), sink.file_default_policy_or_none()) {
(Some(current), Some(defaults)) => ignore((current, defaults))
_ => ()
}
```
In this example, comparisons happen only when real file semantics exist.
### Error Case
e.g.:
- If the runtime sink is not file-backed, the method returns `None`.
- If callers only need a drift boolean, `file_policy_matches_default()` remains simpler.
### Notes
1. Prefer this helper over `file_default_policy()` for truthful runtime diagnostics.
2. `file_default_policy()` remains available as the compatibility fallback API.
@@ -15,6 +15,8 @@ key-word:
Read the initial default file policy associated with a `RuntimeSink`. This helper exposes the baseline file policy captured when the runtime sink was created.
The returned `FileSinkPolicy` value is owned by `src/file_model`; this method belongs to the runtime facade layer and reads defaults through `src/runtime` over file-backed variants that ultimately use `src/file_runtime.FileSink`.
### Interface
```moonbit
@@ -35,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants return the default policy captured at creation time.
- `QueuedFile` runtime variants forward the default policy from the wrapped inner `FileSink`.
- The returned policy object itself is the shared `@file_model.FileSinkPolicy` model, not a runtime-owned concrete type.
- Non-file runtime variants return the neutral fallback policy `FileSinkPolicy::new(append=false, auto_flush=false, rotation=None)`.
- This helper is useful when callers need to compare runtime drift or restore defaults later.
+10 -8
View File
@@ -2,8 +2,8 @@
name: runtime-sink-file-flush
group: api
category: runtime
update-time: 20260614
description: Flush the file sink behind a RuntimeSink when one is present.
update-time: 20260707
description: Flush the file sink behind a RuntimeSink when one is present, with queued-file success tied to both queue drain and file flush outcome.
key-word:
- runtime
- sink
@@ -27,16 +27,16 @@ pub fn RuntimeSink::file_flush(self : RuntimeSink) -> Bool {
#### output
- `Bool` - Whether the underlying file flush succeeded.
- `Bool` - Whether the file-specific flush path succeeded.
### Explanation
Detailed rules explaining key parameters and behaviors
- Plain `File` runtime variants forward directly to `FileSink::flush()`.
- `QueuedFile` runtime variants first attempt `sink.flush()` on the queue wrapper, then call `sink.sink.flush()` on the wrapped file sink.
- For `QueuedFile`, the queue flush result is ignored and the returned `Bool` comes from the inner file flush.
- For `QueuedFile`, this step may also be the point where queued records finally reach the inner file sink, so write-failure counters can change here even if earlier `log(...)` calls only enqueued records.
- `QueuedFile` runtime variants first drain the queue, then flush the wrapped file sink.
- For `QueuedFile`, the returned `Bool` is `true` only when all queued records were consumed, the final file flush succeeded, and no new write, flush, or rotation failures were observed during that flush path.
- For `QueuedFile`, this step may also be the point where queued records finally reach the inner file sink, so failure counters can change here even if earlier `log(...)` calls only enqueued records.
- Non-file runtime variants return `false`.
- After a file-backed runtime sink has already cleared its handle, later `file_flush()` calls return `false`.
@@ -60,7 +60,7 @@ When code should branch on the outcome of a direct file flush:
let ok = sink.file_flush()
```
In this example, callers can distinguish file flush success from a non-file sink shape.
In this example, callers can distinguish file-specific flush success from a non-file sink shape.
### Error Case
@@ -71,10 +71,12 @@ e.g.:
- If callers want generic queue or sink advancement instead of file-specific behavior, `flush()` is the broader API.
- On queued file sinks, a `false` result may mean queue consumption happened but file delivery still observed a new failure.
### Notes
1. Prefer this helper when direct runtime code specifically cares about file flush behavior.
2. Queued file sinks may perform both queue flush work and file flush work here, including surfacing delayed write failures from previously queued records.
2. Queued file sinks may perform both queue drain work and file flush work here, including surfacing delayed failures from previously queued records.
3. Library or application facades that share the same wrapped file-backed runtime sink still observe the same flush availability state.

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