From a3d0a695def90536d85ae19a72f6bc47236be88d Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Sun, 7 Jun 2026 20:34:25 +0800 Subject: [PATCH] :bookmark: prepare 0.5.2 release notes --- .github/workflows/ci.yml | 4 ++ README.md | 4 +- docs/changes/0.5.2.md | 23 +++++++++ src-async/BitLoggerAsync_test.mbt | 81 ++++++++++++++++++++++++++++++ src-async/async_logger_shared.mbt | 4 +- src-async/library_async_logger.mbt | 2 +- src/BitLogger_test.mbt | 26 ++++++++++ 7 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 docs/changes/0.5.2.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 840027b..7cfabbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/README.md b/README.md index a762626..721bd9c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,9 @@ ignore(logger.flush()) ## 支持情况 -- 当前已验证目标:`native`、`js`、`wasm`、`wasm-gc` +- `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 入口提供 diff --git a/docs/changes/0.5.2.md b/docs/changes/0.5.2.md new file mode 100644 index 0000000..abf3836 --- /dev/null +++ b/docs/changes/0.5.2.md @@ -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` diff --git a/src-async/BitLoggerAsync_test.mbt b/src-async/BitLoggerAsync_test.mbt index 7376ac0..2dde1f7 100644 --- a/src-async/BitLoggerAsync_test.mbt +++ b/src-async/BitLoggerAsync_test.mbt @@ -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() @@ -251,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([]) diff --git a/src-async/async_logger_shared.mbt b/src-async/async_logger_shared.mbt index 520c4bf..e1ff32f 100644 --- a/src-async/async_logger_shared.mbt +++ b/src-async/async_logger_shared.mbt @@ -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, diff --git a/src-async/library_async_logger.mbt b/src-async/library_async_logger.mbt index 07d0d5d..f89c68b 100644 --- a/src-async/library_async_logger.mbt +++ b/src-async/library_async_logger.mbt @@ -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( diff --git a/src/BitLogger_test.mbt b/src/BitLogger_test.mbt index 6fc4a57..461e4bd 100644 --- a/src/BitLogger_test.mbt +++ b/src/BitLogger_test.mbt @@ -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(