mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bcfb35d7ae | |||
| 55540f56a2 | |||
| 0342603b9e | |||
| ea6113354a | |||
| b18dcf972d | |||
| a745cd5172 | |||
| b32de63d57 | |||
| 4e2ef69b11 | |||
| c6962fe71a | |||
| 74b9815bab | |||
| 6ee0d97aac | |||
| 465e5ecd5d | |||
| 52b26b31f2 | |||
| 9405a04c13 | |||
| e5a048090f | |||
| e3097ba4fc | |||
| 5d9924026e | |||
| bdbb161865 | |||
| 83371bb4d5 | |||
| 0e02f3d2cf | |||
| 46e87403bf | |||
| 3bdab60412 | |||
| e001315319 | |||
| 93e9bd966d | |||
| e8a2c76652 | |||
| c8023b0ded | |||
| 7557e37cc8 | |||
| 637962a934 | |||
| 265cd69ea9 | |||
| 4f2ad097af | |||
| 81385d5c0b |
+149
-33
@@ -7,74 +7,190 @@ 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-gc --deny-warn
|
||||
moon check --target js --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-gc and js
|
||||
shell: bash
|
||||
run: |
|
||||
moon check
|
||||
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-gc and js
|
||||
shell: bash
|
||||
run: |
|
||||
moon test
|
||||
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
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: docs-pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build docs site
|
||||
env:
|
||||
DOCS_BASE: /
|
||||
run: npm run docs:build
|
||||
|
||||
- name: Configure Pages
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload Pages artifact
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs/.vitepress/dist
|
||||
|
||||
deploy:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
+7
-1
@@ -16,9 +16,15 @@ desktop.ini
|
||||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
node_modules/
|
||||
|
||||
# Dev Documentations
|
||||
docs/dev/*
|
||||
|
||||
# Docs site build artifacts
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
docs/.vitepress/generated/
|
||||
|
||||
# vibecoding
|
||||
AGENTS/
|
||||
AGENTS/
|
||||
|
||||
@@ -4,29 +4,53 @@ 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.1
|
||||
```
|
||||
|
||||
推荐从 `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)
|
||||
|
||||
## 支持情况
|
||||
|
||||
@@ -46,19 +70,11 @@ ignore(logger.flush())
|
||||
- 组合能力: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/`:异步日志示例
|
||||
|
||||
## 文档
|
||||
|
||||
- [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 文档一一对应;文档解释选择、前提、运行命令和后续扩展,源码保持为可执行参考。
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
import { basename } from 'node:path'
|
||||
import { createMoonbitLanguageRegistration } from 'moonbit-syntax-highlighter'
|
||||
import { defineConfig, type DefaultTheme } from 'vitepress'
|
||||
|
||||
import { docsData } from './generated/docs-data.mjs'
|
||||
|
||||
type ApiDocEntry = {
|
||||
file: string
|
||||
text: string
|
||||
group: string
|
||||
category: string
|
||||
}
|
||||
|
||||
const repoSlug = process.env.GITHUB_REPOSITORY?.split('/')[1] ?? 'BitLogger'
|
||||
const docsBase = process.env.DOCS_BASE ?? (process.env.GITHUB_ACTIONS ? `/${repoSlug}/` : '/')
|
||||
const repository = 'https://github.com/Nanaloveyuki/BitLogger'
|
||||
|
||||
function splitWords(input: string): string[] {
|
||||
return input
|
||||
.split(/[^A-Za-z0-9]+/)
|
||||
.map(part => part.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function formatWord(word: string): string {
|
||||
const lower = word.toLowerCase()
|
||||
if (lower === 'api') return 'API'
|
||||
if (lower === 'json') return 'JSON'
|
||||
if (lower === 'js') return 'JS'
|
||||
if (lower === 'wasm') return 'WASM'
|
||||
if (lower === 'llvm') return 'LLVM'
|
||||
return lower.charAt(0).toUpperCase() + lower.slice(1)
|
||||
}
|
||||
|
||||
function humanize(input: string): string {
|
||||
const words = splitWords(input)
|
||||
if (words.length === 0) return input
|
||||
return words.map(formatWord).join(' ')
|
||||
}
|
||||
|
||||
function readApiDocs(): ApiDocEntry[] {
|
||||
return docsData.api.entries.map(entry => ({
|
||||
file: entry.file,
|
||||
text: entry.title,
|
||||
group: entry.group,
|
||||
category: entry.category,
|
||||
}))
|
||||
}
|
||||
|
||||
function buildApiSidebar(): DefaultTheme.SidebarItem[] {
|
||||
const entries = readApiDocs()
|
||||
const sidebar: DefaultTheme.SidebarItem[] = [{ text: 'Overview', link: '/api/' }]
|
||||
const groups = new Map<string, Map<string, DefaultTheme.SidebarItem[]>>()
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.file === 'index.md') continue
|
||||
const group = groups.get(entry.group) ?? new Map<string, DefaultTheme.SidebarItem[]>()
|
||||
const categoryItems = group.get(entry.category) ?? []
|
||||
categoryItems.push({
|
||||
text: entry.text,
|
||||
link: `/api/${basename(entry.file, '.md')}`,
|
||||
})
|
||||
group.set(entry.category, categoryItems)
|
||||
groups.set(entry.group, group)
|
||||
}
|
||||
|
||||
if (groups.size === 1 && groups.has('api')) {
|
||||
const categories = groups.get('api')!
|
||||
for (const category of [...categories.keys()].sort((a, b) => humanize(a).localeCompare(humanize(b)))) {
|
||||
sidebar.push({
|
||||
text: humanize(category),
|
||||
collapsed: true,
|
||||
items: categories.get(category)!,
|
||||
})
|
||||
}
|
||||
return sidebar
|
||||
}
|
||||
|
||||
for (const group of [...groups.keys()].sort((a, b) => humanize(a).localeCompare(humanize(b)))) {
|
||||
const categories = groups.get(group)!
|
||||
sidebar.push({
|
||||
text: humanize(group),
|
||||
collapsed: true,
|
||||
items: [...categories.keys()]
|
||||
.sort((a, b) => humanize(a).localeCompare(humanize(b)))
|
||||
.map(category => ({
|
||||
text: humanize(category),
|
||||
collapsed: true,
|
||||
items: categories.get(category)!,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
return sidebar
|
||||
}
|
||||
|
||||
function buildChangesSidebar(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{ text: 'Overview', link: '/changes/' },
|
||||
{
|
||||
text: 'Versions',
|
||||
items: docsData.changes.map(item => ({ text: item.version, link: item.link })),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
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' },
|
||||
]
|
||||
}
|
||||
|
||||
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' },
|
||||
]
|
||||
}
|
||||
|
||||
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.',
|
||||
base: docsBase,
|
||||
cleanUrls: true,
|
||||
srcExclude: ['dev/**'],
|
||||
lastUpdated: true,
|
||||
markdown: {
|
||||
languages: [createMoonbitLanguageRegistration()],
|
||||
},
|
||||
themeConfig: englishThemeConfig,
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
lang: 'en-US',
|
||||
themeConfig: englishThemeConfig,
|
||||
},
|
||||
zh: {
|
||||
label: '简体中文',
|
||||
lang: 'zh-CN',
|
||||
link: '/zh/',
|
||||
themeConfig: chineseThemeConfig,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,219 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, shallowRef } from 'vue'
|
||||
import { withBase } from 'vitepress'
|
||||
|
||||
import { docsData } from '../../generated/docs-data.mjs'
|
||||
|
||||
const selectedGroup = shallowRef('all')
|
||||
const query = shallowRef('')
|
||||
|
||||
const groups = computed(() => docsData.api.groups)
|
||||
|
||||
const filteredGroups = computed(() => {
|
||||
const keyword = query.value.trim().toLowerCase()
|
||||
return groups.value
|
||||
.filter(group => selectedGroup.value === 'all' || group.id === selectedGroup.value)
|
||||
.map(group => ({
|
||||
...group,
|
||||
categories: group.categories
|
||||
.map(category => ({
|
||||
...category,
|
||||
entries: category.entries.filter(entry => {
|
||||
if (!keyword) return true
|
||||
const haystack = [entry.title, entry.description, entry.name, ...entry.keywords]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
return haystack.includes(keyword)
|
||||
}),
|
||||
}))
|
||||
.filter(category => category.entries.length > 0),
|
||||
}))
|
||||
.filter(group => group.categories.length > 0)
|
||||
})
|
||||
|
||||
const resultCount = computed(() =>
|
||||
filteredGroups.value.reduce(
|
||||
(total, group) => total + group.categories.reduce((sum, category) => sum + category.entries.length, 0),
|
||||
0,
|
||||
),
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="api-shell">
|
||||
<div class="api-toolbar">
|
||||
<label class="api-search">
|
||||
<span>Find API</span>
|
||||
<input v-model="query" type="search" placeholder="Search logger, sink, config, async...">
|
||||
</label>
|
||||
|
||||
<label class="api-filter">
|
||||
<span>Doc Group</span>
|
||||
<select v-model="selectedGroup">
|
||||
<option value="all">All groups</option>
|
||||
<option v-for="group in groups" :key="group.id" :value="group.id">
|
||||
{{ group.label }}
|
||||
</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="api-summary">
|
||||
<strong>{{ resultCount }}</strong>
|
||||
<span>matching API pages across {{ filteredGroups.length }} group views</span>
|
||||
</div>
|
||||
|
||||
<div class="api-groups">
|
||||
<article v-for="group in filteredGroups" :key="group.id" class="api-group-card">
|
||||
<header class="api-group-header">
|
||||
<div>
|
||||
<p class="api-group-kicker">{{ group.label }}</p>
|
||||
<h3>{{ group.entryCount }} APIs in this group</h3>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="api-category-grid">
|
||||
<section v-for="category in group.categories" :key="category.id" class="api-category-card">
|
||||
<h4>{{ category.label }}</h4>
|
||||
<p>{{ category.entries.length }} entries</p>
|
||||
<ul>
|
||||
<li v-for="entry in category.entries.slice(0, 8)" :key="entry.slug">
|
||||
<a :href="withBase(entry.link)">{{ entry.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a v-if="category.entries.length > 8" class="api-more" :href="withBase('/api/')">
|
||||
Browse {{ category.entries.length - 8 }} more in sidebar
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.api-shell {
|
||||
margin: 1.5rem 0 2rem;
|
||||
}
|
||||
|
||||
.api-toolbar {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.api-search,
|
||||
.api-filter {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.api-search span,
|
||||
.api-filter span {
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #9b4d28;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.api-search input,
|
||||
.api-filter select {
|
||||
width: 100%;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 0.85rem 0.95rem;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.api-summary {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.65rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.api-summary strong {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.api-summary span {
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.api-groups {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.api-group-card {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 28px;
|
||||
padding: 1rem;
|
||||
background: linear-gradient(180deg, rgba(255, 251, 247, 0.96), rgba(246, 242, 236, 0.96));
|
||||
}
|
||||
|
||||
.api-group-header h3,
|
||||
.api-category-card h4,
|
||||
.api-category-card p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.api-group-kicker {
|
||||
margin: 0 0 0.25rem;
|
||||
color: #9b4d28;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.api-category-grid {
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
margin-top: 0.9rem;
|
||||
}
|
||||
|
||||
.api-category-card {
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(155, 77, 40, 0.12);
|
||||
padding: 0.95rem;
|
||||
}
|
||||
|
||||
.api-category-card p {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.api-category-card ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.85rem 0 0;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.api-category-card a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.api-more {
|
||||
display: inline-block;
|
||||
margin-top: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (min-width: 820px) {
|
||||
.api-toolbar {
|
||||
grid-template-columns: minmax(0, 2fr) minmax(220px, 0.8fr);
|
||||
}
|
||||
|
||||
.api-category-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,191 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { withBase } from 'vitepress'
|
||||
|
||||
import { docsData } from '../../generated/docs-data.mjs'
|
||||
|
||||
const topCategories = computed(() => docsData.api.categories.slice(0, 6))
|
||||
const recentChanges = computed(() => docsData.changes.slice(0, 4))
|
||||
const apiCount = computed(() => docsData.api.entries.filter(entry => entry.slug !== 'index').length)
|
||||
const groupCount = computed(() => docsData.api.groups.length)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="hub-grid">
|
||||
<article class="hub-panel hub-panel-accent">
|
||||
<p class="hub-eyebrow">Docs Hub</p>
|
||||
<h2 class="hub-title">Start from use case, not from filenames.</h2>
|
||||
<p class="hub-copy">
|
||||
Browse builders, runtime helpers, presets, sync logging, and async logging from one place.
|
||||
</p>
|
||||
<div class="hub-stats">
|
||||
<div>
|
||||
<strong>{{ apiCount }}</strong>
|
||||
<span>API pages</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ groupCount }}</strong>
|
||||
<span>doc groups</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ docsData.changes.length }}</strong>
|
||||
<span>release notes</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="hub-panel">
|
||||
<p class="hub-eyebrow">Popular Areas</p>
|
||||
<ul class="hub-chip-list">
|
||||
<li v-for="category in topCategories" :key="category.id">
|
||||
<a class="hub-chip" :href="withBase('/api/')">
|
||||
<span>{{ category.label }}</span>
|
||||
<small>{{ category.entryCount }}</small>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="hub-panel">
|
||||
<p class="hub-eyebrow">Release Trail</p>
|
||||
<ul class="hub-link-list">
|
||||
<li v-for="item in recentChanges" :key="item.version">
|
||||
<a :href="withBase(item.link)">Version {{ item.version }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.hub-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
margin: 1.5rem 0 2rem;
|
||||
}
|
||||
|
||||
.hub-panel {
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
border-radius: 24px;
|
||||
padding: 1.25rem;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(247, 245, 239, 0.95));
|
||||
box-shadow: 0 16px 40px rgba(62, 46, 31, 0.08);
|
||||
}
|
||||
|
||||
.hub-panel-accent {
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(191, 68, 32, 0.16), transparent 34%),
|
||||
linear-gradient(180deg, rgba(255, 250, 244, 0.98), rgba(247, 241, 232, 0.98));
|
||||
}
|
||||
|
||||
.hub-eyebrow {
|
||||
margin: 0 0 0.5rem;
|
||||
color: #9b4d28;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hub-title {
|
||||
margin: 0;
|
||||
font-size: 1.55rem;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.hub-copy {
|
||||
margin: 0.85rem 0 0;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
.hub-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.15rem;
|
||||
}
|
||||
|
||||
.hub-stats strong,
|
||||
.hub-stats span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hub-stats strong {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.hub-stats span {
|
||||
color: var(--vp-c-text-2);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hub-chip-list,
|
||||
.hub-link-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hub-chip-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.hub-chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
min-height: 56px;
|
||||
gap: 0.8rem;
|
||||
padding: 0.7rem 0.9rem;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(155, 77, 40, 0.14);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hub-chip span {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hub-chip small {
|
||||
flex: 0 0 auto;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.hub-chip-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.hub-link-list {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hub-link-list a {
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (min-width: 860px) {
|
||||
.hub-grid {
|
||||
grid-template-columns: 1.35fr 1fr;
|
||||
}
|
||||
|
||||
.hub-panel-accent {
|
||||
grid-row: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.hub-stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
:root {
|
||||
--vp-c-brand-1: #b34a24;
|
||||
--vp-c-brand-2: #92381a;
|
||||
--vp-c-brand-3: #d06d39;
|
||||
--vp-c-brand-soft: rgba(176, 74, 36, 0.14);
|
||||
--vp-home-hero-name-color: #6b2f17;
|
||||
--vp-home-hero-text-color: #27170f;
|
||||
--vp-home-hero-tagline-color: #5f5249;
|
||||
--vp-font-family-base: "Segoe UI", "PingFang SC", "Hiragino Sans GB", sans-serif;
|
||||
--vp-font-family-mono: "Cascadia Mono", "JetBrains Mono", monospace;
|
||||
}
|
||||
|
||||
.VPContent {
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(208, 109, 57, 0.08), transparent 28%),
|
||||
radial-gradient(circle at left 20%, rgba(179, 74, 36, 0.06), transparent 22%);
|
||||
}
|
||||
|
||||
.VPHomeHero .name,
|
||||
.VPDoc h1,
|
||||
.VPDoc h2,
|
||||
.VPDoc h3 {
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.VPHomeHero .image-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vp-doc a {
|
||||
text-underline-offset: 0.18em;
|
||||
}
|
||||
|
||||
.vp-doc .custom-block,
|
||||
.vp-doc div[class*='language-'] {
|
||||
border-radius: 18px;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import type { Theme } from 'vitepress'
|
||||
|
||||
import ApiOverview from './components/ApiOverview.vue'
|
||||
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, 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/')
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default theme
|
||||
+37
-28
@@ -3,35 +3,50 @@
|
||||
BitLogger is a structured logging library for MoonBit projects.
|
||||
|
||||
- [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [Chinese README](../README.md)
|
||||
- [Chinese README](https://github.com/Nanaloveyuki/BitLogger/blob/main/README.md)
|
||||
|
||||
## Overview
|
||||
|
||||
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.1
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
- Currently verified targets: `native`, `js`, `wasm`, `wasm-gc`
|
||||
- `llvm` is still treated as experimental in the current release context
|
||||
- Current local verification covers `native`, `js`, `wasm`, 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
|
||||
- `src-async` is available, while `examples/async_basic` is still shipped as a native entry example
|
||||
|
||||
@@ -44,19 +59,13 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
- 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
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API index](./api/index.md)
|
||||
- [src package README](../src/README.mbt.md)
|
||||
- [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
|
||||
- `docs/dev/`: developer reference material kept in the repository, intentionally excluded from the public static docs site
|
||||
|
||||
Common entry points: `text_console(...)`, `file(...)`, `with_queue(...)`, `build_logger(...)`, `build_async_logger(...)`
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: application-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the runtime-sink async logger surface.
|
||||
update-time: 20260614
|
||||
description: Application-facing alias for the runtime-sink async logger surface, preserving the same async calling semantics as AsyncLogger.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -31,6 +31,15 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same async lifecycle helpers such as `run()`, `shutdown()`, `pending_count()`, and `state()`.
|
||||
- Because it is `AsyncLogger[@bitlogger.RuntimeSink]`, the alias also keeps ordinary async 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(...)`.
|
||||
- Unlike the synchronous application alias, async `with_context_fields(...)` and `bind(...)` preserve the visible `ApplicationAsyncLogger` shape because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- Because this is only an alias, methods that are async on `AsyncLogger[@bitlogger.RuntimeSink]` remain async here as well.
|
||||
- The alias therefore keeps the same runtime-sink lifecycle, queue, failure-state, and runtime-dependent post-close semantics already documented on `AsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
- In the current direct alias coverage, values built through `build_application_async_logger(...)` keep the same serialized state snapshot shape, queue counters, lifecycle flags, failure fields, and runtime-sink helper surface that the underlying runtime-sink async logger exposes directly.
|
||||
- When the value is built through `build_application_async_logger(...)`, the sync-first builder route also stays visible through the alias: any optional `LoggerConfig.queue` was already applied before async wrapping, and `logger.sink.kind` had already selected the concrete `RuntimeSink` variant before the application alias reused that value.
|
||||
- That includes queued runtime-sink behavior and file-backed runtime helpers when the configured sink path supports them.
|
||||
- Those helpers remain directly callable on `ApplicationAsyncLogger` itself; callers do not need an unwrap step to reach queue counters, lifecycle state, or `RuntimeSink` file helpers.
|
||||
- The alias exists to give application boot code a clearer public type name for the standard runtime-sink async logger.
|
||||
- Builders such as `build_application_async_logger(...)` and `parse_and_build_application_async_logger(...)` return this alias.
|
||||
|
||||
@@ -53,22 +62,66 @@ In this example, the application alias keeps the same underlying async logger be
|
||||
|
||||
When top-level boot code or services should expose an application-oriented async logger type:
|
||||
```moonbit
|
||||
fn start_async(logger : ApplicationAsyncLogger) -> Unit {
|
||||
async fn start_async(logger : ApplicationAsyncLogger) -> Unit {
|
||||
logger.run()
|
||||
}
|
||||
```
|
||||
|
||||
In this example, callers see the app-facing alias instead of the more explicit generic async logger spelling.
|
||||
In this example, callers see the app-facing alias instead of the more explicit generic async logger spelling, while `run()` keeps its async calling contract.
|
||||
|
||||
And the inherited async logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Direct Async Runtime Helpers On The Application Alias
|
||||
|
||||
When app code should inspect queue or file-backed runtime state without leaving the alias surface:
|
||||
```moonbit
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, lifecycle and queue helpers are called directly on `ApplicationAsyncLogger`.
|
||||
|
||||
And unlike `LibraryAsyncLogger[@bitlogger.RuntimeSink]`, no `to_async_logger()` unwrap is required first.
|
||||
|
||||
#### When Need A One-call Target Override Without Rebuilding The Alias
|
||||
|
||||
When app-level async code should keep the same alias value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.async.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.async.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Async Application Alias
|
||||
|
||||
When app-level async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `ApplicationAsyncLogger`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- Because this is only an alias, any runtime-sink limitations or target-specific async behavior still apply unchanged.
|
||||
|
||||
- If the value came from `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)`, the alias also does not undo the underlying sync-first sink selection; queued and file-backed runtime behavior still depend on the already-built `RuntimeSink` variant.
|
||||
|
||||
- If code needs a narrower public surface than the full async logger API, `LibraryAsyncLogger` is the better facade.
|
||||
|
||||
- If callers need queued runtime-sink helpers or file-backed runtime helpers, they remain directly available on this alias because no wrapper layer strips them away.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This alias is about naming and public intent, not a different async implementation.
|
||||
|
||||
2. Use `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)` for the usual construction paths.
|
||||
2. Inherited `AsyncLogger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_async_logger(...)` or `parse_and_build_application_async_logger(...)` for the usual construction paths.
|
||||
|
||||
4. Use `ApplicationTextAsyncLogger` or `build_application_text_async_logger(...)` when application code should keep the narrower text-console sink type instead of the broader runtime-sink alias.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: application-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the configured sync runtime logger surface.
|
||||
description: Application-facing alias for the configured sync runtime logger surface, preserving the full ConfiguredLogger helper set.
|
||||
key-word:
|
||||
- application
|
||||
- facade
|
||||
@@ -31,6 +31,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same logging, queue, and file helper APIs exposed by `ConfiguredLogger`.
|
||||
- Because `ConfiguredLogger` is itself `Logger[RuntimeSink]`, the alias also keeps ordinary logger composition and write behavior such as `with_target(...)`, `child(...)`, and `log(..., target=...)`.
|
||||
- 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(...)`.
|
||||
- Like the underlying synchronous logger line, `with_context_fields(...)` and `bind(...)` do not preserve the alias spelling. They return a `Logger[ContextSink[RuntimeSink]]` shape because sync shared-field binding extends the sink pipeline instead of storing extra alias-level context metadata.
|
||||
- Because this is only an alias, the application-facing type does not hide any configured-runtime helpers or broader logger surface.
|
||||
- That means queue, drain, flush, and file runtime helpers remain directly callable on `ApplicationLogger` itself; callers do not need an unwrap step to reach the underlying configured runtime logger behavior.
|
||||
- The alias exists to give application boot code a clearer public entry name.
|
||||
- Builders such as `build_application_logger(...)` and `parse_and_build_application_logger(...)` return this alias.
|
||||
|
||||
@@ -58,6 +63,44 @@ fn start(logger : ApplicationLogger) -> Unit {
|
||||
|
||||
In this example, callers see the app-facing alias instead of the lower-level `ConfiguredLogger` name.
|
||||
|
||||
And the same queue/file/runtime helpers remain directly callable because no narrowing wrapper is added.
|
||||
|
||||
#### When Need Direct Runtime Helpers On The Application Alias
|
||||
|
||||
When app code should inspect queue state or file controls without leaving the alias surface:
|
||||
```moonbit
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.flush())
|
||||
```
|
||||
|
||||
In this example, the runtime helpers are called directly on `ApplicationLogger`.
|
||||
|
||||
And unlike `LibraryLogger[RuntimeSink]`, no `to_logger()` unwrap is required first.
|
||||
|
||||
And the inherited logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need A One-call Target Override Without Rebuilding The Alias
|
||||
|
||||
When app-level sync code should keep the same alias value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(Level::Error, "boom", target="app.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Sync Application Alias
|
||||
|
||||
When app-level sync code should attach stable metadata to later records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value has the visible type `Logger[ContextSink[RuntimeSink]]` rather than the alias name `ApplicationLogger`.
|
||||
|
||||
And that type-shape change is expected because sync context binding extends the sink pipeline.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -69,4 +112,8 @@ e.g.:
|
||||
|
||||
1. This alias is about naming and public intent, not a different runtime implementation.
|
||||
|
||||
2. Use `build_application_logger(...)` or `parse_and_build_application_logger(...)` for the usual construction paths.
|
||||
2. Inherited `Logger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_logger(...)` or `parse_and_build_application_logger(...)` for the usual construction paths.
|
||||
|
||||
4. Use `LibraryLogger` instead when a library boundary should intentionally hide configured-runtime helper methods behind a narrower facade.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: application-text-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Application-facing alias for the text-console async logger surface.
|
||||
update-time: 20260614
|
||||
description: Application-facing alias for the text-console async logger surface, preserving the full AsyncLogger helper set for the concrete text sink shape.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -31,6 +31,16 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This alias does not introduce a new runtime type or wrapper layer.
|
||||
- It preserves the same async lifecycle helpers as other async logger aliases.
|
||||
- Because it is `AsyncLogger[@bitlogger.FormattedConsoleSink]`, the alias also keeps ordinary async 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(...)`.
|
||||
- Like the broader runtime-sink async alias, `with_context_fields(...)` and `bind(...)` preserve the visible `ApplicationTextAsyncLogger` shape because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- Because this is only an alias, methods that are async on `AsyncLogger[@bitlogger.FormattedConsoleSink]` remain async here as well.
|
||||
- The alias therefore keeps the same text-console-specific builder and lifecycle semantics already documented on `AsyncLogger[@bitlogger.FormattedConsoleSink]`, including the concrete sink shape plus the same close, queue, and failure-state behavior.
|
||||
- The application-facing type does not hide any async state or lifecycle helpers; queue/backlog/failure inspection remains directly available on this alias just as it is on the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- In the current direct alias coverage, values built through `build_application_text_async_logger(...)` keep the same serialized state snapshot shape, formatter behavior, queue counters, lifecycle flags, and failure fields that the underlying text-console async logger exposes directly.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, the direct async counters come from the outer async logger only; any optional sync queue configured on `LoggerConfig.queue` is not carried into this text-specific build path.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, `logger.sink.kind` also does not decide the runtime sink shape. The builder still constructs `FormattedConsoleSink` from `logger.sink.text_formatter`, even if the config said `Console`, `JsonConsole`, or `File`.
|
||||
- When the value is built through `build_application_text_async_logger(...)`, `flush_policy()` still reports the configured async policy, but the text-specific build path keeps the default no-op async flush callback instead of wiring an explicit sink flush step.
|
||||
- The alias exists to give application code a clearer public name when it wants the concrete text-console sink shape explicitly.
|
||||
- `build_application_text_async_logger(...)` returns this alias.
|
||||
|
||||
@@ -53,12 +63,38 @@ In this example, the application alias keeps the same underlying async logger be
|
||||
|
||||
When caller code should know it is working with the text-console variant:
|
||||
```moonbit
|
||||
fn start_text_async(logger : ApplicationTextAsyncLogger) -> Unit {
|
||||
async fn start_text_async(logger : ApplicationTextAsyncLogger) -> Unit {
|
||||
logger.run()
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the app-facing alias communicates the concrete text-console async shape directly.
|
||||
In this example, the app-facing alias communicates the concrete text-console async shape directly, while `run()` keeps its async calling contract.
|
||||
|
||||
And the same pending-count, state, and failure helpers remain directly available because no narrowing wrapper is added.
|
||||
|
||||
And the inherited async logger target rules stay the same: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need A One-call Target Override On The Text-console Alias
|
||||
|
||||
When app-level text-console async code should keep the same alias value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.text.async.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.text.async.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the alias value's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On The Text-console Async Alias
|
||||
|
||||
When app-level text-console async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `ApplicationTextAsyncLogger`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
### Error Case
|
||||
|
||||
@@ -67,8 +103,18 @@ e.g.:
|
||||
|
||||
- If code does not need the concrete text-console sink shape, `ApplicationAsyncLogger` is the broader runtime-sink async alias.
|
||||
|
||||
- If the value came from `build_application_text_async_logger(...)`, carrying `logger.sink.kind=File` in the config still does not make this alias file-backed; that builder keeps the formatter-driven text-console path.
|
||||
|
||||
- If callers depend on the concrete formatter or direct text-console helper surface, they remain available on this alias because no wrapper layer narrows them away.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This alias is about naming and public intent, not a different async implementation.
|
||||
|
||||
2. Use `build_application_text_async_logger(...)` when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
2. Inherited `AsyncLogger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `build_application_text_async_logger(...)` when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
|
||||
4. Use `ApplicationAsyncLogger` when application code should keep the broader runtime-sink shape instead of the text-console-specific one.
|
||||
|
||||
5. Use `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]` instead when a library boundary should intentionally narrow the exposed async surface while still preserving the concrete text sink type.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-flush-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public flush policy alias used by AsyncLoggerConfig and async worker flushing.
|
||||
update-time: 20260614
|
||||
description: Public flush policy alias used by AsyncLoggerConfig, async parser labels, and async worker flushing.
|
||||
key-word:
|
||||
- async
|
||||
- flush
|
||||
@@ -34,6 +34,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `AsyncFlushPolicy::Batch` calls the configured flush function after each processed batch.
|
||||
- `AsyncFlushPolicy::Shutdown` calls the configured flush function once after the worker loop exits.
|
||||
- The current flush policy is also exposed through `AsyncLogger::flush_policy()` and included in `AsyncLoggerState`.
|
||||
- The canonical labels `Never`, `Batch`, and `Shutdown` are also the labels emitted by async config and logger-state serializers, so parsing and diagnostics share one public vocabulary.
|
||||
- Async config parsing accepts the canonical label `Never` and also the compatibility alias `None`, both mapping to the same public enum variant.
|
||||
- `Batch` flushing happens after the worker finishes one drained batch, not after every individual record write.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -62,10 +65,16 @@ In this example, flushing happens when the worker loop exits instead of after ea
|
||||
e.g.:
|
||||
- If async config text uses unsupported flush policy text, async config parsing raises a failure.
|
||||
|
||||
- The parser error path for unsupported flush text is the same `Failure` surface used by the async config utilities.
|
||||
|
||||
- If a sink never needs explicit flushing, `Batch` or `Shutdown` can add unnecessary work without changing output.
|
||||
|
||||
- If the configured flush callback raises, the worker records failure state and stops instead of silently hiding the error.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This policy only affects the async logger path and only matters when the configured sink has a meaningful flush function.
|
||||
|
||||
2. `AsyncFlushPolicy::Never` is the default in `AsyncLoggerConfig::new(...)`.
|
||||
|
||||
3. Serialized config uses the canonical `Never` label even though the parser also accepts `None`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncLoggerBuildConfig into a JSON value for exporting complete async logger build settings.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncLoggerBuildConfig into a JSON value for exporting the full shared async build shape that can later feed either async builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -38,7 +38,13 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The output always includes `logger` and `async_config`.
|
||||
- Logger export is delegated to `@bitlogger.logger_config_to_json(...)`.
|
||||
- Async export is delegated to `async_logger_config_to_json(...)`.
|
||||
- Because both sections are always materialized, parsed defaults that were originally omitted in JSON input become explicit again in the exported build-config shape.
|
||||
- This helper is useful when generated setup should preserve both sink/logger behavior and async runtime behavior together.
|
||||
- The exported `logger` section keeps the full `LoggerConfig` shape, including fields that only matter on the full sync-first builder path such as the optional sync queue layer.
|
||||
- That means the JSON shape is broader than the consumption pattern of `build_async_text_logger(...)`, which only uses selected text-oriented logger fields when building the sink.
|
||||
- In particular, the exported `logger.sink.kind` remains whatever the config currently says, but a later `build_async_text_logger(...)` call still ignores that sink-kind branch and constructs `FormattedConsoleSink` from `logger.sink.text_formatter`.
|
||||
- The same exported object is also the shared handoff shape for the application and library facade routes after parse. `parse_async_logger_build_config_text(...)` can read this JSON back into `AsyncLoggerBuildConfig`, and that parsed value can then flow unchanged into `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, or `build_library_async_text_logger(...)`.
|
||||
- Because of that, the exported structure is descriptive config data rather than a commitment to one public async type. The later builder or facade API still decides whether the runtime-sink line or the text-console line is taken.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,6 +64,12 @@ let payload = async_logger_build_config_to_json(
|
||||
|
||||
In this example, both layers of configuration are exported together.
|
||||
|
||||
And later consumers can still choose whether to rebuild through `build_async_logger(...)` or the narrower `build_async_text_logger(...)` path.
|
||||
|
||||
And that later text-specific builder choice still matters more than the serialized `logger.sink.kind` value, because only the formatter-backed text path is consumed there.
|
||||
|
||||
And the same exported object can just as well be parsed and then routed into the application or library facade builders when the next consumer wants a narrower public async type.
|
||||
|
||||
#### When Need Roundtrip-friendly Build Config Data
|
||||
|
||||
When generated build config should later be parsed again:
|
||||
@@ -74,3 +86,19 @@ e.g.:
|
||||
|
||||
- If callers want direct text output, they should use `stringify_async_logger_build_config(...)` instead.
|
||||
|
||||
- Exporting the full `logger` section does not imply that every async builder will later consume every logger field equally.
|
||||
|
||||
- Exporting `logger.sink.kind="file"` or `"console"` also does not force the later text-specific builder path to branch that way; only `build_async_logger(...)` follows sink kind when constructing the runtime sink.
|
||||
|
||||
- Choosing an application or library facade builder later does not change the meaning of the exported config by itself; those facade APIs inherit the same runtime-sink-versus-text-console split from the direct builder they delegate to.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when tools or tests need a structured JSON object instead of text.
|
||||
|
||||
2. Use `stringify_async_logger_build_config(...)` when the same build shape should be emitted as JSON text directly.
|
||||
|
||||
3. The resulting object round-trips through `parse_async_logger_build_config_text(...)`, even though different async builders later consume different parts of the embedded `LoggerConfig`.
|
||||
|
||||
4. After parsing, that same object can also feed the application or library facade builders; export preserves one shared build-config shape, not a direct-builder-only route.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async build config alias combining the base logger config and async runtime config.
|
||||
update-time: 20260614
|
||||
description: Public async build config alias combining the base logger config and async runtime config for both the general async builder path and the specialized text-console builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-build-config-type
|
||||
|
||||
`AsyncLoggerBuildConfig` is the public config object that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. It is a direct alias to the build-config model used by async builder APIs, parsers, and serializers.
|
||||
`AsyncLoggerBuildConfig` is the public config object that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. It is a direct alias to the build-config model used by async builder APIs, parsers, and serializers, even though the available builders consume different parts of the embedded sync config.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -31,8 +31,15 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a built logger instance.
|
||||
- The current fields are `logger : LoggerConfig` and `async_config : AsyncLoggerConfig`.
|
||||
- The public `src-async` surface forwards this alias directly from `@utils.AsyncLoggerBuildConfig`, so constructor, parser, export, and stringify helpers all operate on one shared underlying build-config model.
|
||||
- `AsyncLoggerBuildConfig::new(...)` constructs this type as the main handoff object for async build flows.
|
||||
- `build_async_logger(...)`, `build_async_text_logger(...)`, `parse_async_logger_build_config_text(...)`, `async_logger_build_config_to_json(...)`, and `stringify_async_logger_build_config(...)` all consume or produce this same public shape.
|
||||
- The same typed object also feeds the application and library facade builders. `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, and `build_library_async_text_logger(...)` all accept this exact config type and then delegate to one of the two direct builder lines.
|
||||
- The parse-and-build facade helpers use the same model as well. `parse_and_build_application_async_logger(...)` and `parse_and_build_library_async_logger(...)` first parse JSON into this build-config shape and then forward into their respective facade builders.
|
||||
- `build_async_logger(...)` consumes the full sync build path by calling `build_logger(config.logger)` first, so `LoggerConfig.sink`, `LoggerConfig.queue`, and the resulting runtime sink behavior all participate before the async layer is added.
|
||||
- `build_async_text_logger(...)` is narrower: it builds a text console sink directly from `config.logger.sink.text_formatter` and the top-level `min_level`, `target`, and `timestamp` fields, without applying `LoggerConfig.queue`.
|
||||
- On that text-specific path, `config.logger.sink.kind` also does not decide the runtime sink shape. `build_async_text_logger(...)` still constructs `FormattedConsoleSink` from `config.logger.sink.text_formatter` even if the config says `Console`, `JsonConsole`, or `File`.
|
||||
- In practice, the builder function you choose is what decides how the embedded `LoggerConfig` is interpreted. The runtime-sink line (`build_async_logger(...)` and the application/library facades layered on it) preserves the full sync build path, while the text-console line (`build_async_text_logger(...)` and the facades layered on it) ignores the optional sync queue and does not branch on `logger.sink.kind`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -50,6 +57,12 @@ let config : AsyncLoggerBuildConfig = AsyncLoggerBuildConfig::new(
|
||||
|
||||
In this example, both layers of logger setup are kept in one typed value.
|
||||
|
||||
And downstream code can still choose between the full sync-first builder path and the narrower text-console builder path.
|
||||
|
||||
And if downstream code chooses `build_async_text_logger(...)`, that builder choice still matters more than `logger.sink.kind` because only the formatter-backed text path is consumed there.
|
||||
|
||||
And the same typed object can be handed unchanged to `build_application_async_logger(...)`, `build_application_text_async_logger(...)`, `build_library_async_logger(...)`, or `build_library_async_text_logger(...)` when the caller wants a different public facade over the same underlying build decision.
|
||||
|
||||
#### When Need To Export Or Inspect The Full Build Shape
|
||||
|
||||
When application code should inspect the combined async build configuration before constructing the logger:
|
||||
@@ -67,8 +80,20 @@ e.g.:
|
||||
|
||||
- If only async runtime policy is needed and the base sync logger config is irrelevant, this type may be broader than necessary and `AsyncLoggerConfig` is the smaller fit.
|
||||
|
||||
- If callers expect every `LoggerConfig` field to affect every async builder in the same way, that assumption is too broad: the text-specific builder intentionally ignores the optional sync queue layer.
|
||||
|
||||
- In particular, carrying `logger.sink.kind=File` inside this config type does not force the later text-specific builder path to create a file-backed async logger; only `build_async_logger(...)` branches on sink kind.
|
||||
|
||||
- Likewise, choosing an application or library facade builder does not change these config semantics by itself. Those facade APIs inherit the same runtime-sink-versus-text-console split from the direct builder they delegate to.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLoggerBuildConfig::new(...)` when one object should carry both sync and async logger setup.
|
||||
|
||||
2. Use `parse_async_logger_build_config_text(...)` when the same shape should come from JSON text instead of handwritten code.
|
||||
|
||||
3. Pick `build_async_logger(...)` when the full synchronous config path, including `LoggerConfig.queue`, should be preserved before async wrapping.
|
||||
|
||||
4. Pick `build_async_text_logger(...)` when the goal is specifically a concrete text console sink and only the selected text-oriented `LoggerConfig` fields should apply.
|
||||
|
||||
5. Pick the application or library facade builders when the same config should drive one of those narrower public surfaces; the chosen facade changes the exposed type, but the direct builder line underneath still determines whether queue and sink-kind settings are preserved or ignored.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-build-config
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Create the combined sync-and-async build config used by async logger builder APIs.
|
||||
update-time: 20260614
|
||||
description: Create the combined sync-and-async build config used by async logger builder APIs, whether callers later choose the full sync-first builder path or the specialized text-console builder path.
|
||||
key-word:
|
||||
- async
|
||||
- build
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-build-config
|
||||
|
||||
Create an `AsyncLoggerBuildConfig` value that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. This is the constructor used when async builder APIs should receive one typed object carrying both layers of setup.
|
||||
Create an `AsyncLoggerBuildConfig` value that combines the base synchronous `LoggerConfig` with the async runtime `AsyncLoggerConfig`. This is the constructor used when async builder APIs should receive one typed object carrying both layers of setup, even though different builders later consume different parts of the embedded sync config.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn AsyncLoggerBuildConfig::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `logger : LoggerConfig` - Base synchronous logger config describing the sink, level, target, and related sync logger settings.
|
||||
- `logger : LoggerConfig` - Base synchronous logger config describing the sink, level, target, related sync logger settings, and any optional synchronous queue wrapper.
|
||||
- `async_config : AsyncLoggerConfig` - Async runtime config describing queue, batching, linger, and flush behavior.
|
||||
|
||||
#### output
|
||||
@@ -40,6 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Omitting `logger` uses `default_logger_config()`.
|
||||
- Omitting `async_config` uses `AsyncLoggerConfig::new()`.
|
||||
- The constructor simply packages both config objects into one public build shape.
|
||||
- The constructor does not normalize or reinterpret either embedded config beyond those defaults; any normalization has already happened inside the `LoggerConfig` or `AsyncLoggerConfig` values passed in.
|
||||
- When passed to `build_async_logger(...)`, the `logger` portion is built first through the normal synchronous config path before the outer async queue layer is applied.
|
||||
- When passed to `build_async_text_logger(...)`, the same `logger` portion is consumed more narrowly: `text_formatter`, `min_level`, `target`, and `timestamp` are used directly to build a text console sink, while `LoggerConfig.queue` is not applied.
|
||||
- On that text-specific path, `logger.sink.kind` also does not decide the runtime sink shape. `build_async_text_logger(...)` still constructs `FormattedConsoleSink` from `logger.sink.text_formatter` even if the config says `Console`, `JsonConsole`, or `File`.
|
||||
- This helper is the main code-side counterpart to `parse_async_logger_build_config_text(...)`.
|
||||
|
||||
### How to Use
|
||||
@@ -58,6 +62,10 @@ let config = AsyncLoggerBuildConfig::new(
|
||||
|
||||
In this example, the builder input keeps both configuration layers in one typed value.
|
||||
|
||||
And later code can still decide whether that shared config should flow into the full sync-first builder or the narrower text-console builder.
|
||||
|
||||
And if later code chooses `build_async_text_logger(...)`, that builder choice still matters more than `logger.sink.kind` because only the formatter-backed text path is consumed there.
|
||||
|
||||
#### When Need Defaulted Async Build Settings
|
||||
|
||||
When code only wants the standard combined config shape with few overrides:
|
||||
@@ -74,8 +82,16 @@ e.g.:
|
||||
|
||||
- If callers only need async runtime policy and not the full builder input shape, `AsyncLoggerConfig::new(...)` is the smaller API.
|
||||
|
||||
- If callers expect every field inside `LoggerConfig` to affect every async builder equally, that assumption is too broad: `build_async_text_logger(...)` intentionally skips the optional sync queue layer.
|
||||
|
||||
- In particular, carrying `logger.sink.kind=File` inside this config does not force the later text-specific builder path to create a file-backed async logger; only `build_async_logger(...)` branches on sink kind.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when async builder APIs should receive one combined config object.
|
||||
|
||||
2. Pair it with `build_async_logger(...)`, `build_async_text_logger(...)`, or `parse_async_logger_build_config_text(...)` depending on whether the source is code or JSON text.
|
||||
|
||||
3. Prefer `build_async_logger(...)` after constructing this value when configured sync sink behavior, including `LoggerConfig.queue`, should be preserved before async wrapping.
|
||||
|
||||
4. Prefer `build_async_text_logger(...)` after constructing this value when the goal is specifically config-driven text console output with a concrete `FormattedConsoleSink`.
|
||||
|
||||
@@ -28,16 +28,18 @@ pub fn[S] AsyncLogger::child(self : AsyncLogger[S], target : String) -> AsyncLog
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger whose default target is the composed child path.
|
||||
- `AsyncLogger[S]` - A new async logger value whose default target is the composed child path.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- If the parent target is empty, the child target becomes the full target.
|
||||
- If the child target is empty, the parent target is preserved.
|
||||
- If both are non-empty, they are joined with `.`.
|
||||
- Queue settings, sink wiring, and runtime behavior are preserved in the returned logger.
|
||||
- Only the stored target changes. Queue settings, sink wiring, flush policy, level gating, and lifecycle state remain shared with the same underlying async logger setup.
|
||||
- In the current direct async coverage, `timestamp` is also preserved on the derived child logger while the source logger keeps its previous parent target.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -53,6 +55,8 @@ let worker = logger.child("worker")
|
||||
|
||||
In this example, `worker` emits under `service.worker` while keeping the same async queue behavior.
|
||||
|
||||
And `logger` still keeps its original stored target, because `child(...)` returns a derived async logger value instead of mutating the parent.
|
||||
|
||||
#### When Build Deep Async Scopes Step By Step
|
||||
|
||||
When deeper target composition should remain readable:
|
||||
@@ -76,3 +80,7 @@ e.g.:
|
||||
1. This is the preferred API for hierarchical async logger naming.
|
||||
|
||||
2. Composition changes the target only and does not rebuild the queue or sink.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` still operate on the same async logger state after derivation.
|
||||
|
||||
4. Use `child("")` when code should keep the current target while still following a target-composition code path.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-close
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Close the async logger queue and optionally clear pending records immediately.
|
||||
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
|
||||
- logger
|
||||
@@ -35,9 +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 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
|
||||
|
||||
@@ -66,6 +71,10 @@ In this example, queued backlog is counted as dropped instead of waiting for fur
|
||||
e.g.:
|
||||
- If `clear=true`, pending records are intentionally discarded and contribute to `dropped_count()`.
|
||||
|
||||
- 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 and patch/filter side effects stay skipped.
|
||||
|
||||
- If callers need graceful waiting for drain completion, `shutdown()` is usually the better API.
|
||||
|
||||
### Notes
|
||||
@@ -73,3 +82,5 @@ e.g.:
|
||||
1. This is a low-level lifecycle helper; prefer `shutdown()` for normal graceful teardown.
|
||||
|
||||
2. Use `clear=true` only when backlog loss is an acceptable shutdown tradeoff.
|
||||
|
||||
3. Pair it with `pending_count()`, `dropped_count()`, or `state()` when you need to observe what happened to existing backlog after closure.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncLoggerConfig into a JSON value for export, persistence, or generated async config output.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncLoggerConfig into a JSON value for export, persistence, or generated async config output using the stable serialized policy labels.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -36,6 +36,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The output includes `max_pending`, `max_batch`, `linger_ms`, `overflow`, and `flush`.
|
||||
- Policy fields are serialized using the stable labels accepted by the config parser.
|
||||
- This helper exports effective typed config after constructor normalization has already happened.
|
||||
- If `max_pending` is negative in the config object, the exported JSON preserves that negative value because runtime queue clamping happens later, not during serialization.
|
||||
- The JSON shape matches the `async_config` section used by async build config parsing.
|
||||
|
||||
### How to Use
|
||||
@@ -67,5 +68,13 @@ In this example, the exported JSON stays aligned with parser expectations.
|
||||
e.g.:
|
||||
- If `max_batch` or `linger_ms` were normalized during construction, the exported JSON reflects the normalized values rather than the original invalid inputs.
|
||||
|
||||
- If callers need to understand runtime queue behavior for negative `max_pending`, they should document that separately because serialization preserves the config value rather than the later queue-kind clamp.
|
||||
|
||||
- If callers want direct text output instead of a JSON value, they should use `stringify_async_logger_config(...)` instead.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Serialized policy labels round-trip through `parse_async_logger_config_text(...)`.
|
||||
|
||||
2. The serializer emits canonical labels like `DropNewest` and `Never`, even though the parser also accepts compatibility aliases such as `DropLatest` and `None`.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async logger config alias used for queue, batching, linger, and flush settings.
|
||||
update-time: 20260614
|
||||
description: Public async logger config alias used for async queue interpretation, batching, linger, and flush settings.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -31,8 +31,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a runtime logger handle.
|
||||
- The current fields are `max_pending : Int`, `overflow : AsyncOverflowPolicy`, `max_batch : Int`, `linger_ms : Int`, and `flush : AsyncFlushPolicy`.
|
||||
- `AsyncLoggerConfig::new(...)` constructs this type with the same normalization rules used by the runtime.
|
||||
- The public `src-async` surface forwards this alias directly from `@utils.AsyncLoggerConfig`, so constructor, parser, export, and stringify helpers all operate on one shared underlying config model.
|
||||
- `AsyncLoggerConfig::new(...)` normalizes `max_batch` and `linger_ms`, but it preserves the provided `max_pending` value.
|
||||
- Runtime queue creation later interprets negative `max_pending` as `0` when choosing the internal queue kind.
|
||||
- `parse_async_logger_config_text(...)`, `async_logger_config_to_json(...)`, and `stringify_async_logger_config(...)` all operate on this same public config shape.
|
||||
- The parser accepts the stable serialized labels such as `DropNewest` and `Never`, plus compatibility aliases such as `DropLatest` and `None`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,10 +68,14 @@ In this example, the same public config object supports both inspection and late
|
||||
e.g.:
|
||||
- `AsyncLoggerConfig` itself does not have a runtime failure mode.
|
||||
|
||||
- Constructor normalization still applies when the value is created through `AsyncLoggerConfig::new(...)`, so very small or negative timing and batch inputs may be adjusted before later serialization or use.
|
||||
- Constructor normalization still applies when the value is created through `AsyncLoggerConfig::new(...)`, so very small batch sizes and negative linger values may be adjusted before later serialization or use.
|
||||
|
||||
- Negative `max_pending` is not rewritten inside the config object itself; it is only clamped later when the async queue kind is derived at runtime.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLoggerConfig::new(...)` when you need a value of this type in code.
|
||||
|
||||
2. Use `AsyncLoggerBuildConfig` when the async config should travel together with the base synchronous `LoggerConfig`.
|
||||
|
||||
3. Use `parse_async_logger_config_text(...)` when the same shape should come from JSON text, including accepted aliases like `DropLatest` and `None`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-config
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Create the queue, batching, linger, and flush policy config used by async loggers.
|
||||
update-time: 20260614
|
||||
description: Create the async queue, batching, linger, and flush policy config used by async loggers.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -29,7 +29,7 @@ pub fn AsyncLoggerConfig::new(
|
||||
|
||||
#### input
|
||||
|
||||
- `max_pending : Int` - Maximum queued records.
|
||||
- `max_pending : Int` - Requested maximum queued records before overflow policy matters; negative values are preserved in the config object and later treated as `0` by runtime queue creation.
|
||||
- `overflow : AsyncOverflowPolicy` - Queue overflow strategy.
|
||||
- `max_batch : Int` - Maximum records drained per batch.
|
||||
- `linger_ms : Int` - Optional wait window for batch accumulation.
|
||||
@@ -45,6 +45,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `max_batch <= 1` is normalized to `1`.
|
||||
- `linger_ms < 0` is normalized to `0`.
|
||||
- `max_pending` is stored as provided by the constructor.
|
||||
- When the async logger runtime later builds its internal queue, negative `max_pending` is interpreted as `0`.
|
||||
- `overflow` and `flush` define the most important queue/runtime behavior tradeoffs.
|
||||
- This type is used directly by `async_logger(...)` and embedded in `AsyncLoggerBuildConfig`.
|
||||
|
||||
@@ -79,12 +81,14 @@ In this example, the config becomes a stable JSON payload.
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If `max_batch` is set to `0` or below, runtime config normalizes it to `1`.
|
||||
- If `max_batch` is set to `0` or below, constructor normalization changes it to `1`.
|
||||
|
||||
- If `linger_ms` is negative, it is normalized to `0`.
|
||||
- If `linger_ms` is negative, constructor normalization changes it to `0`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This type controls async runtime behavior, not synchronous queue wrapping.
|
||||
|
||||
2. Prefer explicit values for production services so overflow and flush semantics are visible.
|
||||
|
||||
3. If queue limit semantics for negative values matter, document that behavior explicitly in calling code because the config object preserves the negative value while the runtime queue later clamps it to `0`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-debug
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a debug-level record through the async logger using the built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a debug-level record through the async logger using the built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::debug(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Debug, ...)`.
|
||||
- This helper delegates to `log(Level::Debug, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Debug records are useful for development and targeted diagnostics.
|
||||
- Use this helper when a named debug call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When intermediate async flow details should be visible during debugging:
|
||||
```moonbit
|
||||
await logger.debug("loaded worker config")
|
||||
logger.debug("loaded worker config")
|
||||
```
|
||||
|
||||
In this example, the call site communicates its intended diagnostic level directly.
|
||||
@@ -61,7 +62,7 @@ In this example, the call site communicates its intended diagnostic level direct
|
||||
|
||||
When a debug event should include extra fields:
|
||||
```moonbit
|
||||
await logger.debug(
|
||||
logger.debug(
|
||||
"dispatch start",
|
||||
fields=[@bitlogger.field("job_id", "42")],
|
||||
)
|
||||
@@ -80,4 +81,4 @@ e.g.:
|
||||
|
||||
1. Prefer this helper when the event is semantically debug-level.
|
||||
|
||||
2. Use `log(...)` when the level must be chosen dynamically.
|
||||
2. Use `log(...)` when the level must be chosen dynamically or one call needs a target override.
|
||||
|
||||
@@ -35,6 +35,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The counter increases when overflow policy discards records.
|
||||
- The counter can also increase when `close(clear=true)` or `shutdown(clear=true)` abandons queued records.
|
||||
- It can also increase when shutdown fallback logic converts remaining pending records into dropped records during a clear-close path.
|
||||
- After a worker failure short-circuits `wait_idle()`, that shutdown-fallback increase is runtime-dependent in the current implementation: native-worker shutdown can convert leftover pending records into additional dropped records, while compatibility shutdown can leave that closed-queue remainder visible in `pending_count()` instead.
|
||||
- That also means `dropped_count()` can still stay unchanged even after `is_closed=true` and retained failure state on compatibility-style shutdown paths, because closure there does not necessarily convert the leftover failed backlog into dropped records.
|
||||
- Later log attempts against an already closed queue do not add to this counter by themselves.
|
||||
- This is a cumulative counter for the lifetime of the logger value.
|
||||
- Use this helper when you need a focused loss metric rather than a full `state()` snapshot.
|
||||
|
||||
@@ -70,6 +74,8 @@ e.g.:
|
||||
|
||||
- If callers need to know why records were lost, they must interpret this metric together with overflow policy and shutdown behavior.
|
||||
|
||||
- If `wait_idle()` returned early because the worker failed, `dropped_count()` may still stay unchanged on compatibility shutdown paths even though one leftover closed-queue item remains visible in `pending_count()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This helper reports record loss only; it does not explain the full logger state.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-error
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue an error-level record through the async logger using the highest built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an error-level record through the async logger using the highest built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::error(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Error, ...)`.
|
||||
- The record is still subject to patching, filtering, and overflow policy.
|
||||
- This helper delegates to `log(Level::Error, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Error records represent the highest built-in severity in this logger API.
|
||||
- Use this helper when a named error call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When an operation should emit a high-severity failure event:
|
||||
```moonbit
|
||||
await logger.error("worker execution failed")
|
||||
logger.error("worker execution failed")
|
||||
```
|
||||
|
||||
In this example, failure intent is explicit at the call site.
|
||||
@@ -61,7 +62,7 @@ In this example, failure intent is explicit at the call site.
|
||||
|
||||
When an error event should include diagnostic fields:
|
||||
```moonbit
|
||||
await logger.error(
|
||||
logger.error(
|
||||
"dispatch failed",
|
||||
fields=[@bitlogger.field("job_id", "42")],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.error(
|
||||
|
||||
In this example, the error record carries structured context without falling back to the generic `log(...)` form.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,6 @@ e.g.:
|
||||
|
||||
1. Use this helper for high-severity async application failures.
|
||||
|
||||
2. Emitting an error record is separate from the logger worker itself entering failure state.
|
||||
2. Use `log(...)` instead when an error call needs a one-off target override.
|
||||
|
||||
3. Emitting an error record is separate from the logger worker itself entering failure state.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-flush-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the async logger flush policy currently governing batch and shutdown flushing behavior.
|
||||
update-time: 20260707
|
||||
description: Read the async logger flush policy currently governing batch-end and shutdown-end flush-callback timing.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -34,9 +34,13 @@ pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy {
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The returned value reflects the policy captured when the async logger was created.
|
||||
- `Batch` causes explicit flush calls after worker batch processing.
|
||||
- `Shutdown` causes explicit flush calls at worker shutdown.
|
||||
- `Never` leaves flushing entirely to sink behavior or external control.
|
||||
- `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 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
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-has-failed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger worker has encountered a failure during queue drain.
|
||||
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
|
||||
- logger
|
||||
@@ -34,7 +34,10 @@ pub fn[S] AsyncLogger::has_failed(self : AsyncLogger[S]) -> Bool {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` clears previous failure state at startup.
|
||||
- If the worker loop raises an error, the logger records that failure and exposes it through this flag.
|
||||
- `run()` also clears the stored `last_error()` string at startup before drain work begins.
|
||||
- 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.
|
||||
- Failure state is about runtime drain execution, not whether records were dropped due to overflow policy.
|
||||
|
||||
@@ -67,10 +70,19 @@ In this example, the helper exposes a simple pass-fail runtime indicator.
|
||||
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 `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 `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.
|
||||
|
||||
- `close()` or `shutdown()` do not clear this flag by themselves; only a later `run()` that has already started resets it.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This helper reports worker failure, not general queue stress.
|
||||
|
||||
2. Pair it with `last_error()` when you need actionable detail.
|
||||
|
||||
3. Pair it with `is_running()` or `state()` when you also need to know whether the worker has already exited and whether backlog remains.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-info
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue an info-level record through the async logger using the most common built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue an info-level record through the async logger using the most common built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::info(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Info, ...)`.
|
||||
- This helper delegates to `log(Level::Info, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Info is often the default operational logging level for async application events.
|
||||
- Use this helper when explicit info intent is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When async code should report routine progress or lifecycle events:
|
||||
```moonbit
|
||||
await logger.info("worker started")
|
||||
logger.info("worker started")
|
||||
```
|
||||
|
||||
In this example, the event is expressed at the most common operational logging level.
|
||||
@@ -61,7 +62,7 @@ In this example, the event is expressed at the most common operational logging l
|
||||
|
||||
When an info event should include stable structured detail:
|
||||
```moonbit
|
||||
await logger.info(
|
||||
logger.info(
|
||||
"job queued",
|
||||
fields=[@bitlogger.field("queue", "sync")],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.info(
|
||||
|
||||
In this example, the record remains concise while still carrying useful metadata.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,4 @@ e.g.:
|
||||
|
||||
1. This is often the most common convenience method for normal async application events.
|
||||
|
||||
2. Use `log(...)` when the call site needs a dynamic level or target override.
|
||||
2. Use `log(...)` when the call site needs a dynamic level or a one-off target override.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-is-closed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger has been closed and should no longer accept normal new queue traffic.
|
||||
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
|
||||
- logger
|
||||
@@ -36,7 +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 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 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
|
||||
|
||||
@@ -70,8 +74,16 @@ 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 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()`.
|
||||
|
||||
- A closed logger can still report leftover `pending_count()` or `dropped_count()` values from the shutdown path, so `is_closed()` alone is not enough to infer whether backlog was fully drained or explicitly abandoned.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Closed state and running state are related but not identical.
|
||||
|
||||
2. Use this helper when lifecycle control matters more than queue counters.
|
||||
|
||||
3. Pair it with `pending_count()`, `is_running()`, or `state()` when you need to understand what closure means for remaining backlog on a live logger instance.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-is-running
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read whether the async logger worker is currently running and draining the queue.
|
||||
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
|
||||
- logger
|
||||
@@ -36,7 +36,11 @@ 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 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,10 +71,18 @@ 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 the cleanup result from shutdown.
|
||||
|
||||
- If callers need a one-shot lifecycle flow, `shutdown()` is usually better than manual polling.
|
||||
|
||||
- If `is_running()` is `true`, that still does not guarantee healthy drain progress; callers may need `has_failed()` or `state()` for failure context.
|
||||
|
||||
- Under concurrent activity, the returned value may change immediately after it is read.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper for worker activity checks, not as a complete health signal.
|
||||
|
||||
2. Pair it with `has_failed()` or `state()` when diagnosing stalled pipelines.
|
||||
|
||||
3. Pair it with `pending_count()` when you need to distinguish an idle worker from a stopped logger that still has backlog.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-last-error
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the last error recorded by the async logger worker during runtime failure handling.
|
||||
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
|
||||
- logger
|
||||
@@ -33,10 +33,13 @@ pub fn[S] AsyncLogger::last_error(self : AsyncLogger[S]) -> String {}
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` resets the stored error string when the worker starts.
|
||||
- A later `run()` clears the stored error string only after that run has actually started.
|
||||
- If the worker loop fails, the error text is captured from the raised exception.
|
||||
- An empty string normally means no failure has been recorded.
|
||||
- Once a failure string is recorded, it stays in place until a later `run()` invocation actually starts and clears it.
|
||||
- Failure-driven shutdown does not clear the stored error string by itself, so the same `last_error()` text can remain visible even after the logger is already closed.
|
||||
- This helper reports worker execution errors, not ordinary overflow or backpressure conditions.
|
||||
- That reset happens before the restarted worker resumes drain work.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,10 +70,18 @@ In this example, the helper provides the textual failure detail without building
|
||||
e.g.:
|
||||
- If no runtime failure has occurred, the method returns an empty string.
|
||||
|
||||
- An empty string does not prove the queue is empty or the worker is idle; it only means no failure string is currently recorded.
|
||||
|
||||
- If callers need broader context than just the error text, they should use `state()`.
|
||||
|
||||
- `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 `phase=closed_failed` and `terminal=true` while the retained failure record is still intentionally visible.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Read this helper together with `has_failed()` when interpreting worker health.
|
||||
|
||||
2. The stored value is a diagnostic string, not a typed error object.
|
||||
|
||||
3. Pair it with `is_running()` or `pending_count()` when you need to know whether failure left the logger with unfinished backlog, because the previous error string can coexist with remaining pending records until later cleanup or restart.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-log
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
update-time: 20260614
|
||||
description: Enqueue a record into the async logger with an explicit level, message, optional fields, and optional target override.
|
||||
key-word:
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-log
|
||||
|
||||
Enqueue a record into the async logger with an explicit level and message. This is the core write API behind all async level-specific convenience methods.
|
||||
Enqueue a record into the async logger with an explicit level and message. This is the core write API behind all async level-specific convenience methods and the only built-in async write API that accepts a per-call target override.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -43,10 +43,14 @@ pub async fn[S] AsyncLogger::log(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The logger checks `is_enabled(level)` before building a record.
|
||||
- Compatibility runtimes that guard closed writes first can return immediately when `is_closed()` is already `true`, before level checks, record construction, patch logic, filter logic, or queue work.
|
||||
- Otherwise the logger checks `is_enabled(level)` before building a record.
|
||||
- If `target` is empty, the logger uses its stored default target. If `target` is non-empty, that value overrides the stored target for this call only.
|
||||
- Context fields, patch logic, and filter logic are applied before enqueue.
|
||||
- If timestamping is enabled, `@env.now()` is captured before the record enters the queue.
|
||||
- Overflow behavior depends on the configured `AsyncOverflowPolicy`.
|
||||
- Closed-on-log behavior is runtime-dependent: compatibility runtimes short-circuit before record-building work, while native-worker runtimes may still build, patch, and filter the record before queue operations treat a closed queue as a non-accepted write.
|
||||
- In the tested blocking-policy path, a late write against an already closed logger does not become a newly accepted pending record and does not add another dropped record; it simply fails to enter the queue after any runtime-specific pre-queue work finishes.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,7 +60,7 @@ Here are some specific examples provided.
|
||||
|
||||
When code should choose level, fields, and target per event:
|
||||
```moonbit
|
||||
await logger.log(
|
||||
logger.log(
|
||||
@bitlogger.Level::Info,
|
||||
"worker started",
|
||||
fields=[@bitlogger.field("job", "sync")],
|
||||
@@ -64,13 +68,22 @@ await logger.log(
|
||||
)
|
||||
```
|
||||
|
||||
In this example, all per-record inputs are supplied explicitly.
|
||||
In this example, the record overrides the logger's stored target only for this call.
|
||||
|
||||
#### When Reuse The Stored Async Target
|
||||
|
||||
When a call should keep the logger's existing target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
```
|
||||
|
||||
In this example, the logger falls back to its stored target because no `target=` override is provided.
|
||||
|
||||
#### When Build Higher-level Async Logging Helpers
|
||||
|
||||
When application code wants a custom wrapper around the base API:
|
||||
```moonbit
|
||||
await logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
logger.log(@bitlogger.Level::Warn, "slow request")
|
||||
```
|
||||
|
||||
In this example, `log(...)` acts as the common primitive under custom wrappers or convenience methods.
|
||||
@@ -82,8 +95,14 @@ e.g.:
|
||||
|
||||
- If the logger is closed or overflow policy rejects the record, enqueue may not proceed as a normal accepted write.
|
||||
|
||||
- A closed queue does not count as a newly accepted pending record.
|
||||
|
||||
- On compatibility runtimes, a late call after close can be skipped before patch or filter logic runs at all.
|
||||
|
||||
- On native-worker runtimes, a late call after close can still execute patch and filter logic before the queue rejects the write.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when the call site needs full control instead of a fixed severity helper.
|
||||
|
||||
2. Prefer `info()`, `warn()`, and the other shortcuts when only the level differs.
|
||||
2. Prefer `info()`, `warn()`, and the other shortcuts when only the level differs and no per-call target override is needed.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-pending-count
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current number of queued records that have not yet been drained by the async logger worker.
|
||||
update-time: 20260614
|
||||
description: Read the current number of queued records that have not yet been drained or cleared from the async logger pipeline.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -35,6 +35,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The count increases when records are accepted into the queue.
|
||||
- The count decreases as the worker drains records.
|
||||
- The count is also reset to `0` when queued records are abandoned through clear-close paths such as `close(clear=true)`.
|
||||
- Log attempts against a closed queue do not create new pending backlog.
|
||||
- This is a point-in-time metric and may change immediately after it is read.
|
||||
- Use this helper when a single backlog number is enough and a full `state()` snapshot is unnecessary.
|
||||
|
||||
@@ -67,6 +69,12 @@ In this example, the queue backlog is checked directly.
|
||||
e.g.:
|
||||
- If the worker is not running, `pending_count()` may stay above `0` until records are drained or cleared.
|
||||
|
||||
- If `wait_idle()` returns early because `has_failed()` became `true`, `pending_count()` may still be above `0` until later cleanup or clear-close handling runs.
|
||||
- In the current tested split, that later cleanup can either force the leftover pending item into `dropped_count()` on native-worker shutdown paths or leave the closed-queue remainder visible in `pending_count()` on compatibility shutdown paths.
|
||||
- That means `pending_count()` can still stay above `0` even after `is_closed=true` on compatibility-style shutdown paths, because closure there does not necessarily convert the leftover failed backlog into dropped records.
|
||||
|
||||
- A later restarted `run()` can still drain that retained backlog after failure-reset startup, so a nonzero pending count after failure is not necessarily a permanent terminal state.
|
||||
|
||||
- If the queue is empty, the method simply returns `0`.
|
||||
|
||||
### Notes
|
||||
@@ -74,3 +82,5 @@ e.g.:
|
||||
1. Use `state()` when you also need dropped counts, failure state, or runtime mode.
|
||||
|
||||
2. This helper is useful for lightweight health checks and tests.
|
||||
|
||||
3. Pair it with `is_running()` or `has_failed()` when backlog alone is not enough to explain whether the worker is actively draining, stopped, or failed.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-run
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Start the async logger worker loop so queued records are drained to the underlying sink.
|
||||
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
|
||||
- logger
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-run
|
||||
|
||||
Start the async logger worker loop. This is the core runtime API that drains queued records to the underlying sink and updates worker lifecycle state.
|
||||
Start the async logger worker loop. This is the core runtime API that drains queued records to the underlying sink and updates worker lifecycle state around that drain loop.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -33,10 +33,16 @@ 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` while the worker loop is active.
|
||||
- It clears previous failure state before worker execution begins.
|
||||
- On failure, the logger records `has_failed=true` and stores the error text in `last_error`.
|
||||
- The worker exits when the queue is closed or when a failure aborts processing.
|
||||
- `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()` 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,12 +74,24 @@ In this example, the application decides when the worker begins instead of hidin
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the worker loop fails, `has_failed()` becomes `true` and `last_error()` stores the error text.
|
||||
- If the worker loop fails, `has_failed()` becomes `true`, `last_error()` stores the error text, and `run()` raises that failure to the caller.
|
||||
|
||||
- After a failed run, `pending_count()` can still be greater than zero until later shutdown or restart logic finishes handling the retained backlog.
|
||||
|
||||
- If `run()` is never started, accepted records may remain queued and not reach the sink.
|
||||
|
||||
- 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 raises `AsyncLoggerAlreadyRunning` on the later attempt.
|
||||
|
||||
- Starting `run()` after a terminal close path raises `AsyncLoggerClosed`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. `async_logger(...)` only constructs the logger; `run()` is what activates queue draining.
|
||||
|
||||
2. Pair this API with `shutdown()` for a complete worker lifecycle.
|
||||
|
||||
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. A concurrent second startup now fails explicitly with `AsyncLoggerAlreadyRunning`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-shutdown
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Gracefully stop an async logger by waiting for idle or clearing queued work, then waiting for the worker to finish.
|
||||
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,9 +35,12 @@ 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.
|
||||
- If backlog still remains after waiting, shutdown falls back to `close(clear=true)`.
|
||||
- `clear=true` immediately closes and abandons pending records.
|
||||
- The method waits until `is_running()` becomes `false` before returning.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -47,7 +50,7 @@ Here are some specific examples provided.
|
||||
|
||||
When a service should stop logging only after queued records are drained:
|
||||
```moonbit
|
||||
await logger.shutdown()
|
||||
logger.shutdown()
|
||||
```
|
||||
|
||||
In this example, the logger waits for normal drain behavior before final closure.
|
||||
@@ -56,7 +59,7 @@ In this example, the logger waits for normal drain behavior before final closure
|
||||
|
||||
When teardown should prefer speed over preserving backlog:
|
||||
```moonbit
|
||||
await logger.shutdown(clear=true)
|
||||
logger.shutdown(clear=true)
|
||||
```
|
||||
|
||||
In this example, pending work is abandoned intentionally so shutdown can complete sooner.
|
||||
@@ -66,10 +69,22 @@ 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 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.
|
||||
|
||||
- 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.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API over raw `close()` in normal application shutdown paths.
|
||||
|
||||
2. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
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.
|
||||
|
||||
4. Pair it with `state()` or focused counters when tests need to assert whether shutdown drained backlog or converted it into dropped records.
|
||||
|
||||
5. Prefer `shutdown(clear=true)` when teardown must not depend on a still-running drain worker.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-new
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Construct an AsyncLoggerState snapshot from explicit runtime, queue, lifecycle, and failure values.
|
||||
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,10 +45,16 @@ 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.
|
||||
- That includes combinations such as `has_failed=true` together with non-zero `pending_count` or a retained `last_error`, and even a manually chosen runtime snapshot that does not match the current backend, all of which are valid for diagnostic snapshots and test fixtures.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -64,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,
|
||||
)
|
||||
@@ -82,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(),
|
||||
)
|
||||
@@ -101,8 +99,16 @@ e.g.:
|
||||
|
||||
- If callers want a snapshot directly from one live logger instance, `AsyncLogger::state()` is the simpler API.
|
||||
|
||||
- If callers manually combine a runtime snapshot, counters, or flush policy that do not actually belong together, the constructor still accepts that synthetic snapshot.
|
||||
|
||||
- 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 the phase and counters exactly as they want them represented.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when code should construct an `AsyncLoggerState` value explicitly.
|
||||
|
||||
2. Pair it with `async_logger_state_to_json(...)` or `stringify_async_logger_state(...)` when the snapshot should be exported.
|
||||
|
||||
3. Prefer `AsyncLogger::state()` when the goal is to report the actual current state of one live logger instance.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert an AsyncLoggerState snapshot into a JSON value for diagnostics and transport.
|
||||
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
|
||||
- state
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-state-to-json
|
||||
|
||||
Convert `AsyncLoggerState` into a `JsonValue`. This helper is the structured export path for async logger runtime snapshots when callers want machine-readable diagnostics instead of a plain string.
|
||||
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.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
|
||||
#### input
|
||||
|
||||
- `state : AsyncLoggerState` - Snapshot produced by `AsyncLogger::state()`.
|
||||
- `state : AsyncLoggerState` - Snapshot produced by `AsyncLogger::state()` or any manually constructed `AsyncLoggerState` value.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -33,10 +33,16 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The JSON includes runtime mode, worker support, queue counters, lifecycle flags, 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 before serialization.
|
||||
- The state must already have been captured or constructed before serialization.
|
||||
- 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,9 +74,17 @@ e.g.:
|
||||
|
||||
- If the queue is empty, `pending_count` and `dropped_count` are still serialized normally as numeric values.
|
||||
|
||||
- If `has_failed` is `true`, serialization does not force `pending_count` to `0` or clear `last_error`; it reports the snapshot exactly as provided.
|
||||
|
||||
- If callers need newer logger data, they must capture a fresh `AsyncLogger::state()` first instead of expecting JSON conversion itself to refresh stale fields.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this API when downstream code wants a JSON value rather than a ready-made string.
|
||||
1. This helper preserves the nested runtime snapshot instead of flattening `mode` and `background_worker` onto the top level.
|
||||
|
||||
2. Pair it with `AsyncLogger::state()` to capture the snapshot first.
|
||||
2. The resulting object matches the compact string form produced by `stringify_async_logger_state(...)` after JSON stringification.
|
||||
|
||||
3. Use this API when downstream code wants a JSON value rather than a ready-made string.
|
||||
|
||||
4. Pair it with `AsyncLogger::state()` when you want current logger data, or with `AsyncLoggerState::new(...)` when tests or adapters are exporting a synthetic snapshot.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async logger state alias used for queue, lifecycle, and runtime 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,8 +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 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.
|
||||
- `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
|
||||
|
||||
@@ -66,8 +72,16 @@ e.g.:
|
||||
|
||||
- `last_error` may be an empty string when no failure has occurred, which is normal and not a special error condition by itself.
|
||||
|
||||
- Because this is just a data shape, manual construction can represent combinations that do not come from a live logger at one exact instant.
|
||||
|
||||
- Receiving an `AsyncLoggerState` value alone does not prove it came from one current logger read rather than from a synthetic constructor path.
|
||||
|
||||
- This type does not imply cleanup semantics by itself; values only report the supplied or captured fields and do not drain backlog or clear recorded failure state.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `AsyncLogger::state()` when you need a value of this type from one logger instance.
|
||||
|
||||
2. Use `AsyncRuntimeState` when only backend-level capability information is needed and logger-instance state is unnecessary.
|
||||
|
||||
3. Use `async_logger_state_to_json(...)` or `stringify_async_logger_state(...)` when this snapshot should leave typed space and become stable diagnostic output.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read a full async logger runtime snapshot including queue counters, lifecycle flags, and runtime mode.
|
||||
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,16 +27,27 @@ 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`.
|
||||
- `state()` returns a point-in-time snapshot rather than a live handle.
|
||||
- `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.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()` so callers do not need to join separate helpers manually.
|
||||
- `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, 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.
|
||||
- `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
|
||||
|
||||
@@ -66,6 +77,8 @@ if state.has_failed {
|
||||
|
||||
In this example, the same snapshot object works for conditional diagnostics and serialization.
|
||||
|
||||
And the reported failure fields can still appear together with non-zero backlog when a worker stopped early.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -73,8 +86,22 @@ e.g.:
|
||||
|
||||
- If the queue is empty, `pending_count` is `0`; this is normal and not a special error condition.
|
||||
|
||||
- `flush_policy` reports the logger's configured async flush mode, not whether a flush has already happened.
|
||||
|
||||
- The embedded `runtime` object is also just a snapshot of the current backend helper result at read time; `state()` does not cache it onto the logger for later reuse.
|
||||
|
||||
- If concurrent logger activity is still changing counters or flags while `state()` runs, the returned value is still useful for diagnostics but should not be treated as a transactional snapshot.
|
||||
|
||||
- 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, `phase=closed_failed`, `has_failed=true`, and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API over manually combining `pending_count()`, `dropped_count()`, and runtime-mode helpers.
|
||||
|
||||
2. Use `pretty=true` when emitting logs for humans and the compact form for machine-oriented payloads.
|
||||
|
||||
3. Use `AsyncLoggerState::new(...)` only when tests or adapters need to construct a manual snapshot instead of reading one directly from a logger instance.
|
||||
|
||||
4. If consumers need stronger cross-field consistency than a diagnostic snapshot, they should not assume `state()` provides an atomic read barrier.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-to-library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
description: Convert a full async logger into the narrower library-facing async facade.
|
||||
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
|
||||
- library
|
||||
@@ -34,8 +34,16 @@ pub fn[S] AsyncLogger::to_library_async_logger(self : AsyncLogger[S]) -> Library
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This conversion does not rebuild the queue, sink, or runtime state.
|
||||
- Target, min level, async config, and flush behavior are preserved.
|
||||
- Target, min level, async config, flush behavior, pending counts, and failure state are preserved because the same underlying async logger value is wrapped.
|
||||
- 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 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.
|
||||
- That also means runtime sink mutations still alias in both directions: changing file append mode, auto-flush, rotation, or other sink helper state through a later unwrapped logger changes the same live runtime sink that the original `AsyncLogger[S]` already held.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -51,6 +59,19 @@ let public_logger = logger.to_library_async_logger()
|
||||
|
||||
In this example, `public_logger` keeps the same async behavior but exposes the library-facing facade.
|
||||
|
||||
#### When Need To Narrow Surface Without Resetting Runtime State
|
||||
|
||||
When an already-used async logger should be projected to a library boundary without changing its current state:
|
||||
```moonbit
|
||||
let full = build_async_logger(config)
|
||||
let public_logger = full.to_library_async_logger()
|
||||
ignore(public_logger.is_enabled(@bitlogger.Level::Info))
|
||||
```
|
||||
|
||||
In this example, the projection changes the exposed type only; it does not rebuild queue or lifecycle state.
|
||||
|
||||
If `full` is still kept elsewhere, it continues sharing that same live runtime state with `public_logger`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -58,8 +79,20 @@ e.g.:
|
||||
|
||||
- The conversion does not clear pending items or reset runtime state.
|
||||
|
||||
- If callers later need state helpers such as `pending_count()` or `state()`, they must unwrap again with `to_async_logger()`.
|
||||
|
||||
- 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 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.
|
||||
|
||||
- Projection does not create an isolated wrapper copy. If callers keep the original `AsyncLogger[S]`, then later facade-level writes, shutdown, or sink-helper mutations still affect that original handle too.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this when package boundaries should avoid exposing the full async logger type.
|
||||
|
||||
2. This is a projection API, not a reconfiguration step.
|
||||
|
||||
3. Use `build_library_async_logger(...)` or `build_library_async_text_logger(...)` when construction and narrowing should happen together from config.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-trace
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a trace-level record through the async logger using the lowest built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a trace-level record through the async logger using the lowest built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::trace(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Trace, ...)`.
|
||||
- This helper delegates to `log(Level::Trace, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Trace records are often skipped in production because they are the lowest built-in severity.
|
||||
- Use this helper when explicit trace intent is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When low-level execution flow should be observable during debugging:
|
||||
```moonbit
|
||||
await logger.trace("entered reconciliation step")
|
||||
logger.trace("entered reconciliation step")
|
||||
```
|
||||
|
||||
In this example, the call site makes trace intent explicit.
|
||||
@@ -61,7 +62,7 @@ In this example, the call site makes trace intent explicit.
|
||||
|
||||
When a trace event should carry extra fields:
|
||||
```moonbit
|
||||
await logger.trace(
|
||||
logger.trace(
|
||||
"cache probe",
|
||||
fields=[@bitlogger.field("key", "user:42")],
|
||||
)
|
||||
@@ -80,4 +81,6 @@ e.g.:
|
||||
|
||||
1. Prefer this helper when trace intent is more readable than `log(Level::Trace, ...)`.
|
||||
|
||||
2. Trace-level async logging can increase queue pressure quickly under verbose workloads.
|
||||
2. Use `log(...)` instead when one trace call needs a one-off target override.
|
||||
|
||||
3. Trace-level async logging can increase queue pressure quickly under verbose workloads.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public asynchronous logger root type used for queue-backed sink-preserving logging pipelines.
|
||||
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
|
||||
- logger
|
||||
@@ -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,13 @@ 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.
|
||||
- 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -85,8 +87,14 @@ 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 now follows the shared close contract rather than backend-specific divergence, but lifecycle interpretation should still prefer `state()` over manual field reasoning.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `async_logger(...)`, `build_async_logger(...)`, or `build_async_text_logger(...)` when you need a value of this type.
|
||||
|
||||
2. Use `ApplicationAsyncLogger` or `LibraryAsyncLogger[S]` when a more intention-specific async wrapper or alias fits the calling boundary better.
|
||||
2. Use `ApplicationAsyncLogger` when application code wants the same full async lifecycle and state helper surface under an app-facing alias.
|
||||
|
||||
3. Use `LibraryAsyncLogger[S]` when a library boundary should intentionally narrow the public async surface and expose broader lifecycle/state helpers only through `to_async_logger()`.
|
||||
|
||||
4. Prefer the method helpers such as `state()`, `has_failed()`, `last_error()`, `is_running()`, and `shutdown()` instead of reading these public fields conceptually as if they were a stable manual mutation surface.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-wait-idle
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Wait until the async logger backlog drains to zero or a worker failure interrupts normal progress.
|
||||
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
|
||||
- logger
|
||||
@@ -34,8 +34,13 @@ pub async fn[S] AsyncLogger::wait_idle(self : AsyncLogger[S]) -> Unit {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The helper keeps yielding while `pending_count() > 0`.
|
||||
- If `has_failed()` becomes `true`, waiting stops early instead of looping forever.
|
||||
- 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.
|
||||
- It is narrower than `shutdown()` and is useful when the logger should continue to be used later.
|
||||
|
||||
### How to Use
|
||||
@@ -46,7 +51,7 @@ Here are some specific examples provided.
|
||||
|
||||
When code should wait for queued work to flush before continuing:
|
||||
```moonbit
|
||||
await logger.wait_idle()
|
||||
logger.wait_idle()
|
||||
```
|
||||
|
||||
In this example, the caller waits for backlog drain but leaves the logger usable afterward.
|
||||
@@ -55,7 +60,7 @@ In this example, the caller waits for backlog drain but leaves the logger usable
|
||||
|
||||
When a test wants to ensure earlier async logs were processed:
|
||||
```moonbit
|
||||
await logger.wait_idle()
|
||||
logger.wait_idle()
|
||||
println("phase complete")
|
||||
```
|
||||
|
||||
@@ -66,10 +71,17 @@ In this example, the wait acts as a barrier between test phases.
|
||||
e.g.:
|
||||
- If the worker has failed, `wait_idle()` stops waiting even if pending records remain.
|
||||
|
||||
- If the worker was never started, pending records may not drain and callers should not expect idle progress automatically.
|
||||
- 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 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.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when you want a drain barrier without closing the logger.
|
||||
|
||||
2. Prefer `shutdown()` when lifecycle completion matters more than continued reuse.
|
||||
|
||||
3. Pair it with `pending_count()` or `state()` when you need to distinguish true idleness from an early stop caused by worker failure.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-warn
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Enqueue a warning-level record through the async logger using the built-in severity shortcut.
|
||||
update-time: 20260614
|
||||
description: Enqueue a warning-level record through the async logger using the built-in severity shortcut and the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -39,8 +39,9 @@ pub async fn[S] AsyncLogger::warn(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper delegates to `log(Level::Warn, ...)`.
|
||||
- This helper delegates to `log(Level::Warn, ..., fields=fields)`.
|
||||
- The record is still subject to min-level gating, patching, filtering, and overflow policy.
|
||||
- This helper does not accept a per-call target override. It uses the logger's stored target unless the logger was derived earlier with `with_target(...)` or `child(...)`.
|
||||
- Warning records are useful for degraded but non-fatal runtime conditions.
|
||||
- Use this helper when a named warning call is clearer than a raw `log(...)` call.
|
||||
|
||||
@@ -52,7 +53,7 @@ Here are some specific examples provided.
|
||||
|
||||
When the system should report a non-fatal problem:
|
||||
```moonbit
|
||||
await logger.warn("retry budget running low")
|
||||
logger.warn("retry budget running low")
|
||||
```
|
||||
|
||||
In this example, the event is surfaced at warning severity without using the generic `log(...)` form.
|
||||
@@ -61,7 +62,7 @@ In this example, the event is surfaced at warning severity without using the gen
|
||||
|
||||
When a warning event should include context:
|
||||
```moonbit
|
||||
await logger.warn(
|
||||
logger.warn(
|
||||
"queue near capacity",
|
||||
fields=[@bitlogger.field("pending", logger.pending_count().to_string())],
|
||||
)
|
||||
@@ -69,6 +70,10 @@ await logger.warn(
|
||||
|
||||
In this example, the warning carries structured operational detail.
|
||||
|
||||
And any shared context already carried by the logger still participates ahead of these per-call fields when the record is built.
|
||||
|
||||
The write still uses the logger's stored target because this shortcut does not take a one-off `target=` override.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -80,4 +85,4 @@ e.g.:
|
||||
|
||||
1. Use this helper for notable but non-fatal async runtime conditions.
|
||||
|
||||
2. Pair warnings with structured fields when operators need quick context.
|
||||
2. Use `log(...)` instead when one warning call must override the target without deriving a new logger value.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_context_fields(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger carrying the shared field set.
|
||||
- `AsyncLogger[S]` - A new async logger value carrying the shared field set.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -40,7 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Context fields are merged during `log(...)` before enqueue.
|
||||
- When a log call also passes per-record fields, the context fields are placed before those per-call fields.
|
||||
- This API returns a new logger value; it does not mutate the original async logger.
|
||||
- The provided `fields` array replaces the previously stored shared field set on the returned async logger; it does not append onto whatever `context_fields` the source logger already had.
|
||||
- Unlike synchronous `Logger::with_context_fields(...)`, this async variant stores fields directly on `AsyncLogger` instead of changing the visible sink type.
|
||||
- Only the stored `context_fields` value changes. Target, minimum level, timestamp flag, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, the original logger keeps its previous `context_fields`, while the derived logger prepends the stored shared fields ahead of per-call fields exactly once when records are built.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -59,6 +62,8 @@ let logger = async_logger(console_sink(), target="billing")
|
||||
|
||||
In this example, both fields are attached before each record enters the queue.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Build Child Async Loggers For Subsystems
|
||||
|
||||
When a subsystem has both a target and fixed fields:
|
||||
@@ -75,6 +80,8 @@ In this example, target composition and field binding stay separate but work tog
|
||||
e.g.:
|
||||
- If `fields` is empty, the logger remains valid and just adds no extra metadata.
|
||||
|
||||
- If a derived async logger already had shared context fields, calling `with_context_fields(...)` again replaces that stored shared field set on the new derived logger rather than stacking both sets together.
|
||||
|
||||
- If duplicate field keys are provided, all fields are still emitted; conflict handling is left to the consumer side.
|
||||
|
||||
### Notes
|
||||
@@ -82,3 +89,9 @@ e.g.:
|
||||
1. Use this for stable metadata, not highly dynamic event-specific values.
|
||||
|
||||
2. This async variant preserves the visible `AsyncLogger[S]` type while still injecting shared fields.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` remain available on the returned logger because the visible async logger surface is preserved.
|
||||
|
||||
4. Use a fresh derived logger when one code path needs shared metadata and another should stay unchanged.
|
||||
|
||||
5. If you need to combine two shared field sets, combine them in the `fields` argument yourself instead of expecting repeated `with_context_fields(...)` calls to accumulate them.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_filter(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that only enqueues matching records.
|
||||
- `AsyncLogger[S]` - A new async logger value that only enqueues matching records.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -39,8 +39,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Filtering happens after record construction and patch application but before enqueue.
|
||||
- Existing filter logic is preserved and combined with the new predicate using logical `and`.
|
||||
- The original async logger is not mutated.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- Only the stored filter pipeline changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- Filtering avoids unnecessary queue pressure for records that should never be delivered.
|
||||
- In the current direct async coverage, derived filters can compose target, level, and message predicates together while the original logger still accepts writes according to its previous filter state.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,6 +58,8 @@ let logger = async_logger(console_sink(), target="service")
|
||||
|
||||
In this example, non-matching records are dropped before they reach the async queue.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Combine Several Async Filter Rules
|
||||
|
||||
When filtering depends on multiple conditions:
|
||||
@@ -80,3 +84,7 @@ e.g.:
|
||||
1. Use this API for selection logic, not record mutation.
|
||||
|
||||
2. Async filtering is especially useful when queue capacity should be reserved for high-value records.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` remain available on the returned logger because the visible async logger surface is preserved.
|
||||
|
||||
4. Use a derived logger value when one branch should enforce extra filter rules and the base async logger should stay unchanged.
|
||||
|
||||
@@ -39,8 +39,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `log(...)` checks `is_enabled(level)` before creating a record or touching the queue.
|
||||
- Lower-severity records below `min_level` are skipped before enqueue.
|
||||
- This API replaces the logger threshold and does not alter queue configuration.
|
||||
- The returned logger keeps the same sink, target, and timestamp settings.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- This API replaces the stored threshold and does not alter queue configuration.
|
||||
- Only `min_level` changes. Sink, target, timestamp flag, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, the derived logger reports the new threshold through `is_enabled(...)`, while the original logger keeps its previous minimum level and still accepts records that remain enabled there.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -56,6 +58,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, lower-severity records are skipped before queue pressure increases.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Derive A More Verbose Async Branch
|
||||
|
||||
When one branch of code should keep a different threshold:
|
||||
@@ -78,3 +82,7 @@ e.g.:
|
||||
1. This API reduces async queue pressure by dropping disabled levels before enqueue.
|
||||
|
||||
2. Use it before adding more complex async filtering rules.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` remain available on the returned logger because the visible async logger surface is preserved.
|
||||
|
||||
4. Use a derived logger value when one branch should tighten the threshold and the base async logger should keep its broader level gate.
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn[S] AsyncLogger::with_patch(
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLogger[S]` - A new async logger that rewrites each record before filtering and queue insertion.
|
||||
- `AsyncLogger[S]` - A new async logger value that rewrites each record before filtering and queue insertion.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -39,8 +39,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- Patch logic runs after record creation and field merging but before filtering and enqueue.
|
||||
- Existing patch logic is preserved and composed so the new patch wraps the current one.
|
||||
- The original async logger is not mutated.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- Only the stored patch pipeline changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- Patching can normalize, redact, or enrich records before they consume queue capacity.
|
||||
- In the current direct async coverage, patched target/message/field changes are visible to later filter logic, and the original async logger still emits unpatched records when used separately.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,6 +60,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, the added fields are part of the record before filtering and queue insertion.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Need Redaction Before Queueing
|
||||
|
||||
When sensitive fields should be removed early:
|
||||
@@ -80,3 +84,7 @@ e.g.:
|
||||
1. Use patches for transformation, not filtering decisions.
|
||||
|
||||
2. Redaction before enqueue helps keep sensitive data out of the queued pipeline.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` remain available on the returned logger because the visible async logger surface is preserved.
|
||||
|
||||
4. Derive a patched logger when one path needs rewritten records and another should keep the original async record shape.
|
||||
|
||||
@@ -38,6 +38,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This API replaces the default target instead of composing it.
|
||||
- Per-call `target?` arguments on `log(...)` can still override the default target.
|
||||
- The original logger value is not mutated.
|
||||
- In the current direct async coverage, derived loggers keep existing flags such as `timestamp`, while the original logger still retains its previous target.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -76,3 +77,5 @@ e.g.:
|
||||
1. Use this API for replacement, not parent-child target composition.
|
||||
|
||||
2. It is useful when several subsystems should share one async queue policy.
|
||||
|
||||
3. Use it when you want a derived logger value; the original async logger keeps its earlier default target.
|
||||
|
||||
@@ -37,7 +37,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- When enabled, `log(...)` captures `@env.now()` before placing the record into the queue.
|
||||
- When disabled, emitted records use `0UL` as the timestamp value.
|
||||
- This setting affects later emitted records only.
|
||||
- Queue, batching, and flush behavior are unchanged.
|
||||
- The returned logger is derived from `self`; the original async logger value is not mutated.
|
||||
- Only the stored `timestamp` flag changes. Target, minimum level, queue configuration, and lifecycle/failure state stay on the same `AsyncLogger[S]` surface.
|
||||
- In the current direct async coverage, a derived timestamp-enabled logger records non-zero timestamps while the original logger continues emitting `0UL` timestamps when it was left disabled.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -53,6 +55,8 @@ let logger = async_logger(console_sink())
|
||||
|
||||
In this example, each record captures its timestamp before entering the async queue.
|
||||
|
||||
And the returned async logger still keeps the same queue-facing API surface as the source logger.
|
||||
|
||||
#### When Need Deterministic Async Records
|
||||
|
||||
When timestamps should be disabled for tests or reduced output:
|
||||
@@ -75,3 +79,7 @@ e.g.:
|
||||
1. This API controls record creation before enqueue, not formatter display policy.
|
||||
|
||||
2. It is useful for tests, deterministic snapshots, and production timing.
|
||||
|
||||
3. State helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, and `has_failed()` remain available on the returned logger because the visible async logger surface is preserved.
|
||||
|
||||
4. Use a derived logger value when only one branch should capture timestamps and the base logger should remain deterministic.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Create an async logger with bounded queueing, overflow policy, lifecycle helpers, and background run control.
|
||||
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
|
||||
- logger
|
||||
@@ -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 = 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` - Flush callback used by batch/shutdown flush policies.
|
||||
- `flush : (S) -> Unit raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -44,10 +44,25 @@ pub fn[S] async_logger(
|
||||
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 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.
|
||||
- `LibraryAsyncLogger[S]` wraps an `AsyncLogger[S]` value instead of aliasing it. That library facade preserves queue-backed logging behavior, but it narrows the directly exposed helper surface until callers recover the full logger with `to_async_logger()`.
|
||||
- In non-native targets, the implementation uses compatibility behavior while keeping the same public surface.
|
||||
- `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.
|
||||
- `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
|
||||
@@ -70,6 +85,28 @@ In this example, the worker drains queued records in the background and `shutdow
|
||||
|
||||
And the logging call path stays queue-oriented rather than direct-sink oriented.
|
||||
|
||||
#### When Need A One-call Target Override On The Root Async Logger
|
||||
|
||||
When async code should keep one logger value but emit a single record under a different target:
|
||||
```moonbit
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.async.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.async.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Shared Context On A Root Async Logger
|
||||
|
||||
When async code should attach stable metadata to later queued records:
|
||||
```moonbit
|
||||
let contextual = logger.with_context_fields([@bitlogger.field("service", "billing")])
|
||||
```
|
||||
|
||||
In this example, the returned value still has the visible type `AsyncLogger[S]`.
|
||||
|
||||
And that shape preservation is intentional because async context binding updates stored logger metadata instead of changing the exposed sink type.
|
||||
|
||||
#### When Need Configurable Overflow And Flush Behavior
|
||||
|
||||
When queue semantics matter for service durability and load:
|
||||
@@ -93,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
|
||||
|
||||
@@ -103,3 +141,7 @@ e.g.:
|
||||
3. Example entrypoint limitations such as `async fn main` support are separate from the library-level portability of this API.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current local verification matrix.
|
||||
|
||||
5. Pair this constructor with `run()` and `shutdown()` when you need the full worker lifecycle rather than just a configured async logger value.
|
||||
|
||||
6. Choose the facade name based on boundary intent: use `AsyncLogger[S]` for the full surface, `ApplicationAsyncLogger` or `ApplicationTextAsyncLogger` for application-facing alias names, and `LibraryAsyncLogger[S]` when a package boundary should intentionally narrow what downstream code can call directly.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-overflow-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public overflow policy alias used by AsyncLoggerConfig and async queue behavior.
|
||||
update-time: 20260614
|
||||
description: Public overflow policy alias used by AsyncLoggerConfig, async parser labels, and runtime queue behavior.
|
||||
key-word:
|
||||
- async
|
||||
- overflow
|
||||
@@ -34,6 +34,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `AsyncOverflowPolicy::DropOldest` lets the underlying async queue discard older pending records when capacity is limited.
|
||||
- `AsyncOverflowPolicy::DropNewest` discards the incoming record instead of blocking.
|
||||
- The same enum is used by `AsyncLoggerConfig::new(...)`, async config parsing, and the queue-kind mapping inside `AsyncLogger`.
|
||||
- The canonical labels `Blocking`, `DropOldest`, and `DropNewest` are also the labels emitted again by async config serializers, so export and parse stay aligned around one public vocabulary.
|
||||
- Async config parsing accepts the canonical label `DropNewest` and also the compatibility alias `DropLatest`, both mapping to the same public enum variant.
|
||||
- When `try_put(...)` reports that a record was not accepted under a drop policy, `dropped_count()` increases for that rejected enqueue.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -62,10 +65,16 @@ In this example, the incoming record is dropped if the queue cannot accept it.
|
||||
e.g.:
|
||||
- If async config text uses unsupported overflow text, async config parsing raises a failure.
|
||||
|
||||
- The parser error path for unsupported overflow text is the same `Failure` surface used by the async config utilities.
|
||||
|
||||
- Under drop policies, sustained overload increases `dropped_count()` instead of guaranteeing delivery.
|
||||
|
||||
- The precise record discarded by the underlying queue behavior depends on the selected policy and queue implementation, so callers should not assume `dropped_count()` alone identifies which message was lost.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This policy applies to `bitlogger_async`, not synchronous `QueuedSink`.
|
||||
|
||||
2. Choose `Blocking` only when producer-side waiting is acceptable for the caller.
|
||||
|
||||
3. Serialized config uses the canonical `DropNewest` label even though the parser also accepts `DropLatest`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-mode-label
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncRuntimeMode into a stable string label for logs, JSON, and diagnostics.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncRuntimeMode into its canonical stable string label for logs, JSON, and diagnostics.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -36,7 +36,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The returned value is intended for diagnostics and stable output, not just debugging prints.
|
||||
- It keeps mode serialization logic in one place.
|
||||
- This helper is used by async runtime JSON helpers.
|
||||
- `async_runtime_state_to_json(...)` serializes the `mode` field through this helper, so runtime snapshots and direct label rendering share the same canonical text.
|
||||
- Labels are more stable for telemetry and docs than ad hoc manual matching at call sites.
|
||||
- The current canonical labels are exactly `native_worker` for `NativeWorker` and `compatibility` for `Compatibility`.
|
||||
- This helper is only a pure enum-to-string mapping. It does not inspect the active backend or verify that the supplied enum still matches the current runtime environment.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -65,5 +68,13 @@ In this example, code gets a stable string without duplicating enum matching log
|
||||
e.g.:
|
||||
- This API assumes a valid `AsyncRuntimeMode` input and does not expose a normal runtime error path.
|
||||
|
||||
- If callers need the current backend-derived mode rather than a previously stored enum value, they must call `async_runtime_mode()` first and then label that result.
|
||||
|
||||
- If callers need the whole runtime object rather than a string label, use `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when mode values should be rendered as stable text instead of manual enum matching.
|
||||
|
||||
2. `async_runtime_state_to_json(...)` uses these exact labels when serializing runtime snapshots.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-mode
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current async runtime mode and distinguish native worker behavior from compatibility behavior.
|
||||
update-time: 20260614
|
||||
description: Read the current async runtime mode and distinguish native-worker behavior from compatibility behavior using the same mode contract exposed through async runtime snapshots.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -37,6 +37,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `NativeWorker` is the expected mode on native-style backends, while `Compatibility` is the expected mode on targets without native background-worker semantics.
|
||||
- `async_runtime_mode_label(...)` converts the enum into a stable string value.
|
||||
- `async_runtime_supports_background_worker()` is a narrower boolean probe built on the same idea.
|
||||
- `async_runtime_state()` packages this mode together with the current background-worker capability into one `AsyncRuntimeState` snapshot.
|
||||
- In the current backend implementations, `NativeWorker` pairs with `background_worker=true` and `Compatibility` pairs with `background_worker=false`.
|
||||
- Concretely, the native runtime entrypoint returns `native_worker_async_runtime_mode()`, while the compatibility stub returns `compatibility_async_runtime_mode()`.
|
||||
- The enum is therefore selected directly by the active backend helper on each call rather than read back out of a cached runtime snapshot object.
|
||||
- This API is intentionally small and useful for lightweight branching.
|
||||
- The mode result describes runtime behavior only; it should not be read as proof that every backend has been equally re-verified in the current release cycle.
|
||||
|
||||
@@ -70,6 +74,8 @@ In this example, the output becomes a stable string instead of an enum pattern-m
|
||||
e.g.:
|
||||
- This API does not have a normal runtime failure mode; it reflects the compiled backend behavior.
|
||||
|
||||
- If callers need the current mode paired with the matching worker-support flag, prefer a fresh `async_runtime_state()` call instead of combining `async_runtime_mode()` with an older saved runtime snapshot.
|
||||
|
||||
- If you need worker support as a direct boolean, use `async_runtime_supports_background_worker()` instead.
|
||||
|
||||
### Notes
|
||||
@@ -78,6 +84,8 @@ e.g.:
|
||||
|
||||
2. Use `async_runtime_state()` when you also want worker support packaged into one object.
|
||||
|
||||
3. This mode distinction is about runtime behavior, not whether `src-async` itself is expected to compile for the target.
|
||||
3. Use `async_runtime_mode_label(...)` when the result should leave enum space and become stable text such as `native_worker` or `compatibility`.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification status of individual targets.
|
||||
4. This mode distinction is about runtime behavior, not whether `src-async` itself is expected to compile for the target.
|
||||
|
||||
5. See [target-verification.md](./target-verification.md) for the current verification status of individual targets.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-new
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Construct an AsyncRuntimeState snapshot from explicit runtime mode and worker-support values.
|
||||
update-time: 20260614
|
||||
description: Construct an AsyncRuntimeState snapshot from explicit runtime mode and worker-support values without probing or validating the live backend.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -40,7 +40,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This constructor simply packages `mode` and `background_worker` into one public snapshot value.
|
||||
- It does not query the current backend automatically.
|
||||
- `async_runtime_state()` is the higher-level API that reads these values from the live runtime environment.
|
||||
- It also does not validate whether the supplied pair matches the current backend contract.
|
||||
- The supplied `mode` and `background_worker` values are stored exactly as provided; this constructor does not recompute, normalize, or cross-check either field.
|
||||
- The constructed value matches the same public shape used by async runtime serializers.
|
||||
- Because `AsyncRuntimeState` is only a data snapshot type, this constructor is mainly useful for tests, adapters, and synthetic diagnostics rather than ordinary runtime probing.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -73,8 +76,14 @@ e.g.:
|
||||
|
||||
- If callers want the current backend snapshot directly, `async_runtime_state()` is the simpler API.
|
||||
|
||||
- If callers manually pair `NativeWorker` with `false` or `Compatibility` with `true`, the constructor still accepts that snapshot because it does not enforce backend consistency.
|
||||
|
||||
- If callers want the currently probed runtime pair instead of a synthetic one, they must pass `async_runtime_mode()` plus `async_runtime_supports_background_worker()` explicitly or use `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use this helper when code should construct an `AsyncRuntimeState` value explicitly.
|
||||
|
||||
2. Pair it with `AsyncLoggerState::new(...)` when assembling a full async logger snapshot by hand.
|
||||
|
||||
3. Prefer `async_runtime_state()` when the goal is to report the actual current backend pair rather than an arbitrary constructed snapshot.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Convert AsyncRuntimeState into a JSON value for runtime capability and mode diagnostics.
|
||||
update-time: 20260614
|
||||
description: Convert AsyncRuntimeState into a JSON value for runtime capability and mode diagnostics using the canonical mode labels and snapshot field names.
|
||||
key-word:
|
||||
- async
|
||||
- state
|
||||
@@ -35,8 +35,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The output includes `mode` and `background_worker`.
|
||||
- `mode` is serialized through `async_runtime_mode_label(...)`.
|
||||
- The compact serialized shape is `{"mode":"native_worker|compatibility","background_worker":true|false}`.
|
||||
- This helper focuses on runtime capabilities rather than queue counters or logger lifecycle flags.
|
||||
- The exported JSON is suitable for diagnostics endpoints and startup environment checks.
|
||||
- This helper serializes the provided `AsyncRuntimeState` exactly as given; it does not call `async_runtime_state()` or recheck backend capability by itself.
|
||||
- That means manually constructed runtime snapshots are exported unchanged, with `mode` relabeled through `async_runtime_mode_label(...)` and `background_worker` kept exactly as supplied.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,3 +70,11 @@ e.g.:
|
||||
|
||||
- If the runtime is in compatibility mode, the helper still serializes normally using the matching mode label.
|
||||
|
||||
- If callers need the current backend-derived runtime rather than an older or synthetic snapshot, they must capture a fresh `async_runtime_state()` first.
|
||||
|
||||
### Notes
|
||||
|
||||
1. The output field names are fixed as `mode` and `background_worker`.
|
||||
|
||||
2. The `mode` field always uses the canonical labels from `async_runtime_mode_label(...)`, not enum names like `NativeWorker` or `Compatibility`.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260613
|
||||
description: Public async runtime state alias used for backend capability snapshots and diagnostics.
|
||||
update-time: 20260614
|
||||
description: Public async runtime state alias used for backend capability snapshots and diagnostics, pairing runtime mode with background-worker support.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -32,7 +32,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This is a type alias, not a live runtime controller.
|
||||
- The current fields are `mode : AsyncRuntimeMode` and `background_worker : Bool`.
|
||||
- `async_runtime_state()` returns this type directly as an environment-level snapshot.
|
||||
- `async_runtime_state()` currently builds that snapshot from `async_runtime_mode()` and `async_runtime_supports_background_worker()`.
|
||||
- `async_runtime_state_to_json(...)` and `stringify_async_runtime_state(...)` serialize the same snapshot shape for diagnostics.
|
||||
- `AsyncRuntimeState::new(...)` can also construct this type manually, but manual construction is synthetic data and does not probe the current backend by itself.
|
||||
- The type itself does not distinguish live backend snapshots from hand-built ones; callers must track whether a given `AsyncRuntimeState` value came from `async_runtime_state()` or from manual construction.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -66,8 +69,14 @@ e.g.:
|
||||
|
||||
- The snapshot is point-in-time diagnostic data; it should not be treated as proof that every target was re-verified in the current release cycle.
|
||||
|
||||
- Because this is just a data shape, manual construction can represent combinations that do not come from the current backend probe.
|
||||
|
||||
- Receiving an `AsyncRuntimeState` value alone does not prove it came from the current backend rather than from a synthetic constructor path.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Use `async_runtime_state()` when you need a value of this type from the current backend.
|
||||
|
||||
2. Use `AsyncLoggerState` when logger-instance queue and lifecycle information is also required.
|
||||
|
||||
3. Use `async_runtime_mode_label(...)` when the `mode` field should be rendered as stable text such as `native_worker` or `compatibility`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-runtime-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Read the current backend-specific async runtime mode and worker capability.
|
||||
update-time: 20260614
|
||||
description: Read the current backend-specific async runtime snapshot as the paired result of runtime mode and background-worker capability.
|
||||
key-word:
|
||||
- async
|
||||
- runtime
|
||||
@@ -35,8 +35,12 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `mode` is derived from the active backend implementation.
|
||||
- `background_worker` tells callers whether native worker semantics are available.
|
||||
- This helper is equivalent to `AsyncRuntimeState::new(async_runtime_mode(), async_runtime_supports_background_worker())`.
|
||||
- The returned pair is rebuilt from those two lower-level helpers on each call rather than read from a cached runtime object.
|
||||
- In the current backend implementations, the resulting pair is `NativeWorker + true` or `Compatibility + false`.
|
||||
- `async_runtime_state_to_json(...)` and `stringify_async_runtime_state(...)` serialize this state.
|
||||
- This API is environment-scoped and does not depend on a particular `AsyncLogger` instance.
|
||||
- The returned value is a snapshot data object, not a live runtime handle, so later backend checks require calling `async_runtime_state()` again rather than reusing an older value as if it refreshed itself.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -68,6 +72,8 @@ In this example, branch decisions are based on actual runtime capability instead
|
||||
e.g.:
|
||||
- This API does not normally expose a dynamic error path; it reports the currently compiled backend behavior.
|
||||
|
||||
- The returned `AsyncRuntimeState` value is not cached onto the helper. If callers need a newer backend read, they must call `async_runtime_state()` again instead of expecting an older value to refresh itself.
|
||||
|
||||
- If callers need richer runtime state, they should use `AsyncLogger::state()` on a logger instance instead.
|
||||
|
||||
### Notes
|
||||
@@ -75,3 +81,5 @@ e.g.:
|
||||
1. Use this API for environment-level diagnostics.
|
||||
|
||||
2. Use `AsyncLogger::state()` for logger-instance diagnostics.
|
||||
|
||||
3. Use `AsyncRuntimeState::new(...)` only when code or tests need to construct a manual snapshot instead of probing the current backend.
|
||||
|
||||
@@ -36,6 +36,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `true` indicates native worker capability.
|
||||
- `false` indicates compatibility-mode behavior.
|
||||
- This helper is derived from backend-specific async runtime implementation choice.
|
||||
- The boolean is read from the active backend helper on each call rather than from a cached runtime snapshot object.
|
||||
- In the current backend split, the native implementation returns `true` while the compatibility stub returns `false`, matching the same mode pair exposed through `async_runtime_mode()` and `async_runtime_state()`.
|
||||
- The async library still targets multiple backends even when this helper returns `false`.
|
||||
- Use it when an enum branch is unnecessary and a boolean capability check is enough.
|
||||
|
||||
@@ -68,6 +70,8 @@ In this example, a simple boolean can drive compact status output.
|
||||
e.g.:
|
||||
- This API does not normally fail at runtime; it reflects compiled backend behavior.
|
||||
|
||||
- If callers need the current paired mode and worker flag together, prefer a fresh `async_runtime_state()` call instead of mixing this boolean with an older saved mode value.
|
||||
|
||||
- If you need the exact mode name rather than a boolean, use `async_runtime_mode()` or `async_runtime_state()`.
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-application-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the application-facing runtime-sink async logger alias from an AsyncLoggerBuildConfig through the sync-first async builder path.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-application-async-logger
|
||||
|
||||
Build an `ApplicationAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-facing async facade over `build_async_logger(...)`.
|
||||
Build an `ApplicationAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-facing runtime-sink async alias returned through `build_async_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,19 @@ pub fn build_application_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_logger(...)`.
|
||||
- The returned logger keeps the standard async lifecycle and state helper surface.
|
||||
- Use this facade when application code wants a dedicated async app-level entry point.
|
||||
- This API delegates to `build_async_logger(...)` directly.
|
||||
- That means the embedded `LoggerConfig` is built first through the normal synchronous config path before the outer async layer is applied.
|
||||
- Any optional synchronous queue layer and runtime-sink controls chosen by `build_logger(config.logger)` remain active under the returned logger.
|
||||
- In particular, a sync queue configured on `LoggerConfig.queue` is preserved inside the wrapped `RuntimeSink` variant instead of being stripped away by the application alias.
|
||||
- Because the result is only the `ApplicationAsyncLogger` alias over `AsyncLogger[@bitlogger.RuntimeSink]`, this builder does not hide any async helpers or introduce a wrapper layer.
|
||||
- The broader async helper surface is therefore preserved and directly exposed on the returned alias rather than being rebuilt or hidden behind an unwrap step.
|
||||
- The returned alias also keeps inherited async logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- The returned logger keeps the full async lifecycle and state helper surface directly, including helpers such as `run()`, `shutdown()`, `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, and `last_error()`.
|
||||
- It also keeps the same queue counters, failure state, sink shape, and runtime-dependent post-close behavior as the underlying runtime-sink async logger.
|
||||
- Because this facade delegates to `build_async_logger(...)`, `Batch` and `Shutdown` also keep the underlying runtime-sink flush wiring: the returned alias uses the built sink's real `flush()` path instead of the default no-op callback used by the text-specific builder line.
|
||||
- In the current direct builder coverage, this alias matches `build_async_logger(config)` all the way through serialized state snapshots, runtime-sink variant choice, queue counters, lifecycle flags, and later failure fields after `run()` or `shutdown()`.
|
||||
- File-backed runtime helpers on the returned `RuntimeSink` also stay aligned with the direct builder result instead of being hidden behind a separate application-layer wrapper.
|
||||
- Use this alias-oriented builder when application code wants the standard runtime-sink async shape without narrowing the public surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,6 +67,23 @@ let logger = build_application_async_logger(
|
||||
|
||||
In this example, the app-facing async facade is built directly from typed config.
|
||||
|
||||
And any configured synchronous runtime sink controls remain available through the returned `RuntimeSink`-backed async logger.
|
||||
|
||||
And unlike `build_library_async_logger(...)`, no `to_async_logger()` unwrap is required to reach queue, lifecycle, or file-backed runtime helpers.
|
||||
|
||||
The returned value also keeps the ordinary async logger target semantics because the facade does not wrap or narrow the underlying `AsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
|
||||
#### When Need Async State Helpers Immediately After App Construction
|
||||
|
||||
When application code should keep the ordinary async helper surface directly:
|
||||
```moonbit
|
||||
let logger = build_application_async_logger(config)
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, the application alias exposes async state helpers directly because no narrowing wrapper is added.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -64,8 +91,16 @@ e.g.:
|
||||
|
||||
- If the logger is never `run()`, enqueue behavior and lifecycle state still follow the normal async logger rules.
|
||||
|
||||
- If callers rely on file-backed runtime helpers, they should treat this builder as the same runtime-sink result as `build_async_logger(config)`, not as a reduced alias with different helper behavior.
|
||||
|
||||
- If callers specifically want the text-console async path where `Batch` and `Shutdown` keep the default no-op flush callback, `build_application_text_async_logger(...)` is the different contract; this runtime-sink alias preserves the real sink `flush()` behavior from `build_async_logger(...)`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a facade over the existing async runtime logger builder.
|
||||
|
||||
2. Use `parse_and_build_application_async_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_application_text_async_logger(...)` instead when callers should keep the concrete text-console sink type and the direct text-builder path.
|
||||
|
||||
4. Use `build_library_async_logger(...)` instead when a library boundary should narrow the exposed async surface.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: build-application-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing configured logger facade from a LoggerConfig.
|
||||
description: Build the application-facing configured logger alias from a LoggerConfig by delegating directly to the normal runtime logger build path.
|
||||
key-word:
|
||||
- application
|
||||
- facade
|
||||
@@ -33,9 +33,13 @@ pub fn build_application_logger(config : LoggerConfig) -> ApplicationLogger {
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_logger(...)`.
|
||||
- The returned value keeps the same public logging, queue, and file runtime helper surface as `ConfiguredLogger`.
|
||||
- Use this facade when application boot code wants an app-specific entry name without exposing lower-level builder naming in its own code.
|
||||
- This API delegates to `build_logger(...)` directly.
|
||||
- The embedded config still goes through the normal runtime logger build path, including runtime sink selection, optional queue wrapping, and timestamp application.
|
||||
- Because the result is only the `ApplicationLogger` alias over `ConfiguredLogger`, this builder does not hide any queue, drain, flush, or file runtime helper methods.
|
||||
- The configured runtime helper surface is therefore preserved and directly exposed on the returned alias rather than being rebuilt or hidden behind an unwrap step.
|
||||
- The returned alias also keeps inherited `Logger` 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(...)`.
|
||||
- Use this alias-oriented entrypoint when application boot code wants an app-specific name without changing the underlying configured runtime logger surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,6 +56,24 @@ let logger = build_application_logger(
|
||||
|
||||
In this example, the application facade builds the same configured runtime logger shape as `build_logger(...)`.
|
||||
|
||||
And any queue/file/runtime helpers selected by the config remain directly available on the returned alias value.
|
||||
|
||||
And unlike `build_library_logger(...)`, no `to_logger()` unwrap is required to reach that helper surface.
|
||||
|
||||
The returned value also keeps the ordinary logger target semantics because the facade does not wrap or narrow the underlying `ConfiguredLogger`.
|
||||
|
||||
#### When Need A Per-call Target Override After App-oriented Build
|
||||
|
||||
When typed app config should still build a logger that supports a one-write target override:
|
||||
```moonbit
|
||||
let logger = build_application_logger(config)
|
||||
logger.log(Level::Error, "boom", target="app.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.audit` for that write.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -64,3 +86,5 @@ e.g.:
|
||||
1. This is a facade API, not a separate runtime implementation.
|
||||
|
||||
2. Use `parse_and_build_application_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_library_logger(...)` instead when the public surface should intentionally hide configured-runtime helper methods.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-application-text-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the application-facing text-console async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the application-facing text-console async logger alias from an AsyncLoggerBuildConfig using the direct concrete text-sink builder path.
|
||||
key-word:
|
||||
- application
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-application-text-async-logger
|
||||
|
||||
Build an `ApplicationTextAsyncLogger` from `AsyncLoggerBuildConfig`. This facade is the application-oriented async builder for the text-console runtime sink shape returned by `build_async_text_logger(...)`.
|
||||
Build an `ApplicationTextAsyncLogger` from `AsyncLoggerBuildConfig`. This is the application-oriented async alias builder for the concrete text-console sink shape returned by `build_async_text_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,22 @@ pub fn build_application_text_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_text_logger(...)`.
|
||||
- This API delegates to `build_async_text_logger(...)` directly.
|
||||
- It is intended for config-driven async text console output where callers want the concrete text sink shape rather than the broader runtime sink enum wrapper.
|
||||
- The returned logger keeps the usual async lifecycle helpers.
|
||||
- The builder always creates a `FormattedConsoleSink` from `config.logger.sink.text_formatter` instead of selecting among sink kinds.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this facade still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- Unlike `build_application_async_logger(...)`, this alias-oriented builder does not go through the full synchronous configured-logger build path first.
|
||||
- It uses the selected text-oriented `LoggerConfig` fields directly and therefore does not apply `LoggerConfig.queue` or preserve sync runtime sink controls.
|
||||
- A sync queue configured on `LoggerConfig.queue` is therefore ignored by this builder instead of being preserved under the returned async logger.
|
||||
- Because the result is only the `ApplicationTextAsyncLogger` alias over `AsyncLogger[@bitlogger.FormattedConsoleSink]`, this builder returns the same underlying async logger value as `build_async_text_logger(...)` and does not hide any async helpers or introduce a wrapper layer.
|
||||
- The returned alias also keeps inherited async logger target behavior such as `with_target(...)`, `child(...)`, and per-call `target=` overrides on `log(...)`.
|
||||
- In particular, `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- The returned logger keeps the full async lifecycle and state helper surface directly, including helpers such as `run()`, `shutdown()`, `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, and `last_error()`.
|
||||
- It also keeps the same close, queue, and failure-state semantics as the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- In the current direct text-builder coverage, this alias matches `build_async_text_logger(config)` through serialized state snapshots, formatter behavior, queue counters, lifecycle flags, and later failure fields after worker execution.
|
||||
- Its configured `flush_policy` is still visible on the returned alias, but this text-specific build path does not wire the explicit sink flush callback that `build_application_async_logger(...)` inherits through `build_async_logger(...)`.
|
||||
- That means `Batch` and `Shutdown` only drive the default no-op async flush callback here; they do not add an extra explicit sink flush step beyond ordinary `FormattedConsoleSink` writes.
|
||||
- Use `build_library_async_text_logger(...)` instead when the next boundary should keep the same concrete text sink type but intentionally narrow the directly exposed async helper surface.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,15 +70,50 @@ let logger = build_application_text_async_logger(
|
||||
|
||||
In this example, the async logger is built for text-console output specifically.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this facade still uses the text formatter directly because it always builds a `FormattedConsoleSink`.
|
||||
|
||||
And the returned value keeps the ordinary async logger target semantics because this facade does not wrap or narrow the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
|
||||
#### When Need A Per-call Target Override After App Text Construction
|
||||
|
||||
When typed app async text config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_application_text_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="app.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `app.text.audit` for that call.
|
||||
|
||||
And later `debug(...)`, `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Async State Helpers Immediately After Text App Construction
|
||||
|
||||
When application code should keep the ordinary async helper surface directly on the text-console variant:
|
||||
```moonbit
|
||||
let logger = build_application_text_async_logger(config)
|
||||
ignore(logger.pending_count())
|
||||
ignore(logger.state())
|
||||
```
|
||||
|
||||
In this example, the application text alias exposes async state helpers directly because no narrowing wrapper is added.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the embedded logger config selects a non-text sink shape, the caller should use the general async builder facade instead.
|
||||
- If callers need sink-kind-driven branching such as JSON console or file-backed async output, they should use `build_application_async_logger(...)` instead.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this facade still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If runtime draining is never started, records still follow the normal async queue lifecycle rules.
|
||||
|
||||
- If callers rely on the formatter or state shape after text-builder construction, they should treat this as the same direct `build_async_text_logger(config)` result rather than a reduced alias with different helper behavior.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This is a narrower text-console async facade than `build_application_async_logger(...)`.
|
||||
|
||||
2. It is most useful when callers want the `FormattedConsoleSink`-backed async type explicitly.
|
||||
|
||||
3. Use `build_application_async_logger(...)` instead when callers need the broader runtime-sink build path, including sync queue application through `build_logger(config.logger)`.
|
||||
|
||||
4. Use `build_library_async_text_logger(...)` instead when the public boundary should narrow the exposed async surface while preserving the same concrete text sink type.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-async-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260512
|
||||
description: Build an async logger from combined logger and async config without manually wiring the runtime sink.
|
||||
update-time: 20260614
|
||||
description: Build an async logger from combined logger and async config by first building the sync runtime logger and then wrapping its sink in the async layer.
|
||||
key-word:
|
||||
- async
|
||||
- config
|
||||
@@ -34,9 +34,18 @@ pub fn build_async_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@bitlo
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The `logger` section is built through the same config machinery used by synchronous configured loggers.
|
||||
- That means `LoggerConfig.sink` and the optional synchronous `LoggerConfig.queue` are applied before the async layer is added.
|
||||
- The resulting async logger inherits `min_level`, `target`, and timestamp behavior from the built synchronous logger.
|
||||
- File, queue, and formatter choices all come from config rather than direct code-side sink wiring.
|
||||
- File, formatter, and any configured synchronous queue choices all come from config rather than direct code-side sink wiring.
|
||||
- The returned sink type is `RuntimeSink`, which keeps configured control helpers available where relevant.
|
||||
- This builder returns the underlying `AsyncLogger[@bitlogger.RuntimeSink]` value directly. `build_application_async_logger(...)` only re-exports the same result under the `ApplicationAsyncLogger` alias, while `build_library_async_logger(...)` wraps the same result in `LibraryAsyncLogger[@bitlogger.RuntimeSink]`.
|
||||
- The returned async logger also keeps ordinary target rules unchanged: `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- Because this path starts from `build_logger(config.logger)`, it preserves the broader runtime-sink build path, including sync-side queue decoration when `LoggerConfig.queue` is present.
|
||||
- Because this path starts from `build_logger(config.logger)`, `config.logger.sink.kind` has already selected the concrete `RuntimeSink` variant before async wrapping, and optional sync queue decoration can further turn that built sink into queued runtime variants such as `QueuedConsole` or `QueuedFile`.
|
||||
- This runtime-sink path also wires the explicit async flush callback as `flush=fn(sink) { sink.flush() }`, so `Batch` and `Shutdown` policies invoke the built runtime sink's real flush behavior instead of the default no-op callback.
|
||||
- In the current direct builder coverage, the returned logger exposes the expected serialized async state snapshot, runtime-sink variant choice, queue counters, lifecycle flags, and later failure fields after `run()` or `shutdown()`.
|
||||
- File-backed runtime helpers also stay directly available on the returned `RuntimeSink` with the same behavior later observed through the application and library facade equivalence tests.
|
||||
- Use `build_async_text_logger(...)` instead when you want the direct text-console async builder path with `FormattedConsoleSink` and without the sync runtime-sink construction layer.
|
||||
- The `src-async` library is designed to compile on `native / llvm / js / wasm / wasm-gc`, but runtime mode differs by backend.
|
||||
- Current local release-facing verification is explicit for `native / js / wasm / wasm-gc`.
|
||||
- `llvm` remains experimental and did not complete local verification in this environment.
|
||||
@@ -60,6 +69,18 @@ In this example, parsing and async runtime wiring are separated cleanly.
|
||||
|
||||
And the returned logger can immediately be started with `run()`.
|
||||
|
||||
#### When Need A Per-call Target Override After Typed Async Build
|
||||
|
||||
When typed async config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.audit` for that call.
|
||||
|
||||
And later `debug(...)`, `info(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Runtime Sink Features After Async Build
|
||||
|
||||
When the sink shape is configured but runtime features still matter:
|
||||
@@ -70,6 +91,10 @@ println(stringify_async_logger_state(logger.state(), pretty=true))
|
||||
|
||||
In this example, the built async logger remains introspectable even though construction was config-driven.
|
||||
|
||||
And any configured synchronous runtime sink controls are preserved inside the returned `RuntimeSink`.
|
||||
|
||||
And if `AsyncFlushPolicy::Batch` or `Shutdown` is configured, this builder uses the runtime sink's real `flush()` path rather than the default no-op callback used by the text-specific builder line.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -77,6 +102,12 @@ e.g.:
|
||||
|
||||
- If the configured sink shape is unsupported for a specific capability, the resulting runtime behavior follows the existing sink/runtime rules rather than inventing a separate builder-only failure model.
|
||||
|
||||
- If callers depend on queued runtime-sink helpers or file-backed runtime helpers, this builder is the direct API that preserves them rather than a reduced facade path.
|
||||
|
||||
- If callers depend on the exact runtime-sink variant, they should read this builder as preserving the sync-first sink selection result from `build_logger(config.logger)`, not as deferring sink-kind branching until after the async layer is added.
|
||||
|
||||
- If callers need the text-console path where `Batch` and `Shutdown` keep the default no-op flush callback, `build_async_text_logger(...)` is the different builder contract; this runtime-sink path intentionally wires the sink's real `flush()` behavior.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API when applications externalize both sync sink choice and async queue behavior.
|
||||
@@ -86,3 +117,5 @@ e.g.:
|
||||
3. Library portability is broader than example portability: a runnable `async fn main` example may still be target-limited even when the async library itself compiles for that backend.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification boundary.
|
||||
|
||||
5. If the next boundary is library-facing rather than application-facing, build here and then narrow with `build_library_async_logger(...)` instead of documenting the broader runtime helper surface directly.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-async-text-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260520
|
||||
description: Build an async logger with a concrete text-console sink from combined logger and async config.
|
||||
update-time: 20260614
|
||||
description: Build an async logger with a concrete text-console sink from combined logger and async config, using only the selected text-oriented LoggerConfig fields instead of the full sync build path.
|
||||
key-word:
|
||||
- async
|
||||
- text
|
||||
@@ -34,7 +34,16 @@ pub fn build_async_text_logger(config : AsyncLoggerBuildConfig) -> AsyncLogger[@
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This builder converts `config.logger.sink.text_formatter` into a runtime `TextFormatter` and wires it into `text_console_sink(...)`.
|
||||
- It always constructs a `FormattedConsoleSink` directly instead of branching on `config.logger.sink.kind`.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this builder still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- The returned logger inherits `min_level`, `target`, and timestamp behavior from `config.logger`.
|
||||
- Unlike `build_async_logger(...)`, this helper does not run the full synchronous `build_logger(config.logger)` path first.
|
||||
- That means it uses `config.logger.sink.text_formatter`, `min_level`, `target`, and `timestamp` directly, but it does not apply `LoggerConfig.queue` or preserve other sync runtime sink controls.
|
||||
- This builder returns the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]` value directly. `build_application_text_async_logger(...)` only re-exports that same result under the `ApplicationTextAsyncLogger` alias, while `build_library_async_text_logger(...)` wraps the same result in `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- The returned async logger also keeps ordinary target rules unchanged: `log(..., target=...)` can override the target for one call, while severity helpers such as `debug(...)`, `info(...)`, `warn(...)`, and `error(...)` continue to use the stored logger target unless a derived logger was created first with `with_target(...)` or `child(...)`.
|
||||
- In the current direct text-builder coverage, the returned logger exposes the expected serialized async state snapshot, formatter behavior, queue counters, lifecycle flags, and later failure fields after worker execution.
|
||||
- The async `flush_policy` still comes from `config.async_config`, but this text-specific builder does not supply the explicit `flush=fn(sink) { sink.flush() }` callback used by `build_async_logger(...)`.
|
||||
- In practice, `Batch` and `Shutdown` therefore only trigger the default no-op async flush callback on this path, while each record write still follows whatever immediate behavior `FormattedConsoleSink` already has on its own.
|
||||
- This helper is best suited to text-console output paths where callers want the concrete formatted sink type instead of `RuntimeSink`.
|
||||
- This async text path follows the same target story as the broader async library: `native / js / wasm / wasm-gc` have stronger local verification, while `llvm` remains experimental and locally unverified in this environment.
|
||||
|
||||
@@ -56,13 +65,31 @@ let logger = build_async_text_logger(
|
||||
|
||||
In this example, the async logger is built around a text console sink rather than the generic runtime sink enum.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this helper still uses the text formatter directly because it always builds a `FormattedConsoleSink`.
|
||||
|
||||
#### When Need A Per-call Target Override After Built Async Text Construction
|
||||
|
||||
When typed async text config should still build a logger that supports a one-call target override:
|
||||
```moonbit
|
||||
let logger = build_async_text_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="async.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `async.text.audit` for that call.
|
||||
|
||||
And later `debug(...)`, `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the logger config was not intended for text-console style output, the broader `build_async_logger(...)` path may be a better fit.
|
||||
- If callers need sink-kind-driven branching across console, JSON, text, or file output, `build_async_logger(...)` is the better fit.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this builder still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If the logger is never `run()`, pending records still follow the normal async queue lifecycle rules.
|
||||
|
||||
- If callers rely on the concrete formatter or post-run state shape, this builder is the direct API that preserves those text-console details rather than a reduced alias or wrapper.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This API is narrower than `build_async_logger(...)` because it preserves a concrete text sink type.
|
||||
@@ -70,3 +97,5 @@ e.g.:
|
||||
2. It is the base builder used by the application and library async text facades.
|
||||
|
||||
3. See [target-verification.md](./target-verification.md) for the current local verification matrix.
|
||||
|
||||
4. Use this direct builder when callers should keep the full async helper surface on the concrete text sink type rather than a naming alias or a narrowed library wrapper.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the library-facing async logger facade from an AsyncLoggerBuildConfig while intentionally hiding direct async state helpers.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
@@ -35,9 +35,19 @@ pub fn build_library_async_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds the general async runtime logger and then wraps it in the narrower `LibraryAsyncLogger` facade.
|
||||
- This API builds the general async runtime logger and then wraps it in the narrower `LibraryAsyncLogger[@bitlogger.RuntimeSink]` facade.
|
||||
- The embedded `LoggerConfig` still goes through the normal synchronous config path first, so sink shape and any optional synchronous queue layer are already applied before the outer async layer is wrapped and then narrowed.
|
||||
- The returned facade wraps the same underlying `AsyncLogger[@bitlogger.RuntimeSink]` value that `build_async_logger(...)` would produce directly.
|
||||
- The result keeps async lifecycle operations such as `run()` and `shutdown()` while narrowing the public shape.
|
||||
- The broader async helper surface is preserved rather than rebuilt, but it is intentionally not directly exposed on the returned facade. Queue counters, lifecycle state, idle-wait helpers, and file-backed runtime helpers stay behind `to_async_logger()` instead of disappearing.
|
||||
- The narrower facade does not change the underlying runtime-sink queue counters, failure state, sink shape, or runtime-dependent post-close behavior; it only hides the broader helper surface until `to_async_logger()` is used.
|
||||
- Because this facade starts from `build_async_logger(...)`, the wrapped async logger also keeps the runtime-sink flush wiring: `Batch` and `Shutdown` use the built sink's real `flush()` path instead of the default no-op callback used by the text-specific builder line.
|
||||
- The facade still preserves the underlying async 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 derived another logger with `with_target(...)` or `child(...)`.
|
||||
- In the current direct builder coverage, unwrapping this facade produces the same async state snapshot, runtime-sink variant, queue counters, lifecycle flags, and failure fields as calling `build_async_logger(config)` directly.
|
||||
- That same unwrap also preserves file-backed runtime helpers on `RuntimeSink` values rather than replacing them with facade-specific behavior.
|
||||
- Async state helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, and failure-status inspection remain on the underlying `AsyncLogger`, not on the returned facade itself.
|
||||
- `to_async_logger()` can be used to recover the underlying full async logger.
|
||||
- Use this builder when the boundary should preserve the runtime-sink async build path but still hide broader async inspection helpers from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,15 +67,46 @@ let logger = build_library_async_logger(
|
||||
|
||||
In this example, async runtime construction is hidden behind the library facade.
|
||||
|
||||
#### When Need Async State Helpers After Library-oriented Construction
|
||||
|
||||
When config-built async state inspection is still needed internally:
|
||||
```moonbit
|
||||
let logger = build_library_async_logger(config)
|
||||
let full = logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
```
|
||||
|
||||
In this example, the library async facade is unwrapped before using async state helpers.
|
||||
|
||||
And unlike `ApplicationAsyncLogger`, the narrower builder result does not expose those broader async helpers directly on the facade surface.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Async Builder Facade
|
||||
|
||||
When typed library async config should still build a facade that allows a one-call target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_async_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="lib.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.audit` for that call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If backend-specific sink limitations exist, they still apply under the facade.
|
||||
|
||||
- If callers need methods outside the library facade, they must unwrap with `to_async_logger()`.
|
||||
- If callers need async state, failure-status, or idle-wait helpers outside the library facade, they must unwrap with `to_async_logger()`.
|
||||
|
||||
- If callers need file-backed runtime helpers such as file-state or queued runtime inspection, they must unwrap first, but the helper behavior itself stays aligned with the direct `build_async_logger(config)` result.
|
||||
|
||||
- If callers instead want the text-console builder path where `Batch` and `Shutdown` keep the default no-op flush callback, they should use `build_library_async_text_logger(...)`; this runtime-sink facade preserves the real sink `flush()` behavior from `build_async_logger(...)`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this API when library boundaries should stay narrow.
|
||||
|
||||
2. Use `parse_and_build_library_async_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `build_library_async_text_logger(...)` instead when the library-facing async type should preserve the narrower `FormattedConsoleSink` shape rather than `RuntimeSink`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-async-text-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing text-console async logger facade from an AsyncLoggerBuildConfig.
|
||||
update-time: 20260614
|
||||
description: Build the library-facing text-console async logger facade from an AsyncLoggerBuildConfig using the concrete text-console builder path.
|
||||
key-word:
|
||||
- library
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Build-library-async-text-logger
|
||||
|
||||
Build a `LibraryAsyncLogger[FormattedConsoleSink]` from `AsyncLoggerBuildConfig`. This facade is the library-oriented async builder for text-console runtime output.
|
||||
Build a `LibraryAsyncLogger[FormattedConsoleSink]` from `AsyncLoggerBuildConfig`. This facade is the library-oriented async builder for the concrete text-console sink shape returned by `build_async_text_logger(...)`.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -35,9 +35,20 @@ pub fn build_library_async_text_logger(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API delegates to `build_async_text_logger(...)` and then wraps the result as `LibraryAsyncLogger`.
|
||||
- It is useful when library code wants a narrow async facade while preserving a concrete text-console sink type.
|
||||
- This API delegates to `build_async_text_logger(...)` and then narrows the result to `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
- It always produces a concrete `FormattedConsoleSink` from `config.logger.sink.text_formatter` instead of branching on sink kinds.
|
||||
- That means even if `config.logger.sink.kind` says `Console`, `JsonConsole`, or `File`, this facade still follows the text-console path and uses only the configured `text_formatter` details.
|
||||
- Unlike `build_library_async_logger(...)`, this facade does not go through the full synchronous configured-logger build path first.
|
||||
- It uses the selected text-oriented `LoggerConfig` fields directly and therefore does not apply `LoggerConfig.queue` or preserve sync runtime sink controls.
|
||||
- A sync queue configured on `LoggerConfig.queue` is therefore ignored by this builder instead of being preserved behind the wrapped text-console async logger.
|
||||
- The returned facade wraps the same underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]` value that `build_async_text_logger(...)` would return directly, so `run()`, `shutdown()`, and queue or failure-state behavior are unchanged under the narrower public type.
|
||||
- In the current direct text-builder coverage, unwrapping this facade yields the same async state snapshot, formatter behavior, queue counters, lifecycle flags, and later failure fields as calling `build_async_text_logger(config)` directly.
|
||||
- The configured `flush_policy` is still carried by that underlying async logger, but this text-specific builder path does not provide the explicit sink flush callback used by `build_library_async_logger(...)` through `build_async_logger(...)`.
|
||||
- As a result, `Batch` and `Shutdown` only invoke the default no-op async flush callback on this text-console path unless downstream code unwraps and adds different behavior elsewhere.
|
||||
- The facade still preserves the underlying async 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 derived another logger with `with_target(...)` or `child(...)`.
|
||||
- Async state helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, and failure-status inspection remain on the underlying `AsyncLogger[@bitlogger.FormattedConsoleSink]`, not on the returned facade itself.
|
||||
- `to_async_logger()` can recover the underlying full async logger if needed.
|
||||
- Use this builder when the boundary should preserve the concrete text-console sink type while still hiding broader async inspection and helper APIs from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,10 +68,41 @@ let logger = build_library_async_text_logger(
|
||||
|
||||
In this example, the async text sink shape is preserved under the library facade.
|
||||
|
||||
And the chosen builder path matters more than `config.logger.sink.kind`: this facade still uses the text formatter directly because it always builds a `FormattedConsoleSink` before narrowing to `LibraryAsyncLogger[@bitlogger.FormattedConsoleSink]`.
|
||||
|
||||
#### When Need Async State Helpers After Library Text Construction
|
||||
|
||||
When library-facing text-console construction should still allow internal async inspection later:
|
||||
```moonbit
|
||||
let logger = build_library_async_text_logger(config)
|
||||
let full = logger.to_async_logger()
|
||||
ignore(full.pending_count())
|
||||
```
|
||||
|
||||
In this example, the facade is unwrapped before using async state helpers.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Async Text Builder Facade
|
||||
|
||||
When typed library async text config should still build a facade that allows a one-call target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_async_text_logger(config)
|
||||
logger.log(@bitlogger.Level::Error, "boom", target="lib.text.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.text.audit` for that call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If the embedded logger config does not describe text-console output, the caller should use the broader async facade instead.
|
||||
- If callers need sink-kind-driven branching such as JSON console or file-backed async output, they should use `build_library_async_logger(...)` instead.
|
||||
|
||||
- If the config carries file-oriented fields such as `path` only because `sink.kind` was set to `File`, this facade still ignores that sink-kind choice and does not create a file-backed async logger.
|
||||
|
||||
- If callers expect async state or idle-wait helpers directly on the returned facade, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
- If callers need to inspect the actual formatter-backed async state after library-level `run()` or `shutdown()` calls, unwrapping exposes the same post-call counters and failure fields that the direct text builder would have produced.
|
||||
|
||||
- Normal async lifecycle expectations still apply if the logger is never run.
|
||||
|
||||
@@ -69,3 +111,5 @@ e.g.:
|
||||
1. This is the library-side counterpart to `build_application_text_async_logger(...)`.
|
||||
|
||||
2. It is most useful when a concrete text-console async sink type matters to the caller boundary.
|
||||
|
||||
3. Use `build_library_async_logger(...)` instead when the library-facing async type should keep the broader `RuntimeSink` build path, including sync queue application through `build_logger(config.logger)`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: build-library-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Build the library-facing sync logger facade from a LoggerConfig.
|
||||
update-time: 20260613
|
||||
description: Build the library-facing sync logger facade from a LoggerConfig by delegating to the configured runtime logger build path and then wrapping the result.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -33,9 +33,15 @@ pub fn build_library_logger(config : LoggerConfig) -> LibraryLogger[RuntimeSink]
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API builds a configured runtime logger first and then wraps it as `LibraryLogger`.
|
||||
- This API builds a configured runtime logger first and then wraps that same value as `LibraryLogger[RuntimeSink]`.
|
||||
- The embedded config still goes through the normal runtime logger build path, including runtime sink selection, optional queue wrapping, and timestamp application.
|
||||
- The returned facade wraps the same underlying `ConfiguredLogger` value that `build_logger(...)` would produce directly.
|
||||
- The facade intentionally exposes a smaller logging surface than the full configured runtime logger.
|
||||
- In particular, the configured runtime helper surface is preserved rather than rebuilt, but it is intentionally not directly exposed on the returned facade. Queue metrics, flush or drain helpers, and file controls stay behind `to_logger()` instead of disappearing.
|
||||
- The facade still preserves the underlying logger target rules on its exposed write methods: `log(..., target=...)` can override the target for one write, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derived another logger with `with_target(...)` or `child(...)`.
|
||||
- Queue metrics, flush and drain helpers, and file runtime controls remain on the underlying `ConfiguredLogger`, not on the returned facade itself.
|
||||
- Call `to_logger()` if a caller must recover the underlying full logger object.
|
||||
- Use this builder when the boundary should preserve the configured runtime logger path but still hide broader runtime helper methods from downstream callers.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -52,15 +58,44 @@ let logger = build_library_logger(
|
||||
|
||||
In this example, the logger is built from config and then narrowed to the library facade.
|
||||
|
||||
#### When Need Runtime Helpers After Library-oriented Construction
|
||||
|
||||
When config-built runtime queue or file controls are still needed internally:
|
||||
```moonbit
|
||||
let logger = build_library_logger(config)
|
||||
let full = logger.to_logger()
|
||||
ignore(full.sink.pending_count())
|
||||
```
|
||||
|
||||
In this example, the library facade is unwrapped before using runtime-specific helpers through the preserved `RuntimeSink` value.
|
||||
|
||||
And the unwrapped value still carries the same `RuntimeSink` pipeline built from the original config.
|
||||
|
||||
And unlike `ApplicationLogger`, the narrower builder result does not expose those runtime helpers directly on the facade surface.
|
||||
|
||||
#### When Need A Per-call Target Override Through The Library Builder Facade
|
||||
|
||||
When typed library config should still build a facade that allows a one-write target override without unwrapping first:
|
||||
```moonbit
|
||||
let logger = build_library_logger(config)
|
||||
logger.log(Level::Error, "boom", target="lib.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `lib.audit` for that write.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the facade's stored target unless code derives another facade first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If backend-specific sink limitations exist, they still apply after the facade is built.
|
||||
|
||||
- If code later needs methods outside the library facade, it must unwrap with `to_logger()`.
|
||||
- If code later needs queue metrics, flush or drain helpers, or file runtime controls, it must unwrap with `to_logger()`.
|
||||
|
||||
### Notes
|
||||
|
||||
1. Prefer this facade when library APIs should not expose the full configured runtime logger type.
|
||||
|
||||
2. Use `parse_and_build_library_logger(...)` when starting from JSON text.
|
||||
|
||||
3. Use `to_logger()` when internal code later needs broader logger composition or direct access to the preserved `RuntimeSink` value without changing the public facade type.
|
||||
|
||||
@@ -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
|
||||
@@ -33,9 +35,13 @@ pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {}
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `build_logger(...)` constructs the runtime sink shape based on `SinkConfig` and optional queue wrapper.
|
||||
- `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(...)`.
|
||||
- Queue metrics and file controls remain available through forwarding helpers on the configured logger.
|
||||
- `build_application_logger(...)` only re-exports this same configured runtime logger result under the `ApplicationLogger` alias, while `build_library_logger(...)` wraps the same result in `LibraryLogger[RuntimeSink]`.
|
||||
- This API is deterministic and data-driven, making it suitable for bootstrapping from parsed config.
|
||||
|
||||
### How to Use
|
||||
@@ -57,7 +63,19 @@ let logger = build_logger(
|
||||
|
||||
In this example, no JSON parsing is required because config objects were built directly.
|
||||
|
||||
And the runtime logger is ready immediately.
|
||||
And the runtime logger is ready immediately, with the same ordinary logger target semantics as any other `Logger` value.
|
||||
|
||||
#### When Need A Per-call Target Override After Typed Config Build
|
||||
|
||||
When typed config should still build a logger that supports a one-write target override:
|
||||
```moonbit
|
||||
let logger = build_logger(config)
|
||||
logger.log(Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.audit` for that write.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
#### When Need Config-built Queue Or File Runtime Helpers
|
||||
|
||||
@@ -83,3 +101,5 @@ e.g.:
|
||||
|
||||
2. Use `parse_and_build_logger(...)` when the starting point is raw JSON text.
|
||||
|
||||
3. Use the application or library facade builders only when the boundary name or exposed surface should differ; they do not change the underlying configured runtime logger pipeline built here.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,8 +35,12 @@ 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.
|
||||
- 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
|
||||
|
||||
@@ -65,10 +69,18 @@ In this example, callers can branch on the reported close result.
|
||||
e.g.:
|
||||
- If the configured runtime sink shape has no real close action, the helper may still return `true` as a no-op success.
|
||||
|
||||
- If callers need a file-specific close path with queue flush nuances, `file_close()` may be the better API.
|
||||
- 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 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-specific helpers when the configured sink shape is known to be file-backed.
|
||||
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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,16 +27,18 @@ 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`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,17 +60,23 @@ 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
|
||||
|
||||
e.g.:
|
||||
- If the configured sink is not file-backed, the method returns `false`.
|
||||
|
||||
- If the file handle was already closed earlier through this logger or another facade sharing the same wrapped runtime sink state, the method returns `false`.
|
||||
|
||||
- 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 may flush pending records before closing the file.
|
||||
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.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: configured-logger-file-flush
|
||||
group: api
|
||||
category: runtime
|
||||
update-time: 20260512
|
||||
description: Flush the file sink behind a configured runtime logger when one is present.
|
||||
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,19 @@ 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.
|
||||
- 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`.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -58,17 +61,23 @@ 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
|
||||
|
||||
e.g.:
|
||||
- If the configured sink is not file-backed, the method returns `false`.
|
||||
|
||||
- If the file handle was already closed earlier through this logger or another facade sharing the same wrapped runtime sink state, the method returns `false`.
|
||||
|
||||
- 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.
|
||||
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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -72,4 +72,4 @@ e.g.:
|
||||
|
||||
1. Use this helper after diagnostics or recovery, not before capturing needed evidence.
|
||||
|
||||
2. It is the reset companion for the file failure-counter helpers.
|
||||
2. For queued file sinks, it resets the inner file counters only; it does not clear still-pending queued records that may produce new failures on a later `file_flush()`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -35,6 +35,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- File-backed sinks report their recorded write-failure count through the wrapped `RuntimeSink`.
|
||||
- Queued file sinks forward the metric from the wrapped inner file sink.
|
||||
- For queued file sinks, enqueueing a record does not increment this counter by itself. The counter changes only when queued records are drained into the inner file sink, such as through `file_flush()`.
|
||||
- Non-file sinks return `0`.
|
||||
- The counter is cumulative until reset.
|
||||
|
||||
@@ -71,4 +72,4 @@ e.g.:
|
||||
|
||||
1. Use this helper for focused write-failure visibility.
|
||||
|
||||
2. Pair it with `file_flush_failures()` when diagnosing output-path instability.
|
||||
2. Pair it with `file_flush()` and `file_flush_failures()` when diagnosing queued output-path instability.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -31,8 +31,14 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a separate wrapper implementation.
|
||||
- It preserves normal logger methods such as `info(...)`, `warn(...)`, `error(...)`, and the other `Logger` APIs.
|
||||
- It also exposes configured runtime helpers such as `flush()`, `drain()`, `pending_count()`, `dropped_count()`, and file-specific controls when the runtime sink supports them.
|
||||
- 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_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.
|
||||
- `ApplicationLogger` is another direct alias-oriented name for this same configured runtime surface, while `LibraryLogger[RuntimeSink]` is the narrowing facade variant that intentionally hides these runtime helpers until `to_logger()` is used.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -57,6 +63,21 @@ ignore(logger.pending_count())
|
||||
|
||||
In this example, the alias keeps ordinary logging calls while still exposing runtime diagnostics.
|
||||
|
||||
And the inherited logger target rules stay unchanged: `log(..., target=...)` can override the target per call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
And later derived values such as `logger.child("worker")` or `logger.with_patch(...)` still keep the runtime helper surface because the configured logger line is only being recomposed, not narrowed or rebuilt into a different facade.
|
||||
|
||||
#### When Need A One-call Target Override On The Configured Runtime Logger
|
||||
|
||||
When config-built sync code should keep the same logger value but emit one record under a different target:
|
||||
```moonbit
|
||||
logger.log(Level::Error, "boom", target="svc.audit")
|
||||
```
|
||||
|
||||
In this example, the emitted record uses `svc.audit` only for that one call.
|
||||
|
||||
And later `info(...)`, `warn(...)`, or `error(...)` calls still use the logger's stored target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -68,4 +89,8 @@ e.g.:
|
||||
|
||||
1. Use `build_logger(...)` or `parse_and_build_logger(...)` when you need a value of this type.
|
||||
|
||||
2. Use `ApplicationLogger` or `LibraryLogger[RuntimeSink]` when a more intention-specific facade name fits the calling code better.
|
||||
2. Inherited `Logger` behavior stays unchanged on this alias, including target overrides on `log(...)` and derived target composition through `with_target(...)` and `child(...)`.
|
||||
|
||||
3. Use `ApplicationLogger` when application code wants the same full configured-runtime helper surface under an app-facing name.
|
||||
|
||||
4. Use `LibraryLogger[RuntimeSink]` when a library boundary should intentionally hide configured-runtime helper methods behind a narrower facade.
|
||||
|
||||
@@ -3,7 +3,7 @@ name: default-library-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260520
|
||||
description: Create the default library-facing console logger facade from shared global defaults.
|
||||
description: Create the default library-facing console logger facade by wrapping the current shared default logger at call time.
|
||||
key-word:
|
||||
- library
|
||||
- facade
|
||||
@@ -31,7 +31,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This API wraps `default_logger()` as a library facade.
|
||||
- Each call reflects the current shared default minimum level and default target at that moment.
|
||||
- The returned facade wraps the same underlying `Logger[ConsoleSink]` value that `default_logger()` would produce directly at that moment.
|
||||
- The returned value exposes the narrower `LibraryLogger` surface rather than the full `Logger` surface.
|
||||
- Later changes to shared defaults do not mutate an already-created facade value because the wrapped logger is captured when `default_library_logger()` is called.
|
||||
- Call `default_library_logger()` again after shared default changes if a fresh narrowed value should reflect the updated defaults.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -49,6 +52,8 @@ if logger.is_enabled(Level::Info) {
|
||||
|
||||
In this example, the library facade mirrors the current global defaults.
|
||||
|
||||
And if the caller later unwraps it with `to_logger()`, the same captured default target and minimum level are still present on the full logger value.
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
@@ -61,3 +66,5 @@ e.g.:
|
||||
1. Use `to_logger()` if callers need the full sync logger surface.
|
||||
|
||||
2. This helper is useful for library-facing APIs that should stay narrower than `Logger`.
|
||||
|
||||
3. Call `default_library_logger()` again after shared default changes if a fresh facade value should reflect the new defaults.
|
||||
|
||||
@@ -31,6 +31,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This helper returns the same baseline config shape used by `LoggerConfig::new()` defaults.
|
||||
- It includes the default minimum level, empty target, disabled timestamp, default sink config, and no queue wrapper.
|
||||
- The implementation is exactly `LoggerConfig::new()`, so it stays aligned with the constructor defaults rather than duplicating a separate preset object.
|
||||
- That means `build_logger(...)` can consume the returned value directly, and `build_async_logger(...)` reuses the same sync-side defaults before adding the outer async layer.
|
||||
- It is useful for explicit config composition when callers want a known baseline object.
|
||||
|
||||
### How to Use
|
||||
@@ -58,3 +60,5 @@ e.g.:
|
||||
1. This helper is the top-level default config entry point.
|
||||
|
||||
2. It is useful for explicit config-first workflows and tests.
|
||||
|
||||
3. Use `default_sink_config()` when callers only want the baseline sink portion rather than the full top-level logger config.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user