diff --git a/README.md b/README.md index dc91f75..617d90c 100644 --- a/README.md +++ b/README.md @@ -35,13 +35,26 @@ BitLogger 是一个使用 MoonBit 编写的结构化日志库,目标是提供 ## 🚀 快速开始 ```moonbit -let logger = Logger::new(console_sink(), min_level=Level::Info, target="demo") - .with_timestamp() - .with_context_fields([field("service", "bitlogger")]) +let logger = build_logger( + with_queue( + text_console( + min_level=Level::Info, + target="demo", + text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "), + ), + max_pending=32, + overflow=QueueOverflowPolicy::DropOldest, + ), +) logger.info("starting", fields=[field("port", "8080")]) +ignore(logger.flush()) ``` +推荐先从 `presets + build_logger(...)` 路径开始:`console(...)` / `json_console(...)` / `text_console(...)` / `file(...)` 负责拼装 `LoggerConfig`,`with_queue(...)` / `with_file_rotation(...)` 负责做小范围组合,然后再交给 `build_logger(...)` 构建运行时 logger。 + +当你需要 `fanout`、`split`、`callback`、`patch` 这类自定义 sink 图组合时,再优先使用 `Logger::new(...)` 直接进行运行时拼装。 + 异步入口示例: ```moonbit @@ -58,7 +71,13 @@ let logger = async_logger(console_sink(), target="async.demo") - `src/`: 主日志库 package。 - `src-async/`: 基于 `moonbitlang/async` 的异步日志层。 - `docs/api/`: 单接口粒度 API 文档。 -- `examples/basic/`: 最小同步示例。 +- `examples/basic/`: breadth 示例;文件开头就是推荐的 presets + `build_logger(...)` 同步入口,后续继续覆盖更广能力面。 +- `examples/console_basic/`: console 与 json console 最小输出示例。 +- `examples/text_formatter/`: text formatter / template 示例。 +- `examples/style_tags/`: style tag / colored formatter 示例。 +- `examples/file_rotation/`: file sink / rotation 示例。 +- `examples/config_build/`: `build_logger(...)` 配置驱动示例。 +- `examples/presets/`: presets + `build_logger(...)` 示例。 - `examples/async_basic/`: 异步 logger 示例。 ## 🔗 文档入口 @@ -67,3 +86,5 @@ let logger = async_logger(console_sink(), target="async.demo") - [English README](./docs/README-en.md) - [src package README](./src/README.mbt.md) - [API 索引](./docs/api/index.md) +- 推荐起步 API: `text_console(...)` / `file(...)` / `with_queue(...)` / `build_logger(...)` +- facade API: `build_application_logger(...)` / `build_library_logger(...)` / `build_application_async_logger(...)` diff --git a/docs/README-en.md b/docs/README-en.md index 41ec3c2..c4b5e9b 100644 --- a/docs/README-en.md +++ b/docs/README-en.md @@ -29,13 +29,26 @@ BitLogger is designed to provide composable, configurable, and cross-target logg ## Quick Start ```moonbit -let logger = Logger::new(console_sink(), min_level=Level::Info, target="demo") - .with_timestamp() - .with_context_fields([field("service", "bitlogger")]) +let logger = build_logger( + with_queue( + text_console( + min_level=Level::Info, + target="demo", + text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "), + ), + max_pending=32, + overflow=QueueOverflowPolicy::DropOldest, + ), +) logger.info("starting", fields=[field("port", "8080")]) +ignore(logger.flush()) ``` +Start new synchronous code with `presets + build_logger(...)`: use `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)` to assemble `LoggerConfig`, optionally extend it with `with_queue(...)` or `with_file_rotation(...)`, then call `build_logger(...)`. + +Switch to `Logger::new(...)` when you need direct runtime sink composition such as `fanout`, `split`, `callback`, or custom patch/filter graphs. + Async entry example: ```moonbit @@ -52,7 +65,13 @@ let logger = async_logger(console_sink(), target="async.demo") - `src/`: core logging package. - `src-async/`: async logging layer built on `moonbitlang/async`. - `docs/api/`: one-file-per-interface API documentation. -- `examples/basic/`: minimal synchronous example. +- `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/text_formatter/`: text formatter and template example. +- `examples/style_tags/`: style tag and colored formatter example. +- `examples/file_rotation/`: file sink and rotation example. +- `examples/config_build/`: config-driven `build_logger(...)` example. +- `examples/presets/`: presets-based construction example. - `examples/async_basic/`: async logger example. ## Documentation Entry Points @@ -60,11 +79,15 @@ let logger = async_logger(console_sink(), target="async.demo") - [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger) - [Chinese README](../README.md) - [src package README](../src/README.mbt.md) +- Recommended sync starting APIs: `text_console(...)`, `file(...)`, `with_queue(...)`, `build_logger(...)` - Selected API docs in `docs/api/`: - [logger-new.md](./api/logger-new.md) - [async-logger.md](./api/async-logger.md) - [build-logger.md](./api/build-logger.md) - [build-async-logger.md](./api/build-async-logger.md) +- [build-application-logger.md](./api/build-application-logger.md) +- [build-library-logger.md](./api/build-library-logger.md) +- [build-application-async-logger.md](./api/build-application-async-logger.md) ## Notes diff --git a/examples/basic/main.mbt b/examples/basic/main.mbt index 12e71c2..0167d65 100644 --- a/examples/basic/main.mbt +++ b/examples/basic/main.mbt @@ -1,8 +1,25 @@ fn main { + let preset_logger = @lib.build_logger( + @lib.with_queue( + @lib.text_console( + min_level=@lib.Level::Info, + target="preset", + text_formatter=@lib.TextFormatterConfig::new(show_timestamp=false, separator=" | "), + ), + max_pending=2, + overflow=@lib.QueueOverflowPolicy::DropOldest, + ), + ) + preset_logger.info("queued one") + preset_logger.info("queued two") + preset_logger.info("queued three") + ignore(preset_logger.flush()) + @lib.set_default_min_level(@lib.Level::Debug) @lib.set_default_target("bitlogger") @lib.info("hello from BitLogger", fields=[@lib.field("mode", "demo")]) + // Direct Logger::new(...) composition stays useful for custom sink graphs. let logger = @lib.Logger::new(@lib.console_sink(), min_level=@lib.Level::Trace, target="custom") .with_context_fields([@lib.field("service", "bitlogger")]) logger.debug("custom logger ready", fields=[@lib.field("sink", "console")]) @@ -136,16 +153,6 @@ fn main { ])) patched_logger.info("login", fields=[@lib.field("user", "alice"), @lib.field("token", "secret")]) - let queued_logger = @lib.Logger::new( - @lib.console_sink(), - min_level=@lib.Level::Info, - target="queue", - ).with_queue(max_pending=2, overflow=@lib.QueueOverflowPolicy::DropOldest) - queued_logger.info("queued one") - queued_logger.info("queued two") - queued_logger.info("queued three") - ignore(queued_logger.sink.flush()) - let config_logger = @lib.parse_and_build_logger( "{\"min_level\":\"debug\",\"target\":\"config.demo\",\"timestamp\":true,\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"field_separator\":\",\",\"template\":\"[{level}] {target} {message} :: {fields}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}", ) catch { diff --git a/examples/config_build/main.mbt b/examples/config_build/main.mbt new file mode 100644 index 0000000..1549dd5 --- /dev/null +++ b/examples/config_build/main.mbt @@ -0,0 +1,17 @@ +fn main { + let config = @lib.parse_logger_config_text( + "{\"min_level\":\"debug\",\"target\":\"demo.config\",\"sink\":{\"kind\":\"text_console\",\"text_formatter\":{\"show_timestamp\":false,\"separator\":\" | \",\"template\":\"[{level}] {target} {message}\"}},\"queue\":{\"max_pending\":2,\"overflow\":\"DropOldest\"}}", + ) catch { + err => { + ignore(err) + println("invalid config") + return + } + } + + let logger = @lib.build_logger(config) + logger.info("queued one") + logger.info("queued two") + logger.info("queued three") + ignore(logger.flush()) +} diff --git a/examples/config_build/moon.pkg b/examples/config_build/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/config_build/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/examples/console_basic/main.mbt b/examples/console_basic/main.mbt new file mode 100644 index 0000000..d5ff351 --- /dev/null +++ b/examples/console_basic/main.mbt @@ -0,0 +1,8 @@ +fn main { + let logger = @lib.Logger::new(@lib.console_sink(), min_level=@lib.Level::Info, target="demo.console") + .with_context_fields([@lib.field("service", "bitlogger")]) + 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") + structured.info("hello json", fields=[@lib.field("kind", "structured")]) +} diff --git a/examples/console_basic/moon.pkg b/examples/console_basic/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/console_basic/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/examples/file_rotation/main.mbt b/examples/file_rotation/main.mbt new file mode 100644 index 0000000..af0ffd0 --- /dev/null +++ b/examples/file_rotation/main.mbt @@ -0,0 +1,19 @@ +fn main { + if !@lib.native_files_supported() { + println("native file sink is not available on this backend") + return + } + + let logger = @lib.Logger::new( + @lib.file_sink( + "bitlogger-example.log", + auto_flush=true, + rotation=Some(@lib.file_rotation(128, max_backups=2)), + ), + min_level=@lib.Level::Info, + target="demo.file", + ) + logger.info("file rotation ready", fields=[@lib.field("kind", "file")]) + ignore(logger.sink.flush()) + ignore(logger.sink.close()) +} diff --git a/examples/file_rotation/moon.pkg b/examples/file_rotation/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/file_rotation/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/examples/presets/main.mbt b/examples/presets/main.mbt new file mode 100644 index 0000000..32b43b5 --- /dev/null +++ b/examples/presets/main.mbt @@ -0,0 +1,27 @@ +fn main { + let config = @lib.with_queue( + @lib.with_file_rotation( + @lib.file( + "preset-example.log", + min_level=@lib.Level::Info, + target="demo.preset", + auto_flush=true, + ), + 256, + max_backups=2, + ), + max_pending=4, + overflow=@lib.QueueOverflowPolicy::DropOldest, + ) catch { + err => { + ignore(err) + println("invalid preset config") + return + } + } + + let logger = @lib.build_logger(config) + logger.info("preset logger ready", fields=[@lib.field("kind", "preset")]) + ignore(logger.flush()) + ignore(logger.file_close()) +} diff --git a/examples/presets/moon.pkg b/examples/presets/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/presets/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/examples/style_tags/main.mbt b/examples/style_tags/main.mbt new file mode 100644 index 0000000..5242c63 --- /dev/null +++ b/examples/style_tags/main.mbt @@ -0,0 +1,16 @@ +fn main { + let formatter = @lib.text_formatter( + show_timestamp=false, + color_mode=@lib.ColorMode::Always, + ).with_style_tags( + @lib.default_style_tag_registry() + .set_tag("accent", fg=Some("#4cc9f0"), bold=true) + .define_alias("danger", "red"), + ) + let logger = @lib.Logger::new( + @lib.text_console_sink(formatter), + min_level=@lib.Level::Info, + target="demo.style", + ) + logger.info("styled output with alert") +} diff --git a/examples/style_tags/moon.pkg b/examples/style_tags/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/style_tags/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/examples/text_formatter/main.mbt b/examples/text_formatter/main.mbt new file mode 100644 index 0000000..c13d2d1 --- /dev/null +++ b/examples/text_formatter/main.mbt @@ -0,0 +1,14 @@ +fn main { + let formatter = @lib.text_formatter( + show_timestamp=false, + separator=" | ", + field_separator=",", + template="[{level}] {target} {message} :: {fields}", + ) + let logger = @lib.Logger::new( + @lib.text_console_sink(formatter), + min_level=@lib.Level::Info, + target="demo.formatter", + ) + logger.info("formatted output", fields=[@lib.field("user", "alice"), @lib.field("request_id", "42")]) +} diff --git a/examples/text_formatter/moon.pkg b/examples/text_formatter/moon.pkg new file mode 100644 index 0000000..05f243c --- /dev/null +++ b/examples/text_formatter/moon.pkg @@ -0,0 +1,7 @@ +import { + "Nanaloveyuki/BitLogger/src" @lib, +} + +options( + "is-main": true, +) diff --git a/src/README.mbt.md b/src/README.mbt.md index a679442..a02f53d 100644 --- a/src/README.mbt.md +++ b/src/README.mbt.md @@ -21,13 +21,31 @@ BitLogger 是一个使用 MoonBit 编写的结构化日志库. ```moonbit test { - let logger = Logger::new(console_sink(), min_level=Level::Debug, target="demo") - .with_timestamp() - .with_context_fields([field("service", "bitlogger")]) + let logger = build_logger( + text_console( + min_level=Level::Debug, + target="demo", + text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "), + ), + ) logger.info("starting", fields=[field("port", "8080")]) + ignore(logger.flush()) } ``` +Recommended sync path / 推荐同步起步路径: + +- start with `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)` +- 先用这些 presets 组装 `LoggerConfig` +- then apply small config helpers such as `with_queue(...)` or `with_file_rotation(...)` +- 再按需叠加 `with_queue(...)` / `with_file_rotation(...)` +- finally call `build_logger(...)` to get the runtime logger +- 最后交给 `build_logger(...)` 构建运行时 logger + +Use `Logger::new(...)` instead when you need direct sink composition such as `fanout_sink(...)`, `split_by_level(...)`, `callback_sink(...)`, or custom patch/filter graphs. + +如果你需要 `fanout_sink(...)`、`split_by_level(...)`、`callback_sink(...)` 或更自由的 patch/filter 组合,再改用 `Logger::new(...)` 直接拼装运行时 sink 图。 + Project command note / 项目命令说明: - use `moon check` / `moon test` for local project verification @@ -39,13 +57,23 @@ Project command note / 项目命令说明: - examples / 示例: - `../examples/basic/` + - `../examples/console_basic/` + - `../examples/text_formatter/` + - `../examples/style_tags/` + - `../examples/file_rotation/` + - `../examples/config_build/` + - `../examples/presets/` - `../examples/async_basic/` - package-level API docs / 单接口 API 文档: - `../docs/api/` - common entry points / 常用入口: + - `text_console(...)` + `build_logger(...)` + - `file(...)` + `with_file_rotation(...)` + `build_logger(...)` - `Logger::new(...)` - - `async_logger(...)` - `build_logger(...)` + - `build_application_logger(...)` + - `build_library_logger(...)` + - `async_logger(...)` - `build_async_logger(...)` ## Notes / 说明