From f1b7d1d21cc878aaa218b175b6429547778d8a24 Mon Sep 17 00:00:00 2001 From: Nanaloveyuki Date: Tue, 12 May 2026 10:50:42 +0800 Subject: [PATCH] :sparkles: Add async logger runtime snapshots --- bitlogger_async/BitLoggerAsync_test.mbt | 37 +++++++++++++++ bitlogger_async/async_logger_native.mbt | 61 +++++++++++++++++++++++++ bitlogger_async/async_logger_stub.mbt | 61 +++++++++++++++++++++++++ 3 files changed, 159 insertions(+) diff --git a/bitlogger_async/BitLoggerAsync_test.mbt b/bitlogger_async/BitLoggerAsync_test.mbt index fd5bdc9..0449dd7 100644 --- a/bitlogger_async/BitLoggerAsync_test.mbt +++ b/bitlogger_async/BitLoggerAsync_test.mbt @@ -174,6 +174,43 @@ test "async runtime capability helpers stay consistent" { ) } +test "async logger state snapshot reflects current counters and runtime" { + let logger = async_logger( + @bitlogger.callback_sink(fn(_) { + + }), + config=AsyncLoggerConfig::new( + max_pending=3, + overflow=AsyncOverflowPolicy::DropNewest, + flush=AsyncFlushPolicy::Shutdown, + ), + min_level=@bitlogger.Level::Info, + target="async.state", + ) + let state = logger.state() + inspect(async_runtime_mode_label(state.runtime.mode) == async_runtime_mode_label(async_runtime_mode()), content="true") + inspect(state.runtime.background_worker == async_runtime_supports_background_worker(), content="true") + inspect(state.pending_count, content="0") + inspect(state.dropped_count, content="0") + inspect(state.is_closed, content="false") + inspect(state.is_running, content="false") + inspect(state.has_failed, content="false") + inspect(state.last_error, content="") + inspect(match state.flush_policy { + AsyncFlushPolicy::Never => "Never" + AsyncFlushPolicy::Batch => "Batch" + AsyncFlushPolicy::Shutdown => "Shutdown" + }, content="Shutdown") + inspect( + stringify_async_logger_state(state), + content=if async_runtime_supports_background_worker() { + "{\"runtime\":{\"mode\":\"native_worker\",\"background_worker\":true},\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"last_error\":\"\",\"flush_policy\":\"Shutdown\"}" + } else { + "{\"runtime\":{\"mode\":\"compatibility\",\"background_worker\":false},\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"last_error\":\"\",\"flush_policy\":\"Shutdown\"}" + }, + ) +} + async test "run drains queued records in compatibility backends too" { let written : Ref[Array[String]] = Ref::new([]) let logger = async_logger( diff --git a/bitlogger_async/async_logger_native.mbt b/bitlogger_async/async_logger_native.mbt index ecdd90b..d0e04d3 100644 --- a/bitlogger_async/async_logger_native.mbt +++ b/bitlogger_async/async_logger_native.mbt @@ -44,6 +44,17 @@ pub struct AsyncRuntimeState { background_worker : Bool } +pub struct AsyncLoggerState { + runtime : AsyncRuntimeState + pending_count : Int + dropped_count : Int + is_closed : Bool + is_running : Bool + has_failed : Bool + last_error : String + flush_policy : AsyncFlushPolicy +} + pub fn async_runtime_state() -> AsyncRuntimeState { { mode: async_runtime_mode(), @@ -70,6 +81,43 @@ pub fn stringify_async_runtime_state( } } +fn async_flush_policy_label(policy : AsyncFlushPolicy) -> String { + match policy { + AsyncFlushPolicy::Never => "Never" + AsyncFlushPolicy::Batch => "Batch" + AsyncFlushPolicy::Shutdown => "Shutdown" + } +} + +fn async_logger_state_to_json_value(state : AsyncLoggerState) -> @json_parser.JsonValue { + @json_parser.JsonValue::Object({ + "runtime": async_runtime_state_to_json(state.runtime), + "pending_count": @json_parser.JsonValue::Number(state.pending_count.to_double()), + "dropped_count": @json_parser.JsonValue::Number(state.dropped_count.to_double()), + "is_closed": @json_parser.JsonValue::Bool(state.is_closed), + "is_running": @json_parser.JsonValue::Bool(state.is_running), + "has_failed": @json_parser.JsonValue::Bool(state.has_failed), + "last_error": @json_parser.JsonValue::String(state.last_error), + "flush_policy": @json_parser.JsonValue::String(async_flush_policy_label(state.flush_policy)), + }) +} + +pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.JsonValue { + async_logger_state_to_json_value(state) +} + +pub fn stringify_async_logger_state( + state : AsyncLoggerState, + pretty~ : Bool = false, +) -> String { + let value = async_logger_state_to_json_value(state) + if pretty { + @json_parser.stringify_pretty(value, 2) + } else { + @json_parser.stringify(value) + } +} + pub struct AsyncLoggerConfig { max_pending : Int overflow : AsyncOverflowPolicy @@ -494,6 +542,19 @@ pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy { self.flush_policy } +pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState { + { + runtime: async_runtime_state(), + pending_count: self.pending_count(), + dropped_count: self.dropped_count(), + is_closed: self.is_closed(), + is_running: self.is_running(), + has_failed: self.has_failed(), + last_error: self.last_error(), + flush_policy: self.flush_policy(), + } +} + pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Unit { self.is_closed.val = true if clear { diff --git a/bitlogger_async/async_logger_stub.mbt b/bitlogger_async/async_logger_stub.mbt index d4d115d..6fd36b7 100644 --- a/bitlogger_async/async_logger_stub.mbt +++ b/bitlogger_async/async_logger_stub.mbt @@ -44,6 +44,17 @@ pub struct AsyncRuntimeState { background_worker : Bool } +pub struct AsyncLoggerState { + runtime : AsyncRuntimeState + pending_count : Int + dropped_count : Int + is_closed : Bool + is_running : Bool + has_failed : Bool + last_error : String + flush_policy : AsyncFlushPolicy +} + pub fn async_runtime_state() -> AsyncRuntimeState { { mode: async_runtime_mode(), @@ -70,6 +81,43 @@ pub fn stringify_async_runtime_state( } } +fn async_flush_policy_label(policy : AsyncFlushPolicy) -> String { + match policy { + AsyncFlushPolicy::Never => "Never" + AsyncFlushPolicy::Batch => "Batch" + AsyncFlushPolicy::Shutdown => "Shutdown" + } +} + +fn async_logger_state_to_json_value(state : AsyncLoggerState) -> @json_parser.JsonValue { + @json_parser.JsonValue::Object({ + "runtime": async_runtime_state_to_json(state.runtime), + "pending_count": @json_parser.JsonValue::Number(state.pending_count.to_double()), + "dropped_count": @json_parser.JsonValue::Number(state.dropped_count.to_double()), + "is_closed": @json_parser.JsonValue::Bool(state.is_closed), + "is_running": @json_parser.JsonValue::Bool(state.is_running), + "has_failed": @json_parser.JsonValue::Bool(state.has_failed), + "last_error": @json_parser.JsonValue::String(state.last_error), + "flush_policy": @json_parser.JsonValue::String(async_flush_policy_label(state.flush_policy)), + }) +} + +pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.JsonValue { + async_logger_state_to_json_value(state) +} + +pub fn stringify_async_logger_state( + state : AsyncLoggerState, + pretty~ : Bool = false, +) -> String { + let value = async_logger_state_to_json_value(state) + if pretty { + @json_parser.stringify_pretty(value, 2) + } else { + @json_parser.stringify(value) + } +} + pub struct AsyncLoggerConfig { max_pending : Int overflow : AsyncOverflowPolicy @@ -497,6 +545,19 @@ pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy { self.flush_policy } +pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState { + { + runtime: async_runtime_state(), + pending_count: self.pending_count(), + dropped_count: self.dropped_count(), + is_closed: self.is_closed(), + is_running: self.is_running(), + has_failed: self.has_failed(), + last_error: self.last_error(), + flush_policy: self.flush_policy(), + } +} + pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Unit { self.is_closed.val = true if clear {