📝 add example-first documentation paths

This commit is contained in:
Nanaloveyuki
2026-07-17 16:24:06 +08:00
parent c6962fe71a
commit 4e2ef69b11
14 changed files with 480 additions and 66 deletions
+42 -27
View File
@@ -6,28 +6,51 @@ BitLogger 是一个使用 MoonBit 编写的结构化日志库,适合命令行
- [English README](./docs/README-en.md) - [English README](./docs/README-en.md)
- [Wiki](https://bitlogger.naloveyuki.top) - [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")]) ```bash
ignore(logger.flush()) moon new log-demo
cd log-demo
moon add Nanaloveyuki/BitLogger@0.7.0
``` ```
推荐从 `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)
## 支持情况 ## 支持情况
@@ -47,19 +70,11 @@ ignore(logger.flush())
- 组合能力:queue、filter、patch、fanout、split、callback - 组合能力:queue、filter、patch、fanout、split、callback
- 异步日志:独立 `src-async` package - 异步日志:独立 `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) - [src package README](./src/README.mbt.md)
常用入口:`text_console(...)``file(...)``with_queue(...)``build_logger(...)``build_async_logger(...)` 仓库内的 `examples/` 目录与 Examples 文档一一对应;文档解释选择、前提、运行命令和后续扩展,源码保持为可执行参考。
+24
View File
@@ -104,6 +104,26 @@ function buildChangesSidebar(): DefaultTheme.SidebarItem[] {
] ]
} }
function buildExamplesSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: 'Overview', link: '/examples/' },
{ text: 'Console And Fields', link: '/examples/console' },
{ text: 'File Rotation', link: '/examples/file-rotation' },
{ text: 'Configuration', link: '/examples/config' },
{ text: 'Async Lifecycle', link: '/examples/async' },
]
}
function buildExtendSidebar(): DefaultTheme.SidebarItem[] {
return [
{ text: 'Overview', link: '/extend/' },
{ text: 'Queueing', link: '/extend/queue' },
{ text: 'Sink Composition', link: '/extend/composition' },
{ text: 'Text Formatting', link: '/extend/formatting' },
{ text: 'Target Boundaries', link: '/extend/targets' },
]
}
export default defineConfig({ export default defineConfig({
title: 'BitLogger', title: 'BitLogger',
description: 'Structured logging library docs for MoonBit.', description: 'Structured logging library docs for MoonBit.',
@@ -118,6 +138,8 @@ export default defineConfig({
siteTitle: 'BitLogger', siteTitle: 'BitLogger',
nav: [ nav: [
{ text: 'Home', link: '/' }, { text: 'Home', link: '/' },
{ text: 'Examples', link: '/examples/' },
{ text: 'Extend', link: '/extend/' },
{ text: 'API', link: '/api/' }, { text: 'API', link: '/api/' },
{ text: 'Changes', link: '/changes/' }, { text: 'Changes', link: '/changes/' },
{ text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' }, { text: 'Mooncake', link: 'https://mooncakes.io/docs/Nanaloveyuki/BitLogger' },
@@ -131,6 +153,8 @@ export default defineConfig({
text: 'Edit this page on GitHub', text: 'Edit this page on GitHub',
}, },
sidebar: { sidebar: {
'/examples/': buildExamplesSidebar(),
'/extend/': buildExtendSidebar(),
'/api/': buildApiSidebar(), '/api/': buildApiSidebar(),
'/changes/': buildChangesSidebar(), '/changes/': buildChangesSidebar(),
}, },
+29 -23
View File
@@ -9,24 +9,38 @@ BitLogger is a structured logging library for MoonBit projects.
BitLogger gives you consistent levels, targets, structured fields, configurable text output, native file logging, and an async logging layer. 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 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.
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")]) ### 1. Install
ignore(logger.flush())
```bash
moon new log-demo
cd log-demo
moon add Nanaloveyuki/BitLogger@0.7.0
``` ```
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 ## Support Status
@@ -45,18 +59,10 @@ Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
- Composition helpers: queue, filter, patch, fanout, split, callback - Composition helpers: queue, filter, patch, fanout, split, callback
- Separate async package under `src-async` - 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 ## Documentation
- [Examples](./examples/index.md): complete application-facing usage flows
- [Extend](./extend/index.md): queueing, sink composition, formatting, and target boundaries
- [API index](./api/index.md): canonical API reference, organized as one public API per file - [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 - [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/changes/`](./changes/): versioned release notes and publish-facing change summaries
+47
View File
@@ -0,0 +1,47 @@
# Async Logger Lifecycle
Use the async package when a native application needs logging to run through an async queue. The library supports broader targets, but the shipped `async fn main` example is native-focused.
## Import Both Packages
```moonbit
import {
"Nanaloveyuki/BitLogger/src" @log,
"Nanaloveyuki/BitLogger/src-async" @async_log,
"moonbitlang/async",
}
```
## Build, Run, Then Shut Down
```moonbit
async fn main {
let raw = "{\"logger\":{\"min_level\":\"info\",\"target\":\"service.async\",\"sink\":{\"kind\":\"text_console\"}},\"async_config\":{\"max_pending\":64,\"overflow\":\"DropOldest\",\"max_batch\":16,\"linger_ms\":5,\"flush\":\"Batch\"}}"
let config = @async_log.parse_async_logger_build_config_text(raw) catch {
err => {
ignore(err)
return
}
}
let logger = @async_log.build_async_logger(config)
@async.with_task_group(group => {
group.spawn_bg(allow_failure=true, () => logger.run())
logger.info("worker started", fields=[@log.field("mode", "async")])
logger.shutdown()
})
}
```
`run()` owns the worker loop. Start it before producing records, then call `shutdown()` so pending work can follow the configured flush policy.
## Run The Repository Example
```bash
moon run examples/async_basic --target native
```
## Next Steps
- Inspect queue policy and flush choices in [Queueing](../extend/queue.md).
- Reference: [`parse_async_logger_build_config_text(...)`](../api/parse-async-logger-build-config-text.md), [`build_async_logger(...)`](../api/build-async-logger.md), [`run()`](../api/async-logger-run.md), and [`shutdown()`](../api/async-logger-shutdown.md).
+35
View File
@@ -0,0 +1,35 @@
# Configuration-Driven Logging
Use configuration when deployment should select levels, output, formatting, or queue behavior without changing application code.
## Parse, Build, Then Log
```moonbit
let raw = "{\"min_level\":\"info\",\"target\":\"service\",\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"separator\":\" | \"}},\"queue\":{\"max_pending\":64,\"overflow\":\"DropOldest\"}}"
let config = @log.parse_logger_config_text(raw) catch {
err => {
ignore(err)
println("invalid logger configuration")
return
}
}
let logger = @log.build_logger(config)
logger.info("configured logger ready")
ignore(logger.flush())
```
Parsing validates the data before runtime construction. Keep the raw configuration outside application code in a real deployment, but handle parsing errors at the boundary where configuration enters the process.
## Run The Repository Example
```bash
moon run examples/config_build
```
## Next Steps
- Use [Queueing](../extend/queue.md) to choose overflow behavior deliberately.
- Use [Async lifecycle](./async.md) when the queue needs a background worker.
- Reference: [`parse_logger_config_text(...)`](../api/parse-logger-config-text.md), [`build_logger(...)`](../api/build-logger.md), and [`with_queue(...)`](../api/with-queue.md).
+51
View File
@@ -0,0 +1,51 @@
# Console And Structured Fields
Use this flow for command-line tools and services that need a human-readable starting point. A record always has a level, target, message, and optional fields.
## Install And Import
```bash
moon add Nanaloveyuki/BitLogger@0.7.0
```
```moonbit
import {
"Nanaloveyuki/BitLogger/src" @log,
}
```
## Build A Console Logger
```moonbit
fn main {
let logger = @log.build_logger(
@log.console(min_level=@log.Level::Info, target="service.http"),
)
logger.info("listening", fields=[
@log.field("port", "8080"),
@log.field("environment", "development"),
])
logger.warn("slow request", fields=[@log.field("path", "/health")])
}
```
`min_level` filters records before they reach the sink. `target` identifies the subsystem, while fields carry queryable context without interpolating it into the message.
## Switch To JSON
Keep the logging calls unchanged and replace the preset:
```moonbit
let logger = @log.build_logger(
@log.json_console(min_level=@log.Level::Info, target="service.http"),
)
```
This is the preferred output for a collector that consumes one structured record per line.
## Next Steps
- Use [Text formatting](../extend/formatting.md) for a custom terminal format.
- Use [Configuration](./config.md) when levels, targets, and sinks come from JSON.
- Reference: [`console(...)`](../api/console.md), [`json_console(...)`](../api/json-console.md), [`field(...)`](../api/field.md), and [`build_logger(...)`](../api/build-logger.md).
+51
View File
@@ -0,0 +1,51 @@
# File Output And Rotation
File output is for native applications that need local persistence. Treat it as a native-only capability and keep a console fallback for portable code.
## Check The Capability
```moonbit
if !@log.native_files_supported() {
println("file logging is unavailable on this target")
return
}
```
## Build A Rotating File Logger
```moonbit
let config = @log.with_file_rotation(
@log.file(
"service.log",
min_level=@log.Level::Info,
target="service.file",
auto_flush=true,
) catch {
err => {
ignore(err)
return
}
},
1024 * 1024,
max_backups=3,
)
let logger = @log.build_logger(config)
logger.info("file logger ready")
ignore(logger.flush())
ignore(logger.file_close())
```
The rotation limit is the size of the active file in bytes. `max_backups=3` retains the rotated backup chain. Always close a file-backed logger during orderly application shutdown.
## Run The Repository Example
```bash
moon run examples/file_rotation --target native
```
## Next Steps
- Add a bounded queue with [Queueing](../extend/queue.md) when bursts should not write synchronously.
- Read [Target boundaries](../extend/targets.md) before sharing the same logging setup with web targets.
- Reference: [`file(...)`](../api/file.md), [`with_file_rotation(...)`](../api/with-file-rotation.md), and [`file_close(...)`](../api/configured-logger-file-close.md).
+24
View File
@@ -0,0 +1,24 @@
# Examples
Examples are the primary BitLogger learning path. Each page starts from an application concern, shows the complete minimum code, and then points to the API reference for exact contracts.
## Choose A Flow
| Need | Start here | Then extend with |
| --- | --- | --- |
| Print structured logs to a terminal | [Console and fields](./console.md) | [Text formatting](../extend/formatting.md) |
| Keep local logs with bounded disk use | [File rotation](./file-rotation.md) | [Target boundaries](../extend/targets.md) |
| Build logging from app configuration | [Configuration](./config.md) | [Queueing](../extend/queue.md) |
| Avoid blocking a native application on writes | [Async lifecycle](./async.md) | [Sink composition](../extend/composition.md) |
## Repository Examples
The runnable sources live under `examples/`. The guides below explain their intended use instead of duplicating every API detail:
- `examples/console_basic` for terminal and JSON records
- `examples/config_build` for parsed configuration
- `examples/file_rotation` for native file output
- `examples/async_basic` for the async lifecycle
- `examples/presets`, `examples/text_formatter`, and `examples/style_tags` for follow-on customization
Use the [API reference](../api/index.md) when a guide links to a symbol and you need its full signature or edge-case behavior.
+42
View File
@@ -0,0 +1,42 @@
# Sink Composition
Presets cover common console and file configurations. Build a `Logger::new(...)` directly when records need routing or multiple outputs.
## Send A Record To Two Destinations
```moonbit
let logger = @log.Logger::new(
@log.fanout_sink(@log.console_sink(), @log.json_console_sink()),
min_level=@log.Level::Info,
target="service",
)
logger.info("request complete", fields=[@log.field("status", "200")])
```
`fanout_sink(...)` writes the same record to every child sink. This keeps human-oriented terminal output and machine-oriented JSON output in one application path.
## Route Warnings Separately
```moonbit
let logger = @log.Logger::new(
@log.split_by_level(
@log.callback_sink(fn(rec) {
println("alert: \{rec.message}")
}),
@log.console_sink(),
min_level=@log.Level::Warn,
),
min_level=@log.Level::Trace,
target="service",
)
```
Use direct sink composition only when the routing behavior is part of the application design. A preset remains easier to audit when one sink is sufficient.
## API Reference
- [`Logger::new(...)`](../api/logger-new.md)
- [`fanout_sink(...)`](../api/fanout-sink.md)
- [`split_by_level(...)`](../api/split-by-level.md)
- [`callback_sink(...)`](../api/callback-sink.md)
+44
View File
@@ -0,0 +1,44 @@
# Text Formatting And Style Tags
Formatting changes presentation while keeping the record's level, target, message, and fields intact. Use text output for people; use JSON output when another system parses the record.
## Customize The Text Shape
```moonbit
let logger = @log.build_logger(
@log.text_console(
min_level=@log.Level::Info,
target="service",
text_formatter=@log.TextFormatterConfig::new(
show_timestamp=false,
field_separator=",",
template="[{level}] {target} {message} :: {fields}",
),
),
)
logger.info("ready", fields=[@log.field("port", "8080")])
```
The template controls the visible order. Keep fields separate from the message so they can still be rendered consistently across sinks.
## Add Named Styles
```moonbit
let formatter = @log.text_formatter(
show_timestamp=false,
color_mode=@log.ColorMode::Always,
).with_style_tags(
@log.default_style_tag_registry()
.set_tag("accent", fg=Some("#4cc9f0"), bold=true),
)
```
Style tags are terminal presentation metadata. Do not use them as the only way to encode operational meaning; preserve that meaning in level, target, and fields.
## API Reference
- [`text_console(...)`](../api/text-console.md)
- [`TextFormatterConfig`](../api/text-formatter-config.md)
- [`text_formatter(...)`](../api/text-formatter.md)
- [`ColorMode`](../api/color-mode.md)
+14
View File
@@ -0,0 +1,14 @@
# Extend A Logger
Start from an [Example flow](../examples/index.md), then add one extension at a time. These pages explain when an abstraction is useful and which operational boundary it introduces.
## Extension Map
| Need | Extension | Main trade-off |
| --- | --- | --- |
| Absorb short output bursts | [Queueing](./queue.md) | You must choose overflow and flush behavior. |
| Send records to multiple destinations or route by level | [Sink composition](./composition.md) | More explicit construction than presets. |
| Control terminal appearance | [Text formatting](./formatting.md) | Formatting affects presentation, not record structure. |
| Share code across native and web targets | [Target boundaries](./targets.md) | File and async runtime behavior differ by backend. |
For exact function signatures, follow each page's links into the [API reference](../api/index.md).
+30
View File
@@ -0,0 +1,30 @@
# Queueing And Overflow
Use a queue when a short burst of log writes should not immediately reach the output sink. Queueing is explicit because loss behavior is an application decision.
## Add A Synchronous Queue
```moonbit
let config = @log.with_queue(
@log.text_console(target="service"),
max_pending=128,
overflow=@log.QueueOverflowPolicy::DropOldest,
)
let logger = @log.build_logger(config)
logger.info("queued record")
ignore(logger.flush())
```
`DropOldest` preserves the newest operational information when the queue is full. Use `DropNewest` when preserving earlier records matters more. Calling `flush()` at a shutdown boundary makes the pending-record policy visible in application code.
## When To Use Async Instead
The synchronous queue is configuration around a normal runtime logger. When a native async application needs a worker lifecycle and batching, follow [Async logger lifecycle](../examples/async.md) instead.
## API Reference
- [`with_queue(...)`](../api/with-queue.md)
- [`QueueConfig`](../api/queue-config.md)
- [`QueueOverflowPolicy`](../api/queue-overflow-policy.md)
- [`flush()`](../api/configured-logger-flush.md)
+33
View File
@@ -0,0 +1,33 @@
# Target Boundaries
BitLogger keeps a portable structured logging surface, but native file output and async worker behavior are target-sensitive. Keep those boundaries in application code instead of assuming every sink behaves identically everywhere.
## Portable Default
`console(...)`, `json_console(...)`, structured fields, filters, patches, and the ordinary logger surface are the appropriate starting point for code shared across targets.
## Native-Only File Output
```moonbit
if @log.native_files_supported() {
let logger = @log.build_logger(@log.file("service.log") catch {
err => {
ignore(err)
return
}
})
logger.info("file output enabled")
}
```
File sinks need native filesystem support. Do not construct a file-only configuration unconditionally in code intended for web targets.
## Async Library Versus Async Entry Point
The async library exposes a compatibility surface across declared targets, while an executable `async fn main` still has stricter entry-point support. Keep a native-only executable example separate from portable library code.
## API Reference
- [`native_files_supported()`](../api/native-files-supported.md)
- [Target verification](../api/target-verification.md)
- [Async logger lifecycle](../examples/async.md)
+14 -16
View File
@@ -4,31 +4,29 @@ layout: home
hero: hero:
name: BitLogger name: BitLogger
text: Structured logging for MoonBit text: Structured logging for MoonBit
tagline: API reference, examples, and release notes for BitLogger. tagline: Start with a runnable logging flow, then extend it with precise API reference.
actions: actions:
- theme: brand - theme: brand
text: Open API Reference text: Start Examples
link: /api/ link: /examples/
- theme: alt - theme: alt
text: Read Release Notes text: Extend A Logger
link: /changes/ link: /extend/
- theme: alt - theme: alt
text: Mooncake Package text: Mooncake Package
link: https://mooncakes.io/docs/Nanaloveyuki/BitLogger link: https://mooncakes.io/docs/Nanaloveyuki/BitLogger
features: features:
- title: Structured logging - title: Examples first
details: Levels, targets, message fields, and configurable text or JSON output. details: Follow complete console, file, config, and async flows before looking up individual symbols.
- title: Sync and async paths - title: Extend deliberately
details: API reference covers both the main package and the async package surface. details: Add queues, custom sink graphs, formatting, and target-specific behavior only when the base flow needs them.
- title: Config and runtime helpers - title: API as reference
details: Browse builders, presets, file helpers, queue helpers, and verification notes quickly. details: Use the one-symbol-per-page API reference for exact signatures, contracts, and edge cases.
--- ---
## Start Here ## Start Here
<HomeDocHub /> 1. Read [Examples](./examples/index.md) for an end-to-end path from installation to a running logger.
2. Read [Extend](./extend/index.md) when the base console flow needs queueing, custom routing, formatting, files, or target-aware behavior.
- Use [API Reference](./api/index.md) when you want the canonical one-file-per-public-API docs. 3. Use [API Reference](./api/index.md) for exact contracts and [Release Notes](./changes/index.md) for version history.
- Use [Release Notes](./changes/index.md) when you want version-scoped changes and publish-facing summaries.
- Use [Chinese README](https://github.com/Nanaloveyuki/BitLogger/blob/main/README.md) or [English README](./README-en.md) when you want a shorter project overview first.