mirror of
https://github.com/Nanaloveyuki/BitLogger.git
synced 2026-07-24 00:42:18 +00:00
♻️ sync owner migration and facade thinning
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
name: async-logger-close
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Close the async logger queue immediately and optionally convert current pending backlog into dropped records before queue closure.
|
||||
key-word:
|
||||
- async
|
||||
@@ -35,13 +35,14 @@ pub fn[S] AsyncLogger::close(self : AsyncLogger[S], clear? : Bool = false) -> Un
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `close(...)` marks the logger as closed immediately.
|
||||
- If a worker is still active when `close(...)` runs, the lifecycle phase moves into the closing path first and only settles into a terminal closed phase after worker exit.
|
||||
- `clear=false` closes the queue without explicitly abandoning pending records in the helper itself.
|
||||
- `clear=true` counts pending records as dropped and resets `pending_count` to `0` before closing the queue.
|
||||
- This helper does not itself wait for the worker to finish.
|
||||
- `close(...)` also does not change `has_failed()` or `last_error()`; it is a closure primitive, not a failure reset API.
|
||||
- Because this is a low-level close primitive, it does not first run `wait_idle()` or apply the runtime-dependent fallback logic used by `shutdown()`.
|
||||
- After closure, later log attempts do not add new pending or dropped counts, but backend behavior can still differ before the closed queue rejects the record.
|
||||
- In the current direct coverage, compatibility runtimes short-circuit before patch-path work on late log attempts, while native-worker runtimes may still build and patch the record before the closed queue rejects it.
|
||||
- After closure, later log attempts are rejected in the shared async log path before record build, patch, or filter work starts.
|
||||
- That means post-close late logs no longer add pending or dropped counts, and they no longer trigger patch/filter side effects on one backend but not another.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -72,7 +73,7 @@ e.g.:
|
||||
|
||||
- If `clear=false`, pending records may still exist after closure until worker drain or later cleanup resolves them.
|
||||
|
||||
- If callers perform late log attempts after closure, backlog counters still stay unchanged, but patch-path side effects are runtime-dependent and should not be treated as a portable post-close contract.
|
||||
- If callers perform late log attempts after closure, backlog counters still stay unchanged and patch/filter side effects stay skipped.
|
||||
|
||||
- If callers need graceful waiting for drain completion, `shutdown()` is usually the better API.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-flush-policy
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
description: Read the async logger flush policy currently governing batch-end and shutdown-end flushing behavior.
|
||||
update-time: 20260707
|
||||
description: Read the async logger flush policy currently governing batch-end and shutdown-end flush-callback timing.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -37,8 +37,10 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `Batch` means the worker invokes the stored async flush callback after each completed drained batch, not after every individual record write.
|
||||
- `Shutdown` means the worker invokes the stored async flush callback once after the worker loop exits.
|
||||
- `Never` leaves that explicit callback path unused and relies entirely on sink behavior or external control.
|
||||
- This helper reports callback timing policy, not a guarantee that a particular sink type has a meaningful built-in flush effect on every constructor path.
|
||||
- In particular, text-specific async builder paths keep the default no-op flush callback even when the visible policy is `Batch` or `Shutdown`, so the reported policy can describe when the callback would run without implying extra sink work actually happens on that path.
|
||||
- This helper reports callback timing policy, not a progress count contract.
|
||||
- The callback is now treated as an attempted flush action with optional failure, not as a meaningful returned item count.
|
||||
- Builder routes that wrap `RuntimeSink` align with the sync/runtime facade by calling `RuntimeSink::flush_progress()` inside that callback and discarding the returned progress details.
|
||||
- Text-specific async builder paths still keep the default no-op flush callback even when the visible policy is `Batch` or `Shutdown`, so the reported policy can describe when the callback would run without implying extra sink work actually happens on that path.
|
||||
|
||||
### How to Use
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-has-failed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Read whether the async logger worker has recorded a runtime failure after run() startup reset and drain execution.
|
||||
key-word:
|
||||
- async
|
||||
@@ -35,7 +35,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` clears previous failure state at startup.
|
||||
- `run()` also clears the stored `last_error()` string at startup before drain work begins.
|
||||
- If the worker loop raises an error, the logger records that failure and exposes it through this flag.
|
||||
- If the worker loop raises an error, the logger transitions its lifecycle phase to `failed` or `closed_failed` and exposes that failure through this flag.
|
||||
- Once set by a failed run, the flag stays `true` until a later `run()` invocation actually starts and resets it.
|
||||
- Failure-driven shutdown does not clear this flag by itself, so `has_failed()` can remain `true` even after the logger is already closed.
|
||||
- This helper is intentionally compact and should usually be paired with `last_error()` for details.
|
||||
@@ -71,9 +71,9 @@ e.g.:
|
||||
- If `has_failed()` is `false`, queue pressure or dropped records may still exist for non-failure reasons.
|
||||
|
||||
- If `has_failed()` becomes `true`, `wait_idle()` may stop early while pending records still remain until a later close or clear path handles them.
|
||||
- In the current regression coverage, that later handling can be an explicit `close(clear=true)` that resets pending backlog immediately or a runtime-dependent `shutdown(...)` path that either clears pending into dropped records or leaves the closed-queue remainder visible.
|
||||
- In the current regression coverage, that later handling can be an explicit `close(clear=true)` that resets pending backlog immediately or a `shutdown(...)` path that clears retained pending backlog into dropped records before returning.
|
||||
|
||||
- If `has_failed()` is still `true` after shutdown, that does not by itself mean cleanup failed; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
|
||||
- If `has_failed()` is still `true` after shutdown, that does not by itself mean cleanup failed; the logger may already be `phase=closed_failed` and `terminal=true` while the retained failure record is still intentionally visible.
|
||||
|
||||
- If `has_failed()` is `true`, callers should inspect `last_error()` or `state()` for more context.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-is-closed
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Read whether the async logger has entered closed lifecycle state and should no longer be treated as a normal active enqueue target.
|
||||
key-word:
|
||||
- async
|
||||
@@ -36,11 +36,11 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `close(...)` sets the closed state immediately.
|
||||
- `shutdown(...)` also results in a closed logger by the end of its lifecycle flow.
|
||||
- A closed logger should no longer be treated as a normal active enqueue target.
|
||||
- This helper is only a direct read of the current `is_closed` ref; it does not wait for drain completion or clear any other state.
|
||||
- This helper is derived from the current lifecycle phase rather than from an independent closed flag ref.
|
||||
- This helper reflects lifecycle state only and does not indicate whether the worker is still draining.
|
||||
- `is_closed()` becoming `true` does not imply the logger reached a clean success state. Failure flags, retained `last_error()`, and remaining backlog-related counters can still reflect the earlier worker outcome.
|
||||
- After shutdown on a worker-failure path, `is_closed()` can therefore be `true` while backlog cleanup remains runtime-dependent: native-worker runtimes can convert leftover pending records into dropped ones, while compatibility runtimes can still report the remaining queue entries as pending.
|
||||
- Exact post-close logging behavior is runtime-dependent: compatibility runtimes can short-circuit before patch and enqueue work, while native-worker runtimes may still build and patch a record before the closed queue rejects it, so `is_closed()` should be read as a lifecycle signal rather than a full enqueue-policy contract.
|
||||
- After shutdown on a worker-failure path, `is_closed()` can therefore be `true` while retained failure state still remains visible under `phase=closed_failed`.
|
||||
- Late log attempts are rejected in the shared async log path after closure, so `is_closed()` now aligns with a stable post-close enqueue rejection contract.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -74,7 +74,7 @@ e.g.:
|
||||
|
||||
- If callers need to know whether the worker is still active, they should also inspect `is_running()`.
|
||||
|
||||
- If callers need to know whether closure also prevented later log attempts on the current backend, they must interpret this together with the active runtime behavior rather than this flag alone.
|
||||
- If callers need to know whether the logger is also terminal or rerunnable, they should inspect `state().terminal` or `state().can_rerun`.
|
||||
|
||||
- Closing a logger does not by itself reset `has_failed()` or `last_error()`.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-is-running
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Read whether the async logger worker loop is currently running, regardless of queue backlog or recorded failure state.
|
||||
key-word:
|
||||
- async
|
||||
@@ -36,8 +36,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `run()` sets the running state while the worker loop is active.
|
||||
- The flag is cleared when the worker exits normally or after failure handling finishes.
|
||||
- A logger may be closed while still running briefly during final drain or shutdown processing.
|
||||
- After a worker failure, the logger can already be `is_running=false` while still retaining `has_failed=true`, the recorded `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
|
||||
- This helper is only a direct read of the current `is_running` ref; it does not wait, synchronize, or infer why the value is what it is.
|
||||
- After a worker failure, the logger can already be `is_running=false` while still retaining `has_failed=true`, the recorded `last_error()`, and leftover dropped-count cleanup.
|
||||
- This helper is derived from the current lifecycle phase rather than from an independent running flag ref; it does not wait, synchronize, or infer why the value is what it is.
|
||||
- This helper focuses on worker activity rather than queue size or failure details.
|
||||
- `is_running()` can be `false` even when `pending_count()` is still nonzero, for example if the worker was never started or if it exited after a recorded failure.
|
||||
- A later `run()` attempt can flip `is_running()` back to `true` before that retained failure/backlog state has been fully drained, because the restarted worker clears stale failure state only once the new run has actually started.
|
||||
@@ -71,7 +71,7 @@ In this example, callers watch the worker lifecycle directly.
|
||||
e.g.:
|
||||
- If `is_running()` is `false`, pending records may still exist if the worker was never started or has already failed.
|
||||
|
||||
- If `is_running()` is `false`, the logger may also already be closed while still retaining the previous failure record and a runtime-dependent pending-versus-dropped cleanup result from shutdown.
|
||||
- If `is_running()` is `false`, the logger may also already be closed while still retaining the previous failure record and the cleanup result from shutdown.
|
||||
|
||||
- If callers need a one-shot lifecycle flow, `shutdown()` is usually better than manual polling.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-last-error
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Read the last error string recorded by the async logger worker after run() resets and runtime failure capture.
|
||||
key-word:
|
||||
- async
|
||||
@@ -76,7 +76,7 @@ e.g.:
|
||||
|
||||
- `close()` or `shutdown()` do not clear a previously recorded error string by themselves; the reset happens only after a later `run()` has already started.
|
||||
|
||||
- If the same error string is still present after shutdown, that does not by itself mean cleanup was skipped; the logger may already be `is_closed=true` while the remaining pending-versus-dropped outcome still reflects the active runtime's shutdown path.
|
||||
- If the same error string is still present after shutdown, that does not by itself mean cleanup was skipped; the logger may already be `phase=closed_failed` and `terminal=true` while the retained failure record is still intentionally visible.
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-run
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Start the async logger worker loop so queued records are drained to the underlying sink while lifecycle and failure state are reset and updated around execution.
|
||||
key-word:
|
||||
- async
|
||||
@@ -34,13 +34,15 @@ pub async fn[S : @bitlogger.Sink] AsyncLogger::run(self : AsyncLogger[S]) -> Uni
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `run()` sets `is_running` to `true` before worker execution begins.
|
||||
- `run()` moves the logger lifecycle phase from `ready` or `failed` into `running`.
|
||||
- `run()` now enforces a single-worker contract. If the same logger already has a running worker, the later call raises `AsyncLoggerAlreadyRunning` instead of silently starting another drain loop.
|
||||
- If the logger is already closed, `run()` raises `AsyncLoggerClosed` instead of trying to restart a terminal logger.
|
||||
- Every invocation clears previous failure state first by setting `has_failed=false` and `last_error()` to an empty string once that `run()` call has actually started executing.
|
||||
- The method then keeps draining records until `queue.get()` stops with `AsyncLoggerClosed` or a worker error escapes.
|
||||
- On a normal queue-close exit, `run()` clears `is_running` and returns normally.
|
||||
- On failure, the logger records `has_failed=true`, stores the error text in `last_error`, clears `is_running`, and then raises the error back out of `run()`.
|
||||
- On a normal queue-close exit, `run()` leaves the logger in `closed` if shutdown/close had already started, otherwise it returns to `ready`.
|
||||
- On failure, the logger records `last_error`, transitions to `failed` or `closed_failed`, and then raises the error back out of `run()`.
|
||||
- A worker failure does not guarantee the async backlog was fully drained first. If the failure happens after some records were already written, later queued records can remain pending when `run()` exits.
|
||||
- A later `run()` invocation can resume draining that retained backlog, but the stale failure flag and stale `last_error()` value are only cleared after the new worker call actually begins running.
|
||||
- This helper does not enforce a single-worker guard by itself, so the public contract should be treated as application-controlled worker startup rather than an API that deduplicates repeated `run()` calls.
|
||||
|
||||
### How to Use
|
||||
|
||||
@@ -80,7 +82,9 @@ e.g.:
|
||||
|
||||
- A later `run()` attempt starts from a fresh failure flag and empty `last_error()` string once that retrying worker has actually started, even if an earlier run failed.
|
||||
|
||||
- Starting more than one `run()` task for the same logger is not prevented by this method and can produce application-level worker coordination bugs.
|
||||
- Starting more than one `run()` task for the same logger raises `AsyncLoggerAlreadyRunning` on the later attempt.
|
||||
|
||||
- Starting `run()` after a terminal close path raises `AsyncLoggerClosed`.
|
||||
|
||||
### Notes
|
||||
|
||||
@@ -90,4 +94,4 @@ e.g.:
|
||||
|
||||
3. Pair it with `has_failed()`, `last_error()`, or `state()` when tests need to inspect how a worker exit affected logger health.
|
||||
|
||||
4. Start one deliberate worker task per logger unless your own code is intentionally coordinating a different pattern.
|
||||
4. Start one deliberate worker task per logger. A concurrent second startup now fails explicitly with `AsyncLoggerAlreadyRunning`.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-shutdown
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
description: Gracefully stop an async logger by waiting for idle or clearing queued work, with worker-wait behavior depending on the active async runtime.
|
||||
update-time: 20260707
|
||||
description: Gracefully stop an async logger by waiting for idle or clearing queued work, then waiting for any active worker to finish.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -35,12 +35,10 @@ pub async fn[S] AsyncLogger::shutdown(self : AsyncLogger[S], clear? : Bool = fal
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `clear=false` first waits for idle, then closes the logger.
|
||||
- In runtimes where shutdown clearing after idle is enabled, remaining backlog after `wait_idle()` triggers a fallback `close(clear=true)`.
|
||||
- If `wait_idle()` returned early because a worker failure left backlog behind, shutdown now converts that retained pending backlog into dropped records with `close(clear=true)` before returning.
|
||||
- `clear=true` immediately closes and abandons pending records, even if no worker was ever started for that logger.
|
||||
- In runtimes where shutdown waits for workers, the method then waits until `is_running()` becomes `false` before returning.
|
||||
- In the current backend split, native-worker runtimes enable both the post-`wait_idle()` clear fallback and the final wait-for-worker phase, while compatibility runtimes skip both extra steps.
|
||||
- That means a failure-short-circuited `wait_idle()` can still be followed by forced pending-to-dropped cleanup on native-worker runtimes, while compatibility runtimes close without that extra forced clear step.
|
||||
- In the current tested failure path, native-worker shutdown turns the leftover pending item into one more dropped record, while compatibility shutdown leaves that leftover closed-queue count in `pending_count()` instead.
|
||||
- After either shutdown branch starts closing, the helper waits until `is_running()` becomes `false` before returning.
|
||||
- In lifecycle-phase terms, shutdown drives the logger into `closing` while a worker is still active, then settles into `closed` or `closed_failed` once that worker has exited.
|
||||
- Shutdown itself does not clear retained worker failure state. If a previous `run()` already recorded `has_failed=true` and a non-empty `last_error()`, those diagnostics can remain visible after shutdown completes.
|
||||
- Because `clear=false` delegates to `wait_idle()` first, shutdown can also wait indefinitely when pending records exist but no worker is making progress and no failure flag is raised.
|
||||
|
||||
@@ -71,15 +69,10 @@ In this example, pending work is abandoned intentionally so shutdown can complet
|
||||
e.g.:
|
||||
- If `clear=true`, pending records are intentionally dropped rather than drained.
|
||||
|
||||
- If `wait_idle()` returns early because the worker failed, shutdown behavior after that point still depends on the active runtime's fallback and worker-wait rules.
|
||||
|
||||
- After a worker failure, native-worker shutdown may convert the remaining backlog into dropped records, while compatibility shutdown can leave the pending counter reflecting that leftover closed queue state.
|
||||
- In the current direct regression coverage, that split appears as `pending_count() == 0` and `dropped_count()` increasing on native-worker runtimes, versus `pending_count() > 0` and no extra dropped cleanup on compatibility runtimes.
|
||||
- If `wait_idle()` returns early because the worker failed, shutdown still finishes by converting retained pending backlog into dropped records before it returns.
|
||||
|
||||
- Even after shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` from an earlier worker failure.
|
||||
|
||||
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
|
||||
|
||||
- If pending work exists but no worker was started, `shutdown(clear=false)` may never reach its later close step because it is still waiting inside `wait_idle()`.
|
||||
|
||||
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
|
||||
@@ -88,7 +81,7 @@ e.g.:
|
||||
|
||||
1. Prefer this API over raw `close()` in normal application shutdown paths.
|
||||
|
||||
2. Exact post-close waiting behavior depends on the active async runtime mode.
|
||||
2. `shutdown()` now always waits for an already-running worker to leave `is_running=true` before returning.
|
||||
|
||||
3. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-new
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
description: Construct an AsyncLoggerState snapshot from explicit runtime, queue, lifecycle, failure, and flush-policy values without probing a live logger.
|
||||
update-time: 20260707
|
||||
description: Construct an AsyncLoggerState snapshot from explicit runtime, lifecycle phase, queue, failure text, and flush-policy values without probing a live logger.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -20,11 +20,9 @@ Construct an `AsyncLoggerState` snapshot from explicit runtime, queue, lifecycle
|
||||
```moonbit
|
||||
pub fn AsyncLoggerState::new(
|
||||
runtime : AsyncRuntimeState,
|
||||
phase : AsyncLifecyclePhase,
|
||||
pending_count : Int,
|
||||
dropped_count : Int,
|
||||
is_closed : Bool,
|
||||
is_running : Bool,
|
||||
has_failed : Bool,
|
||||
last_error : String,
|
||||
flush_policy : AsyncFlushPolicy,
|
||||
) -> AsyncLoggerState {
|
||||
@@ -33,11 +31,9 @@ pub fn AsyncLoggerState::new(
|
||||
#### input
|
||||
|
||||
- `runtime : AsyncRuntimeState` - Embedded backend-level runtime snapshot.
|
||||
- `phase : AsyncLifecyclePhase` - Primary lifecycle conclusion for the snapshot.
|
||||
- `pending_count : Int` - Current async queue backlog.
|
||||
- `dropped_count : Int` - Current dropped-record count.
|
||||
- `is_closed : Bool` - Whether the logger has been closed.
|
||||
- `is_running : Bool` - Whether the logger worker loop is currently running.
|
||||
- `has_failed : Bool` - Whether the logger has recorded a runtime failure state.
|
||||
- `last_error : String` - Latest error text, or an empty string when no failure has been recorded.
|
||||
- `flush_policy : AsyncFlushPolicy` - Active async flush policy for the logger.
|
||||
|
||||
@@ -49,11 +45,12 @@ pub fn AsyncLoggerState::new(
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This constructor simply packages the supplied fields into one public snapshot value.
|
||||
- This constructor packages the supplied runtime, phase, counters, last error, and flush policy into one public snapshot value.
|
||||
- It does not inspect a live logger instance by itself.
|
||||
- `AsyncLogger::state()` is the higher-level API that reads these values from a concrete logger.
|
||||
- It also does not validate whether the supplied fields represent a combination that could come from one real logger instant.
|
||||
- The supplied `runtime : AsyncRuntimeState` is stored exactly as provided; this constructor does not recompute `mode` or `background_worker` from the active backend.
|
||||
- The constructor derives `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, and `terminal` from the supplied `phase` and `pending_count`.
|
||||
- The constructed value matches the same public shape used by async logger serializers.
|
||||
- Because `AsyncLoggerState` is only a data snapshot type, this constructor is mainly useful for tests, adapters, and synthetic diagnostics rather than ordinary logger inspection.
|
||||
- Serialization helpers accept any `AsyncLoggerState` value, including hand-built ones from this constructor.
|
||||
@@ -69,11 +66,9 @@ When tests or adapters should construct a full async logger state explicitly:
|
||||
```moonbit
|
||||
let state = AsyncLoggerState::new(
|
||||
runtime=AsyncRuntimeState::new(AsyncRuntimeMode::Compatibility, false),
|
||||
phase=AsyncLifecyclePhase::Running,
|
||||
pending_count=0,
|
||||
dropped_count=0,
|
||||
is_closed=false,
|
||||
is_running=true,
|
||||
has_failed=false,
|
||||
last_error="",
|
||||
flush_policy=AsyncFlushPolicy::Never,
|
||||
)
|
||||
@@ -87,11 +82,9 @@ When code should prepare a typed logger state value for later export:
|
||||
```moonbit
|
||||
let state = AsyncLoggerState::new(
|
||||
runtime=async_runtime_state(),
|
||||
phase=logger.phase(),
|
||||
pending_count=logger.pending_count(),
|
||||
dropped_count=logger.dropped_count(),
|
||||
is_closed=logger.is_closed(),
|
||||
is_running=logger.is_running(),
|
||||
has_failed=logger.has_failed(),
|
||||
last_error=logger.last_error(),
|
||||
flush_policy=logger.flush_policy(),
|
||||
)
|
||||
@@ -110,7 +103,7 @@ e.g.:
|
||||
|
||||
- If callers want the current backend-derived runtime pair instead of a synthetic one, they must pass `async_runtime_state()` explicitly or use `AsyncLogger::state()`.
|
||||
|
||||
- This constructor does not apply cleanup semantics such as clearing `last_error` on restart or draining pending records; callers must provide those fields exactly as they want them represented.
|
||||
- This constructor does not apply cleanup semantics such as clearing `last_error` on restart or draining pending records; callers must provide the phase and counters exactly as they want them represented.
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-state-to-json
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Convert an AsyncLoggerState snapshot into a JSON value for diagnostics and transport using the canonical nested runtime shape and flush-policy labels.
|
||||
key-word:
|
||||
- async
|
||||
@@ -33,14 +33,14 @@ pub fn async_logger_state_to_json(state : AsyncLoggerState) -> @json_parser.Json
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- The JSON includes runtime mode, worker support, queue counters, lifecycle flags, last error, and flush policy.
|
||||
- The top-level fields are `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
|
||||
- The JSON includes runtime mode, lifecycle phase, derived lifecycle conclusions, queue counters, last error, and flush policy.
|
||||
- The top-level fields are `runtime`, `phase`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, `terminal`, `last_error`, and `flush_policy`.
|
||||
- The nested `runtime` field reuses `async_runtime_state_to_json(...)`, and `flush_policy` is serialized with the canonical labels `Never`, `Batch`, or `Shutdown`.
|
||||
- The public helper returns the same internal JSON snapshot shape used by `stringify_async_logger_state(...)`, so both export paths stay aligned without duplicate field assembly logic.
|
||||
- This helper is suitable for health endpoints, diagnostics payloads, and custom serialization flows.
|
||||
- It shares the same stable field names used by `stringify_async_logger_state(...)`.
|
||||
- The state must already have been captured or constructed before serialization.
|
||||
- Serialization preserves whatever snapshot combination it receives, including failure flags together with remaining backlog counts.
|
||||
- Serialization preserves whatever snapshot combination it receives, including lifecycle phase and failure/backlog combinations together with remaining backlog counts.
|
||||
- This helper never rereads a logger instance by itself. If callers pass an older or manually constructed `AsyncLoggerState`, the JSON reflects that provided value exactly rather than refreshing fields from live runtime state.
|
||||
- It also does not normalize mixed diagnostic combinations. If the provided snapshot says `is_closed=true` together with retained `has_failed=true`, `last_error`, or non-zero backlog counters, those exact combinations are emitted unchanged.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
description: Public async logger state alias used for queue, lifecycle, runtime, and flush-policy diagnostics.
|
||||
update-time: 20260707
|
||||
description: Public async logger state alias used for queue, lifecycle phase, derived lifecycle conclusions, runtime, and flush-policy diagnostics.
|
||||
key-word:
|
||||
- async
|
||||
- logger
|
||||
@@ -23,7 +23,7 @@ pub type AsyncLoggerState = @utils.AsyncLoggerState
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLoggerState` - Public async logger snapshot containing `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
|
||||
- `AsyncLoggerState` - Public async logger snapshot containing `runtime`, `phase`, queue counters, derived lifecycle conclusions, `last_error`, and `flush_policy`.
|
||||
|
||||
### Explanation
|
||||
|
||||
@@ -31,13 +31,14 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a type alias, not a live logger handle.
|
||||
- The `runtime` field embeds an `AsyncRuntimeState` snapshot.
|
||||
- The remaining fields capture queue counts, lifecycle status, failure state, last error text, and active flush policy.
|
||||
- The remaining fields capture queue counts, lifecycle phase, derived lifecycle conclusions, failure state, last error text, and active flush policy.
|
||||
- `AsyncLogger::state()` returns this type directly, while `async_logger_state_to_json(...)` and `stringify_async_logger_state(...)` export the same data shape for diagnostics.
|
||||
- `AsyncLogger::state()` currently builds this snapshot from `async_runtime_state()` plus the logger's current counters, lifecycle flags, last error, and flush policy.
|
||||
- `AsyncLogger::state()` currently builds this snapshot from `async_runtime_state()` plus the logger's current counters, lifecycle phase, last error, and flush policy.
|
||||
- When the value comes from `AsyncLogger::state()`, the fields are read one by one rather than through a transactional snapshot primitive.
|
||||
- `AsyncLoggerState::new(...)` can also construct this type manually, but manual construction is synthetic snapshot data and does not read one live logger instant by itself.
|
||||
- The type itself does not distinguish live logger snapshots from hand-built ones; callers must track whether a given `AsyncLoggerState` value came from `AsyncLogger::state()` or from manual construction.
|
||||
- When a live snapshot is taken after worker failure, `has_failed=true`, a retained `last_error`, and non-zero `pending_count` may legitimately coexist until later cleanup or restart.
|
||||
- `phase` is the strongest lifecycle conclusion in the snapshot, while `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, and `terminal` are the main derived convenience reads.
|
||||
- When a live snapshot is taken after worker failure, `phase=failed`, a retained `last_error`, `backlog_retained=true`, and non-zero `pending_count` may legitimately coexist until later cleanup or restart.
|
||||
|
||||
### How to Use
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: async-logger-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
description: Read a full async logger runtime snapshot including the embedded runtime snapshot, queue counters, lifecycle flags, last error, and flush policy.
|
||||
update-time: 20260707
|
||||
description: Read a full async logger runtime snapshot including runtime, lifecycle phase, derived lifecycle conclusions, queue counters, last error, and flush policy.
|
||||
key-word:
|
||||
- async
|
||||
- state
|
||||
@@ -27,22 +27,26 @@ pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState {}
|
||||
|
||||
#### output
|
||||
|
||||
- `AsyncLoggerState` - A snapshot containing runtime mode, worker support, queue counts, lifecycle flags, last error, and flush policy.
|
||||
- `AsyncLoggerState` - A snapshot containing runtime mode, lifecycle phase, derived lifecycle conclusions, queue counts, last error, and flush policy.
|
||||
|
||||
### Explanation
|
||||
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `AsyncLoggerState` includes `runtime`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `last_error`, and `flush_policy`.
|
||||
- `AsyncLoggerState` includes `runtime`, `phase`, `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, `backlog_retained`, `can_rerun`, `terminal`, `last_error`, and `flush_policy`.
|
||||
- `state()` returns a value snapshot rather than a live handle.
|
||||
- This helper is equivalent to `AsyncLoggerState::new(async_runtime_state(), self.pending_count(), self.dropped_count(), self.is_closed(), self.is_running(), self.has_failed(), self.last_error(), self.flush_policy())`.
|
||||
- This helper is equivalent to `AsyncLoggerState::new(async_runtime_state(), self.phase(), self.pending_count(), self.dropped_count(), self.last_error(), self.flush_policy())`.
|
||||
- `async_logger_state_to_json(...)` and `stringify_async_logger_state(...)` convert the snapshot to stable diagnostic output.
|
||||
- `runtime` embeds the result of `async_runtime_state()` from the moment `state()` is called, so callers do not need to join separate helpers manually.
|
||||
- The runtime portion is therefore recomputed from the active backend helper on each call, while the remaining fields come from the logger's current counters, flags, and flush policy at that same general moment.
|
||||
- The runtime portion is therefore recomputed from the active backend helper on each call, while the remaining fields come from the logger's current counters, lifecycle phase, derived lifecycle conclusions, and flush policy at that same general moment.
|
||||
- Because the snapshot is assembled field by field when `state()` is called, later logger changes require calling `state()` again rather than reusing an older `AsyncLoggerState` value as if it refreshed itself.
|
||||
- That field-by-field assembly also means this helper is not an atomic freeze across all refs; under concurrent logger activity, neighboring fields can reflect slightly different instants.
|
||||
- After a worker failure, `has_failed=true`, a non-empty `last_error`, and `pending_count>0` can legitimately appear together in one snapshot until later cleanup or a later started `run()` changes them.
|
||||
- After shutdown cleanup on an already failed logger, snapshots can also legitimately show `is_closed=true` together with retained `has_failed=true` and the same `last_error()`, while `pending_count` versus `dropped_count` still reflects the active runtime's cleanup path.
|
||||
- `phase` is the primary lifecycle conclusion. The current public phases are `ready`, `running`, `failed`, `closing`, `closed`, and `closed_failed`.
|
||||
- `backlog_retained=true` means pending backlog still exists while the logger is no longer actively draining it.
|
||||
- `can_rerun=true` means the current phase still allows a future `run()` call to restart drain work.
|
||||
- `terminal=true` means the logger is closed, no worker is active, and no pending backlog remains.
|
||||
- After a worker failure, snapshots can legitimately report `phase=failed`, `has_failed=true`, `backlog_retained=true`, a non-empty `last_error`, and `pending_count>0` together until later cleanup or restart changes them.
|
||||
- After shutdown cleanup on an already failed logger, snapshots can also legitimately report `phase=closed_failed`, `is_closed=true`, `terminal=true`, retained `has_failed=true`, and the same `last_error()`.
|
||||
- `state()` only reports the current field values; it does not clear failure state, drain backlog, or synchronize pending work by itself.
|
||||
|
||||
### How to Use
|
||||
@@ -90,7 +94,7 @@ e.g.:
|
||||
|
||||
- A snapshot showing `has_failed=true` does not imply `pending_count` is already `0`; remaining queued records may still be visible until later cleanup or restart.
|
||||
|
||||
- A snapshot showing `is_closed=true` also does not imply failure state was cleared; after failure-driven shutdown, `has_failed=true` and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
|
||||
- A snapshot showing `is_closed=true` also does not imply failure state was cleared; after failure-driven shutdown, `phase=closed_failed`, `has_failed=true`, and the recorded `last_error` can still remain visible until a later `run()` actually restarts the logger.
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-to-library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Convert a full async logger into the narrower library-facing async facade without rebuilding or detaching the underlying async state.
|
||||
key-word:
|
||||
- async
|
||||
@@ -38,8 +38,8 @@ Detailed rules explaining key parameters and behaviors
|
||||
- The original `AsyncLogger[S]` handle remains the same live logger. If caller code keeps that original value, later facade calls and later unwraps still observe the same shared queue, counters, sink helpers, and lifecycle mutations.
|
||||
- The returned facade keeps library-facing async operations including `log(...)`, `run()`, and `shutdown(...)`.
|
||||
- Async inspection helpers and broader composition APIs remain on the underlying `AsyncLogger[S]` and are intentionally hidden until `to_async_logger()` is used again.
|
||||
- If later facade-level `run()` or `shutdown()` calls record worker failure, leave backlog behind, or follow runtime-dependent shutdown cleanup rules, unwrapping later still exposes that same post-call state instead of a translated facade copy.
|
||||
- That includes states where delegated shutdown already finished with `is_closed=true` while retained `has_failed()` and `last_error()` remain on the wrapped logger, together with the same runtime-dependent pending-versus-dropped cleanup outcome.
|
||||
- If later facade-level `run()` or `shutdown()` calls record worker failure or perform shutdown cleanup, unwrapping later still exposes that same post-call state instead of a translated facade copy.
|
||||
- That includes states where delegated shutdown already finished with `is_closed=true` while retained `has_failed()` and `last_error()` remain on the wrapped logger, together with the same retained dropped-count cleanup outcome.
|
||||
- When `S` itself exposes richer runtime helpers, projecting to the library facade does not strip those capabilities from the wrapped logger; they are still reachable after `to_async_logger()`.
|
||||
- When `S` is `RuntimeSink`, projection also preserves queued runtime state and file-backed runtime helper behavior behind the facade instead of replacing them with a library-specific copy.
|
||||
- Unwrapping later with `to_async_logger()` therefore exposes the same queue counters, failure snapshots, file state, and runtime file controls that the original async logger already carried.
|
||||
@@ -83,7 +83,7 @@ e.g.:
|
||||
|
||||
- Projection does not normalize failure snapshots; a later unwrap can still show combinations such as retained `last_error()` with remaining `pending_count()` when the wrapped async logger really ended up in that state.
|
||||
|
||||
- Projection also does not normalize delegated shutdown results; a later unwrap can still show `is_closed=true` together with retained `has_failed()`, the same `last_error()`, and the same runtime-dependent leftover backlog or dropped-count cleanup that accumulated behind the facade.
|
||||
- Projection also does not normalize delegated shutdown results; a later unwrap can still show `is_closed=true` together with retained `has_failed()`, the same `last_error()`, and the same retained dropped-count cleanup that accumulated behind the facade.
|
||||
|
||||
- Projection also does not normalize richer runtime sink state; if the original async logger already carried queued runtime data or file-backed helper state, a later unwrap still exposes that same live state.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-type
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Public asynchronous logger root type used for queue-backed sink-preserving logging pipelines with explicit lifecycle, failure, and flush-callback state.
|
||||
key-word:
|
||||
- async
|
||||
@@ -13,7 +13,7 @@ key-word:
|
||||
|
||||
## Async-logger-type
|
||||
|
||||
`AsyncLogger[S]` is the public asynchronous root logger type. It stores a concrete sink type `S` together with queue policy, runtime state counters, and lifecycle flags, then serves as the base value for async composition, worker control, and async write APIs.
|
||||
`AsyncLogger[S]` is the public asynchronous root logger type. It stores a concrete sink type `S` together with queue policy, runtime state counters, and lifecycle state, then serves as the base value for async composition, worker control, and async write APIs.
|
||||
|
||||
### Interface
|
||||
|
||||
@@ -27,16 +27,14 @@ pub struct AsyncLogger[S] {
|
||||
linger_ms : Int
|
||||
flush_policy : AsyncFlushPolicy
|
||||
sink : S
|
||||
flush_sink : (S) -> Int raise
|
||||
flush_callback : (S) -> Unit raise
|
||||
context_fields : Array[@bitlogger.Field]
|
||||
filter : (@bitlogger.Record) -> Bool
|
||||
patch : @bitlogger.RecordPatch
|
||||
queue : @async.Queue[@bitlogger.Record]
|
||||
pending_count : Ref[Int]
|
||||
dropped_count : Ref[Int]
|
||||
is_closed : Ref[Bool]
|
||||
is_running : Ref[Bool]
|
||||
has_failed : Ref[Bool]
|
||||
phase : Ref[AsyncLifecyclePhase]
|
||||
last_error : Ref[String]
|
||||
}
|
||||
```
|
||||
@@ -50,9 +48,10 @@ pub struct AsyncLogger[S] {
|
||||
Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This is a public root struct, not a type alias.
|
||||
- The current fields cover targeting, overflow policy, batching, linger timing, flush policy, the wrapped sink, context/filter/patch behavior, queue state, and worker lifecycle flags.
|
||||
- `flush_sink : (S) -> Int raise` stores the raising flush callback used by batch and shutdown flush policies.
|
||||
- `pending_count`, `dropped_count`, `is_closed`, `is_running`, `has_failed`, and `last_error` are mutable runtime refs that power the higher-level lifecycle and diagnostics helpers.
|
||||
- The current fields cover targeting, overflow policy, batching, linger timing, flush policy, the wrapped sink, context/filter/patch behavior, queue state, and the internal lifecycle phase model.
|
||||
- `flush_callback : (S) -> Unit raise` stores the raising flush callback used by batch and shutdown flush policies.
|
||||
- `pending_count`, `dropped_count`, `phase`, and `last_error` are the mutable runtime refs that power the higher-level lifecycle and diagnostics helpers.
|
||||
- `phase` is the stronger internal lifecycle source of truth; helper reads such as `is_closed()`, `is_running()`, `has_failed()`, and the richer `state()` snapshot are derived from it.
|
||||
- The sink type parameter is preserved across async composition, which is why helpers such as `with_target(...)`, `with_context_fields(...)`, `with_filter(...)`, and `with_patch(...)` keep returning `AsyncLogger[S]`.
|
||||
- `async_logger(...)` constructs this type as the main asynchronous entry point.
|
||||
- This root type is also what sits underneath both async facade families: `ApplicationAsyncLogger` is a direct alias over the runtime-sink line, while `LibraryAsyncLogger[S]` is a narrowing wrapper around an `AsyncLogger[S]` value.
|
||||
@@ -88,7 +87,7 @@ e.g.:
|
||||
|
||||
- Actual enqueue, worker, and flush behavior still depends on the wrapped sink `S`, async runtime support, and the configured queue policy.
|
||||
|
||||
- Post-close logging behavior is not a property of the struct shape alone; it also depends on the active runtime implementation.
|
||||
- Post-close logging behavior now follows the shared close contract rather than backend-specific divergence, but lifecycle interpretation should still prefer `state()` over manual field reasoning.
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger-wait-idle
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Wait until the async logger backlog drains to zero or a worker failure interrupts normal progress, using the repo's direct async call style.
|
||||
key-word:
|
||||
- async
|
||||
@@ -37,6 +37,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- If `has_failed()` becomes `true`, waiting stops early instead of continuing to spin.
|
||||
- This API does not close the logger or stop the worker.
|
||||
- A return from `wait_idle()` therefore means either backlog reached `0` or failure interrupted normal drain progress.
|
||||
- In the stronger lifecycle model, the common post-failure interpretation is `phase=failed` together with `backlog_retained=true` while `pending_count() > 0` remains visible.
|
||||
- `wait_idle()` does not clear retained failure state by itself, so if pending records remain after a worker failure, later `wait_idle()` calls also short-circuit until another path changes that state.
|
||||
- A later `run()` can make `wait_idle()` meaningful again for the retained backlog, but only after that new worker invocation has actually started and reset the stale failure flag.
|
||||
- If no worker is draining the queue and no failure flag is raised, `wait_idle()` can wait indefinitely.
|
||||
@@ -73,7 +74,7 @@ e.g.:
|
||||
- If the worker was never started, or if nothing is making pending records decrease, `wait_idle()` can block indefinitely.
|
||||
|
||||
- If callers need backlog cleanup after a failure-short-circuit, they still need a later `close(clear=true)` or `shutdown(...)` path.
|
||||
- In the current direct coverage, `wait_idle()` can return with `pending_count() > 0` after a worker failure, and the later cleanup path may either clear that backlog explicitly or follow the runtime-dependent shutdown split documented on `shutdown(...)`.
|
||||
- In the current direct coverage, `wait_idle()` can return with `pending_count() > 0` after a worker failure, and a later `shutdown(...)` call will convert that retained backlog into dropped records before returning.
|
||||
|
||||
- If callers retry `wait_idle()` immediately after such a failure without restarting the worker or forcing cleanup first, the call can return again with the same retained pending backlog.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: async-logger
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Create an async logger with bounded queueing, overflow policy, lifecycle helpers, background run control, and a raising flush callback.
|
||||
key-word:
|
||||
- async
|
||||
@@ -23,7 +23,7 @@ pub fn[S] async_logger(
|
||||
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
|
||||
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
|
||||
target~ : String = "",
|
||||
flush~ : (S) -> Int raise = fn(_) { 0 },
|
||||
flush~ : (S) -> Unit raise = fn(_) { () },
|
||||
) -> AsyncLogger[S] {}
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn[S] async_logger(
|
||||
- `config : AsyncLoggerConfig` - Queue size, overflow behavior, batching, linger, and flush policy.
|
||||
- `min_level : Level` - Level gate applied before enqueue.
|
||||
- `target : String` - Default target for emitted records.
|
||||
- `flush : (S) -> Int raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
|
||||
- `flush : (S) -> Unit raise` - Flush callback used by batch/shutdown flush policies and allowed to raise if sink flushing fails.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -45,7 +45,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- `async_logger(...)` only builds the logger. Actual background draining is started by `run()`.
|
||||
- `async_logger(...)` returns the full `AsyncLogger[S]` surface directly. It is therefore the underlying constructor used by both application-facing async aliases and the narrower `LibraryAsyncLogger[S]` wrapper line.
|
||||
- The constructed logger starts with `is_closed=false`, `is_running=false`, `has_failed=false`, `last_error=""`, and zeroed pending/dropped counters.
|
||||
- The constructed logger starts in lifecycle phase `ready`, with `last_error=""` and zeroed pending/dropped counters.
|
||||
- The constructed logger also keeps the core async target contract unchanged: `log(..., target=...)` can override the target for one call, while fixed-level helpers such as `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless code derives another logger first with `with_target(...)` or `child(...)`.
|
||||
- Unlike synchronous `Logger`, async `with_context_fields(...)` and `bind(...)` preserve the visible `AsyncLogger[S]` type because shared fields are stored directly on the async logger value instead of being modeled as a separate sink wrapper.
|
||||
- `ApplicationAsyncLogger` and `ApplicationTextAsyncLogger` are only alias names over concrete `AsyncLogger[...]` shapes, so they keep the same lifecycle, queue, failure, and state helpers without adding a wrapper layer.
|
||||
@@ -54,10 +54,15 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `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.
|
||||
- The callback expresses attempted flush timing and failure, not a returned progress count contract.
|
||||
- If the supplied flush callback raises, worker failure state is recorded through `has_failed()` and `last_error()`.
|
||||
- `wait_idle()` is failure-aware rather than a pure backlog-to-zero guarantee. If a worker failure sets `has_failed=true`, waiting stops early and the logger can still report `pending_count() > 0` until later cleanup or restart work happens.
|
||||
- A later `run()` attempt starts by clearing stale failure state back to `has_failed=false` and `last_error=""` before it resumes draining any backlog still left in the queue.
|
||||
- The exact behavior of late log attempts after closure is runtime-dependent, so callers should use lifecycle helpers like `is_closed()` and `shutdown()` instead of assuming identical post-close enqueue semantics everywhere.
|
||||
- `state()` exposes the stronger lifecycle conclusion through `phase`, plus derived reads such as `backlog_retained`, `can_rerun`, and `terminal` so callers do not have to infer these states from raw flag combinations.
|
||||
- Late log attempts after closure are rejected in the shared async log path before patch/filter work runs, so post-close side effects no longer vary by backend.
|
||||
- `run()` now enforces a single-worker contract and raises `AsyncLoggerAlreadyRunning` when a second worker startup is attempted for the same live logger.
|
||||
- `run()` also rejects already-closed loggers with `AsyncLoggerClosed`, which keeps rerun semantics aligned with the stronger lifecycle model: rerun is for retained `failed` backlog, not for terminal closed phases.
|
||||
- `shutdown()` now always waits for any active worker to finish and converts retained post-failure backlog into dropped records before returning.
|
||||
- Queue overflow behavior depends on `AsyncOverflowPolicy`.
|
||||
|
||||
### How to Use
|
||||
@@ -125,6 +130,7 @@ e.g.:
|
||||
- If the logger is closed, further enqueue attempts stop being normal active logging operations.
|
||||
|
||||
- If queue drain fails internally, runtime state can reflect that through `has_failed()` and `last_error()`.
|
||||
- If a second worker startup is attempted while `is_running()` is already true, `run()` raises `AsyncLoggerAlreadyRunning`.
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: library-async-logger-new
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
update-time: 20260707
|
||||
description: Create a narrower library-facing async logger facade by building a regular async logger and wrapping the same underlying state.
|
||||
key-word:
|
||||
- async
|
||||
@@ -23,7 +23,7 @@ pub fn[S] LibraryAsyncLogger::new(
|
||||
config~ : AsyncLoggerConfig = AsyncLoggerConfig::new(),
|
||||
min_level~ : @bitlogger.Level = @bitlogger.Level::Info,
|
||||
target~ : String = "",
|
||||
flush~ : (S) -> Int raise = fn(_) { 0 },
|
||||
flush~ : (S) -> Unit raise = fn(_) { () },
|
||||
) -> LibraryAsyncLogger[S] {
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn[S] LibraryAsyncLogger::new(
|
||||
- `config : AsyncLoggerConfig` - Queue size, overflow behavior, batching, linger, and flush policy.
|
||||
- `min_level : Level` - Minimum enabled level. Messages below this threshold are ignored before enqueue.
|
||||
- `target : String` - Default target attached to emitted records unless later overridden.
|
||||
- `flush : (S) -> Int raise` - Flush callback used when async batch or shutdown policy needs explicit flushing, and allowed to raise if flushing fails.
|
||||
- `flush : (S) -> Unit raise` - Flush callback used when async batch or shutdown policy needs explicit flushing, and allowed to raise if flushing fails.
|
||||
|
||||
#### output
|
||||
|
||||
@@ -73,7 +73,7 @@ When the sink requires a flush callback for batch or shutdown policy:
|
||||
```moonbit
|
||||
let logger = LibraryAsyncLogger::new(
|
||||
@bitlogger.console_sink(),
|
||||
flush=fn(sink) { 0 },
|
||||
flush=fn(sink) { () },
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: library-async-logger-run
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260613
|
||||
update-time: 20260707
|
||||
description: Start the LibraryAsyncLogger worker loop by delegating to the wrapped async logger's run behavior.
|
||||
key-word:
|
||||
- async
|
||||
@@ -35,6 +35,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped async logger's `run()` behavior.
|
||||
- It starts the worker loop that makes accepted queued records reach the underlying sink.
|
||||
- The delegated `run()` call now preserves the wrapped logger's single-worker guard, so a second concurrent facade-level startup raises `AsyncLoggerAlreadyRunning`.
|
||||
- Failure and lifecycle state are still tracked by the wrapped async logger.
|
||||
- Once a delegated `run()` call has actually started, it clears any previous `has_failed()` flag and stored `last_error()` string on that same wrapped logger before draining resumes.
|
||||
- The narrower library facade does not hide the need to explicitly activate queue draining.
|
||||
@@ -78,6 +79,7 @@ e.g.:
|
||||
- If `run()` is never started, accepted records may remain queued and not reach the sink.
|
||||
|
||||
- A later delegated `run()` attempt starts from a fresh failure flag and empty `last_error()` string on the same wrapped logger, even if an earlier facade-level run failed.
|
||||
- A concurrent delegated `run()` attempt while the wrapped logger is already active fails explicitly with `AsyncLoggerAlreadyRunning`.
|
||||
|
||||
- If callers need to inspect `is_running()`, `has_failed()`, or `last_error()` directly, they must unwrap first with `to_async_logger()`.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
name: library-async-logger-shutdown
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260614
|
||||
description: Gracefully stop a LibraryAsyncLogger by delegating to the wrapped async logger's shutdown behavior, including runtime-dependent drain and worker-wait rules.
|
||||
update-time: 20260707
|
||||
description: Gracefully stop a LibraryAsyncLogger by delegating to the wrapped async logger's shutdown behavior, including drain and worker-wait rules.
|
||||
key-word:
|
||||
- async
|
||||
- library
|
||||
@@ -39,13 +39,12 @@ Detailed rules explaining key parameters and behaviors
|
||||
|
||||
- This method delegates directly to the wrapped async logger's `shutdown(...)` behavior.
|
||||
- `clear=false` first waits for idle, then closes the logger.
|
||||
- If the active async runtime uses shutdown clearing after idle and backlog still remains, the wrapped logger falls back to `close(clear=true)`.
|
||||
- If backlog still remains after `wait_idle()` because failure stopped progress early, the wrapped logger falls back to `close(clear=true)`.
|
||||
- `clear=true` immediately closes and abandons pending records, even if the facade never started a drain worker for the wrapped logger.
|
||||
- In runtimes where shutdown waits for workers, the method then waits until the worker is no longer running before returning.
|
||||
- After a worker-failure short-circuit, native-worker backends can still convert remaining backlog into dropped records, while compatibility backends skip that extra forced-clear step.
|
||||
- In the current direct facade coverage, the wrapped logger ends that path with `pending_count() == 0` and one more dropped item on native-worker runtimes, versus a still-nonzero pending count and no extra dropped cleanup on compatibility runtimes.
|
||||
- The method then waits until the worker is no longer running before returning.
|
||||
- After a worker-failure short-circuit, delegated shutdown converts remaining backlog into dropped records before it returns.
|
||||
- Delegated shutdown also does not clear retained worker failure state by itself. If the wrapped logger had already recorded a worker error, later inspection through `to_async_logger()` can still show `has_failed=true` and the same `last_error()` string after shutdown completes.
|
||||
- The narrower library facade does not change any of these runtime-dependent shutdown rules; it only keeps the broader inspection helpers out of the direct public surface.
|
||||
- The narrower library facade does not change any of these shutdown rules; it only keeps the broader inspection helpers out of the direct public surface.
|
||||
- Inspecting the logger later through `to_async_logger()` reveals the same delegated shutdown result rather than a rebuilt or translated lifecycle snapshot.
|
||||
|
||||
### How to Use
|
||||
@@ -79,8 +78,6 @@ e.g.:
|
||||
|
||||
- `clear=true` can therefore be used as the facade-level no-worker cleanup path when queued records were accepted but no `run()` task was ever started.
|
||||
|
||||
- In compatibility-style runtimes without background-worker waiting, shutdown still closes the logger but may not perform the extra wait-for-worker phase described for native-worker runtimes.
|
||||
|
||||
- Even when delegated shutdown finishes with `is_closed=true`, callers can still observe retained `has_failed()` and `last_error()` on the unwrapped logger if the worker had already failed before shutdown cleanup completed.
|
||||
|
||||
- If callers skip `shutdown()` and only inspect flags manually, it is easier to leave the worker lifecycle in an unclear state.
|
||||
@@ -91,7 +88,7 @@ e.g.:
|
||||
|
||||
1. Prefer this API over low-level closure control in normal library shutdown paths.
|
||||
|
||||
2. Exact post-close waiting behavior depends on the active async runtime mode.
|
||||
2. Delegated shutdown waits for an already-running worker to finish before returning.
|
||||
|
||||
3. Choose `clear=true` only when loss of queued records is acceptable.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: library-async-logger-to-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Recover the underlying full async logger from the library-facing async facade without rebuilding, copying, or detaching the wrapped async state.
|
||||
key-word:
|
||||
- async
|
||||
@@ -40,9 +40,9 @@ Detailed rules explaining key parameters and behaviors
|
||||
- Use this when code needs wider async logger APIs outside the library facade.
|
||||
- This is the step required for helpers such as `pending_count()`, `dropped_count()`, `state()`, `wait_idle()`, `has_failed()`, `last_error()`, or broader composition methods that are intentionally hidden by `LibraryAsyncLogger[S]`.
|
||||
- It is also the step that exposes the real post-run or post-shutdown state after facade-level lifecycle calls, because those calls delegated to this same wrapped logger all along.
|
||||
- That includes failure/backlog combinations and runtime-dependent shutdown results exactly as they accumulated behind the facade.
|
||||
- That includes failure/backlog combinations and shutdown results exactly as they accumulated behind the facade.
|
||||
- Because the same live value is shared, code can unwrap once, keep that `AsyncLogger[S]` handle, and then observe `pending_count()`, `state()`, `is_closed()`, or failure fields changing as later facade-level `info(...)`, `log(...)`, `run()`, or `shutdown(...)` calls execute.
|
||||
- That also means the unwrapped logger can already be both `is_closed=true` and `has_failed=true`, with the same retained `last_error()` string and the same runtime-dependent pending-versus-dropped cleanup outcome that delegated shutdown left behind.
|
||||
- That also means the unwrapped logger can already be both `is_closed=true` and `has_failed=true`, with the same retained `last_error()` string and the same dropped-count cleanup outcome that delegated shutdown left behind.
|
||||
- If the wrapped sink type has richer helper APIs, unwrapping also restores access to that same sink helper surface through the original `AsyncLogger[S]` value.
|
||||
- When the wrapped sink is `RuntimeSink`, that same unwrap also preserves queued runtime state and file-backed helper behavior exactly as they existed behind the facade, including runtime file state snapshots and file control methods.
|
||||
- Runtime sink helper mutations are still live too. Changing file append mode, auto-flush, rotation, reopen state, or related helper-managed file state through the unwrapped logger changes the same wrapped runtime sink that later facade writes and shutdown behavior continue using.
|
||||
@@ -87,7 +87,7 @@ e.g.:
|
||||
|
||||
- Unwrapping does not convert facade lifecycle history into a simplified snapshot; any retained `last_error()`, pending backlog, dropped counts, or sink runtime details stay exactly as they were on the wrapped logger.
|
||||
|
||||
- If facade-level `run()` or `shutdown()` already ended in a failure-retaining state, unwrapping can therefore expose combinations such as `is_closed=true` together with `has_failed=true`, the same `last_error()`, and runtime-dependent leftover backlog or dropped-count cleanup.
|
||||
- If facade-level `run()` or `shutdown()` already ended in a failure-retaining state, unwrapping can therefore expose combinations such as `is_closed=true` together with `has_failed=true`, the same `last_error()`, and the same leftover dropped-count cleanup.
|
||||
|
||||
- Recovering the full async logger also does not translate runtime sink helper state into a simpler snapshot; queue counters, file availability, file failure counters, and runtime file controls stay exactly as they were on the wrapped logger.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: library-async-logger
|
||||
group: api
|
||||
category: facade
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Public library-facing async logger facade type used to expose a narrower surface than AsyncLogger while preserving sink typing.
|
||||
key-word:
|
||||
- library
|
||||
@@ -34,13 +34,13 @@ Detailed rules explaining key parameters and behaviors
|
||||
- This is a public facade struct, not a type alias.
|
||||
- The wrapped sink type `S` is preserved, so sink-specific typing remains available through the facade type parameter.
|
||||
- The facade keeps common library-safe async operations such as `new(...)`, `with_target(...)`, `child(...)`, `bind(...)`, `is_enabled(...)`, `run()`, `shutdown()`, and the main async write methods `log(...)`, `info(...)`, `warn(...)`, and `error(...)`.
|
||||
- `LibraryAsyncLogger::new(...)` delegates to `async_logger(...)`, so the same async queue, raising flush callback, and runtime-dependent lifecycle behavior still exist behind the narrower facade.
|
||||
- `LibraryAsyncLogger::new(...)` delegates to `async_logger(...)`, so the same async queue, raising flush callback, and lifecycle behavior still exist behind the narrower facade.
|
||||
- Because `new(...)` delegates directly, `AsyncLoggerConfig` values such as pending limits, overflow mode, batch sizing, linger timing, and flush policy are preserved exactly rather than being translated into facade-specific defaults.
|
||||
- The facade also preserves the wrapped async logger's target rules on its exposed write methods: `log(..., target=...)` can override the target for one call, while `info(...)`, `warn(...)`, and `error(...)` continue using the stored logger target unless the facade first derives another logger with `with_target(...)` or `child(...)`.
|
||||
- Most facade reshaping helpers keep the same visible sink type, and unlike synchronous `LibraryLogger`, async `with_context_fields(...)` and `bind(...)` also preserve the visible `LibraryAsyncLogger[S]` type because they update stored async context metadata instead of changing the exposed sink wrapper.
|
||||
- It does not expose the wider `AsyncLogger[S]` composition surface or the async state and lifecycle inspection helpers directly.
|
||||
- Call `to_async_logger()` when later code must recover the full underlying `AsyncLogger[S]` surface.
|
||||
- That recovered logger is the same live async value, so queue counters, retained failure state, runtime-dependent shutdown outcomes, and sink helper access are preserved rather than recomputed.
|
||||
- That recovered logger is the same live async value, so queue counters, retained failure state, shutdown outcomes, and sink helper access are preserved rather than recomputed.
|
||||
- If the delegated async flush callback fails, the facade preserves the same `has_failed()`, `last_error()`, `is_running()`, pending-backlog, and shutdown-cleanup semantics that the underlying `AsyncLogger[S]` would have exposed directly.
|
||||
- If `S` is `RuntimeSink`, queued runtime state and file-backed helper behavior also stay on that same wrapped logger value; the facade only hides direct access until `to_async_logger()` is used.
|
||||
- If `S` is `RuntimeSink` and the value came from `build_library_async_logger(...)` or `parse_and_build_library_async_logger(...)`, the wrapped logger also keeps that sync-first builder route unchanged: any optional `LoggerConfig.queue` was already applied before async wrapping, and `logger.sink.kind` had already decided the concrete `RuntimeSink` variant before the facade narrowed the surface.
|
||||
@@ -111,7 +111,7 @@ e.g.:
|
||||
|
||||
- If the wrapped async logger later ends in a mixed diagnostic state such as `has_failed=true` with retained backlog, unwrapping exposes that exact state instead of a library-specific translation.
|
||||
|
||||
- Shutdown through the facade keeps the wrapped logger's runtime-dependent failure cleanup behavior: after a worker-side failure, unwrapped pending versus dropped cleanup can still differ between native-worker and compatibility runtimes.
|
||||
- Shutdown through the facade keeps the wrapped logger's failure cleanup behavior: after a worker-side failure, unwrapped retained pending backlog is converted into dropped records before shutdown returns.
|
||||
|
||||
- Helpers such as `pending_count()`, `dropped_count()`, `is_closed()`, `is_running()`, `has_failed()`, `last_error()`, `flush_policy()`, `state()`, and `wait_idle()` remain on the underlying `AsyncLogger[S]` and require `to_async_logger()` first.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: stringify-async-logger-state
|
||||
group: api
|
||||
category: async
|
||||
update-time: 20260614
|
||||
update-time: 20260707
|
||||
description: Serialize AsyncLoggerState into compact or pretty JSON text for logs and diagnostics using the canonical nested runtime snapshot shape.
|
||||
key-word:
|
||||
- async
|
||||
@@ -41,7 +41,7 @@ Detailed rules explaining key parameters and behaviors
|
||||
- `pretty=true` returns indented JSON suitable for human diagnostics.
|
||||
- This helper is built on top of `async_logger_state_to_json(...)`.
|
||||
- Internally it stringifies the same JSON snapshot shape returned by the public export helper, using `@json_parser.stringify(...)` or `@json_parser.stringify_pretty(value, 2)`.
|
||||
- The compact form matches snapshots such as `{"runtime":{"mode":"native_worker","background_worker":true},"pending_count":0,...}`.
|
||||
- The compact form matches snapshots such as `{"runtime":{"mode":"native_worker","background_worker":true},"phase":"ready","pending_count":0,...}`.
|
||||
- String output is convenient for logs, CLI output, and startup diagnostics.
|
||||
- The serializer does not care whether the input came from a live logger read or a synthetic `AsyncLoggerState::new(...)` call.
|
||||
- It also preserves mixed diagnostic states exactly as provided, such as a failure flag together with non-zero backlog.
|
||||
|
||||
Reference in New Issue
Block a user