diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index a4731d4..1d7555a 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -124,6 +124,84 @@ function buildExtendSidebar(): DefaultTheme.SidebarItem[] { ] } +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' }, + ] +} + +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: '/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(), + }, + footer: { + message: '由仓库中的 VitePress 文档构建。', + copyright: 'MIT', + }, +} + export default defineConfig({ title: 'BitLogger', description: 'Structured logging library docs for MoonBit.', @@ -134,33 +212,18 @@ export default defineConfig({ markdown: { languages: [createMoonbitLanguageRegistration()], }, - themeConfig: { - 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', + themeConfig: englishThemeConfig, + locales: { + root: { + label: 'English', + lang: 'en-US', + themeConfig: englishThemeConfig, }, - socialLinks: [{ icon: 'github', link: repository }], - editLink: { - pattern: `${repository}/edit/main/docs/:path`, - text: 'Edit this page on GitHub', - }, - sidebar: { - '/examples/': buildExamplesSidebar(), - '/extend/': buildExtendSidebar(), - '/api/': buildApiSidebar(), - '/changes/': buildChangesSidebar(), - }, - footer: { - message: 'Published from the repository docs folder with VitePress.', - copyright: 'MIT', + zh: { + label: '简体中文', + lang: 'zh-CN', + link: '/zh/', + themeConfig: chineseThemeConfig, }, }, }) diff --git a/docs/zh/examples/async.md b/docs/zh/examples/async.md new file mode 100644 index 0000000..7e7f72b --- /dev/null +++ b/docs/zh/examples/async.md @@ -0,0 +1,47 @@ +# 异步日志生命周期 + +当 native 异步应用需要通过异步队列处理日志写入时,使用异步包。库声明的可用目标比可执行 `async fn main` 示例更广,仓库示例仍以 native 为主。 + +## 导入两个包 + +```moonbit +import { + "Nanaloveyuki/BitLogger/src" @log, + "Nanaloveyuki/BitLogger/src-async" @async_log, + "moonbitlang/async", +} +``` + +## 构建、运行、关闭 + +```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()` 持有 worker 循环。先启动它再记录日志,并调用 `shutdown()`,使待处理记录按配置的 flush 策略结束。 + +## 运行仓库示例 + +```bash +moon run examples/async_basic --target native +``` + +## 下一步 + +- 查看[队列](../extend/queue.md)中的溢出与 flush 选择。 +- 英文 API:[`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)、[`shutdown()`](../../api/async-logger-shutdown.md)。 diff --git a/docs/zh/examples/config.md b/docs/zh/examples/config.md new file mode 100644 index 0000000..1fa5a14 --- /dev/null +++ b/docs/zh/examples/config.md @@ -0,0 +1,35 @@ +# 配置驱动构建 + +当 level、目标名、输出方式或队列策略应由部署配置决定时,使用这一流程。 + +## 解析、构建、记录 + +```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()) +``` + +解析先验证数据,再构造运行时 Logger。真实部署应将原始配置放在应用代码之外,并在配置进入进程的边界处理错误。 + +## 运行仓库示例 + +```bash +moon run examples/config_build +``` + +## 下一步 + +- 需要明确选择满队列时的行为,阅读[队列](../extend/queue.md)。 +- 需要后台 worker 生命周期时,阅读[异步日志生命周期](./async.md)。 +- 英文 API:[`parse_logger_config_text(...)`](../../api/parse-logger-config-text.md)、[`build_logger(...)`](../../api/build-logger.md)、[`with_queue(...)`](../../api/with-queue.md)。 diff --git a/docs/zh/examples/console.md b/docs/zh/examples/console.md new file mode 100644 index 0000000..77f6077 --- /dev/null +++ b/docs/zh/examples/console.md @@ -0,0 +1,55 @@ +# 控制台与结构化字段 + +命令行工具和服务都可以从这一流程开始。一条记录由 level、target、message 与可选 fields 构成。 + +## 安装与导入 + +```bash +moon new log-demo +cd log-demo +moon add Nanaloveyuki/BitLogger@0.7.0 +``` + +在应用的 `moon.pkg` 中加入: + +```moonbit +import { + "Nanaloveyuki/BitLogger/src" @log, +} +``` + +## 构建控制台 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` 在记录到达 sink 前过滤日志;`target` 用于识别子系统;fields 用于携带可查询上下文,而不是拼接到 message 中。 + +## 切换为 JSON + +保持日志调用不变,只替换配置: + +```moonbit +let logger = @log.build_logger( + @log.json_console(min_level=@log.Level::Info, target="service.http"), +) +``` + +当日志采集器按行处理结构化记录时,JSON 输出更合适。 + +## 下一步 + +- 需要自定义终端输出时,阅读[文本格式](../extend/formatting.md)。 +- 需要由 JSON 配置构建时,阅读[配置构建](./config.md)。 +- 英文 API:[`console(...)`](../../api/console.md)、[`json_console(...)`](../../api/json-console.md)、[`field(...)`](../../api/field.md)、[`build_logger(...)`](../../api/build-logger.md)。 diff --git a/docs/zh/examples/file-rotation.md b/docs/zh/examples/file-rotation.md new file mode 100644 index 0000000..07f25a6 --- /dev/null +++ b/docs/zh/examples/file-rotation.md @@ -0,0 +1,51 @@ +# 文件输出与轮转 + +文件输出面向需要本地持久化的 native 应用。跨端代码应先判断能力,并保留控制台回退方案。 + +## 检查能力 + +```moonbit +if !@log.native_files_supported() { + println("file logging is unavailable on this target") + return +} +``` + +## 构建可轮转文件 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()) +``` + +轮转阈值是活动文件的字节数,`max_backups=3` 保留备份链。应用有序退出时必须关闭文件 Logger。 + +## 运行仓库示例 + +```bash +moon run examples/file_rotation --target native +``` + +## 下一步 + +- 写入突发较多时,加入[队列](../extend/queue.md)。 +- 与 web 目标共享代码前,阅读[目标平台边界](../extend/targets.md)。 +- 英文 API:[`file(...)`](../../api/file.md)、[`with_file_rotation(...)`](../../api/with-file-rotation.md)、[`file_close(...)`](../../api/configured-logger-file-close.md)。 diff --git a/docs/zh/examples/index.md b/docs/zh/examples/index.md new file mode 100644 index 0000000..e59cd98 --- /dev/null +++ b/docs/zh/examples/index.md @@ -0,0 +1,24 @@ +# 示例 + +示例是 BitLogger 的主学习路径。每页从一个应用需求开始,给出完整最小代码,再链接到英文 API 参考查看精确契约。 + +## 选择流程 + +| 需求 | 从这里开始 | 后续扩展 | +| --- | --- | --- | +| 在终端输出结构化日志 | [控制台与字段](./console.md) | [文本格式](../extend/formatting.md) | +| 保存本地日志并限制磁盘占用 | [文件轮转](./file-rotation.md) | [目标平台边界](../extend/targets.md) | +| 由应用配置决定日志行为 | [配置构建](./config.md) | [队列](../extend/queue.md) | +| 在 native 异步应用中处理日志写入 | [异步生命周期](./async.md) | [Sink 组合](../extend/composition.md) | + +## 可运行源码 + +仓库中的 `examples/` 是可执行参考;本目录说明何时选择它们、如何运行,以及下一步应该扩展什么: + +- `examples/console_basic` +- `examples/config_build` +- `examples/file_rotation` +- `examples/async_basic` +- `examples/presets`、`examples/text_formatter`、`examples/style_tags` + +遇到函数签名、错误语义或边界条件时,请查询[英文 API 参考](../../api/index.md)。 diff --git a/docs/zh/extend/composition.md b/docs/zh/extend/composition.md new file mode 100644 index 0000000..ae2ccef --- /dev/null +++ b/docs/zh/extend/composition.md @@ -0,0 +1,42 @@ +# Sink 组合 + +Preset 覆盖常见的控制台和文件配置。只有当记录需要路由或多个输出目的地时,才直接构造 `Logger::new(...)`。 + +## 同时发送到两个目的地 + +```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(...)` 将同一记录写入每个子 Sink,因此应用可以同时保留面向人的控制台输出和面向采集器的 JSON 输出。 + +## 单独处理警告以上日志 + +```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", +) +``` + +只有当路由规则属于应用设计时才使用直接 Sink 组合;单一输出时,preset 更容易审查和维护。 + +## 英文 API + +- [`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) diff --git a/docs/zh/extend/formatting.md b/docs/zh/extend/formatting.md new file mode 100644 index 0000000..bd5ada0 --- /dev/null +++ b/docs/zh/extend/formatting.md @@ -0,0 +1,44 @@ +# 文本格式与样式 + +格式化只改变呈现,记录本身的 level、target、message 和 fields 不变。面向人时使用文本输出;由其他系统解析时使用 JSON。 + +## 自定义文本形状 + +```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")]) +``` + +模板控制可见顺序。fields 应保持独立于 message,以便不同 Sink 一致渲染。 + +## 添加命名样式 + +```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), +) +``` + +样式标签是终端呈现元数据,不应作为唯一的业务或运维语义;语义仍应保存在 level、target 与 fields 中。 + +## 英文 API + +- [`text_console(...)`](../../api/text-console.md) +- [`TextFormatterConfig`](../../api/text-formatter-config.md) +- [`text_formatter(...)`](../../api/text-formatter.md) +- [`ColorMode`](../../api/color-mode.md) diff --git a/docs/zh/extend/index.md b/docs/zh/extend/index.md new file mode 100644 index 0000000..c6db287 --- /dev/null +++ b/docs/zh/extend/index.md @@ -0,0 +1,14 @@ +# 扩展 Logger + +先完成一个[示例流程](../examples/index.md),再一次只加入一种扩展能力。这些页面说明某个抽象何时有用,以及它会引入什么运行时边界。 + +## 扩展地图 + +| 需求 | 扩展 | 主要取舍 | +| --- | --- | --- | +| 吸收短时输出突发 | [队列](./queue.md) | 必须选择溢出与 flush 行为。 | +| 同时发送到多个位置或按 level 路由 | [Sink 组合](./composition.md) | 比 preset 更需要显式构造。 | +| 调整终端可读性 | [文本格式](./formatting.md) | 格式只改变呈现,不改变记录结构。 | +| 在 native 与 web 目标之间共享代码 | [目标平台边界](./targets.md) | 文件与异步运行时行为因后端而异。 | + +需要精确函数签名时,沿页面链接进入[英文 API 参考](../../api/index.md)。 diff --git a/docs/zh/extend/queue.md b/docs/zh/extend/queue.md new file mode 100644 index 0000000..204fb60 --- /dev/null +++ b/docs/zh/extend/queue.md @@ -0,0 +1,30 @@ +# 队列与溢出策略 + +当短时日志突发不应立即写入输出 Sink 时使用队列。队列是显式能力,因为丢弃策略必须由应用决定。 + +## 加入同步队列 + +```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` 在队列已满时保留最新运行信息。若更重视先到的记录,可选择 `DropNewest`。在退出边界调用 `flush()`,让待处理记录策略在应用代码中清晰可见。 + +## 何时改用异步 Logger + +同步队列只是普通运行时 Logger 的一层配置。当 native 异步应用需要 worker 生命周期和批处理时,转到[异步日志生命周期](../examples/async.md)。 + +## 英文 API + +- [`with_queue(...)`](../../api/with-queue.md) +- [`QueueConfig`](../../api/queue-config.md) +- [`QueueOverflowPolicy`](../../api/queue-overflow-policy.md) +- [`flush()`](../../api/configured-logger-flush.md) diff --git a/docs/zh/extend/targets.md b/docs/zh/extend/targets.md new file mode 100644 index 0000000..5818099 --- /dev/null +++ b/docs/zh/extend/targets.md @@ -0,0 +1,33 @@ +# 目标平台边界 + +BitLogger 保持可移植的结构化日志表面,但 native 文件输出与异步 worker 行为依赖目标平台。应把这些边界写在应用代码中,而不是假设每种 Sink 在所有后端都相同。 + +## 可移植默认值 + +`console(...)`、`json_console(...)`、结构化 fields、filter、patch 与普通 Logger 表面适合作为跨端共享代码的起点。 + +## 仅 native 的文件输出 + +```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") +} +``` + +文件 Sink 需要 native 文件系统支持。面向 web 目标的代码不应无条件构造只包含文件输出的配置。 + +## 异步库与异步入口 + +异步库在声明目标上提供兼容表面,但可执行 `async fn main` 的入口支持更严格。请把 native-only 可执行示例与可移植库代码分开。 + +## 英文 API + +- [`native_files_supported()`](../../api/native-files-supported.md) +- [目标验证](../../api/target-verification.md) +- [异步日志生命周期](../examples/async.md) diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 0000000..9c5bffb --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,32 @@ +--- +layout: home + +hero: + name: BitLogger + text: MoonBit 结构化日志库 + tagline: 先完成一条可运行的日志流程,再按需扩展并查询 API。 + actions: + - theme: brand + text: 从示例开始 + link: /zh/examples/ + - theme: alt + text: 扩展 Logger + link: /zh/extend/ + - theme: alt + text: API 参考(英文) + link: /api/ + +features: + - title: 示例优先 + details: 从控制台、文件、配置和异步四条完整流程开始,而不是从 API 文件名开始。 + - title: 按需扩展 + details: 只在基础流程需要时再加入队列、组合 Sink、格式化和跨端处理。 + - title: API 作为参考 + details: 英文 API 页面保留精确签名、契约与边界说明。 +--- + +## 从这里开始 + +1. 阅读[示例](./examples/index.md),完成从安装到运行 Logger 的全流程。 +2. 当基础控制台流程需要更多能力时,阅读[扩展](./extend/index.md)。 +3. 需要精确签名和边界时,查询[英文 API 参考](../api/index.md);API 的中文翻译会按使用频率逐步补充。