diff --git a/docs/api/async-logger-dropped-count.md b/docs/api/async-logger-dropped-count.md index 67b5788..79b2e7f 100644 --- a/docs/api/async-logger-dropped-count.md +++ b/docs/api/async-logger-dropped-count.md @@ -36,6 +36,7 @@ Detailed rules explaining key parameters and behaviors - The counter increases when overflow policy discards records. - The counter can also increase when `close(clear=true)` or `shutdown(clear=true)` abandons queued records. - It can also increase when shutdown fallback logic converts remaining pending records into dropped records during a clear-close path. +- Later log attempts against an already closed queue do not add to this counter by themselves. - This is a cumulative counter for the lifetime of the logger value. - Use this helper when you need a focused loss metric rather than a full `state()` snapshot. diff --git a/docs/api/async-logger-log.md b/docs/api/async-logger-log.md index 7497a07..d5df8f4 100644 --- a/docs/api/async-logger-log.md +++ b/docs/api/async-logger-log.md @@ -47,6 +47,7 @@ Detailed rules explaining key parameters and behaviors - Context fields, patch logic, and filter logic are applied before enqueue. - If timestamping is enabled, `@env.now()` is captured before the record enters the queue. - Overflow behavior depends on the configured `AsyncOverflowPolicy`. +- Closed-on-log behavior is runtime-dependent: compatibility runtimes short-circuit before enqueue work, while native-worker runtimes may still reach the queue operations and then treat a closed queue as a non-accepted write. ### How to Use @@ -82,6 +83,8 @@ e.g.: - If the logger is closed or overflow policy rejects the record, enqueue may not proceed as a normal accepted write. +- A closed queue does not count as a newly accepted pending record. + ### Notes 1. Use this API when the call site needs full control instead of a fixed severity helper. diff --git a/docs/api/async-logger-pending-count.md b/docs/api/async-logger-pending-count.md index a127f5d..eef1ac6 100644 --- a/docs/api/async-logger-pending-count.md +++ b/docs/api/async-logger-pending-count.md @@ -36,6 +36,7 @@ Detailed rules explaining key parameters and behaviors - The count increases when records are accepted into the queue. - The count decreases as the worker drains records. - The count is also reset to `0` when queued records are abandoned through clear-close paths such as `close(clear=true)`. +- Log attempts against a closed queue do not create new pending backlog. - This is a point-in-time metric and may change immediately after it is read. - Use this helper when a single backlog number is enough and a full `state()` snapshot is unnecessary. diff --git a/src-async/BitLoggerAsync_test.mbt b/src-async/BitLoggerAsync_test.mbt index 2dde1f7..3eefa52 100644 --- a/src-async/BitLoggerAsync_test.mbt +++ b/src-async/BitLoggerAsync_test.mbt @@ -91,6 +91,29 @@ async test "shutdown clear closes without worker startup" { inspect(logger.dropped_count(), content="1") } +async test "closed blocking logger does not add pending count on later log attempts" { + let logger = async_logger( + @bitlogger.callback_sink(fn(_) { + + }), + config=AsyncLoggerConfig::new( + max_pending=2, + overflow=AsyncOverflowPolicy::Blocking, + ), + min_level=@bitlogger.Level::Info, + target="async.closed.blocking", + ) + + logger.info("one") + logger.close(clear=true) + inspect(logger.pending_count(), content="0") + inspect(logger.dropped_count(), content="1") + + logger.info("late") + inspect(logger.pending_count(), content="0") + inspect(logger.dropped_count(), content="1") +} + test "async logger config stringify roundtrips stable fields" { let text = stringify_async_logger_config( AsyncLoggerConfig::new( diff --git a/src-async/async_logger_shared.mbt b/src-async/async_logger_shared.mbt index e1ff32f..d76be25 100644 --- a/src-async/async_logger_shared.mbt +++ b/src-async/async_logger_shared.mbt @@ -254,11 +254,16 @@ pub async fn[S] AsyncLogger::log( } else { match self.overflow { AsyncOverflowPolicy::Blocking => { - self.queue.put(rec) catch { - err if err is AsyncLoggerClosed => () + let accepted = (async fn() -> Bool raise { + self.queue.put(rec) + true + })() catch { + err if err is AsyncLoggerClosed => false err => raise err } - self.pending_count.val += 1 + if accepted { + self.pending_count.val += 1 + } } AsyncOverflowPolicy::DropOldest | AsyncOverflowPolicy::DropNewest => { self.dropped_count.val += 1