mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbd63b5e4a | |||
| a3d0a695de | |||
| c0fe7999c4 | |||
| 5c8067d009 | |||
| 6c19708d7b | |||
| 3eee5893f5 | |||
| 3e8d4c50a9 |
@@ -59,6 +59,10 @@ jobs:
|
||||
run: |
|
||||
moon check src-async --target js
|
||||
|
||||
- name: Test bitlogger_async native
|
||||
run: |
|
||||
moon test src-async --target native
|
||||
|
||||
- name: Test bitlogger_async wasm-gc
|
||||
run: |
|
||||
moon test src-async --target wasm-gc
|
||||
|
||||
@@ -1,49 +1,22 @@
|
||||
<html>
|
||||
<div style="display: flex; justify-content: center; align-items: center; height: 15vh;">
|
||||
<h3 title="https://moonbitlang.github.io/OSC2026/index.html#top">2026 MoonBit 国产基础软件生态开源大赛参赛作品</h3>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: center; align-items: center; height: 8vh;">
|
||||
<a href="https://mooncakes.io/docs/Nanaloveyuki/BitLogger" title="点击前往 Mooncake 页面"><b>Mooncake@Nanaloveyuki/BitLogger</b></a>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: center; align-items: center; height: 2vh;">
|
||||
<b>中文 | <a href="./docs/README-en.md">English</a></b>
|
||||
</div>
|
||||
</html>
|
||||
# BitLogger
|
||||
|
||||
## 📖 介绍
|
||||
BitLogger 是一个使用 MoonBit 编写的结构化日志库,适合命令行工具、服务和需要统一日志输出的项目。
|
||||
|
||||
BitLogger 是一个使用 MoonBit 编写的结构化日志库,目标是提供可组合、可配置、可跨端编译的日志基础设施。
|
||||
- [Mooncake 文档页](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [English README](./docs/README-en.md)
|
||||
|
||||
## 🧭 后端兼容
|
||||
## 介绍
|
||||
|
||||
| 模块 / 能力 | native / llvm | js / wasm / wasm-gc |
|
||||
| --- | --- | --- |
|
||||
| `src` 主包 | 支持 | 支持 |
|
||||
| `file_sink(...)` | 支持 | 不支持, `native_files_supported()` 返回 `false` |
|
||||
| `src-async` | 支持原生 worker 语义 | 支持兼容实现 |
|
||||
| `examples/async_basic` | 支持 | 受 `async fn main` 入口限制, 当前不提供 |
|
||||
BitLogger 提供统一的日志级别、目标名、结构化字段和可定制格式,既可以直接输出到控制台,也可以在 native 环境写入文件,并提供异步日志版本。
|
||||
|
||||
## ❇️ 关键特性
|
||||
|
||||
- 结构化日志基础能力:level、record、formatter、sink、target、context field。
|
||||
- 组合式设计:支持 filter、patch、fanout、split、callback、queued sink 等组合能力。
|
||||
- 配置驱动:支持 JSON 配置解析、导出与 `build_logger(...)` / `build_async_logger(...)` 运行时组装。
|
||||
- 文本格式化:支持 `text_formatter(...)`、template、style tag、`color_mode` / `style_markup`。
|
||||
- Native 文件输出:支持 file sink、基础 size rotation、reopen、failure counter 与运行时状态读取。
|
||||
- 异步层:提供独立 `src-async` package,支持 queue、worker lifecycle、runtime state 和跨端兼容实现。
|
||||
|
||||
## 🚀 快速开始
|
||||
## 快速开始
|
||||
|
||||
```moonbit
|
||||
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,
|
||||
text_console(
|
||||
min_level=Level::Info,
|
||||
target="demo",
|
||||
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -51,40 +24,41 @@ 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。
|
||||
推荐从 `console(...)`、`json_console(...)`、`text_console(...)`、`file(...)` 这几个入口开始,再按需配合 `with_queue(...)` 或 `with_file_rotation(...)`。
|
||||
|
||||
当你需要 `fanout`、`split`、`callback`、`patch` 这类自定义 sink 图组合时,再优先使用 `Logger::new(...)` 直接进行运行时拼装。
|
||||
如果你需要自己组合 sink,比如 `fanout`、`split`、`callback`,再使用 `Logger::new(...)`。
|
||||
|
||||
异步入口示例:
|
||||
## 支持情况
|
||||
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink(), target="async.demo")
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(() => logger.run())
|
||||
logger.info("started")
|
||||
logger.shutdown()
|
||||
})
|
||||
```
|
||||
- `BitLogger` 当前在 CI 中检查/验证的目标是 `native`、`js`、`wasm-gc`
|
||||
- `bitlogger_async` 当前在 CI 中检查 `native`、`js`、`wasm-gc`,测试覆盖 `native`、`js`、`wasm-gc`
|
||||
- `wasm` 目标在源码 `moon.pkg` 中保留声明,但当前未纳入 CI 验证口径
|
||||
- `llvm` 目前按实验性目标处理,当前环境未完成验证
|
||||
- 文件输出是 native 能力;跨端代码里建议先判断 `native_files_supported()`
|
||||
- `src-async` 可用,但示例 `examples/async_basic` 目前仍按 native 入口提供
|
||||
|
||||
## 📂 仓库结构
|
||||
## 主要能力
|
||||
|
||||
- `src/`: 主日志库 package。
|
||||
- `src-async/`: 基于 `moonbitlang/async` 的异步日志层。
|
||||
- `docs/api/`: 单接口粒度 API 文档。
|
||||
- `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 示例。
|
||||
- 结构化日志:level、target、message、fields
|
||||
- 多种输出方式:console、json console、text console、file
|
||||
- 可定制文本格式:模板、style tag、颜色控制
|
||||
- 配置驱动构建:`build_logger(...)`、`build_async_logger(...)`
|
||||
- 组合能力: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/`:异步日志示例
|
||||
|
||||
## 文档
|
||||
|
||||
- [Mooncake 文档页](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [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(...)`
|
||||
- [src package README](./src/README.mbt.md)
|
||||
|
||||
常用入口:`text_console(...)`、`file(...)`、`with_queue(...)`、`build_logger(...)`、`build_async_logger(...)`
|
||||
|
||||
+35
-69
@@ -1,43 +1,22 @@
|
||||
# BitLogger
|
||||
|
||||
BitLogger is a structured logging library written in MoonBit.
|
||||
BitLogger is a structured logging library for MoonBit projects.
|
||||
|
||||
This README focuses on project positioning, core capabilities, and entry points. Detailed API coverage now lives in `docs/api/`.
|
||||
- [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [Chinese README](../README.md)
|
||||
|
||||
## Overview
|
||||
|
||||
BitLogger is designed to provide composable, configurable, and cross-target logging infrastructure for MoonBit projects.
|
||||
|
||||
## Backend Compatibility
|
||||
|
||||
| Module / capability | native / llvm | js / wasm / wasm-gc |
|
||||
| --- | --- | --- |
|
||||
| `src` core package | Supported | Supported |
|
||||
| `file_sink(...)` | Supported | Not available, `native_files_supported()` returns `false` |
|
||||
| `src-async` | Native worker semantics | Compatibility implementation |
|
||||
| `examples/async_basic` | Supported | Not shipped currently because `async fn main` entry support is still limited |
|
||||
|
||||
## Key Features
|
||||
|
||||
- Structured logging with levels, targets, and key-value fields.
|
||||
- Composable sinks, filters, patches, fanout, routing, and queue wrappers.
|
||||
- Config-driven runtime assembly through JSON parse/export helpers and `build_logger(...)` / `build_async_logger(...)`.
|
||||
- Configurable text formatting with templates, style tags, and color control.
|
||||
- Native file sink support with basic rotation, reopen helpers, and runtime observability.
|
||||
- Separate async layer with bounded queueing, worker lifecycle control, runtime state, and cross-target compatibility.
|
||||
BitLogger gives you consistent levels, targets, structured fields, configurable text output, native file logging, and an async logging layer.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```moonbit
|
||||
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,
|
||||
text_console(
|
||||
min_level=Level::Info,
|
||||
target="demo",
|
||||
text_formatter=TextFormatterConfig::new(show_timestamp=false, separator=" | "),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -45,52 +24,39 @@ 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(...)`.
|
||||
Start with `console(...)`, `json_console(...)`, `text_console(...)`, or `file(...)`, then add `with_queue(...)` or `with_file_rotation(...)` only when needed.
|
||||
|
||||
Switch to `Logger::new(...)` when you need direct runtime sink composition such as `fanout`, `split`, `callback`, or custom patch/filter graphs.
|
||||
Use `Logger::new(...)` when you want to assemble custom sink graphs directly.
|
||||
|
||||
Async entry example:
|
||||
## Support Status
|
||||
|
||||
```moonbit
|
||||
let logger = async_logger(console_sink(), target="async.demo")
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(() => logger.run())
|
||||
logger.info("started")
|
||||
logger.shutdown()
|
||||
})
|
||||
```
|
||||
- Currently verified targets: `native`, `js`, `wasm`, `wasm-gc`
|
||||
- `llvm` is still treated as experimental in the current release context
|
||||
- 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
|
||||
|
||||
## Repository Layout
|
||||
## Main Features
|
||||
|
||||
- `src/`: core logging package.
|
||||
- `src-async/`: async logging layer built on `moonbitlang/async`.
|
||||
- `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/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.
|
||||
- Structured logs with levels, targets, messages, and fields
|
||||
- Multiple outputs: console, JSON console, text console, and file
|
||||
- Custom text formatting with templates, style tags, and color control
|
||||
- Config-based builders: `build_logger(...)` and `build_async_logger(...)`
|
||||
- Composition helpers: queue, filter, patch, fanout, split, callback
|
||||
- Separate async package under `src-async`
|
||||
|
||||
## Documentation Entry Points
|
||||
## Examples
|
||||
|
||||
- [Mooncake package page](https://mooncakes.io/docs/Nanaloveyuki/BitLogger)
|
||||
- [Chinese README](../README.md)
|
||||
- `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)
|
||||
- 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
|
||||
|
||||
- `docs/README-en.md` no longer acts as an API catalog.
|
||||
- Detailed API references, config fields, runtime control helpers, and lifecycle surfaces now live under `docs/api/`.
|
||||
- For concrete runnable flows, prefer `examples/`.
|
||||
Common entry points: `text_console(...)`, `file(...)`, `with_queue(...)`, `build_logger(...)`, `build_async_logger(...)`
|
||||
|
||||
@@ -45,6 +45,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `async_logger(...)` only builds the logger. Actual background draining is started by `run()`.
|
||||
- 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.
|
||||
- Queue overflow behavior depends on `AsyncOverflowPolicy`.
|
||||
|
||||
@@ -98,3 +100,6 @@ e.g.:
|
||||
|
||||
2. Use `state()`, `pending_count()`, and `dropped_count()` for runtime diagnostics.
|
||||
|
||||
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.
|
||||
|
||||
@@ -34,9 +34,11 @@ pub fn async_runtime_mode() -> AsyncRuntimeMode {}
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The return value is determined by the active backend implementation.
|
||||
- `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.
|
||||
- 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -76,3 +78,6 @@ 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.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification status of individual targets.
|
||||
|
||||
@@ -36,6 +36,7 @@ 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 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.
|
||||
|
||||
### How to Use
|
||||
@@ -75,3 +76,6 @@ e.g.:
|
||||
|
||||
2. Prefer `async_runtime_state()` when you want the same information in a richer object.
|
||||
|
||||
3. A `false` result should be read as "compatibility-mode runtime behavior" rather than "async library unsupported on this target".
|
||||
|
||||
4. This helper does not by itself imply that every non-worker backend has been equally re-verified for the current release; see [target-verification.md](./target-verification.md).
|
||||
|
||||
@@ -37,6 +37,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- 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.
|
||||
- The returned sink type is `RuntimeSink`, which keeps configured control helpers available where relevant.
|
||||
- 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.
|
||||
- On non-native targets, the async library still compiles and exposes the same public surface through compatibility-mode behavior rather than native background-worker semantics.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -79,3 +83,6 @@ e.g.:
|
||||
|
||||
2. Use `async_logger(...)` directly when you want explicit code-defined sink wiring.
|
||||
|
||||
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.
|
||||
|
||||
@@ -36,6 +36,7 @@ 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(...)`.
|
||||
- The returned logger inherits `min_level`, `target`, and timestamp behavior from `config.logger`.
|
||||
- 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.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -67,3 +68,5 @@ e.g.:
|
||||
1. This API is narrower than `build_async_logger(...)` because it preserves a concrete text sink type.
|
||||
|
||||
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.
|
||||
|
||||
@@ -43,7 +43,9 @@ pub fn file_sink(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This sink is only available on `native/llvm`; use `native_files_supported()` for capability detection.
|
||||
- This sink is designed for host-file capable backends, with current local verification centered on `native`.
|
||||
- `llvm` should be treated as experimental in the current release context and was not successfully re-verified in this environment.
|
||||
- The `src` library is still designed for multi-target compilation, but file sink availability remains backend-sensitive inside that broader portable surface.
|
||||
- `append` is persistent policy state and also affects later reopen behavior.
|
||||
- `auto_flush` trades durability for more flush work per record.
|
||||
- Rotation is currently size-based and rename-retention-oriented, not time-based.
|
||||
@@ -91,3 +93,6 @@ e.g.:
|
||||
|
||||
2. Prefer `state()`, `policy()`, and failure counters when integrating diagnostics.
|
||||
|
||||
3. Non-native targets can still compile code referencing this API, but callers should treat actual file availability and successful writes as target-sensitive runtime behavior.
|
||||
|
||||
4. See [target-verification.md](./target-verification.md) for the current verification boundary between design intent and locally re-checked targets.
|
||||
|
||||
@@ -15,6 +15,10 @@ key-word:
|
||||
|
||||
BitLogger API navigation.
|
||||
|
||||
## Target and verification
|
||||
|
||||
- [target-verification.md](./target-verification.md)
|
||||
|
||||
## Core logger
|
||||
|
||||
- [logger-new.md](./logger-new.md)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: target-verification
|
||||
group: api
|
||||
category: verification
|
||||
update-time: 20260521
|
||||
description: Current release-facing local verification boundary for BitLogger multi-target support claims.
|
||||
key-word:
|
||||
- target
|
||||
- verification
|
||||
- matrix
|
||||
- public
|
||||
---
|
||||
|
||||
## Target-verification
|
||||
|
||||
This note records the current release-facing local verification boundary for BitLogger's multi-target claims. It is intentionally small: the goal is to separate design intent from what was actually re-checked in the current environment.
|
||||
|
||||
### Verification Matrix
|
||||
|
||||
| Area | native | js | wasm | wasm-gc | llvm |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `src` compile check | verified | verified | verified | verified | not locally verified |
|
||||
| `src-async` compile check | verified | verified | verified | verified | not locally verified |
|
||||
| `moon test` | verified in current local environment | not separately re-run | not separately re-run | not separately re-run | not run |
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `verified` here means a local verification command was re-run successfully in the current environment.
|
||||
- `not locally verified` means the release should not imply fresh local confirmation even if the package is still designed for that target.
|
||||
- `llvm` is currently experimental in practice for this release context and did not complete local verification in this environment.
|
||||
- `wasm` has been re-checked and should be distinguished from the earlier state where only `wasm-gc` and `js` had been explicitly re-confirmed.
|
||||
- Example-level limitations, such as `async fn main` entry support, are separate from the library-level compile matrix above.
|
||||
|
||||
### Commands Used
|
||||
|
||||
The current local verification evidence for this note is based on commands such as:
|
||||
|
||||
```text
|
||||
moon test
|
||||
moon check --target native
|
||||
moon check --target js
|
||||
moon check --target wasm
|
||||
moon check --target wasm-gc
|
||||
moon check src-async --target native
|
||||
moon check src-async --target js
|
||||
moon check src-async --target wasm
|
||||
moon check src-async --target wasm-gc
|
||||
```
|
||||
|
||||
### Error Case
|
||||
|
||||
e.g.:
|
||||
- If a target is described as part of the design intent but not locally re-verified here, readers should not treat it as freshly confirmed release evidence.
|
||||
|
||||
- If `llvm` toolchain support changes later, this page should be updated together with the relevant verification commands rather than silently relying on old wording.
|
||||
|
||||
### Notes
|
||||
|
||||
1. This page is a release-facing verification note, not a portability promise by itself.
|
||||
|
||||
2. API pages that mention target-sensitive behavior should defer to this page when readers need the current verification boundary.
|
||||
@@ -0,0 +1,22 @@
|
||||
## BitLogger Update Changes
|
||||
|
||||
version 0.5.1
|
||||
|
||||
### Docs
|
||||
|
||||
- docs: simplify the Chinese and English README entry pages so they focus on what BitLogger is, how to start, and where to find examples and API docs
|
||||
- docs: remove misplaced banner-style content and reduce overly dense engineering wording in release-facing README sections
|
||||
- docs: clarify API-side target verification wording and add a small verification note for current release claims
|
||||
|
||||
### Example
|
||||
|
||||
- docs: align portable examples with the recommended `build_logger(...)` entry path and keep native-only example boundaries explicit
|
||||
|
||||
### Test
|
||||
|
||||
- test: strengthen cross-target capability checks for configured file logging, fallback file behavior, and async runtime mode reporting
|
||||
|
||||
### Notes
|
||||
|
||||
- this is a small follow-up release focused on release-facing clarity rather than new core functionality
|
||||
- verified release-facing targets remain `native`, `js`, `wasm`, and `wasm-gc`, while `llvm` is still treated as experimental in the current environment
|
||||
@@ -0,0 +1,23 @@
|
||||
## BitLogger Update Changes
|
||||
|
||||
version 0.5.2
|
||||
|
||||
### Docs
|
||||
|
||||
- docs: align the README support matrix with the actual CI validation scope for both `BitLogger` and `bitlogger_async`
|
||||
|
||||
### Test
|
||||
|
||||
- test: add sync config parser failure-path coverage for malformed JSON, wrong field types, and invalid enum values
|
||||
- test: add async config parser failure-path coverage for malformed input, wrong async field types, invalid build roots, and nested sync config parse failures
|
||||
- test: cover async worker failure reporting and `wait_idle()` early-stop behavior when batch flush fails
|
||||
- test: extend CI so `bitlogger_async` native tests run alongside the existing `js` and `wasm-gc` coverage
|
||||
|
||||
### Runtime
|
||||
|
||||
- runtime: allow async flush callbacks to raise so async worker failure state and `last_error()` reporting are reachable through the public API
|
||||
|
||||
### Notes
|
||||
|
||||
- this release is a focused validation follow-up that closes the gap between published verification claims and the real test matrix
|
||||
- release verification now covers sync checks/tests plus async checks/tests across `native`, `js`, and `wasm-gc`
|
||||
@@ -1,4 +1,5 @@
|
||||
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))
|
||||
|
||||
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 {
|
||||
let logger = @lib.Logger::new(@lib.console_sink(), min_level=@lib.Level::Info, target="demo.console")
|
||||
.with_context_fields([@lib.field("service", "bitlogger")])
|
||||
let logger = @lib.build_logger(
|
||||
@lib.console(min_level=@lib.Level::Info, target="demo.console"),
|
||||
)
|
||||
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")])
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
fn main {
|
||||
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
|
||||
}
|
||||
|
||||
let logger = @lib.Logger::new(
|
||||
@lib.file_sink(
|
||||
"bitlogger-example.log",
|
||||
auto_flush=true,
|
||||
rotation=Some(@lib.file_rotation(128, max_backups=2)),
|
||||
let logger = @lib.build_logger(
|
||||
@lib.with_file_rotation(
|
||||
@lib.file(
|
||||
"bitlogger-example.log",
|
||||
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")])
|
||||
ignore(logger.sink.flush())
|
||||
ignore(logger.sink.close())
|
||||
ignore(logger.flush())
|
||||
ignore(logger.file_close())
|
||||
}
|
||||
|
||||
+33
-15
@@ -1,27 +1,45 @@
|
||||
fn main {
|
||||
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(
|
||||
"preset-example.log",
|
||||
min_level=@lib.Level::Info,
|
||||
target="demo.preset",
|
||||
target="demo.preset.file",
|
||||
auto_flush=true,
|
||||
),
|
||||
256,
|
||||
max_backups=2,
|
||||
),
|
||||
max_pending=4,
|
||||
overflow=@lib.QueueOverflowPolicy::DropOldest,
|
||||
) catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
println("invalid preset config")
|
||||
return
|
||||
) catch {
|
||||
err => {
|
||||
ignore(err)
|
||||
println("invalid file 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())
|
||||
let file_logger = @lib.build_logger(file_config)
|
||||
file_logger.info("file preset ready", fields=[@lib.field("kind", "native-only")])
|
||||
ignore(file_logger.flush())
|
||||
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 {
|
||||
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",
|
||||
let logger = @lib.build_logger(
|
||||
@lib.text_console(
|
||||
min_level=@lib.Level::Info,
|
||||
target="demo.style",
|
||||
text_formatter=@lib.TextFormatterConfig::new(
|
||||
show_timestamp=false,
|
||||
color_mode=@lib.ColorMode::Always,
|
||||
style_tags={
|
||||
"accent": @lib.text_style(fg=Some("#4cc9f0"), bold=true),
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
logger.info("<accent>styled</> output with <danger>alert</>")
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
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",
|
||||
let logger = @lib.build_logger(
|
||||
@lib.text_console(
|
||||
min_level=@lib.Level::Info,
|
||||
target="demo.formatter",
|
||||
text_formatter=@lib.TextFormatterConfig::new(
|
||||
show_timestamp=false,
|
||||
separator=" | ",
|
||||
field_separator=",",
|
||||
template="[{level}] {target} {message} :: {fields}",
|
||||
),
|
||||
),
|
||||
)
|
||||
logger.info("formatted output", fields=[@lib.field("user", "alice"), @lib.field("request_id", "42")])
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Nanaloveyuki/BitLogger",
|
||||
"version": "0.5.0",
|
||||
"version": "0.5.2",
|
||||
"deps": {
|
||||
"maria/json_parser": "0.1.1",
|
||||
"moonbitlang/async": "0.19.0"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
suberror TestFlushError {
|
||||
TestFlushError(String)
|
||||
}
|
||||
|
||||
async test "shutdown drains pending records" {
|
||||
inspect(async_runtime_mode_label(async_runtime_mode()) == "native_worker" || async_runtime_mode_label(async_runtime_mode()) == "compatibility", content="true")
|
||||
let written : Ref[Array[String]] = Ref([])
|
||||
@@ -150,6 +154,48 @@ test "async build config stringify roundtrips nested logger and async fields" {
|
||||
}, content="Shutdown")
|
||||
}
|
||||
|
||||
test "async config parsers reject malformed input" {
|
||||
let invalid_json_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_config_text("{"))
|
||||
"no error"
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(invalid_json_error.contains("UnexpectedToken"), content="true")
|
||||
|
||||
let wrong_type_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_config_text("{\"max_pending\":\"many\"}"))
|
||||
"no error"
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(wrong_type_error.contains("Expected number at async_config.max_pending"), content="true")
|
||||
|
||||
let invalid_enum_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_config_text("{\"overflow\":\"Burst\"}"))
|
||||
"no error"
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(invalid_enum_error.contains("Unsupported async overflow policy: Burst"), content="true")
|
||||
|
||||
let invalid_build_root_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_build_config_text("[]"))
|
||||
"no error"
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(invalid_build_root_error.contains("Expected object at async logger build config root"), content="true")
|
||||
|
||||
let nested_sync_error = (fn() -> String raise {
|
||||
ignore(parse_async_logger_build_config_text("{\"logger\":{\"timestamp\":\"true\"}}"))
|
||||
"no error"
|
||||
})() catch {
|
||||
err => err.to_string()
|
||||
}
|
||||
inspect(nested_sync_error.contains("ConfigError.InvalidConfig"), content="true")
|
||||
}
|
||||
|
||||
test "async runtime capability helpers stay consistent" {
|
||||
let mode = async_runtime_mode()
|
||||
let state = async_runtime_state()
|
||||
@@ -174,6 +220,14 @@ test "async runtime capability helpers stay consistent" {
|
||||
)
|
||||
}
|
||||
|
||||
test "async runtime mode and worker flag encode target contract" {
|
||||
let mode_label = async_runtime_mode_label(async_runtime_mode())
|
||||
let worker = async_runtime_supports_background_worker()
|
||||
inspect(mode_label == "native_worker" || mode_label == "compatibility", content="true")
|
||||
inspect((mode_label == "native_worker") == worker, content="true")
|
||||
inspect((mode_label == "compatibility") == !worker, content="true")
|
||||
}
|
||||
|
||||
test "async logger state snapshot reflects current counters and runtime" {
|
||||
let logger = async_logger(
|
||||
@bitlogger.callback_sink(fn(_) {
|
||||
@@ -243,6 +297,41 @@ async test "run drains queued records in compatibility backends too" {
|
||||
inspect(written.val[1], content="two")
|
||||
}
|
||||
|
||||
async test "async logger records worker failures and wait_idle stops early" {
|
||||
let writes : Ref[Int] = Ref(0)
|
||||
let logger = async_logger(
|
||||
@bitlogger.callback_sink(fn(_) {
|
||||
writes.val += 1
|
||||
}),
|
||||
config=AsyncLoggerConfig::new(
|
||||
max_pending=4,
|
||||
overflow=AsyncOverflowPolicy::Blocking,
|
||||
flush=AsyncFlushPolicy::Batch,
|
||||
),
|
||||
min_level=@bitlogger.Level::Info,
|
||||
target="async.failure",
|
||||
flush=fn(_) -> Int raise {
|
||||
raise TestFlushError("flush exploded")
|
||||
},
|
||||
)
|
||||
|
||||
@async.with_task_group(group => {
|
||||
group.spawn_bg(allow_failure=true, () => logger.run())
|
||||
logger.info("ok")
|
||||
logger.info("flush-now")
|
||||
logger.wait_idle()
|
||||
inspect(logger.has_failed(), content="true")
|
||||
inspect(logger.pending_count(), content="1")
|
||||
logger.close(clear=true)
|
||||
})
|
||||
|
||||
inspect(writes.val, content="1")
|
||||
inspect(logger.has_failed(), content="true")
|
||||
inspect(logger.last_error().contains("TestFlushError"), content="true")
|
||||
inspect(logger.is_running(), content="false")
|
||||
inspect(logger.pending_count(), content="0")
|
||||
}
|
||||
|
||||
async test "library async logger keeps a smaller async facade" {
|
||||
let written_targets : Ref[Array[String]] = Ref([])
|
||||
let written_messages : Ref[Array[String]] = Ref([])
|
||||
|
||||
@@ -88,7 +88,7 @@ pub struct AsyncLogger[S] {
|
||||
linger_ms : Int
|
||||
flush_policy : AsyncFlushPolicy
|
||||
sink : S
|
||||
flush_sink : (S) -> Int
|
||||
flush_sink : (S) -> Int raise
|
||||
context_fields : Array[@bitlogger.Field]
|
||||
filter : (@bitlogger.Record) -> Bool
|
||||
patch : @bitlogger.RecordPatch
|
||||
@@ -106,7 +106,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) -> Int raise = fn(_) { 0 },
|
||||
) -> AsyncLogger[S] {
|
||||
{
|
||||
min_level,
|
||||
|
||||
@@ -15,7 +15,7 @@ pub fn[S] LibraryAsyncLogger::new(
|
||||
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
|
||||
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
|
||||
target~ : String = "",
|
||||
flush~ : (S) -> Int = fn(_) { 0 },
|
||||
flush~ : (S) -> Int raise = fn(_) { 0 },
|
||||
) -> LibraryAsyncLogger[S] {
|
||||
library_async_logger(
|
||||
async_logger(
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
|
||||
options(
|
||||
targets: {
|
||||
"async_logger_shared.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
|
||||
"application_async_logger.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
|
||||
"library_async_logger.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
|
||||
"async_logger_native.mbt": [ "native", "llvm" ],
|
||||
"async_logger_stub.mbt": [ "js", "wasm", "wasm-gc" ],
|
||||
},
|
||||
|
||||
@@ -116,6 +116,32 @@ test "logger config parser reads file rotation options" {
|
||||
}
|
||||
}
|
||||
|
||||
test "logger config parser rejects malformed sync config input" {
|
||||
let invalid_json_error = (fn() -> String raise ConfigError {
|
||||
ignore(parse_logger_config_text("{"))
|
||||
"no error"
|
||||
})() catch {
|
||||
ConfigError::InvalidConfig(message) => message
|
||||
}
|
||||
inspect(invalid_json_error.contains("Invalid JSON:"), content="true")
|
||||
|
||||
let wrong_type_error = (fn() -> String raise ConfigError {
|
||||
ignore(parse_logger_config_text("{\"timestamp\":\"true\"}"))
|
||||
"no error"
|
||||
})() catch {
|
||||
ConfigError::InvalidConfig(message) => message
|
||||
}
|
||||
inspect(wrong_type_error, content="Expected bool at key timestamp")
|
||||
|
||||
let invalid_enum_error = (fn() -> String raise ConfigError {
|
||||
ignore(parse_logger_config_text("{\"sink\":{\"kind\":\"console\",\"text_formatter\":{\"color_mode\":\"loud\"}}}"))
|
||||
"no error"
|
||||
})() catch {
|
||||
ConfigError::InvalidConfig(message) => message
|
||||
}
|
||||
inspect(invalid_enum_error, content="Unsupported color mode: loud")
|
||||
}
|
||||
|
||||
test "logger config stringify roundtrips stable fields" {
|
||||
let text = stringify_logger_config(
|
||||
LoggerConfig::new(
|
||||
@@ -560,6 +586,23 @@ test "configured non-file logger has no file runtime state" {
|
||||
inspect(logger.file_runtime_state() is None, content="true")
|
||||
}
|
||||
|
||||
test "configured file logger mirrors backend file capability" {
|
||||
let logger = build_logger(
|
||||
LoggerConfig::new(
|
||||
sink=SinkConfig::new(kind=SinkKind::File, path="config-capability.log"),
|
||||
),
|
||||
)
|
||||
inspect(logger.file_available() == native_files_supported(), content="true")
|
||||
if logger.file_available() {
|
||||
inspect(logger.file_state().available, content="true")
|
||||
ignore(logger.close())
|
||||
} else {
|
||||
inspect(logger.file_state().available, content="false")
|
||||
inspect(logger.file_flush(), content="false")
|
||||
inspect(logger.file_close(), content="false")
|
||||
}
|
||||
}
|
||||
|
||||
test "configured logger file setters update file sink policy state" {
|
||||
let logger = build_logger(
|
||||
LoggerConfig::new(
|
||||
|
||||
@@ -406,6 +406,31 @@ test "file sink availability reflects backend support" {
|
||||
}
|
||||
}
|
||||
|
||||
test "file sink unavailable backend keeps stable fallback state" {
|
||||
let sink = file_sink("bitlogger-unavailable.log", rotation=Some(file_rotation(64, max_backups=2)))
|
||||
if sink.is_available() {
|
||||
inspect(sink.state().available, content="true")
|
||||
ignore(sink.close())
|
||||
} else {
|
||||
let state = sink.state()
|
||||
inspect(native_files_supported(), content="false")
|
||||
inspect(state.available, content="false")
|
||||
inspect(sink.flush(), content="false")
|
||||
inspect(sink.close(), content="false")
|
||||
inspect(sink.write_failures(), content="0")
|
||||
sink.write(record(Level::Info, "dropped"))
|
||||
inspect(sink.write_failures(), content="1")
|
||||
inspect(sink.rotation_failures(), content="0")
|
||||
match state.rotation {
|
||||
Some(rotation) => {
|
||||
inspect(rotation.max_bytes, content="64")
|
||||
inspect(rotation.max_backups, content="2")
|
||||
}
|
||||
None => inspect(false, content="true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "file sink rotation config normalizes invalid inputs" {
|
||||
let rotation = file_rotation(0, max_backups=0)
|
||||
inspect(rotation.max_bytes, content="1")
|
||||
|
||||
@@ -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 图。
|
||||
|
||||
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 / 项目命令说明:
|
||||
|
||||
- use `moon check` / `moon test` for local project verification
|
||||
@@ -64,6 +73,10 @@ Project command note / 项目命令说明:
|
||||
- `../examples/config_build/`
|
||||
- `../examples/presets/`
|
||||
- `../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 文档:
|
||||
- `../docs/api/`
|
||||
- common entry points / 常用入口:
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
|
||||
options(
|
||||
targets: {
|
||||
"sinks_file.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
|
||||
"runtime_file_controls.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
|
||||
"file_backend_native.mbt": [ "native", "llvm" ],
|
||||
"file_backend_stub.mbt": [ "js", "wasm", "wasm-gc" ],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user