mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-30 15:04:48 +00:00
📝 align portable examples and release messaging
This commit is contained in:
@@ -16,12 +16,18 @@ BitLogger 是一个使用 MoonBit 编写的结构化日志库,目标是提供
|
|||||||
|
|
||||||
## 🧭 后端兼容
|
## 🧭 后端兼容
|
||||||
|
|
||||||
| 模块 / 能力 | native / llvm | js / wasm / wasm-gc |
|
当前仓库已验证目标:`native`、`js`、`wasm`、`wasm-gc`。
|
||||||
|
|
||||||
|
`llvm` 目前仍应视为实验性目标:当前环境未完成验证,本地复核依赖 nightly / llvm toolchain 条件,现阶段不作为与 `native` 同等级的稳定承诺。
|
||||||
|
|
||||||
|
| 模块 / 能力 | 已验证目标 | `llvm` |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `src` 主包 | 支持 | 支持 |
|
| `src` 主包 | `native`、`js`、`wasm`、`wasm-gc` 已验证 | 实验性;当前环境未完成验证 |
|
||||||
| `file_sink(...)` | 支持 | 不支持, `native_files_supported()` 返回 `false` |
|
| `file(...)` / `file_sink(...)` | 仅 `native` 已验证;非 native 下不支持,`native_files_supported()` 返回 `false` | 不作为已验证能力声明 |
|
||||||
| `src-async` | 支持原生 worker 语义 | 支持兼容实现 |
|
| `src-async` | `native`、`wasm` 已本地复核通过;其余非 native 仍按兼容实现表述 | 实验性;当前环境未完成验证 |
|
||||||
| `examples/async_basic` | 支持 | 受 `async fn main` 入口限制, 当前不提供 |
|
| portable examples (`console_basic` / `text_formatter` / `style_tags` / `config_build` / `presets`) | `native`、`wasm-gc` 已验证 | 当前未完成验证 |
|
||||||
|
| `examples/file_rotation` | `native` 已验证;非 native 会清晰提示 file sink 不可用 | 当前未完成验证 |
|
||||||
|
| `examples/async_basic` | `native` 已验证;非 native 受 `async fn main` 入口限制, 当前不提供 | 当前未完成验证 |
|
||||||
|
|
||||||
## ❇️ 关键特性
|
## ❇️ 关键特性
|
||||||
|
|
||||||
@@ -55,6 +61,8 @@ ignore(logger.flush())
|
|||||||
|
|
||||||
当你需要 `fanout`、`split`、`callback`、`patch` 这类自定义 sink 图组合时,再优先使用 `Logger::new(...)` 直接进行运行时拼装。
|
当你需要 `fanout`、`split`、`callback`、`patch` 这类自定义 sink 图组合时,再优先使用 `Logger::new(...)` 直接进行运行时拼装。
|
||||||
|
|
||||||
|
跨端优先时,优先选择 console / json_console / text_console / config parser 这类 portable surface;file sink / file preset 仍然是 native 能力,建议先用 `native_files_supported()` 做能力判断。
|
||||||
|
|
||||||
异步入口示例:
|
异步入口示例:
|
||||||
|
|
||||||
```moonbit
|
```moonbit
|
||||||
@@ -72,13 +80,13 @@ let logger = async_logger(console_sink(), target="async.demo")
|
|||||||
- `src-async/`: 基于 `moonbitlang/async` 的异步日志层。
|
- `src-async/`: 基于 `moonbitlang/async` 的异步日志层。
|
||||||
- `docs/api/`: 单接口粒度 API 文档。
|
- `docs/api/`: 单接口粒度 API 文档。
|
||||||
- `examples/basic/`: breadth 示例;文件开头就是推荐的 presets + `build_logger(...)` 同步入口,后续继续覆盖更广能力面。
|
- `examples/basic/`: breadth 示例;文件开头就是推荐的 presets + `build_logger(...)` 同步入口,后续继续覆盖更广能力面。
|
||||||
- `examples/console_basic/`: console 与 json console 最小输出示例。
|
- `examples/console_basic/`: portable 的 console 与 json console 最小输出示例。
|
||||||
- `examples/text_formatter/`: text formatter / template 示例。
|
- `examples/text_formatter/`: portable 的 text formatter / template 示例。
|
||||||
- `examples/style_tags/`: style tag / colored formatter 示例。
|
- `examples/style_tags/`: portable 的 style tag / colored formatter 示例。
|
||||||
- `examples/file_rotation/`: file sink / rotation 示例。
|
- `examples/file_rotation/`: target-sensitive 的 file sink / rotation 示例,非 native 会直接提示跳过原因。
|
||||||
- `examples/config_build/`: `build_logger(...)` 配置驱动示例。
|
- `examples/config_build/`: portable 的 `build_logger(...)` 配置驱动示例。
|
||||||
- `examples/presets/`: presets + `build_logger(...)` 示例。
|
- `examples/presets/`: portable-first 的 presets + `build_logger(...)` 示例;native 下额外演示 file preset。
|
||||||
- `examples/async_basic/`: 异步 logger 示例。
|
- `examples/async_basic/`: native-only 异步 logger 示例,并说明限制来自 `async fn main` 入口而不是 `src-async` API 缺失。
|
||||||
|
|
||||||
## 🔗 文档入口
|
## 🔗 文档入口
|
||||||
|
|
||||||
|
|||||||
+20
-12
@@ -10,12 +10,18 @@ BitLogger is designed to provide composable, configurable, and cross-target logg
|
|||||||
|
|
||||||
## Backend Compatibility
|
## Backend Compatibility
|
||||||
|
|
||||||
| Module / capability | native / llvm | js / wasm / wasm-gc |
|
Currently verified targets: `native`, `js`, `wasm`, and `wasm-gc`.
|
||||||
|
|
||||||
|
`llvm` should still be treated as experimental here: verification did not complete in the current environment, and local reproduction depends on nightly / llvm toolchain conditions, so it is not presented as a stable promise at the same level as `native`.
|
||||||
|
|
||||||
|
| Module / capability | Verified targets | `llvm` |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `src` core package | Supported | Supported |
|
| `src` core package | Verified on `native`, `js`, `wasm`, and `wasm-gc` | Experimental; not fully verified in the current environment |
|
||||||
| `file_sink(...)` | Supported | Not available, `native_files_supported()` returns `false` |
|
| `file(...)` / `file_sink(...)` | Verified only on `native`; unavailable on non-native targets and `native_files_supported()` returns `false` | Not presented as a verified capability |
|
||||||
| `src-async` | Native worker semantics | Compatibility implementation |
|
| `src-async` | Local verification passed on `native` and `wasm`; other non-native targets still follow the compatibility-implementation wording | Experimental; not fully verified in the current environment |
|
||||||
| `examples/async_basic` | Supported | Not shipped currently because `async fn main` entry support is still limited |
|
| portable examples (`console_basic` / `text_formatter` / `style_tags` / `config_build` / `presets`) | Verified on `native` and `wasm-gc` | Not yet verified here |
|
||||||
|
| `examples/file_rotation` | Verified on `native`; non-native targets print a clear skip message because file sinks are unavailable | Not yet verified here |
|
||||||
|
| `examples/async_basic` | Verified on `native`; non-native example shipping is still limited by the current `async fn main` entry path | Not yet verified here |
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
@@ -49,6 +55,8 @@ Start new synchronous code with `presets + build_logger(...)`: use `console(...)
|
|||||||
|
|
||||||
Switch to `Logger::new(...)` when you need direct runtime sink composition such as `fanout`, `split`, `callback`, or custom patch/filter graphs.
|
Switch to `Logger::new(...)` when you need direct runtime sink composition such as `fanout`, `split`, `callback`, or custom patch/filter graphs.
|
||||||
|
|
||||||
|
For cross-target code, prefer the console, json console, text console, and config-parser paths first. File sinks and file presets remain native-sensitive and should be gated with `native_files_supported()`.
|
||||||
|
|
||||||
Async entry example:
|
Async entry example:
|
||||||
|
|
||||||
```moonbit
|
```moonbit
|
||||||
@@ -66,13 +74,13 @@ let logger = async_logger(console_sink(), target="async.demo")
|
|||||||
- `src-async/`: async logging layer built on `moonbitlang/async`.
|
- `src-async/`: async logging layer built on `moonbitlang/async`.
|
||||||
- `docs/api/`: one-file-per-interface API documentation.
|
- `docs/api/`: one-file-per-interface API documentation.
|
||||||
- `examples/basic/`: breadth example; starts with the recommended presets-based sync path and then sweeps broader capabilities.
|
- `examples/basic/`: breadth example; starts with the recommended presets-based sync path and then sweeps broader capabilities.
|
||||||
- `examples/console_basic/`: minimal console and JSON console output.
|
- `examples/console_basic/`: portable minimal console and JSON console output.
|
||||||
- `examples/text_formatter/`: text formatter and template example.
|
- `examples/text_formatter/`: portable text formatter and template example.
|
||||||
- `examples/style_tags/`: style tag and colored formatter example.
|
- `examples/style_tags/`: portable style tag and colored formatter example.
|
||||||
- `examples/file_rotation/`: file sink and rotation example.
|
- `examples/file_rotation/`: target-sensitive file sink and rotation example with an explicit non-native skip path.
|
||||||
- `examples/config_build/`: config-driven `build_logger(...)` example.
|
- `examples/config_build/`: portable config-driven `build_logger(...)` example.
|
||||||
- `examples/presets/`: presets-based construction example.
|
- `examples/presets/`: portable-first presets example; adds file preset usage only when native file support is available.
|
||||||
- `examples/async_basic/`: async logger example.
|
- `examples/async_basic/`: native-only async logger example; the limitation is the `async fn main` entry path, not the absence of `src-async` compatibility APIs.
|
||||||
|
|
||||||
## Documentation Entry Points
|
## Documentation Entry Points
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
async fn main {
|
async fn main {
|
||||||
|
println("examples/async_basic is shipped as a native-only example because async fn main entry support is still limited on non-native targets, even though src-async itself keeps compatibility behavior.")
|
||||||
println(@lib_async.stringify_async_runtime_state(@lib_async.async_runtime_state(), pretty=true))
|
println(@lib_async.stringify_async_runtime_state(@lib_async.async_runtime_state(), pretty=true))
|
||||||
|
|
||||||
let raw = "{\"logger\":{\"min_level\":\"info\",\"target\":\"async.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"separator\":\" | \"}}},\"async_config\":{\"max_pending\":2,\"overflow\":\"DropOldest\",\"max_batch\":4,\"linger_ms\":5,\"flush\":\"Batch\"}}"
|
let raw = "{\"logger\":{\"min_level\":\"info\",\"target\":\"async.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"separator\":\" | \"}}},\"async_config\":{\"max_pending\":2,\"overflow\":\"DropOldest\",\"max_batch\":4,\"linger_ms\":5,\"flush\":\"Batch\"}}"
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
fn main {
|
fn main {
|
||||||
let logger = @lib.Logger::new(@lib.console_sink(), min_level=@lib.Level::Info, target="demo.console")
|
let logger = @lib.build_logger(
|
||||||
.with_context_fields([@lib.field("service", "bitlogger")])
|
@lib.console(min_level=@lib.Level::Info, target="demo.console"),
|
||||||
|
)
|
||||||
logger.info("hello console", fields=[@lib.field("mode", "basic")])
|
logger.info("hello console", fields=[@lib.field("mode", "basic")])
|
||||||
|
|
||||||
let structured = @lib.Logger::new(@lib.json_console_sink(), min_level=@lib.Level::Info, target="demo.json")
|
let structured = @lib.build_logger(
|
||||||
|
@lib.json_console(min_level=@lib.Level::Info, target="demo.json"),
|
||||||
|
)
|
||||||
structured.info("hello json", fields=[@lib.field("kind", "structured")])
|
structured.info("hello json", fields=[@lib.field("kind", "structured")])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
fn main {
|
fn main {
|
||||||
if !@lib.native_files_supported() {
|
if !@lib.native_files_supported() {
|
||||||
println("native file sink is not available on this backend")
|
println("native file sink is not available on this backend; examples/presets stays portable, while file_rotation remains native-focused")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let logger = @lib.Logger::new(
|
let logger = @lib.build_logger(
|
||||||
@lib.file_sink(
|
@lib.with_file_rotation(
|
||||||
"bitlogger-example.log",
|
@lib.file(
|
||||||
auto_flush=true,
|
"bitlogger-example.log",
|
||||||
rotation=Some(@lib.file_rotation(128, max_backups=2)),
|
min_level=@lib.Level::Info,
|
||||||
|
target="demo.file",
|
||||||
|
auto_flush=true,
|
||||||
|
) catch {
|
||||||
|
err => {
|
||||||
|
ignore(err)
|
||||||
|
println("invalid file preset config")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
128,
|
||||||
|
max_backups=2,
|
||||||
),
|
),
|
||||||
min_level=@lib.Level::Info,
|
|
||||||
target="demo.file",
|
|
||||||
)
|
)
|
||||||
logger.info("file rotation ready", fields=[@lib.field("kind", "file")])
|
logger.info("file rotation ready", fields=[@lib.field("kind", "file")])
|
||||||
ignore(logger.sink.flush())
|
ignore(logger.flush())
|
||||||
ignore(logger.sink.close())
|
ignore(logger.file_close())
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-15
@@ -1,27 +1,45 @@
|
|||||||
fn main {
|
fn main {
|
||||||
let config = @lib.with_queue(
|
let config = @lib.with_queue(
|
||||||
@lib.with_file_rotation(
|
@lib.text_console(
|
||||||
|
min_level=@lib.Level::Info,
|
||||||
|
target="demo.preset",
|
||||||
|
text_formatter=@lib.TextFormatterConfig::new(
|
||||||
|
show_timestamp=false,
|
||||||
|
separator=" | ",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
max_pending=4,
|
||||||
|
overflow=@lib.QueueOverflowPolicy::DropOldest,
|
||||||
|
)
|
||||||
|
|
||||||
|
let logger = @lib.build_logger(config)
|
||||||
|
logger.info("preset logger ready", fields=[@lib.field("kind", "portable")])
|
||||||
|
logger.info("preset logger queued", fields=[@lib.field("step", "two")])
|
||||||
|
ignore(logger.flush())
|
||||||
|
|
||||||
|
if @lib.native_files_supported() {
|
||||||
|
let file_config = @lib.with_file_rotation(
|
||||||
@lib.file(
|
@lib.file(
|
||||||
"preset-example.log",
|
"preset-example.log",
|
||||||
min_level=@lib.Level::Info,
|
min_level=@lib.Level::Info,
|
||||||
target="demo.preset",
|
target="demo.preset.file",
|
||||||
auto_flush=true,
|
auto_flush=true,
|
||||||
),
|
),
|
||||||
256,
|
256,
|
||||||
max_backups=2,
|
max_backups=2,
|
||||||
),
|
) catch {
|
||||||
max_pending=4,
|
err => {
|
||||||
overflow=@lib.QueueOverflowPolicy::DropOldest,
|
ignore(err)
|
||||||
) catch {
|
println("invalid file preset config")
|
||||||
err => {
|
return
|
||||||
ignore(err)
|
}
|
||||||
println("invalid preset config")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let logger = @lib.build_logger(config)
|
let file_logger = @lib.build_logger(file_config)
|
||||||
logger.info("preset logger ready", fields=[@lib.field("kind", "preset")])
|
file_logger.info("file preset ready", fields=[@lib.field("kind", "native-only")])
|
||||||
ignore(logger.flush())
|
ignore(file_logger.flush())
|
||||||
ignore(logger.file_close())
|
ignore(file_logger.file_close())
|
||||||
|
} else {
|
||||||
|
println("file presets are skipped on this backend because native file support is unavailable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
fn main {
|
fn main {
|
||||||
let formatter = @lib.text_formatter(
|
let logger = @lib.build_logger(
|
||||||
show_timestamp=false,
|
@lib.text_console(
|
||||||
color_mode=@lib.ColorMode::Always,
|
min_level=@lib.Level::Info,
|
||||||
).with_style_tags(
|
target="demo.style",
|
||||||
@lib.default_style_tag_registry()
|
text_formatter=@lib.TextFormatterConfig::new(
|
||||||
.set_tag("accent", fg=Some("#4cc9f0"), bold=true)
|
show_timestamp=false,
|
||||||
.define_alias("danger", "red"),
|
color_mode=@lib.ColorMode::Always,
|
||||||
)
|
style_tags={
|
||||||
let logger = @lib.Logger::new(
|
"accent": @lib.text_style(fg=Some("#4cc9f0"), bold=true),
|
||||||
@lib.text_console_sink(formatter),
|
},
|
||||||
min_level=@lib.Level::Info,
|
),
|
||||||
target="demo.style",
|
),
|
||||||
)
|
)
|
||||||
logger.info("<accent>styled</> output with <danger>alert</>")
|
logger.info("<accent>styled</> output with <danger>alert</>")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
fn main {
|
fn main {
|
||||||
let formatter = @lib.text_formatter(
|
let logger = @lib.build_logger(
|
||||||
show_timestamp=false,
|
@lib.text_console(
|
||||||
separator=" | ",
|
min_level=@lib.Level::Info,
|
||||||
field_separator=",",
|
target="demo.formatter",
|
||||||
template="[{level}] {target} {message} :: {fields}",
|
text_formatter=@lib.TextFormatterConfig::new(
|
||||||
)
|
show_timestamp=false,
|
||||||
let logger = @lib.Logger::new(
|
separator=" | ",
|
||||||
@lib.text_console_sink(formatter),
|
field_separator=",",
|
||||||
min_level=@lib.Level::Info,
|
template="[{level}] {target} {message} :: {fields}",
|
||||||
target="demo.formatter",
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
logger.info("formatted output", fields=[@lib.field("user", "alice"), @lib.field("request_id", "42")])
|
logger.info("formatted output", fields=[@lib.field("user", "alice"), @lib.field("request_id", "42")])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,15 @@ Use `Logger::new(...)` instead when you need direct sink composition such as `fa
|
|||||||
|
|
||||||
如果你需要 `fanout_sink(...)`、`split_by_level(...)`、`callback_sink(...)` 或更自由的 patch/filter 组合,再改用 `Logger::new(...)` 直接拼装运行时 sink 图。
|
如果你需要 `fanout_sink(...)`、`split_by_level(...)`、`callback_sink(...)` 或更自由的 patch/filter 组合,再改用 `Logger::new(...)` 直接拼装运行时 sink 图。
|
||||||
|
|
||||||
|
Portable-first note / 跨端优先说明:
|
||||||
|
|
||||||
|
- prefer `console(...)`, `json_console(...)`, `text_console(...)`, parsed config, and `build_logger(...)` for cross-target examples
|
||||||
|
- 跨端示例优先使用这些 console / config 路径
|
||||||
|
- file sink and `file(...)` remain native-sensitive; gate them with `native_files_supported()` when portability matters
|
||||||
|
- `file(...)` / file sink 仍然是 native 敏感能力,跨端代码里应先做 `native_files_supported()` 判断
|
||||||
|
- verified README targets currently center on `native`, `js`, `wasm`, and `wasm-gc`; treat `llvm` as experimental until it is locally re-verified with the required toolchain
|
||||||
|
- 当前 README 的已验证目标以 `native`、`js`、`wasm`、`wasm-gc` 为准;`llvm` 在完成本地 toolchain 复核前应按实验性目标理解
|
||||||
|
|
||||||
Project command note / 项目命令说明:
|
Project command note / 项目命令说明:
|
||||||
|
|
||||||
- use `moon check` / `moon test` for local project verification
|
- use `moon check` / `moon test` for local project verification
|
||||||
@@ -64,6 +73,10 @@ Project command note / 项目命令说明:
|
|||||||
- `../examples/config_build/`
|
- `../examples/config_build/`
|
||||||
- `../examples/presets/`
|
- `../examples/presets/`
|
||||||
- `../examples/async_basic/`
|
- `../examples/async_basic/`
|
||||||
|
- `console_basic` / `text_formatter` / `style_tags` / `config_build` / `presets` are the portable-first set
|
||||||
|
- `console_basic` / `text_formatter` / `style_tags` / `config_build` / `presets` 是 portable-first 示例集
|
||||||
|
- `file_rotation` is native-sensitive, and `async_basic` stays native-only because of the current `async fn main` entry limitation
|
||||||
|
- `file_rotation` 是 native-sensitive,`async_basic` 的 native-only 限制来自当前 `async fn main` 入口能力
|
||||||
- package-level API docs / 单接口 API 文档:
|
- package-level API docs / 单接口 API 文档:
|
||||||
- `../docs/api/`
|
- `../docs/api/`
|
||||||
- common entry points / 常用入口:
|
- common entry points / 常用入口:
|
||||||
|
|||||||
Reference in New Issue
Block a user