♻️ sync owner migration and facade thinning

This commit is contained in:
Nanaloveyuki
2026-07-07 20:03:22 +08:00
parent 5d9924026e
commit e3097ba4fc
79 changed files with 5255 additions and 3191 deletions
+5 -4
View File
@@ -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.
+6 -4
View File
@@ -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
+4 -4
View File
@@ -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.
+5 -5
View File
@@ -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()`.
+4 -4
View File
@@ -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 -2
View File
@@ -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
+10 -6
View File
@@ -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`.
+7 -14
View File
@@ -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.
+9 -16
View File
@@ -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
+4 -4
View File
@@ -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.
+7 -6
View File
@@ -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
+13 -9
View File
@@ -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.
+9 -10
View File
@@ -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
+3 -2
View File
@@ -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.
+11 -5
View File
@@ -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
+4 -4
View File
@@ -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) { () },
)
```
+3 -1
View File
@@ -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()`.
+7 -10
View File
@@ -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.
+4 -4
View File
@@ -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 -2
View File
@@ -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.
+258 -23
View File
@@ -25,7 +25,7 @@ async test "shutdown drains pending records" {
target="async.test",
flush=fn(_) {
flushes.val += 1
1
()
},
)
@@ -141,14 +141,84 @@ async test "closed logger runtime determines whether later log reaches patch pat
inspect(logger.dropped_count(), content="1")
logger.info("late")
inspect(
patched.val,
content=if async_runtime_supports_background_worker() { "2" } else { "1" },
)
inspect(patched.val, content="1")
inspect(logger.pending_count(), content="0")
inspect(logger.dropped_count(), content="1")
}
///|
async test "closed logger does not enter filter path on later log attempts" {
let filtered : Ref[Int] = Ref(0)
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.filter",
).with_filter(fn(_) {
filtered.val += 1
true
})
logger.info("one")
inspect(filtered.val, content="1")
logger.close(clear=true)
logger.info("late")
inspect(filtered.val, content="1")
inspect(logger.pending_count(), content="0")
inspect(logger.dropped_count(), content="1")
}
///|
async test "run rejects duplicate worker startup for same logger" {
let logger = async_logger(
@bitlogger.callback_sink(fn(_) { }),
config=AsyncLoggerConfig::new(
max_pending=2,
overflow=AsyncOverflowPolicy::Blocking,
),
min_level=@bitlogger.Level::Info,
target="async.run.duplicate",
)
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
while !logger.is_running() {
@async.pause()
}
let second_error = (async fn() -> String raise {
logger.run()
"no error"
})() catch {
err => err.to_string()
}
inspect(second_error.contains("AsyncLoggerAlreadyRunning"), content="true")
let state = logger.state()
inspect(
match state.phase {
AsyncLifecyclePhase::Running => "running"
_ => "other"
},
content="running",
)
inspect(logger.has_failed(), content="false")
inspect(logger.last_error(), content="")
inspect(state.can_rerun, content="false")
inspect(state.terminal, content="false")
logger.shutdown(clear=true)
})
inspect(logger.is_closed(), content="true")
inspect(logger.is_running(), content="false")
inspect(logger.has_failed(), content="false")
inspect(logger.last_error(), content="")
}
///|
async test "async logger with_filter composes and leaves base logger unchanged" {
let written_targets : Ref[Array[String]] = Ref([])
@@ -1128,9 +1198,23 @@ test "async logger state snapshot reflects current counters and runtime" {
)
inspect(state.pending_count, content="0")
inspect(state.dropped_count, content="0")
inspect(
match state.phase {
AsyncLifecyclePhase::Ready => "ready"
AsyncLifecyclePhase::Running => "running"
AsyncLifecyclePhase::Failed => "failed"
AsyncLifecyclePhase::Closing => "closing"
AsyncLifecyclePhase::Closed => "closed"
AsyncLifecyclePhase::ClosedFailed => "closed_failed"
},
content="ready",
)
inspect(state.is_closed, content="false")
inspect(state.is_running, content="false")
inspect(state.has_failed, content="false")
inspect(state.backlog_retained, content="false")
inspect(state.can_rerun, content="true")
inspect(state.terminal, content="false")
inspect(state.last_error, content="")
inspect(
match state.flush_policy {
@@ -1143,21 +1227,54 @@ test "async logger state snapshot reflects current counters and runtime" {
inspect(
@json_parser.stringify(async_logger_state_to_json(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\"}"
"{\"runtime\":{\"mode\":\"native_worker\",\"background_worker\":true},\"phase\":\"ready\",\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"backlog_retained\":false,\"can_rerun\":true,\"terminal\":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\"}"
"{\"runtime\":{\"mode\":\"compatibility\",\"background_worker\":false},\"phase\":\"ready\",\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"backlog_retained\":false,\"can_rerun\":true,\"terminal\":false,\"last_error\":\"\",\"flush_policy\":\"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\"}"
"{\"runtime\":{\"mode\":\"native_worker\",\"background_worker\":true},\"phase\":\"ready\",\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"backlog_retained\":false,\"can_rerun\":true,\"terminal\":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\"}"
"{\"runtime\":{\"mode\":\"compatibility\",\"background_worker\":false},\"phase\":\"ready\",\"pending_count\":0,\"dropped_count\":0,\"is_closed\":false,\"is_running\":false,\"has_failed\":false,\"backlog_retained\":false,\"can_rerun\":true,\"terminal\":false,\"last_error\":\"\",\"flush_policy\":\"Shutdown\"}"
},
)
}
///|
async test "async logger state enters running phase while worker is active" {
let logger = async_logger(
@bitlogger.callback_sink(fn(_) { }),
config=AsyncLoggerConfig::new(max_pending=2),
min_level=@bitlogger.Level::Info,
target="async.state.running",
)
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
while !logger.is_running() {
@async.pause()
}
let state = logger.state()
inspect(
match state.phase {
AsyncLifecyclePhase::Running => "running"
_ => "other"
},
content="running",
)
inspect(state.is_closed, content="false")
inspect(state.is_running, content="true")
inspect(state.has_failed, content="false")
inspect(state.backlog_retained, content="false")
inspect(state.can_rerun, content="false")
inspect(state.terminal, content="false")
logger.shutdown(clear=true)
})
}
///|
async test "run drains queued records in compatibility backends too" {
let written : Ref[Array[String]] = Ref([])
@@ -1201,7 +1318,7 @@ async test "async logger records worker failures and wait_idle stops early" {
),
min_level=@bitlogger.Level::Info,
target="async.failure",
flush=fn(_) -> Int raise { raise TestFlushError("flush exploded") },
flush=fn(_) -> Unit raise { raise TestFlushError("flush exploded") },
)
@async.with_task_group(group => {
@@ -1219,6 +1336,18 @@ async test "async logger records worker failures and wait_idle stops early" {
inspect(logger.last_error().contains("TestFlushError"), content="true")
inspect(logger.is_running(), content="false")
inspect(logger.pending_count(), content="0")
let state = logger.state()
inspect(
match state.phase {
AsyncLifecyclePhase::ClosedFailed => "closed_failed"
_ => "other"
},
content="closed_failed",
)
inspect(state.backlog_retained, content="false")
inspect(state.can_rerun, content="false")
inspect(state.terminal, content="true")
}
///|
@@ -1234,12 +1363,12 @@ async test "later started run resets async failure state before draining remaini
),
min_level=@bitlogger.Level::Info,
target="async.retry",
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
flushes.val += 1
if flushes.val == 1 {
raise TestFlushError("flush exploded once")
}
1
()
},
)
@@ -1253,12 +1382,33 @@ async test "later started run resets async failure state before draining remaini
inspect(logger.is_running(), content="false")
inspect(logger.pending_count(), content="1")
let failed_state = logger.state()
inspect(
match failed_state.phase {
AsyncLifecyclePhase::Failed => "failed"
_ => "other"
},
content="failed",
)
inspect(failed_state.backlog_retained, content="true")
inspect(failed_state.can_rerun, content="true")
inspect(failed_state.terminal, content="false")
group.spawn_bg(() => logger.run())
while !logger.is_running() {
@async.pause()
}
inspect(logger.has_failed(), content="false")
inspect(logger.last_error(), content="")
let running_state = logger.state()
inspect(
match running_state.phase {
AsyncLifecyclePhase::Running => "running"
_ => "other"
},
content="running",
)
logger.shutdown()
})
@@ -1272,7 +1422,7 @@ async test "later started run resets async failure state before draining remaini
}
///|
async test "shutdown after worker failure uses runtime-specific pending cleanup" {
async test "shutdown after worker failure clears remaining pending backlog" {
let writes : Ref[Int] = Ref(0)
let logger = async_logger(
@bitlogger.callback_sink(fn(_) { writes.val += 1 }),
@@ -1283,7 +1433,7 @@ async test "shutdown after worker failure uses runtime-specific pending cleanup"
),
min_level=@bitlogger.Level::Info,
target="async.failure.shutdown",
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
raise TestFlushError("flush exploded on shutdown path")
},
)
@@ -1303,14 +1453,58 @@ async test "shutdown after worker failure uses runtime-specific pending cleanup"
inspect(logger.last_error().contains("TestFlushError"), content="true")
inspect(logger.is_running(), content="false")
inspect(writes.val, content="1")
inspect(logger.pending_count(), content="0")
inspect(logger.dropped_count(), content="1")
let state = logger.state()
inspect(
logger.pending_count(),
content=if async_runtime_supports_background_worker() { "0" } else { "1" },
match state.phase {
AsyncLifecyclePhase::ClosedFailed => "closed_failed"
_ => "other"
},
content="closed_failed",
)
inspect(state.is_closed, content="true")
inspect(state.is_running, content="false")
inspect(state.has_failed, content="true")
inspect(state.pending_count, content="0")
inspect(state.dropped_count, content="1")
inspect(state.backlog_retained, content="false")
inspect(state.can_rerun, content="false")
inspect(state.terminal, content="true")
}
///|
async test "shutdown clear reaches terminal closed phase without worker startup" {
let logger = async_logger(
@bitlogger.callback_sink(fn(_) { }),
config=AsyncLoggerConfig::new(
max_pending=2,
overflow=AsyncOverflowPolicy::Blocking,
),
min_level=@bitlogger.Level::Info,
target="async.shutdown.clear.state",
)
logger.info("one")
logger.shutdown(clear=true)
let state = logger.state()
inspect(
logger.dropped_count(),
content=if async_runtime_supports_background_worker() { "1" } else { "0" },
match state.phase {
AsyncLifecyclePhase::Closed => "closed"
_ => "other"
},
content="closed",
)
inspect(state.is_closed, content="true")
inspect(state.is_running, content="false")
inspect(state.has_failed, content="false")
inspect(state.pending_count, content="0")
inspect(state.dropped_count, content="1")
inspect(state.backlog_retained, content="false")
inspect(state.can_rerun, content="false")
inspect(state.terminal, content="true")
}
///|
@@ -1358,7 +1552,7 @@ async test "library async new preserves config and flush failure contract" {
),
min_level=@bitlogger.Level::Warn,
target="async.lib.new",
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
flushes.val += 1
raise TestFlushError("library new flush exploded")
},
@@ -1687,7 +1881,7 @@ async test "library async shutdown preserves wrapped failure cleanup semantics"
),
min_level=@bitlogger.Level::Info,
target="async.lib.failure",
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
raise TestFlushError("library facade flush exploded")
},
)
@@ -2847,6 +3041,47 @@ async test "build async logger keeps direct helper surface and runtime sink path
inspect(logger.dropped_count(), content="1")
inspect(logger.has_failed(), content="false")
inspect(logger.last_error(), content="")
inspect(logger.sink.pending_count(), content="0")
}
///|
async test "build async logger batch flush policy drains queued runtime sink work at batch boundary" {
let logger = build_async_logger(
AsyncLoggerBuildConfig::new(
logger=@bitlogger.LoggerConfig::new(
min_level=@bitlogger.Level::Info,
target="async.direct.batch.flush.runtime",
queue=Some(
@bitlogger.QueueConfig::new(
4,
overflow=@bitlogger.QueueOverflowPolicy::DropNewest,
),
),
sink=@bitlogger.SinkConfig::new(kind=@bitlogger.SinkKind::Console),
),
async_config=AsyncLoggerConfig::new(
max_pending=4,
overflow=AsyncOverflowPolicy::Blocking,
max_batch=2,
flush=AsyncFlushPolicy::Batch,
),
),
)
@async.with_task_group(group => {
group.spawn_bg(() => logger.run())
logger.info("one")
logger.info("two")
logger.wait_idle()
inspect(logger.pending_count(), content="0")
inspect(logger.sink.pending_count(), content="0")
inspect(logger.has_failed(), content="false")
logger.shutdown()
})
inspect(logger.is_closed(), content="true")
inspect(logger.is_running(), content="false")
inspect(logger.sink.pending_count(), content="0")
}
///|
@@ -2959,7 +3194,7 @@ async test "async logger projection preserves async queue and failure state" {
),
min_level=@bitlogger.Level::Info,
target="async.projected.state",
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
raise TestFlushError("projected facade flush exploded")
},
).to_library_async_logger()
@@ -4471,7 +4706,7 @@ async test "build async text logger batch flush policy keeps default no-op flush
config=config.async_config,
min_level=config.logger.min_level,
target=config.logger.target,
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
raise TestFlushError("text batch flush exploded")
},
).with_timestamp(enabled=config.logger.timestamp)
@@ -4556,7 +4791,7 @@ async test "build async text logger shutdown flush policy keeps default no-op fl
config=config.async_config,
min_level=config.logger.min_level,
target=config.logger.target,
flush=fn(_) -> Int raise {
flush=fn(_) -> Unit raise {
raise TestFlushError("text shutdown flush exploded")
},
).with_timestamp(enabled=config.logger.timestamp)
-15
View File
@@ -8,18 +8,3 @@ pub fn async_runtime_supports_background_worker() -> Bool {
ignore(all_async_runtime_modes())
true
}
///|
fn async_runtime_guard_closed_on_log() -> Bool {
false
}
///|
fn async_runtime_shutdown_clears_pending_after_wait_idle() -> Bool {
true
}
///|
fn async_runtime_shutdown_waits_for_worker() -> Bool {
true
}
+104 -31
View File
@@ -3,6 +3,11 @@ pub(all) suberror AsyncLoggerClosed {
AsyncLoggerClosed
}
///|
pub(all) suberror AsyncLoggerAlreadyRunning {
AsyncLoggerAlreadyRunning
}
///|
pub type AsyncOverflowPolicy = @utils.AsyncOverflowPolicy
@@ -23,6 +28,9 @@ fn all_async_runtime_modes() -> Array[AsyncRuntimeMode] {
///|
pub type AsyncRuntimeState = @utils.AsyncRuntimeState
///|
pub type AsyncLifecyclePhase = @utils.AsyncLifecyclePhase
///|
pub type AsyncLoggerState = @utils.AsyncLoggerState
@@ -129,16 +137,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]
}
@@ -148,7 +154,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] {
{
min_level,
@@ -159,20 +165,87 @@ pub fn[S] async_logger(
linger_ms: config.linger_ms,
flush_policy: config.flush,
sink,
flush_sink: flush,
flush_callback: flush,
context_fields: [],
filter: fn(_) { true },
patch: @bitlogger.identity_patch(),
queue: @async.Queue(kind=queue_kind_of(config)),
pending_count: Ref(0),
dropped_count: Ref(0),
is_closed: Ref(false),
is_running: Ref(false),
has_failed: Ref(false),
phase: Ref(AsyncLifecyclePhase::Ready),
last_error: Ref(""),
}
}
///|
fn async_logger_phase_is_closed(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Closing |
AsyncLifecyclePhase::Closed |
AsyncLifecyclePhase::ClosedFailed => true
_ => false
}
}
///|
fn async_logger_phase_is_running(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Running | AsyncLifecyclePhase::Closing => true
_ => false
}
}
///|
fn async_logger_phase_has_failed(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Failed | AsyncLifecyclePhase::ClosedFailed => true
_ => false
}
}
///|
fn async_logger_phase_after_close(
phase : AsyncLifecyclePhase,
) -> AsyncLifecyclePhase {
match phase {
AsyncLifecyclePhase::Running | AsyncLifecyclePhase::Closing =>
AsyncLifecyclePhase::Closing
AsyncLifecyclePhase::Failed | AsyncLifecyclePhase::ClosedFailed =>
AsyncLifecyclePhase::ClosedFailed
AsyncLifecyclePhase::Closed => AsyncLifecyclePhase::Closed
AsyncLifecyclePhase::Ready => AsyncLifecyclePhase::Closed
}
}
///|
fn async_logger_phase_after_run_exit(
phase : AsyncLifecyclePhase,
) -> AsyncLifecyclePhase {
match phase {
AsyncLifecyclePhase::Closing => AsyncLifecyclePhase::Closed
AsyncLifecyclePhase::Running => AsyncLifecyclePhase::Ready
AsyncLifecyclePhase::Closed | AsyncLifecyclePhase::ClosedFailed => phase
_ => phase
}
}
///|
fn async_logger_phase_after_run_failure(
phase : AsyncLifecyclePhase,
) -> AsyncLifecyclePhase {
match phase {
AsyncLifecyclePhase::Closing => AsyncLifecyclePhase::ClosedFailed
AsyncLifecyclePhase::Closed | AsyncLifecyclePhase::ClosedFailed =>
AsyncLifecyclePhase::ClosedFailed
_ => AsyncLifecyclePhase::Failed
}
}
///|
pub fn[S] AsyncLogger::phase(self : AsyncLogger[S]) -> AsyncLifecyclePhase {
self.phase.val
}
///|
fn queue_kind_of(config : AsyncLoggerConfig) -> @aqueue.Kind {
let limit = if config.max_pending < 0 { 0 } else { config.max_pending }
@@ -282,7 +355,7 @@ pub async fn[S] AsyncLogger::log(
fields? : Array[@bitlogger.Field] = [],
target? : String = "",
) -> Unit {
guard !(async_runtime_guard_closed_on_log() && self.is_closed()) else { () }
guard !self.is_closed() else { () }
guard self.is_enabled(level) else { () }
let actual_target = if target == "" { self.target } else { target }
let timestamp_ms = if self.timestamp { @env.now() } else { 0UL }
@@ -378,17 +451,17 @@ pub fn[S] AsyncLogger::dropped_count(self : AsyncLogger[S]) -> Int {
///|
pub fn[S] AsyncLogger::is_closed(self : AsyncLogger[S]) -> Bool {
self.is_closed.val
async_logger_phase_is_closed(self.phase())
}
///|
pub fn[S] AsyncLogger::is_running(self : AsyncLogger[S]) -> Bool {
self.is_running.val
async_logger_phase_is_running(self.phase())
}
///|
pub fn[S] AsyncLogger::has_failed(self : AsyncLogger[S]) -> Bool {
self.has_failed.val
async_logger_phase_has_failed(self.phase())
}
///|
@@ -405,11 +478,9 @@ pub fn[S] AsyncLogger::flush_policy(self : AsyncLogger[S]) -> AsyncFlushPolicy {
pub fn[S] AsyncLogger::state(self : AsyncLogger[S]) -> AsyncLoggerState {
AsyncLoggerState::new(
async_runtime_state(),
self.phase(),
self.pending_count(),
self.dropped_count(),
self.is_closed(),
self.is_running(),
self.has_failed(),
self.last_error(),
self.flush_policy(),
)
@@ -420,7 +491,7 @@ pub fn[S] AsyncLogger::close(
self : AsyncLogger[S],
clear? : Bool = false,
) -> Unit {
self.is_closed.val = true
self.phase.val = async_logger_phase_after_close(self.phase())
if clear {
let abandoned = self.pending_count()
if abandoned > 0 {
@@ -450,20 +521,22 @@ pub async fn[S] AsyncLogger::shutdown(
self.close(clear=true)
} else {
self.wait_idle()
if async_runtime_shutdown_clears_pending_after_wait_idle() &&
self.pending_count() > 0 {
if self.pending_count() > 0 {
self.close(clear=true)
} else {
self.close()
}
}
if async_runtime_shutdown_waits_for_worker() {
while self.is_running() {
@async.pause()
}
while self.is_running() {
@async.pause()
}
}
///|
fn[S] run_flush_callback(logger : AsyncLogger[S]) -> Unit raise {
(logger.flush_callback)(logger.sink)
}
///|
async fn[S : @bitlogger.Sink] run_worker(logger : AsyncLogger[S]) -> Unit {
while true {
@@ -512,12 +585,12 @@ async fn[S : @bitlogger.Sink] run_worker(logger : AsyncLogger[S]) -> Unit {
}
}
match logger.flush_policy {
AsyncFlushPolicy::Batch => ignore((logger.flush_sink)(logger.sink))
AsyncFlushPolicy::Batch => run_flush_callback(logger)
_ => ()
}
}
match logger.flush_policy {
AsyncFlushPolicy::Shutdown => ignore((logger.flush_sink)(logger.sink))
AsyncFlushPolicy::Shutdown => run_flush_callback(logger)
_ => ()
}
}
@@ -526,18 +599,18 @@ async fn[S : @bitlogger.Sink] run_worker(logger : AsyncLogger[S]) -> Unit {
pub async fn[S : @bitlogger.Sink] AsyncLogger::run(
self : AsyncLogger[S],
) -> Unit {
self.is_running.val = true
self.has_failed.val = false
guard !self.is_closed() else { raise AsyncLoggerClosed }
guard !self.is_running() else { raise AsyncLoggerAlreadyRunning }
self.phase.val = AsyncLifecyclePhase::Running
self.last_error.val = ""
run_worker(self) catch {
err => {
self.has_failed.val = true
self.phase.val = async_logger_phase_after_run_failure(self.phase())
self.last_error.val = err.to_string()
self.is_running.val = false
raise err
}
}
self.is_running.val = false
self.phase.val = async_logger_phase_after_run_exit(self.phase())
}
///|
@@ -550,7 +623,7 @@ pub fn build_async_logger(
config=config.async_config,
min_level=logger.min_level,
target=logger.target,
flush=fn(sink) { sink.flush() },
flush=fn(sink) { ignore(sink.flush_progress()) },
).with_timestamp(enabled=logger.timestamp)
}
-15
View File
@@ -8,18 +8,3 @@ pub fn async_runtime_supports_background_worker() -> Bool {
ignore(all_async_runtime_modes())
false
}
///|
fn async_runtime_guard_closed_on_log() -> Bool {
true
}
///|
fn async_runtime_shutdown_clears_pending_after_wait_idle() -> Bool {
false
}
///|
fn async_runtime_shutdown_waits_for_worker() -> Bool {
false
}
+1 -1
View File
@@ -21,7 +21,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] {
library_async_logger(async_logger(sink, config~, min_level~, target~, flush~))
}
+157
View File
@@ -0,0 +1,157 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src-async"
import {
"Nanaloveyuki/BitLogger/src",
"Nanaloveyuki/BitLogger/src-async/utils",
"Nanaloveyuki/BitLogger/src/core",
"maria/json_parser",
"moonbitlang/async/aqueue",
"moonbitlang/core/ref",
}
// Values
pub fn[S] async_logger(S, config? : @utils.AsyncLoggerConfig, min_level? : @core.Level, target? : String, flush? : (S) -> Unit raise) -> AsyncLogger[S]
pub fn async_logger_build_config_to_json(@utils.AsyncLoggerBuildConfig) -> @json_parser.JsonValue
pub fn async_logger_config_to_json(@utils.AsyncLoggerConfig) -> @json_parser.JsonValue
pub fn async_logger_state_to_json(@utils.AsyncLoggerState) -> @json_parser.JsonValue
pub fn async_runtime_mode() -> @utils.AsyncRuntimeMode
pub fn async_runtime_mode_label(@utils.AsyncRuntimeMode) -> String
pub fn async_runtime_state() -> @utils.AsyncRuntimeState
pub fn async_runtime_state_to_json(@utils.AsyncRuntimeState) -> @json_parser.JsonValue
pub fn async_runtime_supports_background_worker() -> Bool
pub fn build_application_async_logger(@utils.AsyncLoggerBuildConfig) -> AsyncLogger[@src.RuntimeSink]
pub fn build_application_text_async_logger(@utils.AsyncLoggerBuildConfig) -> AsyncLogger[@src.FormattedConsoleSink]
pub fn build_async_logger(@utils.AsyncLoggerBuildConfig) -> AsyncLogger[@src.RuntimeSink]
pub fn build_async_text_logger(@utils.AsyncLoggerBuildConfig) -> AsyncLogger[@src.FormattedConsoleSink]
pub fn build_library_async_logger(@utils.AsyncLoggerBuildConfig) -> LibraryAsyncLogger[@src.RuntimeSink]
pub fn build_library_async_text_logger(@utils.AsyncLoggerBuildConfig) -> LibraryAsyncLogger[@src.FormattedConsoleSink]
pub fn parse_and_build_application_async_logger(String) -> AsyncLogger[@src.RuntimeSink] raise
pub fn parse_and_build_library_async_logger(String) -> LibraryAsyncLogger[@src.RuntimeSink] raise
pub fn parse_async_logger_build_config_text(String) -> @utils.AsyncLoggerBuildConfig raise
pub fn parse_async_logger_config_text(String) -> @utils.AsyncLoggerConfig raise
pub fn stringify_async_logger_build_config(@utils.AsyncLoggerBuildConfig, pretty? : Bool) -> String
pub fn stringify_async_logger_config(@utils.AsyncLoggerConfig, pretty? : Bool) -> String
pub fn stringify_async_logger_state(@utils.AsyncLoggerState, pretty? : Bool) -> String
pub fn stringify_async_runtime_state(@utils.AsyncRuntimeState, pretty? : Bool) -> String
// Errors
pub(all) suberror AsyncLoggerAlreadyRunning {
AsyncLoggerAlreadyRunning
}
pub(all) suberror AsyncLoggerClosed {
AsyncLoggerClosed
}
// Types and methods
pub struct AsyncLogger[S] {
min_level : @core.Level
target : String
timestamp : Bool
overflow : @utils.AsyncOverflowPolicy
max_batch : Int
linger_ms : Int
flush_policy : @utils.AsyncFlushPolicy
sink : S
flush_callback : (S) -> Unit raise
context_fields : Array[@core.Field]
filter : (@core.Record) -> Bool
patch : (@core.Record) -> @core.Record
queue : @aqueue.Queue[@core.Record]
pending_count : @ref.Ref[Int]
dropped_count : @ref.Ref[Int]
phase : @ref.Ref[@utils.AsyncLifecyclePhase]
last_error : @ref.Ref[String]
}
pub fn[S] AsyncLogger::child(Self[S], String) -> Self[S]
pub fn[S] AsyncLogger::close(Self[S], clear? : Bool) -> Unit
pub async fn[S] AsyncLogger::debug(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] AsyncLogger::dropped_count(Self[S]) -> Int
pub async fn[S] AsyncLogger::error(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] AsyncLogger::flush_policy(Self[S]) -> @utils.AsyncFlushPolicy
pub fn[S] AsyncLogger::has_failed(Self[S]) -> Bool
pub async fn[S] AsyncLogger::info(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] AsyncLogger::is_closed(Self[S]) -> Bool
pub fn[S] AsyncLogger::is_enabled(Self[S], @core.Level) -> Bool
pub fn[S] AsyncLogger::is_running(Self[S]) -> Bool
pub fn[S] AsyncLogger::last_error(Self[S]) -> String
pub async fn[S] AsyncLogger::log(Self[S], @core.Level, String, fields? : Array[@core.Field], target? : String) -> Unit
pub fn[S] AsyncLogger::pending_count(Self[S]) -> Int
pub fn[S] AsyncLogger::phase(Self[S]) -> @utils.AsyncLifecyclePhase
pub async fn[S : @src.Sink] AsyncLogger::run(Self[S]) -> Unit
pub async fn[S] AsyncLogger::shutdown(Self[S], clear? : Bool) -> Unit
pub fn[S] AsyncLogger::state(Self[S]) -> @utils.AsyncLoggerState
pub fn[S] AsyncLogger::to_library_async_logger(Self[S]) -> LibraryAsyncLogger[S]
pub async fn[S] AsyncLogger::trace(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub async fn[S] AsyncLogger::wait_idle(Self[S]) -> Unit
pub async fn[S] AsyncLogger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] AsyncLogger::with_context_fields(Self[S], Array[@core.Field]) -> Self[S]
pub fn[S] AsyncLogger::with_filter(Self[S], (@core.Record) -> Bool) -> Self[S]
pub fn[S] AsyncLogger::with_min_level(Self[S], @core.Level) -> Self[S]
pub fn[S] AsyncLogger::with_patch(Self[S], (@core.Record) -> @core.Record) -> Self[S]
pub fn[S] AsyncLogger::with_target(Self[S], String) -> Self[S]
pub fn[S] AsyncLogger::with_timestamp(Self[S], enabled? : Bool) -> Self[S]
pub struct LibraryAsyncLogger[S] {
inner : AsyncLogger[S]
}
pub fn[S] LibraryAsyncLogger::bind(Self[S], Array[@core.Field]) -> Self[S]
pub fn[S] LibraryAsyncLogger::child(Self[S], String) -> Self[S]
pub async fn[S] LibraryAsyncLogger::error(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub async fn[S] LibraryAsyncLogger::info(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] LibraryAsyncLogger::is_enabled(Self[S], @core.Level) -> Bool
pub async fn[S] LibraryAsyncLogger::log(Self[S], @core.Level, String, fields? : Array[@core.Field], target? : String) -> Unit
pub fn[S] LibraryAsyncLogger::new(S, config? : @utils.AsyncLoggerConfig, min_level? : @core.Level, target? : String, flush? : (S) -> Unit raise) -> Self[S]
pub async fn[S : @src.Sink] LibraryAsyncLogger::run(Self[S]) -> Unit
pub async fn[S] LibraryAsyncLogger::shutdown(Self[S], clear? : Bool) -> Unit
pub fn[S] LibraryAsyncLogger::to_async_logger(Self[S]) -> AsyncLogger[S]
pub async fn[S] LibraryAsyncLogger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] LibraryAsyncLogger::with_context_fields(Self[S], Array[@core.Field]) -> Self[S]
pub fn[S] LibraryAsyncLogger::with_target(Self[S], String) -> Self[S]
// Type aliases
pub type ApplicationAsyncLogger = AsyncLogger[@src.RuntimeSink]
pub type ApplicationTextAsyncLogger = AsyncLogger[@src.FormattedConsoleSink]
pub using @utils {type AsyncFlushPolicy}
pub using @utils {type AsyncLifecyclePhase}
pub using @utils {type AsyncLoggerBuildConfig}
pub using @utils {type AsyncLoggerConfig}
pub using @utils {type AsyncLoggerState}
pub using @utils {type AsyncOverflowPolicy}
pub using @utils {type AsyncRuntimeMode}
pub using @utils {type AsyncRuntimeState}
// Traits
+79 -3
View File
@@ -50,14 +50,74 @@ pub fn AsyncRuntimeState::new(
{ mode, background_worker }
}
///|
pub(all) enum AsyncLifecyclePhase {
Ready
Running
Failed
Closing
Closed
ClosedFailed
}
///|
fn async_lifecycle_phase_label(phase : AsyncLifecyclePhase) -> String {
match phase {
AsyncLifecyclePhase::Ready => "ready"
AsyncLifecyclePhase::Running => "running"
AsyncLifecyclePhase::Failed => "failed"
AsyncLifecyclePhase::Closing => "closing"
AsyncLifecyclePhase::Closed => "closed"
AsyncLifecyclePhase::ClosedFailed => "closed_failed"
}
}
///|
fn async_lifecycle_phase_is_closed(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Closing |
AsyncLifecyclePhase::Closed |
AsyncLifecyclePhase::ClosedFailed => true
_ => false
}
}
///|
fn async_lifecycle_phase_is_running(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Running | AsyncLifecyclePhase::Closing => true
_ => false
}
}
///|
fn async_lifecycle_phase_has_failed(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Failed | AsyncLifecyclePhase::ClosedFailed => true
_ => false
}
}
///|
fn async_lifecycle_phase_can_rerun(phase : AsyncLifecyclePhase) -> Bool {
match phase {
AsyncLifecyclePhase::Ready | AsyncLifecyclePhase::Failed => true
_ => false
}
}
///|
pub struct AsyncLoggerState {
runtime : AsyncRuntimeState
phase : AsyncLifecyclePhase
pending_count : Int
dropped_count : Int
is_closed : Bool
is_running : Bool
has_failed : Bool
backlog_retained : Bool
can_rerun : Bool
terminal : Bool
last_error : String
flush_policy : AsyncFlushPolicy
}
@@ -65,21 +125,29 @@ pub struct AsyncLoggerState {
///|
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 {
let is_closed = async_lifecycle_phase_is_closed(phase)
let is_running = async_lifecycle_phase_is_running(phase)
let has_failed = async_lifecycle_phase_has_failed(phase)
let backlog_retained = pending_count > 0 && !is_running
let can_rerun = async_lifecycle_phase_can_rerun(phase)
let terminal = is_closed && !is_running && pending_count == 0
{
runtime,
phase,
pending_count,
dropped_count,
is_closed,
is_running,
has_failed,
backlog_retained,
can_rerun,
terminal,
last_error,
flush_policy,
}
@@ -123,6 +191,9 @@ fn async_logger_state_to_json_value(
) -> @json_parser.JsonValue {
@json_parser.JsonValue::Object({
"runtime": async_runtime_state_to_json(state.runtime),
"phase": @json_parser.JsonValue::String(
async_lifecycle_phase_label(state.phase),
),
"pending_count": @json_parser.JsonValue::Number(
state.pending_count.to_double(),
),
@@ -132,6 +203,11 @@ fn async_logger_state_to_json_value(
"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),
"backlog_retained": @json_parser.JsonValue::Bool(
state.backlog_retained,
),
"can_rerun": @json_parser.JsonValue::Bool(state.can_rerun),
"terminal": @json_parser.JsonValue::Bool(state.terminal),
"last_error": @json_parser.JsonValue::String(state.last_error),
"flush_policy": @json_parser.JsonValue::String(
async_flush_policy_label(state.flush_policy),
+19 -19
View File
@@ -1,46 +1,46 @@
///|
pub type ConfigError = @utils.ConfigError
pub type ConfigError = @config_model.ConfigError
///|
pub type SinkKind = @utils.SinkKind
pub type SinkKind = @config_model.SinkKind
///|
pub type TextFormatterConfig = @utils.TextFormatterConfig
pub type TextFormatterConfig = @config_model.TextFormatterConfig
///|
pub type QueueConfig = @utils.QueueConfig
pub type QueueConfig = @config_model.QueueConfig
///|
pub type SinkConfig = @utils.SinkConfig
pub type SinkConfig = @config_model.SinkConfig
///|
pub type LoggerConfig = @utils.LoggerConfig
pub type LoggerConfig = @config_model.LoggerConfig
///|
pub fn default_text_formatter_config() -> TextFormatterConfig {
@utils.default_text_formatter_config()
@config_model.default_text_formatter_config()
}
///|
pub fn default_sink_config() -> SinkConfig {
@utils.default_sink_config()
@config_model.default_sink_config()
}
///|
pub fn default_logger_config() -> LoggerConfig {
@utils.default_logger_config()
@config_model.default_logger_config()
}
///|
pub fn parse_logger_config_text(
input : String,
) -> LoggerConfig raise ConfigError {
@utils.parse_logger_config_text(input)
@config_model.parse_logger_config_text(input)
}
///|
pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {
@utils.queue_config_to_json(queue)
@config_model.queue_config_to_json(queue)
}
///|
@@ -48,14 +48,14 @@ pub fn stringify_queue_config(
queue : QueueConfig,
pretty? : Bool = false,
) -> String {
@utils.stringify_queue_config(queue, pretty~)
@config_model.stringify_queue_config(queue, pretty~)
}
///|
pub fn text_formatter_config_to_json(
config : TextFormatterConfig,
) -> @json_parser.JsonValue {
@utils.text_formatter_config_to_json(config)
@config_model.text_formatter_config_to_json(config)
}
///|
@@ -63,19 +63,19 @@ pub fn stringify_text_formatter_config(
config : TextFormatterConfig,
pretty? : Bool = false,
) -> String {
@utils.stringify_text_formatter_config(config, pretty~)
@config_model.stringify_text_formatter_config(config, pretty~)
}
///|
pub fn file_rotation_config_to_json(
config : FileRotation,
) -> @json_parser.JsonValue {
@utils.file_rotation_config_to_json(config)
@file_model.file_rotation_config_to_json(config)
}
///|
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
@utils.sink_config_to_json(config)
@config_model.sink_config_to_json(config)
}
///|
@@ -83,12 +83,12 @@ pub fn stringify_sink_config(
config : SinkConfig,
pretty? : Bool = false,
) -> String {
@utils.stringify_sink_config(config, pretty~)
@config_model.stringify_sink_config(config, pretty~)
}
///|
pub fn logger_config_to_json(config : LoggerConfig) -> @json_parser.JsonValue {
@utils.logger_config_to_json(config)
@config_model.logger_config_to_json(config)
}
///|
@@ -96,5 +96,5 @@ pub fn stringify_logger_config(
config : LoggerConfig,
pretty? : Bool = false,
) -> String {
@utils.stringify_logger_config(config, pretty~)
@config_model.stringify_logger_config(config, pretty~)
}
@@ -20,12 +20,12 @@ pub struct TextFormatterConfig {
separator : String
field_separator : String
template : String
color_mode : ColorMode
color_support : ColorSupport
style_markup : StyleMarkupMode
target_style_markup : StyleMarkupMode
fields_style_markup : StyleMarkupMode
style_tags : Map[String, TextStyle]
color_mode : @formatting.ColorMode
color_support : @formatting.ColorSupport
style_markup : @formatting.StyleMarkupMode
target_style_markup : @formatting.StyleMarkupMode
fields_style_markup : @formatting.StyleMarkupMode
style_tags : Map[String, @formatting.TextStyle]
}
///|
@@ -37,12 +37,12 @@ pub fn TextFormatterConfig::new(
separator? : String = " ",
field_separator? : String = " ",
template? : String = "",
color_mode? : ColorMode = ColorMode::Never,
color_support? : ColorSupport = ColorSupport::TrueColor,
style_markup? : StyleMarkupMode = StyleMarkupMode::Full,
target_style_markup? : StyleMarkupMode = StyleMarkupMode::Disabled,
fields_style_markup? : StyleMarkupMode = StyleMarkupMode::Disabled,
style_tags? : Map[String, TextStyle] = {},
color_mode? : @formatting.ColorMode = @formatting.ColorMode::Never,
color_support? : @formatting.ColorSupport = @formatting.ColorSupport::TrueColor,
style_markup? : @formatting.StyleMarkupMode = @formatting.StyleMarkupMode::Full,
target_style_markup? : @formatting.StyleMarkupMode = @formatting.StyleMarkupMode::Disabled,
fields_style_markup? : @formatting.StyleMarkupMode = @formatting.StyleMarkupMode::Disabled,
style_tags? : Map[String, @formatting.TextStyle] = {},
) -> TextFormatterConfig {
{
show_timestamp,
@@ -63,9 +63,9 @@ pub fn TextFormatterConfig::new(
///|
fn style_tag_registry_from_config(
style_tags : Map[String, TextStyle],
) -> StyleTagRegistry {
let registry = style_tag_registry()
style_tags : Map[String, @formatting.TextStyle],
) -> @formatting.StyleTagRegistry {
let registry = @formatting.style_tag_registry()
for name, style in style_tags {
ignore(registry.set_tag(name, style~))
}
@@ -75,8 +75,8 @@ fn style_tag_registry_from_config(
///|
pub fn TextFormatterConfig::to_formatter(
self : TextFormatterConfig,
) -> TextFormatter {
text_formatter(
) -> @formatting.TextFormatter {
@formatting.text_formatter(
show_timestamp=self.show_timestamp,
show_level=self.show_level,
show_target=self.show_target,
@@ -100,13 +100,13 @@ pub fn TextFormatterConfig::to_formatter(
///|
pub struct QueueConfig {
max_pending : Int
overflow : QueueOverflowPolicy
overflow : @queue_model.QueueOverflowPolicy
}
///|
pub fn QueueConfig::new(
max_pending : Int,
overflow? : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
overflow? : @queue_model.QueueOverflowPolicy = @queue_model.QueueOverflowPolicy::DropNewest,
) -> QueueConfig {
{ max_pending, overflow }
}
@@ -117,7 +117,7 @@ pub struct SinkConfig {
path : String
append : Bool
auto_flush : Bool
rotation : FileRotation?
rotation : @file_model.FileRotation?
text_formatter : TextFormatterConfig
}
@@ -127,7 +127,7 @@ pub fn SinkConfig::new(
path? : String = "",
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
rotation? : @file_model.FileRotation? = None,
text_formatter? : TextFormatterConfig = default_text_formatter_config(),
) -> SinkConfig {
{ kind, path, append, auto_flush, rotation, text_formatter }
@@ -282,11 +282,13 @@ fn parse_level(name : String) -> @core.Level raise ConfigError {
}
///|
fn parse_overflow(name : String) -> QueueOverflowPolicy raise ConfigError {
fn parse_overflow(
name : String,
) -> @queue_model.QueueOverflowPolicy raise ConfigError {
match name.to_upper() {
"DROPNEWEST" => QueueOverflowPolicy::DropNewest
"DROPPOLDEST" => QueueOverflowPolicy::DropOldest
"DROPOLDEST" => QueueOverflowPolicy::DropOldest
"DROPNEWEST" => @queue_model.QueueOverflowPolicy::DropNewest
"DROPPOLDEST" => @queue_model.QueueOverflowPolicy::DropOldest
"DROPOLDEST" => @queue_model.QueueOverflowPolicy::DropOldest
_ =>
raise ConfigError::InvalidConfig(
"Unsupported queue overflow policy: " + name,
@@ -308,30 +310,34 @@ fn parse_sink_kind(name : String) -> SinkKind raise ConfigError {
}
///|
fn parse_color_mode(name : String) -> ColorMode raise ConfigError {
fn parse_color_mode(name : String) -> @formatting.ColorMode raise ConfigError {
match name.to_upper() {
"NEVER" => ColorMode::Never
"AUTO" => ColorMode::Auto
"ALWAYS" => ColorMode::Always
"NEVER" => @formatting.ColorMode::Never
"AUTO" => @formatting.ColorMode::Auto
"ALWAYS" => @formatting.ColorMode::Always
_ => raise ConfigError::InvalidConfig("Unsupported color mode: " + name)
}
}
///|
fn parse_color_support(name : String) -> ColorSupport raise ConfigError {
fn parse_color_support(
name : String,
) -> @formatting.ColorSupport raise ConfigError {
match name.to_upper() {
"BASIC" => ColorSupport::Basic
"TRUECOLOR" => ColorSupport::TrueColor
"BASIC" => @formatting.ColorSupport::Basic
"TRUECOLOR" => @formatting.ColorSupport::TrueColor
_ => raise ConfigError::InvalidConfig("Unsupported color support: " + name)
}
}
///|
fn parse_style_markup_mode(name : String) -> StyleMarkupMode raise ConfigError {
fn parse_style_markup_mode(
name : String,
) -> @formatting.StyleMarkupMode raise ConfigError {
match name.to_upper() {
"DISABLED" => StyleMarkupMode::Disabled
"BUILTIN" => StyleMarkupMode::Builtin
"FULL" => StyleMarkupMode::Full
"DISABLED" => @formatting.StyleMarkupMode::Disabled
"BUILTIN" => @formatting.StyleMarkupMode::Builtin
"FULL" => @formatting.StyleMarkupMode::Full
_ =>
raise ConfigError::InvalidConfig("Unsupported style markup mode: " + name)
}
@@ -384,9 +390,9 @@ fn parse_text_formatter_config(
fn parse_text_style_config(
value : @json_parser.JsonValue,
context : String,
) -> TextStyle raise ConfigError {
) -> @formatting.TextStyle raise ConfigError {
let obj = expect_object(value, context)
text_style(
@formatting.text_style(
fg=get_optional_string(obj, "fg"),
bg=get_optional_string(obj, "bg"),
bold=get_bool(obj, "bold", default=false),
@@ -399,9 +405,9 @@ fn parse_text_style_config(
///|
fn parse_style_tags_config(
value : @json_parser.JsonValue,
) -> Map[String, TextStyle] raise ConfigError {
) -> Map[String, @formatting.TextStyle] raise ConfigError {
let obj = expect_object(value, "text_formatter.style_tags")
let style_tags : Map[String, TextStyle] = {}
let style_tags : Map[String, @formatting.TextStyle] = {}
for name, item in obj {
style_tags[name] = parse_text_style_config(
item,
@@ -425,12 +431,17 @@ fn parse_queue_config(
///|
fn parse_file_rotation_config(
value : @json_parser.JsonValue,
) -> FileRotation raise ConfigError {
) -> @file_model.FileRotation raise ConfigError {
let obj = expect_object(value, "sink.rotation")
let max_backups = get_int(obj, "max_backups", default=1)
match get_optional_int64_string(obj, "max_bytes_i64") {
Some(max_bytes_i64) => file_rotation_i64(max_bytes_i64, max_backups~)
None => file_rotation(get_int(obj, "max_bytes", default=1), max_backups~)
Some(max_bytes_i64) =>
@file_model.file_rotation_i64(max_bytes_i64, max_backups~)
None =>
@file_model.file_rotation(
get_int(obj, "max_bytes", default=1),
max_backups~,
)
}
}
@@ -494,8 +505,8 @@ pub fn queue_config_to_json(queue : QueueConfig) -> @json_parser.JsonValue {
"max_pending": @json_parser.JsonValue::Number(queue.max_pending.to_double()),
"overflow": @json_parser.JsonValue::String(
match queue.overflow {
QueueOverflowPolicy::DropNewest => "DropNewest"
QueueOverflowPolicy::DropOldest => "DropOldest"
@queue_model.QueueOverflowPolicy::DropNewest => "DropNewest"
@queue_model.QueueOverflowPolicy::DropOldest => "DropOldest"
},
),
})
@@ -527,19 +538,19 @@ pub fn text_formatter_config_to_json(
"field_separator": @json_parser.JsonValue::String(config.field_separator),
"template": @json_parser.JsonValue::String(config.template),
"color_mode": @json_parser.JsonValue::String(
color_mode_label(config.color_mode),
@formatting.color_mode_label(config.color_mode),
),
"color_support": @json_parser.JsonValue::String(
color_support_label(config.color_support),
@formatting.color_support_label(config.color_support),
),
"style_markup": @json_parser.JsonValue::String(
style_markup_mode_label(config.style_markup),
@formatting.style_markup_mode_label(config.style_markup),
),
"target_style_markup": @json_parser.JsonValue::String(
style_markup_mode_label(config.target_style_markup),
@formatting.style_markup_mode_label(config.target_style_markup),
),
"fields_style_markup": @json_parser.JsonValue::String(
style_markup_mode_label(config.fields_style_markup),
@formatting.style_markup_mode_label(config.fields_style_markup),
),
}
if config.style_tags.length() != 0 {
@@ -549,7 +560,9 @@ pub fn text_formatter_config_to_json(
}
///|
fn text_style_config_to_json(style : TextStyle) -> @json_parser.JsonValue {
fn text_style_config_to_json(
style : @formatting.TextStyle,
) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
"bold": @json_parser.JsonValue::Bool(style.bold),
"dim": @json_parser.JsonValue::Bool(style.dim),
@@ -569,7 +582,7 @@ fn text_style_config_to_json(style : TextStyle) -> @json_parser.JsonValue {
///|
fn style_tags_config_to_json(
style_tags : Map[String, TextStyle],
style_tags : Map[String, @formatting.TextStyle],
) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {}
for name, style in style_tags {
@@ -591,24 +604,6 @@ pub fn stringify_text_formatter_config(
}
}
///|
pub fn file_rotation_config_to_json(
config : FileRotation,
) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
"max_bytes": @json_parser.JsonValue::Number(config.max_bytes.to_double()),
"max_backups": @json_parser.JsonValue::Number(
config.max_backups.to_double(),
),
}
match config.native_wide_max_bytes {
Some(value) =>
obj["max_bytes_i64"] = @json_parser.JsonValue::String(value.to_string())
None => ()
}
@json_parser.JsonValue::Object(obj)
}
///|
pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
@@ -620,7 +615,8 @@ pub fn sink_config_to_json(config : SinkConfig) -> @json_parser.JsonValue {
}
match config.rotation {
None => ()
Some(rotation) => obj["rotation"] = file_rotation_config_to_json(rotation)
Some(rotation) =>
obj["rotation"] = @file_model.file_rotation_config_to_json(rotation)
}
@json_parser.JsonValue::Object(obj)
}
+8
View File
@@ -0,0 +1,8 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/queue_model",
"maria/json_parser",
"moonbitlang/core/string",
}
+96
View File
@@ -0,0 +1,96 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/config_model"
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/queue_model",
"maria/json_parser",
}
// Values
pub fn default_logger_config() -> LoggerConfig
pub fn default_sink_config() -> SinkConfig
pub fn default_text_formatter_config() -> TextFormatterConfig
pub fn logger_config_to_json(LoggerConfig) -> @json_parser.JsonValue
pub fn parse_logger_config_text(String) -> LoggerConfig raise ConfigError
pub fn queue_config_to_json(QueueConfig) -> @json_parser.JsonValue
pub fn sink_config_to_json(SinkConfig) -> @json_parser.JsonValue
pub fn stringify_logger_config(LoggerConfig, pretty? : Bool) -> String
pub fn stringify_queue_config(QueueConfig, pretty? : Bool) -> String
pub fn stringify_sink_config(SinkConfig, pretty? : Bool) -> String
pub fn stringify_text_formatter_config(TextFormatterConfig, pretty? : Bool) -> String
pub fn text_formatter_config_to_json(TextFormatterConfig) -> @json_parser.JsonValue
// Errors
pub(all) suberror ConfigError {
InvalidConfig(String)
}
// Types and methods
pub struct LoggerConfig {
min_level : @core.Level
target : String
timestamp : Bool
sink : SinkConfig
queue : QueueConfig?
}
pub fn LoggerConfig::new(min_level? : @core.Level, target? : String, timestamp? : Bool, sink? : SinkConfig, queue? : QueueConfig?) -> Self
pub struct QueueConfig {
max_pending : Int
overflow : @queue_model.QueueOverflowPolicy
}
pub fn QueueConfig::new(Int, overflow? : @queue_model.QueueOverflowPolicy) -> Self
pub struct SinkConfig {
kind : SinkKind
path : String
append : Bool
auto_flush : Bool
rotation : @file_model.FileRotation?
text_formatter : TextFormatterConfig
}
pub fn SinkConfig::new(kind? : SinkKind, path? : String, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, text_formatter? : TextFormatterConfig) -> Self
pub(all) enum SinkKind {
Console
JsonConsole
TextConsole
File
}
pub struct TextFormatterConfig {
show_timestamp : Bool
show_level : Bool
show_target : Bool
show_fields : Bool
separator : String
field_separator : String
template : String
color_mode : @formatting.ColorMode
color_support : @formatting.ColorSupport
style_markup : @formatting.StyleMarkupMode
target_style_markup : @formatting.StyleMarkupMode
fields_style_markup : @formatting.StyleMarkupMode
style_tags : Map[String, @formatting.TextStyle]
}
pub fn TextFormatterConfig::new(show_timestamp? : Bool, show_level? : Bool, show_target? : Bool, show_fields? : Bool, separator? : String, field_separator? : String, template? : String, color_mode? : @formatting.ColorMode, color_support? : @formatting.ColorSupport, style_markup? : @formatting.StyleMarkupMode, target_style_markup? : @formatting.StyleMarkupMode, fields_style_markup? : @formatting.StyleMarkupMode, style_tags? : Map[String, @formatting.TextStyle]) -> Self
pub fn TextFormatterConfig::to_formatter(Self) -> @formatting.TextFormatter
// Type aliases
// Traits
-47
View File
@@ -1,47 +0,0 @@
///|
type FileHandle = @utils.FileHandle
///|
fn open_file_handle_internal(path : String, append : Bool) -> FileHandle? {
@utils.open_file_handle_internal(path, append)
}
///|
fn write_file_handle_internal(handle : FileHandle, content : String) -> Bool {
@utils.write_file_handle_internal(handle, content)
}
///|
fn flush_file_handle_internal(handle : FileHandle) -> Bool {
@utils.flush_file_handle_internal(handle)
}
///|
fn close_file_handle_internal(handle : FileHandle) -> Bool {
@utils.close_file_handle_internal(handle)
}
///|
fn file_size_i64_internal(handle : FileHandle) -> Int64 {
@utils.file_size_i64_internal(handle)
}
///|
fn rename_file_internal(from_path : String, to_path : String) -> Bool {
@utils.rename_file_internal(from_path, to_path)
}
///|
fn remove_file_internal(path : String) -> Bool {
@utils.remove_file_internal(path)
}
///|
fn string_byte_length_internal(content : String) -> Int {
@utils.string_byte_length_internal(content)
}
///|
fn native_files_supported_internal() -> Bool {
@utils.native_files_supported_internal()
}
-47
View File
@@ -1,47 +0,0 @@
///|
type FileHandle = @utils.FileHandle
///|
fn open_file_handle_internal(path : String, append : Bool) -> FileHandle? {
@utils.open_file_handle_internal(path, append)
}
///|
fn write_file_handle_internal(handle : FileHandle, content : String) -> Bool {
@utils.write_file_handle_internal(handle, content)
}
///|
fn flush_file_handle_internal(handle : FileHandle) -> Bool {
@utils.flush_file_handle_internal(handle)
}
///|
fn close_file_handle_internal(handle : FileHandle) -> Bool {
@utils.close_file_handle_internal(handle)
}
///|
fn file_size_i64_internal(handle : FileHandle) -> Int64 {
@utils.file_size_i64_internal(handle)
}
///|
fn rename_file_internal(from_path : String, to_path : String) -> Bool {
@utils.rename_file_internal(from_path, to_path)
}
///|
fn remove_file_internal(path : String) -> Bool {
@utils.remove_file_internal(path)
}
///|
fn string_byte_length_internal(content : String) -> Int {
@utils.string_byte_length_internal(content)
}
///|
fn native_files_supported_internal() -> Bool {
@utils.native_files_supported_internal()
}
@@ -1,23 +1,23 @@
///|
pub struct RuntimeFileState {
file : FileSinkState
queued : Bool
pending_count : Int
dropped_count : Int
pub fn file_rotation_config_to_json(
config : FileRotation,
) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
"max_bytes": @json_parser.JsonValue::Number(config.max_bytes.to_double()),
"max_backups": @json_parser.JsonValue::Number(
config.max_backups.to_double(),
),
}
match config.native_wide_max_bytes {
Some(value) =>
obj["max_bytes_i64"] = @json_parser.JsonValue::String(value.to_string())
None => ()
}
@json_parser.JsonValue::Object(obj)
}
///|
pub fn RuntimeFileState::new(
file : FileSinkState,
queued? : Bool = false,
pending_count? : Int = 0,
dropped_count? : Int = 0,
) -> RuntimeFileState {
{ file, queued, pending_count, dropped_count }
}
///|
fn file_sink_policy_to_json_value(
pub fn file_sink_policy_to_json(
policy : FileSinkPolicy,
) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
@@ -31,19 +31,12 @@ fn file_sink_policy_to_json_value(
@json_parser.JsonValue::Object(obj)
}
///|
pub fn file_sink_policy_to_json(
policy : FileSinkPolicy,
) -> @json_parser.JsonValue {
file_sink_policy_to_json_value(policy)
}
///|
pub fn stringify_file_sink_policy(
policy : FileSinkPolicy,
pretty? : Bool = false,
) -> String {
let value = file_sink_policy_to_json_value(policy)
let value = file_sink_policy_to_json(policy)
if pretty {
@json_parser.stringify_pretty(value, 2)
} else {
@@ -52,9 +45,7 @@ pub fn stringify_file_sink_policy(
}
///|
fn file_sink_state_to_json_value(
state : FileSinkState,
) -> @json_parser.JsonValue {
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
let obj : Map[String, @json_parser.JsonValue] = {
"path": @json_parser.JsonValue::String(state.path),
"available": @json_parser.JsonValue::Bool(state.available),
@@ -80,46 +71,12 @@ fn file_sink_state_to_json_value(
@json_parser.JsonValue::Object(obj)
}
///|
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
file_sink_state_to_json_value(state)
}
///|
pub fn stringify_file_sink_state(
state : FileSinkState,
pretty? : Bool = false,
) -> String {
let value = file_sink_state_to_json_value(state)
if pretty {
@json_parser.stringify_pretty(value, 2)
} else {
@json_parser.stringify(value)
}
}
///|
pub fn runtime_file_state_to_json(
state : RuntimeFileState,
) -> @json_parser.JsonValue {
@json_parser.JsonValue::Object({
"file": file_sink_state_to_json_value(state.file),
"queued": @json_parser.JsonValue::Bool(state.queued),
"pending_count": @json_parser.JsonValue::Number(
state.pending_count.to_double(),
),
"dropped_count": @json_parser.JsonValue::Number(
state.dropped_count.to_double(),
),
})
}
///|
pub fn stringify_runtime_file_state(
state : RuntimeFileState,
pretty? : Bool = false,
) -> String {
let value = runtime_file_state_to_json(state)
let value = file_sink_state_to_json(state)
if pretty {
@json_parser.stringify_pretty(value, 2)
} else {
+99
View File
@@ -0,0 +1,99 @@
///|
pub struct FileRotation {
max_bytes : Int
max_backups : Int
native_wide_max_bytes : Int64?
}
///|
pub struct FileSinkState {
path : String
available : Bool
append : Bool
auto_flush : Bool
rotation : FileRotation?
open_failures : Int
write_failures : Int
flush_failures : Int
rotation_failures : Int
}
///|
pub struct FileSinkPolicy {
append : Bool
auto_flush : Bool
rotation : FileRotation?
}
///|
pub fn FileSinkPolicy::new(
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
) -> FileSinkPolicy {
{ append, auto_flush, rotation }
}
///|
pub fn FileSinkState::new(
path : String,
available? : Bool = false,
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
open_failures? : Int = 0,
write_failures? : Int = 0,
flush_failures? : Int = 0,
rotation_failures? : Int = 0,
) -> FileSinkState {
{
path,
available,
append,
auto_flush,
rotation,
open_failures,
write_failures,
flush_failures,
rotation_failures,
}
}
///|
pub fn file_rotation(max_bytes : Int, max_backups? : Int = 1) -> FileRotation {
{
max_bytes: if max_bytes <= 0 {
1
} else {
max_bytes
},
max_backups: if max_backups <= 0 {
1
} else {
max_backups
},
native_wide_max_bytes: None,
}
}
///|
pub fn file_rotation_i64(
max_bytes : Int64,
max_backups? : Int = 1,
) -> FileRotation {
let normalized = if max_bytes <= 0L { 1L } else { max_bytes }
let clamped_max_bytes = if normalized > 2147483647L {
2147483647
} else {
normalized.to_int()
}
{
max_bytes: clamped_max_bytes,
max_backups: if max_backups <= 0 {
1
} else {
max_backups
},
native_wide_max_bytes: Some(normalized),
}
}
+3
View File
@@ -0,0 +1,3 @@
import {
"maria/json_parser",
}
+67
View File
@@ -0,0 +1,67 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/file_model"
import {
"maria/json_parser",
}
// Values
pub fn file_rotation(Int, max_backups? : Int) -> FileRotation
pub fn file_rotation_config_to_json(FileRotation) -> @json_parser.JsonValue
pub fn file_rotation_i64(Int64, max_backups? : Int) -> FileRotation
pub fn file_sink_policy_to_json(FileSinkPolicy) -> @json_parser.JsonValue
pub fn file_sink_state_to_json(FileSinkState) -> @json_parser.JsonValue
pub fn runtime_file_state_to_json(RuntimeFileState) -> @json_parser.JsonValue
pub fn stringify_file_sink_policy(FileSinkPolicy, pretty? : Bool) -> String
pub fn stringify_file_sink_state(FileSinkState, pretty? : Bool) -> String
pub fn stringify_runtime_file_state(RuntimeFileState, pretty? : Bool) -> String
// Errors
// Types and methods
pub struct FileRotation {
max_bytes : Int
max_backups : Int
native_wide_max_bytes : Int64?
}
pub struct FileSinkPolicy {
append : Bool
auto_flush : Bool
rotation : FileRotation?
}
pub fn FileSinkPolicy::new(append? : Bool, auto_flush? : Bool, rotation? : FileRotation?) -> Self
pub struct FileSinkState {
path : String
available : Bool
append : Bool
auto_flush : Bool
rotation : FileRotation?
open_failures : Int
write_failures : Int
flush_failures : Int
rotation_failures : Int
}
pub fn FileSinkState::new(String, available? : Bool, append? : Bool, auto_flush? : Bool, rotation? : FileRotation?, open_failures? : Int, write_failures? : Int, flush_failures? : Int, rotation_failures? : Int) -> Self
pub struct RuntimeFileState {
file : FileSinkState
queued : Bool
pending_count : Int
dropped_count : Int
}
pub fn RuntimeFileState::new(FileSinkState, queued? : Bool, pending_count? : Int, dropped_count? : Int) -> Self
// Type aliases
// Traits
+46
View File
@@ -0,0 +1,46 @@
///|
pub struct RuntimeFileState {
file : FileSinkState
queued : Bool
pending_count : Int
dropped_count : Int
}
///|
pub fn RuntimeFileState::new(
file : FileSinkState,
queued? : Bool = false,
pending_count? : Int = 0,
dropped_count? : Int = 0,
) -> RuntimeFileState {
{ file, queued, pending_count, dropped_count }
}
///|
pub fn runtime_file_state_to_json(
state : RuntimeFileState,
) -> @json_parser.JsonValue {
@json_parser.JsonValue::Object({
"file": file_sink_state_to_json(state.file),
"queued": @json_parser.JsonValue::Bool(state.queued),
"pending_count": @json_parser.JsonValue::Number(
state.pending_count.to_double(),
),
"dropped_count": @json_parser.JsonValue::Number(
state.dropped_count.to_double(),
),
})
}
///|
pub fn stringify_runtime_file_state(
state : RuntimeFileState,
pretty? : Bool = false,
) -> String {
let value = runtime_file_state_to_json(state)
if pretty {
@json_parser.stringify_pretty(value, 2)
} else {
@json_parser.stringify(value)
}
}
+385
View File
@@ -0,0 +1,385 @@
///|
type FileHandle = @utils.FileHandle
///|
type FileRotation = @file_model.FileRotation
///|
type FileSinkState = @file_model.FileSinkState
///|
type FileSinkPolicy = @file_model.FileSinkPolicy
///|
type Record = @core.Record
///|
type RecordFormatter = @formatting.RecordFormatter
///|
pub struct FileSink {
priv path : String
priv append : Ref[Bool]
priv default_append : Bool
priv handle : Ref[FileHandle?]
priv formatter : RecordFormatter
priv auto_flush : Ref[Bool]
priv default_auto_flush : Bool
priv rotation : Ref[FileRotation?]
priv default_rotation : FileRotation?
priv open_failures : Ref[Int]
priv write_failures : Ref[Int]
priv flush_failures : Ref[Int]
priv rotation_failures : Ref[Int]
}
///|
pub fn native_files_supported() -> Bool {
@utils.native_files_supported_internal()
}
///|
pub fn file_sink(
path : String,
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
formatter? : RecordFormatter = fn(rec) { @formatting.format_text(rec) },
) -> FileSink {
let handle = @utils.open_file_handle_internal(path, append)
{
path,
append: Ref(append),
default_append: append,
handle: Ref(handle),
formatter,
auto_flush: Ref(auto_flush),
default_auto_flush: auto_flush,
rotation: Ref(rotation),
default_rotation: rotation,
open_failures: Ref(if handle is Some(_) { 0 } else { 1 }),
write_failures: Ref(0),
flush_failures: Ref(0),
rotation_failures: Ref(0),
}
}
///|
pub fn FileSink::is_available(self : FileSink) -> Bool {
self.handle.val is Some(_)
}
///|
pub fn FileSink::flush(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = @utils.flush_file_handle_internal(handle)
if !ok {
self.flush_failures.val += 1
}
ok
}
}
}
///|
pub fn FileSink::append_mode(self : FileSink) -> Bool {
self.append.val
}
///|
pub fn FileSink::set_append_mode(self : FileSink, append : Bool) -> Unit {
self.append.val = append
}
///|
pub fn FileSink::path(self : FileSink) -> String {
self.path
}
///|
pub fn FileSink::auto_flush_enabled(self : FileSink) -> Bool {
self.auto_flush.val
}
///|
pub fn FileSink::rotation_enabled(self : FileSink) -> Bool {
self.rotation.val is Some(_)
}
///|
pub fn FileSink::rotation_config(self : FileSink) -> FileRotation? {
self.rotation.val
}
///|
pub fn FileSink::set_auto_flush(self : FileSink, enabled : Bool) -> Unit {
self.auto_flush.val = enabled
}
///|
pub fn FileSink::set_policy(self : FileSink, policy : FileSinkPolicy) -> Unit {
self.append.val = policy.append
self.auto_flush.val = policy.auto_flush
self.rotation.val = policy.rotation
}
///|
pub fn FileSink::set_rotation(
self : FileSink,
rotation : FileRotation?,
) -> Unit {
self.rotation.val = rotation
}
///|
pub fn FileSink::clear_rotation(self : FileSink) -> Unit {
self.rotation.val = None
}
///|
pub fn FileSink::close(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = @utils.close_file_handle_internal(handle)
self.handle.val = None
ok
}
}
}
///|
pub fn FileSink::rotation_failures(self : FileSink) -> Int {
self.rotation_failures.val
}
///|
pub fn FileSink::open_failures(self : FileSink) -> Int {
self.open_failures.val
}
///|
pub fn FileSink::write_failures(self : FileSink) -> Int {
self.write_failures.val
}
///|
pub fn FileSink::flush_failures(self : FileSink) -> Int {
self.flush_failures.val
}
///|
pub fn FileSink::reset_failure_counters(self : FileSink) -> Unit {
self.open_failures.val = 0
self.write_failures.val = 0
self.flush_failures.val = 0
self.rotation_failures.val = 0
}
///|
pub fn FileSink::reset_policy(self : FileSink) -> Unit {
self.append.val = self.default_append
self.auto_flush.val = self.default_auto_flush
self.rotation.val = self.default_rotation
}
///|
pub fn FileSink::policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.append.val,
auto_flush=self.auto_flush.val,
rotation=self.rotation.val,
)
}
///|
pub fn FileSink::default_policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.default_append,
auto_flush=self.default_auto_flush,
rotation=self.default_rotation,
)
}
///|
pub fn FileSink::policy_matches_default(self : FileSink) -> Bool {
let current = self.policy()
let default = self.default_policy()
current.append == default.append &&
current.auto_flush == default.auto_flush &&
policy_rotation_equals_internal(current.rotation, default.rotation)
}
///|
fn policy_rotation_equals_internal(
left : FileRotation?,
right : FileRotation?,
) -> Bool {
match (left, right) {
(None, None) => true
(Some(a), Some(b)) =>
a.max_bytes == b.max_bytes &&
a.max_backups == b.max_backups &&
a.native_wide_max_bytes == b.native_wide_max_bytes
_ => false
}
}
///|
fn rotation_max_bytes_internal(rotation : FileRotation) -> Int64 {
match rotation.native_wide_max_bytes {
Some(value) => value
None => rotation.max_bytes.to_int64()
}
}
///|
pub fn FileSink::state(self : FileSink) -> FileSinkState {
FileSinkState::new(
self.path,
available=self.is_available(),
append=self.append.val,
auto_flush=self.auto_flush.val,
rotation=self.rotation.val,
open_failures=self.open_failures.val,
write_failures=self.write_failures.val,
flush_failures=self.flush_failures.val,
rotation_failures=self.rotation_failures.val,
)
}
///|
pub fn FileSink::reopen(self : FileSink, append? : Bool? = None) -> Bool {
let append_mode = append.unwrap_or(self.append.val)
self.append.val = append_mode
match self.handle.val {
None => ()
Some(handle) => {
ignore(@utils.close_file_handle_internal(handle))
self.handle.val = None
}
}
let reopened = @utils.open_file_handle_internal(self.path, append_mode)
self.handle.val = reopened
if reopened is Some(_) {
true
} else {
self.open_failures.val += 1
false
}
}
///|
pub fn FileSink::reopen_with_current_policy(self : FileSink) -> Bool {
self.reopen()
}
///|
pub fn FileSink::reopen_append(self : FileSink) -> Bool {
self.reopen(append=Some(true))
}
///|
pub fn FileSink::reopen_truncate(self : FileSink) -> Bool {
self.reopen(append=Some(false))
}
///|
fn rotated_file_path(path : String, index : Int) -> String {
"\{path}.\{index}"
}
///|
fn rotate_file_sink_internal(sink : FileSink, rotation : FileRotation) -> Bool {
let closed = match sink.handle.val {
None => true
Some(handle) => {
let ok = @utils.close_file_handle_internal(handle)
sink.handle.val = None
ok
}
}
if !closed {
return false
}
if rotation.max_backups > 0 {
ignore(
@utils.remove_file_internal(
rotated_file_path(sink.path, rotation.max_backups),
),
)
for index = rotation.max_backups - 1; index >= 1; {
let from_path = rotated_file_path(sink.path, index)
let to_path = rotated_file_path(sink.path, index + 1)
ignore(@utils.rename_file_internal(from_path, to_path))
continue index - 1
}
ignore(
@utils.rename_file_internal(sink.path, rotated_file_path(sink.path, 1)),
)
} else {
ignore(@utils.remove_file_internal(sink.path))
}
sink.handle.val = @utils.open_file_handle_internal(sink.path, false)
sink.handle.val is Some(_)
}
///|
fn rotate_if_needed_internal(sink : FileSink, next_line_bytes : Int) -> Bool {
match sink.rotation.val {
None => true
Some(rotation) =>
match sink.handle.val {
None => false
Some(handle) => {
let size = @utils.file_size_i64_internal(handle)
let next_line = next_line_bytes.to_int64()
if size + next_line <= rotation_max_bytes_internal(rotation) {
true
} else {
let rotated = rotate_file_sink_internal(sink, rotation)
if !rotated {
sink.rotation_failures.val += 1
}
rotated
}
}
}
}
}
///|
pub impl @sink_graph.Sink for FileSink with fn write(self, rec : Record) {
match self.handle.val {
None => self.write_failures.val += 1
Some(_) => {
let line = "\{(self.formatter)(rec)}\n"
let can_write = rotate_if_needed_internal(
self,
@utils.string_byte_length_internal(line),
)
if can_write {
match self.handle.val {
None => self.write_failures.val += 1
Some(active) => {
let wrote = @utils.write_file_handle_internal(active, line)
if wrote {
if self.auto_flush.val {
let flushed = @utils.flush_file_handle_internal(active)
if !flushed {
self.flush_failures.val += 1
}
}
} else {
self.write_failures.val += 1
}
}
}
} else {
self.write_failures.val += 1
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/sink_graph",
"Nanaloveyuki/BitLogger/src/utils",
"moonbitlang/core/ref",
}
+53
View File
@@ -0,0 +1,53 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/file_runtime"
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/sink_graph",
}
// Values
pub fn file_sink(String, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, formatter? : (@core.Record) -> String) -> FileSink
pub fn native_files_supported() -> Bool
// Errors
// Types and methods
pub struct FileSink {
// private fields
}
pub fn FileSink::append_mode(Self) -> Bool
pub fn FileSink::auto_flush_enabled(Self) -> Bool
pub fn FileSink::clear_rotation(Self) -> Unit
pub fn FileSink::close(Self) -> Bool
pub fn FileSink::default_policy(Self) -> @file_model.FileSinkPolicy
pub fn FileSink::flush(Self) -> Bool
pub fn FileSink::flush_failures(Self) -> Int
pub fn FileSink::is_available(Self) -> Bool
pub fn FileSink::open_failures(Self) -> Int
pub fn FileSink::path(Self) -> String
pub fn FileSink::policy(Self) -> @file_model.FileSinkPolicy
pub fn FileSink::policy_matches_default(Self) -> Bool
pub fn FileSink::reopen(Self, append? : Bool?) -> Bool
pub fn FileSink::reopen_append(Self) -> Bool
pub fn FileSink::reopen_truncate(Self) -> Bool
pub fn FileSink::reopen_with_current_policy(Self) -> Bool
pub fn FileSink::reset_failure_counters(Self) -> Unit
pub fn FileSink::reset_policy(Self) -> Unit
pub fn FileSink::rotation_config(Self) -> @file_model.FileRotation?
pub fn FileSink::rotation_enabled(Self) -> Bool
pub fn FileSink::rotation_failures(Self) -> Int
pub fn FileSink::set_append_mode(Self, Bool) -> Unit
pub fn FileSink::set_auto_flush(Self, Bool) -> Unit
pub fn FileSink::set_policy(Self, @file_model.FileSinkPolicy) -> Unit
pub fn FileSink::set_rotation(Self, @file_model.FileRotation?) -> Unit
pub fn FileSink::state(Self) -> @file_model.FileSinkState
pub fn FileSink::write_failures(Self) -> Int
pub impl @sink_graph.Sink for FileSink
// Type aliases
// Traits
+10 -10
View File
@@ -1,47 +1,47 @@
///|
pub type RecordPredicate = @utils.RecordPredicate
pub type RecordPredicate = @record_ops.RecordPredicate
///|
pub fn level_at_least(min_level : Level) -> RecordPredicate {
@utils.level_at_least(min_level)
@record_ops.level_at_least(min_level)
}
///|
pub fn target_is(target : String) -> RecordPredicate {
@utils.target_is(target)
@record_ops.target_is(target)
}
///|
pub fn target_has_prefix(prefix : String) -> RecordPredicate {
@utils.target_has_prefix(prefix)
@record_ops.target_has_prefix(prefix)
}
///|
pub fn message_contains(fragment : String) -> RecordPredicate {
@utils.message_contains(fragment)
@record_ops.message_contains(fragment)
}
///|
pub fn has_field(key : String) -> RecordPredicate {
@utils.has_field(key)
@record_ops.has_field(key)
}
///|
pub fn field_equals(key : String, value : String) -> RecordPredicate {
@utils.field_equals(key, value)
@record_ops.field_equals(key, value)
}
///|
pub fn not_(predicate : RecordPredicate) -> RecordPredicate {
@utils.not_(predicate)
@record_ops.not_(predicate)
}
///|
pub fn all_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
@utils.all_of(predicates)
@record_ops.all_of(predicates)
}
///|
pub fn any_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
@utils.any_of(predicates)
@record_ops.any_of(predicates)
}
+19 -19
View File
@@ -1,17 +1,17 @@
///|
pub type RecordFormatter = @utils.RecordFormatter
pub type RecordFormatter = @formatting.RecordFormatter
///|
pub type ColorMode = @utils.ColorMode
pub type ColorMode = @formatting.ColorMode
///|
pub type ColorSupport = @utils.ColorSupport
pub type ColorSupport = @formatting.ColorSupport
///|
pub type StyleMarkupMode = @utils.StyleMarkupMode
pub type StyleMarkupMode = @formatting.StyleMarkupMode
///|
pub type TextStyle = @utils.TextStyle
pub type TextStyle = @formatting.TextStyle
///|
pub fn text_style(
@@ -22,39 +22,39 @@ pub fn text_style(
italic? : Bool = false,
underline? : Bool = false,
) -> TextStyle {
@utils.text_style(fg~, bg~, bold~, dim~, italic~, underline~)
@formatting.text_style(fg~, bg~, bold~, dim~, italic~, underline~)
}
///|
pub type StyleTagRegistry = @utils.StyleTagRegistry
pub type StyleTagRegistry = @formatting.StyleTagRegistry
///|
pub fn style_tag_registry() -> StyleTagRegistry {
@utils.style_tag_registry()
@formatting.style_tag_registry()
}
///|
pub fn default_style_tag_registry() -> StyleTagRegistry {
@utils.default_style_tag_registry()
@formatting.default_style_tag_registry()
}
///|
pub fn global_style_tag_registry() -> StyleTagRegistry {
@utils.global_style_tag_registry()
@formatting.global_style_tag_registry()
}
///|
pub fn set_global_style_tag_registry(registry : StyleTagRegistry) -> Unit {
@utils.set_global_style_tag_registry(registry)
@formatting.set_global_style_tag_registry(registry)
}
///|
pub fn reset_global_style_tag_registry() -> Unit {
@utils.reset_global_style_tag_registry()
@formatting.reset_global_style_tag_registry()
}
///|
pub type TextFormatter = @utils.TextFormatter
pub type TextFormatter = @formatting.TextFormatter
///|
pub fn text_formatter(
@@ -72,7 +72,7 @@ pub fn text_formatter(
fields_style_markup? : StyleMarkupMode = StyleMarkupMode::Disabled,
style_tags? : StyleTagRegistry? = None,
) -> TextFormatter {
@utils.text_formatter(
@formatting.text_formatter(
show_timestamp~,
show_level~,
show_target~,
@@ -91,17 +91,17 @@ pub fn text_formatter(
///|
pub fn color_support_label(support : ColorSupport) -> String {
@utils.color_support_label(support)
@formatting.color_support_label(support)
}
///|
pub fn style_markup_mode_label(mode : StyleMarkupMode) -> String {
@utils.style_markup_mode_label(mode)
@formatting.style_markup_mode_label(mode)
}
///|
pub fn color_mode_label(mode : ColorMode) -> String {
@utils.color_mode_label(mode)
@formatting.color_mode_label(mode)
}
///|
@@ -109,10 +109,10 @@ pub fn format_text(
rec : Record,
formatter? : TextFormatter = text_formatter(),
) -> String {
@utils.format_text(rec, formatter~)
@formatting.format_text(rec, formatter~)
}
///|
pub fn format_json(rec : Record) -> String {
@utils.format_json(rec)
@formatting.format_json(rec)
}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"moonbitlang/core/env",
"moonbitlang/core/json",
"moonbitlang/core/ref",
"moonbitlang/core/string",
}
+96
View File
@@ -0,0 +1,96 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/formatting"
import {
"Nanaloveyuki/BitLogger/src/core",
}
// Values
pub fn color_mode_label(ColorMode) -> String
pub fn color_support_label(ColorSupport) -> String
pub fn default_style_tag_registry() -> StyleTagRegistry
pub fn format_json(@core.Record) -> String
pub fn format_text(@core.Record, formatter? : TextFormatter) -> String
pub fn global_style_tag_registry() -> StyleTagRegistry
pub fn reset_global_style_tag_registry() -> Unit
pub fn set_global_style_tag_registry(StyleTagRegistry) -> Unit
pub fn style_markup_mode_label(StyleMarkupMode) -> String
pub fn style_tag_registry() -> StyleTagRegistry
pub fn text_formatter(show_timestamp? : Bool, show_level? : Bool, show_target? : Bool, show_fields? : Bool, separator? : String, field_separator? : String, template? : String, color_mode? : ColorMode, color_support? : ColorSupport, style_markup? : StyleMarkupMode, target_style_markup? : StyleMarkupMode, fields_style_markup? : StyleMarkupMode, style_tags? : StyleTagRegistry?) -> TextFormatter
pub fn text_style(fg? : String?, bg? : String?, bold? : Bool, dim? : Bool, italic? : Bool, underline? : Bool) -> TextStyle
// Errors
// Types and methods
pub(all) enum ColorMode {
Never
Auto
Always
}
pub(all) enum ColorSupport {
Basic
TrueColor
}
pub(all) enum StyleMarkupMode {
Disabled
Builtin
Full
}
pub struct StyleTagRegistry {
entries : Map[String, TextStyle]
}
pub fn StyleTagRegistry::contains(Self, String) -> Bool
pub fn StyleTagRegistry::define_alias(Self, String, String) -> Self
pub fn StyleTagRegistry::get(Self, String) -> TextStyle?
pub fn StyleTagRegistry::set_tag(Self, String, style? : TextStyle, fg? : String?, bg? : String?, bold? : Bool, dim? : Bool, italic? : Bool, underline? : Bool) -> Self
pub struct TextFormatter {
show_timestamp : Bool
show_level : Bool
show_target : Bool
show_fields : Bool
separator : String
field_separator : String
template : String
color_mode : ColorMode
color_support : ColorSupport
style_markup : StyleMarkupMode
target_style_markup : StyleMarkupMode
fields_style_markup : StyleMarkupMode
style_tags : StyleTagRegistry?
}
pub fn TextFormatter::with_color_support(Self, ColorSupport) -> Self
pub fn TextFormatter::with_fields_style_markup(Self, StyleMarkupMode) -> Self
pub fn TextFormatter::with_style_markup(Self, StyleMarkupMode) -> Self
pub fn TextFormatter::with_style_tags(Self, StyleTagRegistry) -> Self
pub fn TextFormatter::with_target_style_markup(Self, StyleMarkupMode) -> Self
pub fn TextFormatter::without_style_markup(Self) -> Self
pub struct TextStyle {
fg : String?
bg : String?
bold : Bool
dim : Bool
italic : Bool
underline : Bool
}
// Type aliases
pub type RecordFormatter = (@core.Record) -> String
// Traits
+1 -1
View File
@@ -43,7 +43,7 @@ pub fn[S] Logger::with_context_fields(
) -> Logger[ContextSink[S]] {
{
min_level: self.min_level,
sink: ContextSink::{ sink: self.sink, context_fields: fields },
sink: context_sink(self.sink, fields),
target: self.target,
timestamp: self.timestamp,
}
+12 -9
View File
@@ -1,6 +1,14 @@
import {
"Nanaloveyuki/BitLogger/src/record_ops",
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/utils",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/file_runtime",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/presets_pkg",
"Nanaloveyuki/BitLogger/src/queue_model",
"Nanaloveyuki/BitLogger/src/runtime",
"Nanaloveyuki/BitLogger/src/sink_graph",
"maria/json_parser",
"moonbitlang/core/array",
"moonbitlang/core/builtin",
@@ -10,11 +18,6 @@ import {
"moonbitlang/core/ref",
}
options(
targets: {
"sinks_file.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
"runtime_file_controls.mbt": [ "native", "llvm", "js", "wasm", "wasm-gc" ],
"file_backend_native.mbt": [ "native", "llvm" ],
"file_backend_stub.mbt": [ "js", "wasm", "wasm-gc" ],
},
)
import {
"Nanaloveyuki/BitLogger/src/utils",
} for "wbtest"
+8 -8
View File
@@ -1,29 +1,29 @@
///|
pub type RecordPatch = @utils.RecordPatch
pub type RecordPatch = @record_ops.RecordPatch
///|
pub fn identity_patch() -> RecordPatch {
@utils.identity_patch()
@record_ops.identity_patch()
}
///|
pub fn set_target(target : String) -> RecordPatch {
@utils.set_target(target)
@record_ops.set_target(target)
}
///|
pub fn prefix_message(prefix : String) -> RecordPatch {
@utils.prefix_message(prefix)
@record_ops.prefix_message(prefix)
}
///|
pub fn append_fields(extra_fields : Array[Field]) -> RecordPatch {
@utils.append_fields(extra_fields)
@record_ops.append_fields(extra_fields)
}
///|
pub fn redact_field(key : String, placeholder? : String = "***") -> RecordPatch {
@utils.redact_field(key, placeholder~)
@record_ops.redact_field(key, placeholder~)
}
///|
@@ -31,10 +31,10 @@ pub fn redact_fields(
keys : Array[String],
placeholder? : String = "***",
) -> RecordPatch {
@utils.redact_fields(keys, placeholder~)
@record_ops.redact_fields(keys, placeholder~)
}
///|
pub fn compose_patches(patches : Array[RecordPatch]) -> RecordPatch {
@utils.compose_patches(patches)
@record_ops.compose_patches(patches)
}
+365
View File
@@ -0,0 +1,365 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src"
import {
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/file_runtime",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/queue_model",
"Nanaloveyuki/BitLogger/src/runtime",
"Nanaloveyuki/BitLogger/src/sink_graph",
"maria/json_parser",
}
// Values
pub fn all_of(Array[(@core.Record) -> Bool]) -> (@core.Record) -> Bool
pub fn any_of(Array[(@core.Record) -> Bool]) -> (@core.Record) -> Bool
pub fn append_fields(Array[@core.Field]) -> (@core.Record) -> @core.Record
pub fn[S] buffered_sink(S, flush_limit? : Int) -> @sink_graph.BufferedSink[S]
pub fn build_application_logger(@config_model.LoggerConfig) -> Logger[@runtime.RuntimeSink]
pub fn build_library_logger(@config_model.LoggerConfig) -> LibraryLogger[@runtime.RuntimeSink]
pub fn build_logger(@config_model.LoggerConfig) -> Logger[@runtime.RuntimeSink]
pub fn callback_sink((@core.Record) -> Unit) -> @sink_graph.CallbackSink
pub fn color_mode_label(@formatting.ColorMode) -> String
pub fn color_support_label(@formatting.ColorSupport) -> String
pub fn compose_patches(Array[(@core.Record) -> @core.Record]) -> (@core.Record) -> @core.Record
pub fn console(min_level? : @core.Level, target? : String, timestamp? : Bool) -> @config_model.LoggerConfig
pub fn console_sink() -> @sink_graph.ConsoleSink
pub fn[S] context_sink(S, Array[@core.Field]) -> @sink_graph.ContextSink[S]
pub fn debug(String, fields? : Array[@core.Field]) -> Unit
pub fn default_library_logger() -> LibraryLogger[@sink_graph.ConsoleSink]
pub fn default_logger() -> Logger[@sink_graph.ConsoleSink]
pub fn default_logger_config() -> @config_model.LoggerConfig
pub fn default_sink_config() -> @config_model.SinkConfig
pub fn default_style_tag_registry() -> @formatting.StyleTagRegistry
pub fn default_text_formatter_config() -> @config_model.TextFormatterConfig
pub fn error(String, fields? : Array[@core.Field]) -> Unit
pub fn[A, B] fanout_sink(A, B) -> @sink_graph.FanoutSink[A, B]
pub fn field(String, String) -> @core.Field
pub fn field_equals(String, String) -> (@core.Record) -> Bool
pub fn fields(Array[(String, String)]) -> Array[@core.Field]
pub fn file(String, min_level? : @core.Level, target? : String, timestamp? : Bool, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, text_formatter? : @config_model.TextFormatterConfig) -> @config_model.LoggerConfig raise @config_model.ConfigError
pub fn file_rotation(Int, max_backups? : Int) -> @file_model.FileRotation
pub fn file_rotation_config_to_json(@file_model.FileRotation) -> @json_parser.JsonValue
pub fn file_rotation_i64(Int64, max_backups? : Int) -> @file_model.FileRotation
pub fn file_sink(String, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, formatter? : (@core.Record) -> String) -> @file_runtime.FileSink
pub fn file_sink_policy_to_json(@file_model.FileSinkPolicy) -> @json_parser.JsonValue
pub fn file_sink_state_to_json(@file_model.FileSinkState) -> @json_parser.JsonValue
pub fn[S] filter_sink(S, (@core.Record) -> Bool) -> @sink_graph.FilterSink[S]
pub fn format_json(@core.Record) -> String
pub fn format_text(@core.Record, formatter? : @formatting.TextFormatter) -> String
pub fn formatted_callback_sink((@core.Record) -> String, (String) -> Unit) -> @sink_graph.FormattedCallbackSink
pub fn formatted_console_sink((@core.Record) -> String) -> @sink_graph.FormattedConsoleSink
pub fn global_style_tag_registry() -> @formatting.StyleTagRegistry
pub fn has_field(String) -> (@core.Record) -> Bool
pub fn identity_patch() -> (@core.Record) -> @core.Record
pub fn info(String, fields? : Array[@core.Field]) -> Unit
pub fn json_console(min_level? : @core.Level, target? : String, timestamp? : Bool) -> @config_model.LoggerConfig
pub fn json_console_sink() -> @sink_graph.JsonConsoleSink
pub fn level_at_least(@core.Level) -> (@core.Record) -> Bool
pub fn log(@core.Level, String, fields? : Array[@core.Field]) -> Unit
pub fn logger_config_to_json(@config_model.LoggerConfig) -> @json_parser.JsonValue
pub fn message_contains(String) -> (@core.Record) -> Bool
pub fn native_files_supported() -> Bool
pub fn not_((@core.Record) -> Bool) -> (@core.Record) -> Bool
pub fn parse_and_build_application_logger(String) -> Logger[@runtime.RuntimeSink] raise @config_model.ConfigError
pub fn parse_and_build_library_logger(String) -> LibraryLogger[@runtime.RuntimeSink] raise @config_model.ConfigError
pub fn parse_and_build_logger(String) -> Logger[@runtime.RuntimeSink] raise @config_model.ConfigError
pub fn parse_logger_config_text(String) -> @config_model.LoggerConfig raise @config_model.ConfigError
pub fn[S] patch_sink(S, (@core.Record) -> @core.Record) -> @sink_graph.PatchSink[S]
pub fn prefix_message(String) -> (@core.Record) -> @core.Record
pub fn queue_config_to_json(@config_model.QueueConfig) -> @json_parser.JsonValue
pub fn[S] queued_sink(S, max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> @sink_graph.QueuedSink[S]
pub fn redact_field(String, placeholder? : String) -> (@core.Record) -> @core.Record
pub fn redact_fields(Array[String], placeholder? : String) -> (@core.Record) -> @core.Record
pub fn reset_global_style_tag_registry() -> Unit
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> @json_parser.JsonValue
pub fn set_default_min_level(@core.Level) -> Unit
pub fn set_default_target(String) -> Unit
pub fn set_global_style_tag_registry(@formatting.StyleTagRegistry) -> Unit
pub fn set_target(String) -> (@core.Record) -> @core.Record
pub fn sink_config_to_json(@config_model.SinkConfig) -> @json_parser.JsonValue
pub fn[A, B] split_by_level(A, B, min_level? : @core.Level) -> @sink_graph.SplitSink[A, B]
pub fn[A, B] split_sink(A, B, (@core.Record) -> Bool) -> @sink_graph.SplitSink[A, B]
pub fn stringify_file_sink_policy(@file_model.FileSinkPolicy, pretty? : Bool) -> String
pub fn stringify_file_sink_state(@file_model.FileSinkState, pretty? : Bool) -> String
pub fn stringify_logger_config(@config_model.LoggerConfig, pretty? : Bool) -> String
pub fn stringify_queue_config(@config_model.QueueConfig, pretty? : Bool) -> String
pub fn stringify_runtime_file_state(@file_model.RuntimeFileState, pretty? : Bool) -> String
pub fn stringify_sink_config(@config_model.SinkConfig, pretty? : Bool) -> String
pub fn stringify_text_formatter_config(@config_model.TextFormatterConfig, pretty? : Bool) -> String
pub fn style_markup_mode_label(@formatting.StyleMarkupMode) -> String
pub fn style_tag_registry() -> @formatting.StyleTagRegistry
pub fn target_has_prefix(String) -> (@core.Record) -> Bool
pub fn target_is(String) -> (@core.Record) -> Bool
pub fn text_callback_sink(@formatting.TextFormatter, (String) -> Unit) -> @sink_graph.FormattedCallbackSink
pub fn text_console(min_level? : @core.Level, target? : String, timestamp? : Bool, text_formatter? : @config_model.TextFormatterConfig) -> @config_model.LoggerConfig
pub fn text_console_sink(@formatting.TextFormatter) -> @sink_graph.FormattedConsoleSink
pub fn text_formatter(show_timestamp? : Bool, show_level? : Bool, show_target? : Bool, show_fields? : Bool, separator? : String, field_separator? : String, template? : String, color_mode? : @formatting.ColorMode, color_support? : @formatting.ColorSupport, style_markup? : @formatting.StyleMarkupMode, target_style_markup? : @formatting.StyleMarkupMode, fields_style_markup? : @formatting.StyleMarkupMode, style_tags? : @formatting.StyleTagRegistry?) -> @formatting.TextFormatter
pub fn text_formatter_config_to_json(@config_model.TextFormatterConfig) -> @json_parser.JsonValue
pub fn text_style(fg? : String?, bg? : String?, bold? : Bool, dim? : Bool, italic? : Bool, underline? : Bool) -> @formatting.TextStyle
pub fn trace(String, fields? : Array[@core.Field]) -> Unit
pub fn warn(String, fields? : Array[@core.Field]) -> Unit
pub fn with_file_rotation(@config_model.LoggerConfig, Int, max_backups? : Int) -> @config_model.LoggerConfig
pub fn with_file_rotation_i64(@config_model.LoggerConfig, Int64, max_backups? : Int) -> @config_model.LoggerConfig
pub fn with_queue(@config_model.LoggerConfig, max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> @config_model.LoggerConfig
// Errors
// Types and methods
pub struct LibraryLogger[S] {
inner : Logger[S]
}
pub fn[S] LibraryLogger::bind(Self[S], Array[@core.Field]) -> Self[@sink_graph.ContextSink[S]]
pub fn[S] LibraryLogger::child(Self[S], String) -> Self[S]
pub fn[S : @sink_graph.Sink] LibraryLogger::error(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S : @sink_graph.Sink] LibraryLogger::info(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] LibraryLogger::is_enabled(Self[S], @core.Level) -> Bool
pub fn[S : @sink_graph.Sink] LibraryLogger::log(Self[S], @core.Level, String, fields? : Array[@core.Field], target? : String) -> Unit
pub fn[S] LibraryLogger::new(S, min_level? : @core.Level, target? : String) -> Self[S]
pub fn[S] LibraryLogger::to_logger(Self[S]) -> Logger[S]
pub fn[S : @sink_graph.Sink] LibraryLogger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] LibraryLogger::with_context_fields(Self[S], Array[@core.Field]) -> Self[@sink_graph.ContextSink[S]]
pub fn[S] LibraryLogger::with_target(Self[S], String) -> Self[S]
pub struct Logger[S] {
min_level : @core.Level
sink : S
target : String
timestamp : Bool
}
pub fn[S] Logger::bind(Self[S], Array[@core.Field]) -> Self[@sink_graph.ContextSink[S]]
pub fn[S] Logger::child(Self[S], String) -> Self[S]
pub fn Logger::close(Self[@runtime.RuntimeSink]) -> Bool
pub fn[S : @sink_graph.Sink] Logger::debug(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn Logger::drain(Self[@runtime.RuntimeSink], max_items? : Int) -> Int
pub fn Logger::drain_progress(Self[@runtime.RuntimeSink], max_items? : Int) -> @runtime.RuntimeSinkProgress
pub fn Logger::dropped_count(Self[@runtime.RuntimeSink]) -> Int
pub fn[S : @sink_graph.Sink] Logger::error(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn Logger::file_append_mode(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_auto_flush(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_available(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_clear_rotation(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_close(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_default_policy(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkPolicy
pub fn Logger::file_default_policy_or_none(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkPolicy?
pub fn Logger::file_flush(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_flush_failures(Self[@runtime.RuntimeSink]) -> Int
pub fn Logger::file_open_failures(Self[@runtime.RuntimeSink]) -> Int
pub fn Logger::file_path(Self[@runtime.RuntimeSink]) -> String
pub fn Logger::file_path_or_none(Self[@runtime.RuntimeSink]) -> String?
pub fn Logger::file_policy(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkPolicy
pub fn Logger::file_policy_matches_default(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_policy_or_none(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkPolicy?
pub fn Logger::file_reopen(Self[@runtime.RuntimeSink], append? : Bool?) -> Bool
pub fn Logger::file_reopen_append(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_reopen_truncate(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_reopen_with_current_policy(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_reset_failure_counters(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_reset_policy(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_rotation_config(Self[@runtime.RuntimeSink]) -> @file_model.FileRotation?
pub fn Logger::file_rotation_enabled(Self[@runtime.RuntimeSink]) -> Bool
pub fn Logger::file_rotation_failures(Self[@runtime.RuntimeSink]) -> Int
pub fn Logger::file_runtime_state(Self[@runtime.RuntimeSink]) -> @file_model.RuntimeFileState?
pub fn Logger::file_set_append_mode(Self[@runtime.RuntimeSink], Bool) -> Bool
pub fn Logger::file_set_auto_flush(Self[@runtime.RuntimeSink], Bool) -> Bool
pub fn Logger::file_set_policy(Self[@runtime.RuntimeSink], @file_model.FileSinkPolicy) -> Bool
pub fn Logger::file_set_rotation(Self[@runtime.RuntimeSink], @file_model.FileRotation?) -> Bool
pub fn Logger::file_state(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkState
pub fn Logger::file_state_or_none(Self[@runtime.RuntimeSink]) -> @file_model.FileSinkState?
pub fn Logger::file_write_failures(Self[@runtime.RuntimeSink]) -> Int
pub fn Logger::flush(Self[@runtime.RuntimeSink]) -> Int
pub fn Logger::flush_progress(Self[@runtime.RuntimeSink]) -> @runtime.RuntimeSinkProgress
pub fn[S : @sink_graph.Sink] Logger::info(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] Logger::is_enabled(Self[S], @core.Level) -> Bool
pub fn[S : @sink_graph.Sink] Logger::log(Self[S], @core.Level, String, fields? : Array[@core.Field], target? : String) -> Unit
pub fn[S] Logger::new(S, min_level? : @core.Level, target? : String) -> Self[S]
pub fn Logger::pending_count(Self[@runtime.RuntimeSink]) -> Int
pub fn[S] Logger::to_library_logger(Self[S]) -> LibraryLogger[S]
pub fn[S : @sink_graph.Sink] Logger::trace(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S : @sink_graph.Sink] Logger::warn(Self[S], String, fields? : Array[@core.Field]) -> Unit
pub fn[S] Logger::with_context_fields(Self[S], Array[@core.Field]) -> Self[@sink_graph.ContextSink[S]]
pub fn[S] Logger::with_filter(Self[S], (@core.Record) -> Bool) -> Self[@sink_graph.FilterSink[S]]
pub fn[S] Logger::with_min_level(Self[S], @core.Level) -> Self[S]
pub fn[S] Logger::with_patch(Self[S], (@core.Record) -> @core.Record) -> Self[@sink_graph.PatchSink[S]]
pub fn[S] Logger::with_queue(Self[S], max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> Self[@sink_graph.QueuedSink[S]]
pub fn[S] Logger::with_target(Self[S], String) -> Self[S]
pub fn[S] Logger::with_timestamp(Self[S], enabled? : Bool) -> Self[S]
// Type aliases
pub type ApplicationLogger = Logger[@runtime.RuntimeSink]
pub using @sink_graph {type BufferedSink}
pub using @sink_graph {type CallbackSink}
pub using @formatting {type ColorMode}
pub using @formatting {type ColorSupport}
pub using @config_model {type ConfigError}
pub type ConfiguredLogger = Logger[@runtime.RuntimeSink]
pub using @sink_graph {type ConsoleSink}
pub using @sink_graph {type ContextSink}
pub using @sink_graph {type FanoutSink}
pub using @core {type Field}
pub using @file_model {type FileRotation}
pub using @file_runtime {type FileSink}
pub using @file_model {type FileSinkPolicy}
pub using @file_model {type FileSinkState}
pub using @sink_graph {type FilterSink}
pub using @sink_graph {type FormattedCallbackSink}
pub using @sink_graph {type FormattedConsoleSink}
pub using @sink_graph {type JsonConsoleSink}
pub using @core {type Level}
pub using @config_model {type LoggerConfig}
pub using @sink_graph {type PatchSink}
pub using @config_model {type QueueConfig}
pub using @queue_model {type QueueOverflowPolicy}
pub using @sink_graph {type QueuedSink}
pub using @core {type Record}
pub type RecordFormatter = (@core.Record) -> String
pub type RecordPatch = (@core.Record) -> @core.Record
pub type RecordPredicate = (@core.Record) -> Bool
pub using @file_model {type RuntimeFileState}
pub using @runtime {type RuntimeSink}
pub using @runtime {type RuntimeSinkProgress}
pub using @config_model {type SinkConfig}
pub using @config_model {type SinkKind}
pub using @sink_graph {type SplitSink}
pub using @formatting {type StyleMarkupMode}
pub using @formatting {type StyleTagRegistry}
pub using @formatting {type TextFormatter}
pub using @config_model {type TextFormatterConfig}
pub using @formatting {type TextStyle}
pub using @sink_graph {trait Sink}
// Traits
+12 -73
View File
@@ -4,12 +4,7 @@ pub fn console(
target? : String = "",
timestamp? : Bool = false,
) -> LoggerConfig {
LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=SinkConfig::new(kind=SinkKind::Console),
)
@presets_pkg.console(min_level~, target~, timestamp~)
}
///|
@@ -18,12 +13,7 @@ pub fn json_console(
target? : String = "",
timestamp? : Bool = false,
) -> LoggerConfig {
LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=SinkConfig::new(kind=SinkKind::JsonConsole),
)
@presets_pkg.json_console(min_level~, target~, timestamp~)
}
///|
@@ -33,12 +23,7 @@ pub fn text_console(
timestamp? : Bool = false,
text_formatter? : TextFormatterConfig = default_text_formatter_config(),
) -> LoggerConfig {
LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=SinkConfig::new(kind=SinkKind::TextConsole, text_formatter~),
)
@presets_pkg.text_console(min_level~, target~, timestamp~, text_formatter~)
}
///|
@@ -52,21 +37,15 @@ pub fn file(
rotation? : FileRotation? = None,
text_formatter? : TextFormatterConfig = default_text_formatter_config(),
) -> LoggerConfig raise ConfigError {
if path == "" {
raise ConfigError::InvalidConfig("File sink requires non-empty path")
}
LoggerConfig::new(
@presets_pkg.file(
path,
min_level~,
target~,
timestamp~,
sink=SinkConfig::new(
kind=SinkKind::File,
path~,
append~,
auto_flush~,
rotation~,
text_formatter~,
),
append~,
auto_flush~,
rotation~,
text_formatter~,
)
}
@@ -76,13 +55,7 @@ pub fn with_queue(
max_pending? : Int = 0,
overflow? : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
) -> LoggerConfig {
LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=config.sink,
queue=Some(QueueConfig::new(max_pending, overflow~)),
)
@presets_pkg.with_queue(config, max_pending~, overflow~)
}
///|
@@ -91,24 +64,7 @@ pub fn with_file_rotation(
max_bytes : Int,
max_backups? : Int = 1,
) -> LoggerConfig {
match config.sink.kind {
SinkKind::File =>
LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=SinkConfig::new(
kind=config.sink.kind,
path=config.sink.path,
append=config.sink.append,
auto_flush=config.sink.auto_flush,
rotation=Some(file_rotation(max_bytes, max_backups~)),
text_formatter=config.sink.text_formatter,
),
queue=config.queue,
)
_ => config
}
@presets_pkg.with_file_rotation(config, max_bytes, max_backups~)
}
///|
@@ -117,22 +73,5 @@ pub fn with_file_rotation_i64(
max_bytes : Int64,
max_backups? : Int = 1,
) -> LoggerConfig {
match config.sink.kind {
SinkKind::File =>
LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=SinkConfig::new(
kind=config.sink.kind,
path=config.sink.path,
append=config.sink.append,
auto_flush=config.sink.auto_flush,
rotation=Some(file_rotation_i64(max_bytes, max_backups~)),
text_formatter=config.sink.text_formatter,
),
queue=config.queue,
)
_ => config
}
@presets_pkg.with_file_rotation_i64(config, max_bytes, max_backups~)
}
+6
View File
@@ -0,0 +1,6 @@
import {
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/queue_model",
}
+33
View File
@@ -0,0 +1,33 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/presets_pkg"
import {
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/queue_model",
}
// Values
pub fn console(min_level? : @core.Level, target? : String, timestamp? : Bool) -> @config_model.LoggerConfig
pub fn file(String, min_level? : @core.Level, target? : String, timestamp? : Bool, append? : Bool, auto_flush? : Bool, rotation? : @file_model.FileRotation?, text_formatter? : @config_model.TextFormatterConfig) -> @config_model.LoggerConfig raise @config_model.ConfigError
pub fn json_console(min_level? : @core.Level, target? : String, timestamp? : Bool) -> @config_model.LoggerConfig
pub fn text_console(min_level? : @core.Level, target? : String, timestamp? : Bool, text_formatter? : @config_model.TextFormatterConfig) -> @config_model.LoggerConfig
pub fn with_file_rotation(@config_model.LoggerConfig, Int, max_backups? : Int) -> @config_model.LoggerConfig
pub fn with_file_rotation_i64(@config_model.LoggerConfig, Int64, max_backups? : Int) -> @config_model.LoggerConfig
pub fn with_queue(@config_model.LoggerConfig, max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> @config_model.LoggerConfig
// Errors
// Types and methods
// Type aliases
// Traits
+143
View File
@@ -0,0 +1,143 @@
///|
pub fn console(
min_level? : @core.Level = @core.Level::Info,
target? : String = "",
timestamp? : Bool = false,
) -> @config_model.LoggerConfig {
@config_model.LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=@config_model.SinkConfig::new(kind=@config_model.SinkKind::Console),
)
}
///|
pub fn json_console(
min_level? : @core.Level = @core.Level::Info,
target? : String = "",
timestamp? : Bool = false,
) -> @config_model.LoggerConfig {
@config_model.LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=@config_model.SinkConfig::new(kind=@config_model.SinkKind::JsonConsole),
)
}
///|
pub fn text_console(
min_level? : @core.Level = @core.Level::Info,
target? : String = "",
timestamp? : Bool = false,
text_formatter? : @config_model.TextFormatterConfig = @config_model.default_text_formatter_config(),
) -> @config_model.LoggerConfig {
@config_model.LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=@config_model.SinkConfig::new(
kind=@config_model.SinkKind::TextConsole,
text_formatter~,
),
)
}
///|
pub fn file(
path : String,
min_level? : @core.Level = @core.Level::Info,
target? : String = "",
timestamp? : Bool = false,
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : @file_model.FileRotation? = None,
text_formatter? : @config_model.TextFormatterConfig = @config_model.default_text_formatter_config(),
) -> @config_model.LoggerConfig raise @config_model.ConfigError {
if path == "" {
raise @config_model.ConfigError::InvalidConfig(
"File sink requires non-empty path",
)
}
@config_model.LoggerConfig::new(
min_level~,
target~,
timestamp~,
sink=@config_model.SinkConfig::new(
kind=@config_model.SinkKind::File,
path~,
append~,
auto_flush~,
rotation~,
text_formatter~,
),
)
}
///|
pub fn with_queue(
config : @config_model.LoggerConfig,
max_pending? : Int = 0,
overflow? : @queue_model.QueueOverflowPolicy = @queue_model.QueueOverflowPolicy::DropNewest,
) -> @config_model.LoggerConfig {
@config_model.LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=config.sink,
queue=Some(@config_model.QueueConfig::new(max_pending, overflow~)),
)
}
///|
pub fn with_file_rotation(
config : @config_model.LoggerConfig,
max_bytes : Int,
max_backups? : Int = 1,
) -> @config_model.LoggerConfig {
match config.sink.kind {
@config_model.SinkKind::File =>
@config_model.LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=@config_model.SinkConfig::new(
kind=config.sink.kind,
path=config.sink.path,
append=config.sink.append,
auto_flush=config.sink.auto_flush,
rotation=Some(@file_model.file_rotation(max_bytes, max_backups~)),
text_formatter=config.sink.text_formatter,
),
queue=config.queue,
)
_ => config
}
}
///|
pub fn with_file_rotation_i64(
config : @config_model.LoggerConfig,
max_bytes : Int64,
max_backups? : Int = 1,
) -> @config_model.LoggerConfig {
match config.sink.kind {
@config_model.SinkKind::File =>
@config_model.LoggerConfig::new(
min_level=config.min_level,
target=config.target,
timestamp=config.timestamp,
sink=@config_model.SinkConfig::new(
kind=config.sink.kind,
path=config.sink.path,
append=config.sink.append,
auto_flush=config.sink.auto_flush,
rotation=Some(@file_model.file_rotation_i64(max_bytes, max_backups~)),
text_formatter=config.sink.text_formatter,
),
queue=config.queue,
)
_ => config
}
}
+1
View File
@@ -0,0 +1 @@
///|
+17
View File
@@ -0,0 +1,17 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/queue_model"
// Values
// Errors
// Types and methods
pub(all) enum QueueOverflowPolicy {
DropNewest
DropOldest
}
// Type aliases
// Traits
+5
View File
@@ -0,0 +1,5 @@
///|
pub(all) enum QueueOverflowPolicy {
DropNewest
DropOldest
}
+75
View File
@@ -0,0 +1,75 @@
///|
pub type RecordPredicate = (@core.Record) -> Bool
///|
pub fn level_at_least(min_level : @core.Level) -> RecordPredicate {
fn(rec) { rec.level.priority() >= min_level.priority() }
}
///|
pub fn target_is(target : String) -> RecordPredicate {
fn(rec) { rec.target == target }
}
///|
pub fn target_has_prefix(prefix : String) -> RecordPredicate {
fn(rec) { rec.target.has_prefix(prefix) }
}
///|
pub fn message_contains(fragment : String) -> RecordPredicate {
fn(rec) { rec.message.contains(fragment) }
}
///|
pub fn has_field(key : String) -> RecordPredicate {
fn(rec) {
for field in rec.fields {
if field.key == key {
return true
}
}
false
}
}
///|
pub fn field_equals(key : String, value : String) -> RecordPredicate {
fn(rec) {
for field in rec.fields {
if field.key == key && field.value == value {
return true
}
}
false
}
}
///|
pub fn not_(predicate : RecordPredicate) -> RecordPredicate {
fn(rec) { !predicate(rec) }
}
///|
pub fn all_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
fn(rec) {
for predicate in predicates {
if !predicate(rec) {
return false
}
}
true
}
}
///|
pub fn any_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
fn(rec) {
for predicate in predicates {
if predicate(rec) {
return true
}
}
false
}
}
+4
View File
@@ -0,0 +1,4 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"moonbitlang/core/string",
}
+74
View File
@@ -0,0 +1,74 @@
///|
pub type RecordPatch = (@core.Record) -> @core.Record
///|
pub fn identity_patch() -> RecordPatch {
fn(rec) { rec }
}
///|
pub fn set_target(target : String) -> RecordPatch {
fn(rec) { rec.with_target(target) }
}
///|
pub fn prefix_message(prefix : String) -> RecordPatch {
fn(rec) { rec.with_message("\{prefix}\{rec.message}") }
}
///|
pub fn append_fields(extra_fields : Array[@core.Field]) -> RecordPatch {
fn(rec) {
if extra_fields.length() == 0 {
rec
} else if rec.fields.length() == 0 {
rec.with_fields(extra_fields)
} else {
rec.with_fields(rec.fields + extra_fields)
}
}
}
///|
pub fn redact_field(key : String, placeholder? : String = "***") -> RecordPatch {
fn(rec) {
rec.with_fields(
rec.fields.map(fn(field) {
if field.key == key {
field.with_value(placeholder)
} else {
field
}
}),
)
}
}
///|
pub fn redact_fields(
keys : Array[String],
placeholder? : String = "***",
) -> RecordPatch {
fn(rec) {
rec.with_fields(
rec.fields.map(fn(field) {
if keys.contains(field.key) {
field.with_value(placeholder)
} else {
field
}
}),
)
}
}
///|
pub fn compose_patches(patches : Array[RecordPatch]) -> RecordPatch {
fn(rec) {
let mut current = rec
for patch in patches {
current = patch(current)
}
current
}
}
+51
View File
@@ -0,0 +1,51 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/record_ops"
import {
"Nanaloveyuki/BitLogger/src/core",
}
// Values
pub fn all_of(Array[(@core.Record) -> Bool]) -> (@core.Record) -> Bool
pub fn any_of(Array[(@core.Record) -> Bool]) -> (@core.Record) -> Bool
pub fn append_fields(Array[@core.Field]) -> (@core.Record) -> @core.Record
pub fn compose_patches(Array[(@core.Record) -> @core.Record]) -> (@core.Record) -> @core.Record
pub fn field_equals(String, String) -> (@core.Record) -> Bool
pub fn has_field(String) -> (@core.Record) -> Bool
pub fn identity_patch() -> (@core.Record) -> @core.Record
pub fn level_at_least(@core.Level) -> (@core.Record) -> Bool
pub fn message_contains(String) -> (@core.Record) -> Bool
pub fn not_((@core.Record) -> Bool) -> (@core.Record) -> Bool
pub fn prefix_message(String) -> (@core.Record) -> @core.Record
pub fn redact_field(String, placeholder? : String) -> (@core.Record) -> @core.Record
pub fn redact_fields(Array[String], placeholder? : String) -> (@core.Record) -> @core.Record
pub fn set_target(String) -> (@core.Record) -> @core.Record
pub fn target_has_prefix(String) -> (@core.Record) -> Bool
pub fn target_is(String) -> (@core.Record) -> Bool
// Errors
// Types and methods
// Type aliases
pub type RecordPatch = (@core.Record) -> @core.Record
pub type RecordPredicate = (@core.Record) -> Bool
// Traits
+8
View File
@@ -0,0 +1,8 @@
import {
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/file_runtime",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/sink_graph",
"maria/json_parser",
}
+87
View File
@@ -0,0 +1,87 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/runtime"
import {
"Nanaloveyuki/BitLogger/src/config_model",
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/file_model",
"Nanaloveyuki/BitLogger/src/file_runtime",
"Nanaloveyuki/BitLogger/src/sink_graph",
"maria/json_parser",
}
// Values
pub fn apply_queue_config(RuntimeSink, @config_model.QueueConfig) -> RuntimeSink
pub fn build_runtime_sink(@config_model.SinkConfig) -> RuntimeSink
pub fn runtime_file_state_to_json(@file_model.RuntimeFileState) -> @json_parser.JsonValue
pub fn stringify_runtime_file_state(@file_model.RuntimeFileState, pretty? : Bool) -> String
// Errors
// Types and methods
pub(all) enum RuntimeSink {
Console(@sink_graph.ConsoleSink)
JsonConsole(@sink_graph.JsonConsoleSink)
TextConsole(@sink_graph.FormattedConsoleSink)
File(@file_runtime.FileSink)
QueuedConsole(@sink_graph.QueuedSink[@sink_graph.ConsoleSink])
QueuedJsonConsole(@sink_graph.QueuedSink[@sink_graph.JsonConsoleSink])
QueuedTextConsole(@sink_graph.QueuedSink[@sink_graph.FormattedConsoleSink])
QueuedFile(@sink_graph.QueuedSink[@file_runtime.FileSink])
}
pub fn RuntimeSink::close(Self) -> Bool
pub fn RuntimeSink::drain(Self, max_items? : Int) -> Int
pub fn RuntimeSink::drain_progress(Self, max_items? : Int) -> RuntimeSinkProgress
pub fn RuntimeSink::dropped_count(Self) -> Int
pub fn RuntimeSink::file_append_mode(Self) -> Bool
pub fn RuntimeSink::file_auto_flush(Self) -> Bool
pub fn RuntimeSink::file_available(Self) -> Bool
pub fn RuntimeSink::file_clear_rotation(Self) -> Bool
pub fn RuntimeSink::file_close(Self) -> Bool
pub fn RuntimeSink::file_default_policy(Self) -> @file_model.FileSinkPolicy
pub fn RuntimeSink::file_default_policy_or_none(Self) -> @file_model.FileSinkPolicy?
pub fn RuntimeSink::file_flush(Self) -> Bool
pub fn RuntimeSink::file_flush_failures(Self) -> Int
pub fn RuntimeSink::file_open_failures(Self) -> Int
pub fn RuntimeSink::file_path(Self) -> String
pub fn RuntimeSink::file_path_or_none(Self) -> String?
pub fn RuntimeSink::file_policy(Self) -> @file_model.FileSinkPolicy
pub fn RuntimeSink::file_policy_matches_default(Self) -> Bool
pub fn RuntimeSink::file_policy_or_none(Self) -> @file_model.FileSinkPolicy?
pub fn RuntimeSink::file_reopen(Self, append? : Bool?) -> Bool
pub fn RuntimeSink::file_reopen_append(Self) -> Bool
pub fn RuntimeSink::file_reopen_truncate(Self) -> Bool
pub fn RuntimeSink::file_reopen_with_current_policy(Self) -> Bool
pub fn RuntimeSink::file_reset_failure_counters(Self) -> Bool
pub fn RuntimeSink::file_reset_policy(Self) -> Bool
pub fn RuntimeSink::file_rotation_config(Self) -> @file_model.FileRotation?
pub fn RuntimeSink::file_rotation_enabled(Self) -> Bool
pub fn RuntimeSink::file_rotation_failures(Self) -> Int
pub fn RuntimeSink::file_runtime_state(Self) -> @file_model.RuntimeFileState?
pub fn RuntimeSink::file_set_append_mode(Self, Bool) -> Bool
pub fn RuntimeSink::file_set_auto_flush(Self, Bool) -> Bool
pub fn RuntimeSink::file_set_policy(Self, @file_model.FileSinkPolicy) -> Bool
pub fn RuntimeSink::file_set_rotation(Self, @file_model.FileRotation?) -> Bool
pub fn RuntimeSink::file_state(Self) -> @file_model.FileSinkState
pub fn RuntimeSink::file_state_or_none(Self) -> @file_model.FileSinkState?
pub fn RuntimeSink::file_write_failures(Self) -> Int
pub fn RuntimeSink::flush(Self) -> Int
pub fn RuntimeSink::flush_progress(Self) -> RuntimeSinkProgress
pub fn RuntimeSink::pending_count(Self) -> Int
pub impl @sink_graph.Sink for RuntimeSink
pub struct RuntimeSinkProgress {
queue_advanced_count : Int
file_flush_step_count : Int
queue_backed : Bool
file_backed : Bool
}
// Type aliases
pub using @file_model {type RuntimeFileState}
// Traits
+746
View File
@@ -0,0 +1,746 @@
///|
type SinkConfig = @config_model.SinkConfig
///|
type SinkKind = @config_model.SinkKind
///|
type QueueConfig = @config_model.QueueConfig
///|
type FileSink = @file_runtime.FileSink
///|
type FileRotation = @file_model.FileRotation
///|
type FileSinkState = @file_model.FileSinkState
///|
type FileSinkPolicy = @file_model.FileSinkPolicy
///|
type ConsoleSink = @sink_graph.ConsoleSink
///|
type JsonConsoleSink = @sink_graph.JsonConsoleSink
///|
type FormattedConsoleSink = @sink_graph.FormattedConsoleSink
///|
type QueuedSink[S] = @sink_graph.QueuedSink[S]
///|
pub(all) enum RuntimeSink {
Console(ConsoleSink)
JsonConsole(JsonConsoleSink)
TextConsole(FormattedConsoleSink)
File(FileSink)
QueuedConsole(QueuedSink[ConsoleSink])
QueuedJsonConsole(QueuedSink[JsonConsoleSink])
QueuedTextConsole(QueuedSink[FormattedConsoleSink])
QueuedFile(QueuedSink[FileSink])
}
///|
pub type RuntimeFileState = @file_model.RuntimeFileState
///|
pub struct RuntimeSinkProgress {
queue_advanced_count : Int
file_flush_step_count : Int
queue_backed : Bool
file_backed : Bool
}
///|
pub fn runtime_file_state_to_json(
state : RuntimeFileState,
) -> @json_parser.JsonValue {
@file_model.runtime_file_state_to_json(state)
}
///|
pub fn stringify_runtime_file_state(
state : RuntimeFileState,
pretty? : Bool = false,
) -> String {
@file_model.stringify_runtime_file_state(state, pretty~)
}
///|
pub impl @sink_graph.Sink for RuntimeSink with fn write(self, rec) {
match self {
Console(sink) => sink.write(rec)
JsonConsole(sink) => sink.write(rec)
TextConsole(sink) => sink.write(rec)
File(sink) => sink.write(rec)
QueuedConsole(sink) => sink.write(rec)
QueuedJsonConsole(sink) => sink.write(rec)
QueuedTextConsole(sink) => sink.write(rec)
QueuedFile(sink) => sink.write(rec)
}
}
///|
fn[S : @sink_graph.Sink] queued_sink_close_internal(
sink : QueuedSink[S],
) -> Bool {
ignore(sink.flush())
sink.pending_count() == 0
}
///|
fn runtime_file_sink_internal(sink : RuntimeSink) -> FileSink? {
match sink {
File(sink) => Some(sink)
QueuedFile(sink) => Some(sink.sink)
_ => None
}
}
///|
fn queued_file_policy_mutation_allowed_internal(
sink : QueuedSink[FileSink],
) -> Bool {
sink.pending_count() == 0
}
///|
fn queued_file_flush_internal(sink : QueuedSink[FileSink]) -> Bool {
let before_write_failures = sink.sink.write_failures()
let before_flush_failures = sink.sink.flush_failures()
let before_rotation_failures = sink.sink.rotation_failures()
ignore(sink.flush())
let flushed = sink.sink.flush()
sink.pending_count() == 0 &&
flushed &&
sink.sink.write_failures() == before_write_failures &&
sink.sink.flush_failures() == before_flush_failures &&
sink.sink.rotation_failures() == before_rotation_failures
}
///|
fn queued_file_close_internal(sink : QueuedSink[FileSink]) -> Bool {
let flushed = queued_file_flush_internal(sink)
let closed = sink.sink.close()
flushed && closed
}
///|
fn runtime_sink_progress_compat_count(progress : RuntimeSinkProgress) -> Int {
progress.queue_advanced_count + progress.file_flush_step_count
}
///|
pub fn RuntimeSink::flush_progress(self : RuntimeSink) -> RuntimeSinkProgress {
match self {
Console(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
JsonConsole(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
TextConsole(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
File(sink) =>
{
queue_advanced_count: 0,
file_flush_step_count: if sink.flush() {
1
} else {
0
},
queue_backed: false,
file_backed: true,
}
QueuedConsole(sink) =>
{
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedJsonConsole(sink) =>
{
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedTextConsole(sink) =>
{
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedFile(sink) =>
{
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: true,
}
}
}
///|
pub fn RuntimeSink::flush(self : RuntimeSink) -> Int {
runtime_sink_progress_compat_count(self.flush_progress())
}
///|
pub fn RuntimeSink::drain_progress(
self : RuntimeSink,
max_items? : Int = -1,
) -> RuntimeSinkProgress {
match self {
Console(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
JsonConsole(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
TextConsole(_) =>
{
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
File(sink) =>
{
queue_advanced_count: 0,
file_flush_step_count: if sink.flush() {
1
} else {
0
},
queue_backed: false,
file_backed: true,
}
QueuedConsole(sink) =>
{
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedJsonConsole(sink) =>
{
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedTextConsole(sink) =>
{
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedFile(sink) =>
{
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: true,
}
}
}
///|
pub fn RuntimeSink::drain(self : RuntimeSink, max_items? : Int = -1) -> Int {
runtime_sink_progress_compat_count(self.drain_progress(max_items~))
}
///|
pub fn RuntimeSink::close(self : RuntimeSink) -> Bool {
match self {
Console(_) => true
JsonConsole(_) => true
TextConsole(_) => true
File(sink) => sink.close()
QueuedConsole(sink) => queued_sink_close_internal(sink)
QueuedJsonConsole(sink) => queued_sink_close_internal(sink)
QueuedTextConsole(sink) => queued_sink_close_internal(sink)
QueuedFile(sink) => queued_file_close_internal(sink)
}
}
///|
pub fn RuntimeSink::pending_count(self : RuntimeSink) -> Int {
match self {
Console(_) => 0
JsonConsole(_) => 0
TextConsole(_) => 0
File(_) => 0
QueuedConsole(sink) => sink.pending_count()
QueuedJsonConsole(sink) => sink.pending_count()
QueuedTextConsole(sink) => sink.pending_count()
QueuedFile(sink) => sink.pending_count()
}
}
///|
pub fn RuntimeSink::dropped_count(self : RuntimeSink) -> Int {
match self {
Console(_) => 0
JsonConsole(_) => 0
TextConsole(_) => 0
File(_) => 0
QueuedConsole(sink) => sink.dropped_count()
QueuedJsonConsole(sink) => sink.dropped_count()
QueuedTextConsole(sink) => sink.dropped_count()
QueuedFile(sink) => sink.dropped_count()
}
}
///|
pub fn RuntimeSink::file_available(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.is_available()
QueuedFile(sink) => sink.sink.is_available()
_ => false
}
}
///|
pub fn RuntimeSink::file_reopen(
self : RuntimeSink,
append? : Bool? = None,
) -> Bool {
match self {
File(sink) => sink.reopen(append~)
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.reopen(append~)
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_reopen_with_current_policy(self : RuntimeSink) -> Bool {
self.file_reopen()
}
///|
pub fn RuntimeSink::file_reopen_append(self : RuntimeSink) -> Bool {
self.file_reopen(append=Some(true))
}
///|
pub fn RuntimeSink::file_reopen_truncate(self : RuntimeSink) -> Bool {
self.file_reopen(append=Some(false))
}
///|
pub fn RuntimeSink::file_append_mode(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.append_mode()
QueuedFile(sink) => sink.sink.append_mode()
_ => false
}
}
///|
pub fn RuntimeSink::file_set_append_mode(
self : RuntimeSink,
append : Bool,
) -> Bool {
match self {
File(sink) => {
sink.set_append_mode(append)
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_append_mode(append)
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_path_or_none(self : RuntimeSink) -> String? {
runtime_file_sink_internal(self).map(fn(sink) { sink.path() })
}
///|
pub fn RuntimeSink::file_path(self : RuntimeSink) -> String {
self.file_path_or_none().unwrap_or("")
}
///|
pub fn RuntimeSink::file_auto_flush(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.auto_flush_enabled()
QueuedFile(sink) => sink.sink.auto_flush_enabled()
_ => false
}
}
///|
pub fn RuntimeSink::file_rotation_enabled(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.rotation_enabled()
QueuedFile(sink) => sink.sink.rotation_enabled()
_ => false
}
}
///|
pub fn RuntimeSink::file_rotation_config(self : RuntimeSink) -> FileRotation? {
match self {
File(sink) => sink.rotation_config()
QueuedFile(sink) => sink.sink.rotation_config()
_ => None
}
}
///|
pub fn RuntimeSink::file_set_auto_flush(
self : RuntimeSink,
enabled : Bool,
) -> Bool {
match self {
File(sink) => {
sink.set_auto_flush(enabled)
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_auto_flush(enabled)
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_set_policy(
self : RuntimeSink,
policy : FileSinkPolicy,
) -> Bool {
match self {
File(sink) => {
sink.set_policy(policy)
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_policy(policy)
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_set_rotation(
self : RuntimeSink,
rotation : FileRotation?,
) -> Bool {
match self {
File(sink) => {
sink.set_rotation(rotation)
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_rotation(rotation)
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_clear_rotation(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.clear_rotation()
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.clear_rotation()
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_flush(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.flush()
QueuedFile(sink) => queued_file_flush_internal(sink)
_ => false
}
}
///|
pub fn RuntimeSink::file_close(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.close()
QueuedFile(sink) => queued_file_close_internal(sink)
_ => false
}
}
///|
pub fn RuntimeSink::file_open_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.open_failures()
QueuedFile(sink) => sink.sink.open_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_write_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.write_failures()
QueuedFile(sink) => sink.sink.write_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_flush_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.flush_failures()
QueuedFile(sink) => sink.sink.flush_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_rotation_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.rotation_failures()
QueuedFile(sink) => sink.sink.rotation_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_reset_failure_counters(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.reset_failure_counters()
true
}
QueuedFile(sink) => {
sink.sink.reset_failure_counters()
true
}
_ => false
}
}
///|
pub fn RuntimeSink::file_reset_policy(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.reset_policy()
true
}
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.reset_policy()
true
} else {
false
}
_ => false
}
}
///|
pub fn RuntimeSink::file_policy_or_none(self : RuntimeSink) -> FileSinkPolicy? {
runtime_file_sink_internal(self).map(fn(sink) { sink.policy() })
}
///|
pub fn RuntimeSink::file_policy(self : RuntimeSink) -> FileSinkPolicy {
self
.file_policy_or_none()
.unwrap_or(FileSinkPolicy::new(append=false, auto_flush=false, rotation=None))
}
///|
pub fn RuntimeSink::file_default_policy_or_none(
self : RuntimeSink,
) -> FileSinkPolicy? {
runtime_file_sink_internal(self).map(fn(sink) { sink.default_policy() })
}
///|
pub fn RuntimeSink::file_default_policy(self : RuntimeSink) -> FileSinkPolicy {
self
.file_default_policy_or_none()
.unwrap_or(FileSinkPolicy::new(append=false, auto_flush=false, rotation=None))
}
///|
pub fn RuntimeSink::file_policy_matches_default(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.policy_matches_default()
QueuedFile(sink) => sink.sink.policy_matches_default()
_ => false
}
}
///|
pub fn RuntimeSink::file_state_or_none(self : RuntimeSink) -> FileSinkState? {
runtime_file_sink_internal(self).map(fn(sink) { sink.state() })
}
///|
pub fn RuntimeSink::file_state(self : RuntimeSink) -> FileSinkState {
self
.file_state_or_none()
.unwrap_or(
FileSinkState::new(
"",
available=false,
append=false,
auto_flush=false,
rotation=None,
open_failures=0,
write_failures=0,
flush_failures=0,
rotation_failures=0,
),
)
}
///|
pub fn RuntimeSink::file_runtime_state(self : RuntimeSink) -> RuntimeFileState? {
match self {
File(sink) => Some(RuntimeFileState::new(sink.state()))
QueuedFile(sink) =>
Some(
RuntimeFileState::new(
sink.sink.state(),
queued=true,
pending_count=sink.pending_count(),
dropped_count=sink.dropped_count(),
),
)
_ => None
}
}
///|
pub fn build_runtime_sink(config : SinkConfig) -> RuntimeSink {
match config.kind {
SinkKind::Console => RuntimeSink::Console(@sink_graph.console_sink())
SinkKind::JsonConsole =>
RuntimeSink::JsonConsole(@sink_graph.json_console_sink())
SinkKind::TextConsole =>
RuntimeSink::TextConsole(
@sink_graph.text_console_sink(config.text_formatter.to_formatter()),
)
SinkKind::File =>
RuntimeSink::File(
@file_runtime.file_sink(
config.path,
append=config.append,
auto_flush=config.auto_flush,
rotation=config.rotation,
formatter=fn(rec) {
@formatting.format_text(
rec,
formatter=config.text_formatter.to_formatter(),
)
},
),
)
}
}
///|
pub fn apply_queue_config(
sink : RuntimeSink,
queue : QueueConfig,
) -> RuntimeSink {
match sink {
Console(inner) =>
RuntimeSink::QueuedConsole(
@sink_graph.queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
JsonConsole(inner) =>
RuntimeSink::QueuedJsonConsole(
@sink_graph.queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
TextConsole(inner) =>
RuntimeSink::QueuedTextConsole(
@sink_graph.queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
File(inner) =>
RuntimeSink::QueuedFile(
@sink_graph.queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
QueuedConsole(_) => sink
QueuedJsonConsole(_) => sink
QueuedTextConsole(_) => sink
QueuedFile(_) => sink
}
}
+79 -472
View File
@@ -1,585 +1,192 @@
///|
fn runtime_file_sink_internal(sink : RuntimeSink) -> FileSink? {
match sink {
File(sink) => Some(sink)
QueuedFile(sink) => Some(sink.sink)
_ => None
}
pub fn Logger::file_available(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_available(self.sink)
}
///|
fn queued_file_policy_mutation_allowed_internal(
sink : QueuedSink[FileSink],
) -> Bool {
sink.pending_count() == 0
}
///|
fn queued_file_flush_internal(sink : QueuedSink[FileSink]) -> Bool {
let before_write_failures = sink.sink.write_failures()
let before_flush_failures = sink.sink.flush_failures()
let before_rotation_failures = sink.sink.rotation_failures()
ignore(sink.flush())
let flushed = sink.sink.flush()
sink.pending_count() == 0 &&
flushed &&
sink.sink.write_failures() == before_write_failures &&
sink.sink.flush_failures() == before_flush_failures &&
sink.sink.rotation_failures() == before_rotation_failures
}
///|
fn queued_file_close_internal(sink : QueuedSink[FileSink]) -> Bool {
let flushed = queued_file_flush_internal(sink)
let closed = sink.sink.close()
flushed && closed
}
///|
pub fn RuntimeSink::file_available(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.is_available()
QueuedFile(sink) => sink.sink.is_available()
_ => false
}
}
///|
pub fn RuntimeSink::file_reopen(
self : RuntimeSink,
pub fn Logger::file_reopen(
self : Logger[RuntimeSink],
append? : Bool? = None,
) -> Bool {
match self {
File(sink) => sink.reopen(append~)
QueuedFile(sink) =>
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.reopen(append~)
} else {
false
}
_ => false
}
@runtime.RuntimeSink::file_reopen(self.sink, append~)
}
///|
pub fn RuntimeSink::file_reopen_with_current_policy(self : RuntimeSink) -> Bool {
self.file_reopen()
pub fn Logger::file_reopen_with_current_policy(
self : Logger[RuntimeSink],
) -> Bool {
@runtime.RuntimeSink::file_reopen_with_current_policy(self.sink)
}
///|
pub fn RuntimeSink::file_reopen_append(self : RuntimeSink) -> Bool {
self.file_reopen(append=Some(true))
pub fn Logger::file_reopen_append(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_reopen_append(self.sink)
}
///|
pub fn RuntimeSink::file_reopen_truncate(self : RuntimeSink) -> Bool {
self.file_reopen(append=Some(false))
pub fn Logger::file_reopen_truncate(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_reopen_truncate(self.sink)
}
///|
pub fn RuntimeSink::file_append_mode(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.append_mode()
QueuedFile(sink) => sink.sink.append_mode()
_ => false
}
pub fn Logger::file_append_mode(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_append_mode(self.sink)
}
///|
pub fn RuntimeSink::file_set_append_mode(
self : RuntimeSink,
pub fn Logger::file_set_append_mode(
self : Logger[RuntimeSink],
append : Bool,
) -> Bool {
match self {
File(sink) => {
sink.set_append_mode(append)
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_append_mode(append)
true
} else {
false
}
}
_ => false
}
@runtime.RuntimeSink::file_set_append_mode(self.sink, append)
}
///|
pub fn RuntimeSink::file_path_or_none(self : RuntimeSink) -> String? {
runtime_file_sink_internal(self).map(fn(sink) { sink.path() })
pub fn Logger::file_path_or_none(self : Logger[RuntimeSink]) -> String? {
@runtime.RuntimeSink::file_path_or_none(self.sink)
}
///|
pub fn RuntimeSink::file_path(self : RuntimeSink) -> String {
self.file_path_or_none().unwrap_or("")
pub fn Logger::file_path(self : Logger[RuntimeSink]) -> String {
@runtime.RuntimeSink::file_path(self.sink)
}
///|
pub fn RuntimeSink::file_auto_flush(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.auto_flush_enabled()
QueuedFile(sink) => sink.sink.auto_flush_enabled()
_ => false
}
pub fn Logger::file_auto_flush(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_auto_flush(self.sink)
}
///|
pub fn RuntimeSink::file_rotation_enabled(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.rotation_enabled()
QueuedFile(sink) => sink.sink.rotation_enabled()
_ => false
}
pub fn Logger::file_rotation_enabled(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_rotation_enabled(self.sink)
}
///|
pub fn RuntimeSink::file_rotation_config(self : RuntimeSink) -> FileRotation? {
match self {
File(sink) => sink.rotation_config()
QueuedFile(sink) => sink.sink.rotation_config()
_ => None
}
}
///|
pub fn RuntimeSink::file_set_auto_flush(
self : RuntimeSink,
enabled : Bool,
) -> Bool {
match self {
File(sink) => {
sink.set_auto_flush(enabled)
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_auto_flush(enabled)
true
} else {
false
}
}
_ => false
}
}
///|
pub fn RuntimeSink::file_set_policy(
self : RuntimeSink,
policy : FileSinkPolicy,
) -> Bool {
match self {
File(sink) => {
sink.set_policy(policy)
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_policy(policy)
true
} else {
false
}
}
_ => false
}
}
///|
pub fn RuntimeSink::file_set_rotation(
self : RuntimeSink,
rotation : FileRotation?,
) -> Bool {
match self {
File(sink) => {
sink.set_rotation(rotation)
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.set_rotation(rotation)
true
} else {
false
}
}
_ => false
}
}
///|
pub fn RuntimeSink::file_clear_rotation(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.clear_rotation()
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.clear_rotation()
true
} else {
false
}
}
_ => false
}
}
///|
pub fn RuntimeSink::file_flush(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.flush()
QueuedFile(sink) => queued_file_flush_internal(sink)
_ => false
}
}
///|
pub fn RuntimeSink::file_close(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.close()
QueuedFile(sink) => queued_file_close_internal(sink)
_ => false
}
}
///|
pub fn RuntimeSink::file_open_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.open_failures()
QueuedFile(sink) => sink.sink.open_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_write_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.write_failures()
QueuedFile(sink) => sink.sink.write_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_flush_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.flush_failures()
QueuedFile(sink) => sink.sink.flush_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_rotation_failures(self : RuntimeSink) -> Int {
match self {
File(sink) => sink.rotation_failures()
QueuedFile(sink) => sink.sink.rotation_failures()
_ => 0
}
}
///|
pub fn RuntimeSink::file_reset_failure_counters(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.reset_failure_counters()
true
}
QueuedFile(sink) => {
sink.sink.reset_failure_counters()
true
}
_ => false
}
}
///|
pub fn RuntimeSink::file_reset_policy(self : RuntimeSink) -> Bool {
match self {
File(sink) => {
sink.reset_policy()
true
}
QueuedFile(sink) => {
if queued_file_policy_mutation_allowed_internal(sink) {
sink.sink.reset_policy()
true
} else {
false
}
}
_ => false
}
}
///|
pub fn RuntimeSink::file_policy_or_none(self : RuntimeSink) -> FileSinkPolicy? {
runtime_file_sink_internal(self).map(fn(sink) { sink.policy() })
}
///|
pub fn RuntimeSink::file_policy(self : RuntimeSink) -> FileSinkPolicy {
self.file_policy_or_none().unwrap_or(
FileSinkPolicy::new(append=false, auto_flush=false, rotation=None),
)
}
///|
pub fn RuntimeSink::file_default_policy_or_none(
self : RuntimeSink,
) -> FileSinkPolicy? {
runtime_file_sink_internal(self).map(fn(sink) { sink.default_policy() })
}
///|
pub fn RuntimeSink::file_default_policy(self : RuntimeSink) -> FileSinkPolicy {
self.file_default_policy_or_none().unwrap_or(
FileSinkPolicy::new(append=false, auto_flush=false, rotation=None),
)
}
///|
pub fn RuntimeSink::file_policy_matches_default(self : RuntimeSink) -> Bool {
match self {
File(sink) => sink.policy_matches_default()
QueuedFile(sink) => sink.sink.policy_matches_default()
_ => false
}
}
///|
pub fn RuntimeSink::file_state_or_none(self : RuntimeSink) -> FileSinkState? {
runtime_file_sink_internal(self).map(fn(sink) { sink.state() })
}
///|
pub fn RuntimeSink::file_state(self : RuntimeSink) -> FileSinkState {
self.file_state_or_none().unwrap_or(
FileSinkState::new(
"",
available=false,
append=false,
auto_flush=false,
rotation=None,
open_failures=0,
write_failures=0,
flush_failures=0,
rotation_failures=0,
),
)
}
///|
pub fn RuntimeSink::file_runtime_state(self : RuntimeSink) -> RuntimeFileState? {
match self {
File(sink) => Some(RuntimeFileState::new(sink.state()))
QueuedFile(sink) =>
Some(
RuntimeFileState::new(
sink.sink.state(),
queued=true,
pending_count=sink.pending_count(),
dropped_count=sink.dropped_count(),
),
)
_ => None
}
}
///|
pub fn ConfiguredLogger::file_available(self : ConfiguredLogger) -> Bool {
self.sink.file_available()
}
///|
pub fn ConfiguredLogger::file_reopen(
self : ConfiguredLogger,
append? : Bool? = None,
) -> Bool {
self.sink.file_reopen(append~)
}
///|
pub fn ConfiguredLogger::file_reopen_with_current_policy(
self : ConfiguredLogger,
) -> Bool {
self.sink.file_reopen_with_current_policy()
}
///|
pub fn ConfiguredLogger::file_reopen_append(self : ConfiguredLogger) -> Bool {
self.sink.file_reopen_append()
}
///|
pub fn ConfiguredLogger::file_reopen_truncate(self : ConfiguredLogger) -> Bool {
self.sink.file_reopen_truncate()
}
///|
pub fn ConfiguredLogger::file_append_mode(self : ConfiguredLogger) -> Bool {
self.sink.file_append_mode()
}
///|
pub fn ConfiguredLogger::file_set_append_mode(
self : ConfiguredLogger,
append : Bool,
) -> Bool {
self.sink.file_set_append_mode(append)
}
///|
pub fn ConfiguredLogger::file_path_or_none(self : ConfiguredLogger) -> String? {
self.sink.file_path_or_none()
}
///|
pub fn ConfiguredLogger::file_path(self : ConfiguredLogger) -> String {
self.sink.file_path()
}
///|
pub fn ConfiguredLogger::file_auto_flush(self : ConfiguredLogger) -> Bool {
self.sink.file_auto_flush()
}
///|
pub fn ConfiguredLogger::file_rotation_enabled(self : ConfiguredLogger) -> Bool {
self.sink.file_rotation_enabled()
}
///|
pub fn ConfiguredLogger::file_rotation_config(
self : ConfiguredLogger,
pub fn Logger::file_rotation_config(
self : Logger[RuntimeSink],
) -> FileRotation? {
self.sink.file_rotation_config()
@runtime.RuntimeSink::file_rotation_config(self.sink)
}
///|
pub fn ConfiguredLogger::file_set_auto_flush(
self : ConfiguredLogger,
pub fn Logger::file_set_auto_flush(
self : Logger[RuntimeSink],
enabled : Bool,
) -> Bool {
self.sink.file_set_auto_flush(enabled)
@runtime.RuntimeSink::file_set_auto_flush(self.sink, enabled)
}
///|
pub fn ConfiguredLogger::file_set_policy(
self : ConfiguredLogger,
pub fn Logger::file_set_policy(
self : Logger[RuntimeSink],
policy : FileSinkPolicy,
) -> Bool {
self.sink.file_set_policy(policy)
@runtime.RuntimeSink::file_set_policy(self.sink, policy)
}
///|
pub fn ConfiguredLogger::file_set_rotation(
self : ConfiguredLogger,
pub fn Logger::file_set_rotation(
self : Logger[RuntimeSink],
rotation : FileRotation?,
) -> Bool {
self.sink.file_set_rotation(rotation)
@runtime.RuntimeSink::file_set_rotation(self.sink, rotation)
}
///|
pub fn ConfiguredLogger::file_clear_rotation(self : ConfiguredLogger) -> Bool {
self.sink.file_clear_rotation()
pub fn Logger::file_clear_rotation(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_clear_rotation(self.sink)
}
///|
pub fn ConfiguredLogger::file_flush(self : ConfiguredLogger) -> Bool {
self.sink.file_flush()
pub fn Logger::file_flush(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_flush(self.sink)
}
///|
pub fn ConfiguredLogger::file_close(self : ConfiguredLogger) -> Bool {
self.sink.file_close()
pub fn Logger::file_close(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_close(self.sink)
}
///|
pub fn ConfiguredLogger::file_open_failures(self : ConfiguredLogger) -> Int {
self.sink.file_open_failures()
pub fn Logger::file_open_failures(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::file_open_failures(self.sink)
}
///|
pub fn ConfiguredLogger::file_write_failures(self : ConfiguredLogger) -> Int {
self.sink.file_write_failures()
pub fn Logger::file_write_failures(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::file_write_failures(self.sink)
}
///|
pub fn ConfiguredLogger::file_flush_failures(self : ConfiguredLogger) -> Int {
self.sink.file_flush_failures()
pub fn Logger::file_flush_failures(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::file_flush_failures(self.sink)
}
///|
pub fn ConfiguredLogger::file_rotation_failures(self : ConfiguredLogger) -> Int {
self.sink.file_rotation_failures()
pub fn Logger::file_rotation_failures(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::file_rotation_failures(self.sink)
}
///|
pub fn ConfiguredLogger::file_reset_failure_counters(
self : ConfiguredLogger,
pub fn Logger::file_reset_failure_counters(
self : Logger[RuntimeSink],
) -> Bool {
self.sink.file_reset_failure_counters()
@runtime.RuntimeSink::file_reset_failure_counters(self.sink)
}
///|
pub fn ConfiguredLogger::file_reset_policy(self : ConfiguredLogger) -> Bool {
self.sink.file_reset_policy()
pub fn Logger::file_reset_policy(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::file_reset_policy(self.sink)
}
///|
pub fn ConfiguredLogger::file_policy_or_none(
self : ConfiguredLogger,
pub fn Logger::file_policy_or_none(
self : Logger[RuntimeSink],
) -> FileSinkPolicy? {
self.sink.file_policy_or_none()
@runtime.RuntimeSink::file_policy_or_none(self.sink)
}
///|
pub fn ConfiguredLogger::file_policy(self : ConfiguredLogger) -> FileSinkPolicy {
self.sink.file_policy()
pub fn Logger::file_policy(self : Logger[RuntimeSink]) -> FileSinkPolicy {
@runtime.RuntimeSink::file_policy(self.sink)
}
///|
pub fn ConfiguredLogger::file_default_policy_or_none(
self : ConfiguredLogger,
pub fn Logger::file_default_policy_or_none(
self : Logger[RuntimeSink],
) -> FileSinkPolicy? {
self.sink.file_default_policy_or_none()
@runtime.RuntimeSink::file_default_policy_or_none(self.sink)
}
///|
pub fn ConfiguredLogger::file_default_policy(
self : ConfiguredLogger,
pub fn Logger::file_default_policy(
self : Logger[RuntimeSink],
) -> FileSinkPolicy {
self.sink.file_default_policy()
@runtime.RuntimeSink::file_default_policy(self.sink)
}
///|
pub fn ConfiguredLogger::file_policy_matches_default(
self : ConfiguredLogger,
pub fn Logger::file_policy_matches_default(
self : Logger[RuntimeSink],
) -> Bool {
self.sink.file_policy_matches_default()
@runtime.RuntimeSink::file_policy_matches_default(self.sink)
}
///|
pub fn ConfiguredLogger::file_state_or_none(
self : ConfiguredLogger,
pub fn Logger::file_state_or_none(
self : Logger[RuntimeSink],
) -> FileSinkState? {
self.sink.file_state_or_none()
@runtime.RuntimeSink::file_state_or_none(self.sink)
}
///|
pub fn ConfiguredLogger::file_state(self : ConfiguredLogger) -> FileSinkState {
self.sink.file_state()
pub fn Logger::file_state(self : Logger[RuntimeSink]) -> FileSinkState {
@runtime.RuntimeSink::file_state(self.sink)
}
///|
pub fn ConfiguredLogger::file_runtime_state(
self : ConfiguredLogger,
pub fn Logger::file_runtime_state(
self : Logger[RuntimeSink],
) -> RuntimeFileState? {
self.sink.file_runtime_state()
@runtime.RuntimeSink::file_runtime_state(self.sink)
}
+26 -322
View File
@@ -1,59 +1,17 @@
///|
pub(all) enum RuntimeSink {
Console(ConsoleSink)
JsonConsole(JsonConsoleSink)
TextConsole(FormattedConsoleSink)
File(FileSink)
QueuedConsole(QueuedSink[ConsoleSink])
QueuedJsonConsole(QueuedSink[JsonConsoleSink])
QueuedTextConsole(QueuedSink[FormattedConsoleSink])
QueuedFile(QueuedSink[FileSink])
}
pub type RuntimeSink = @runtime.RuntimeSink
///|
pub type RuntimeFileState = @utils.RuntimeFileState
pub type RuntimeFileState = @file_model.RuntimeFileState
///|
pub struct RuntimeSinkProgress {
queue_advanced_count : Int
file_flush_step_count : Int
queue_backed : Bool
file_backed : Bool
}
///|
pub fn file_sink_policy_to_json(
policy : FileSinkPolicy,
) -> @json_parser.JsonValue {
@utils.file_sink_policy_to_json(policy)
}
///|
pub fn stringify_file_sink_policy(
policy : FileSinkPolicy,
pretty? : Bool = false,
) -> String {
@utils.stringify_file_sink_policy(policy, pretty~)
}
///|
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
@utils.file_sink_state_to_json(state)
}
///|
pub fn stringify_file_sink_state(
state : FileSinkState,
pretty? : Bool = false,
) -> String {
@utils.stringify_file_sink_state(state, pretty~)
}
pub type RuntimeSinkProgress = @runtime.RuntimeSinkProgress
///|
pub fn runtime_file_state_to_json(
state : RuntimeFileState,
) -> @json_parser.JsonValue {
@utils.runtime_file_state_to_json(state)
@file_model.runtime_file_state_to_json(state)
}
///|
@@ -61,315 +19,61 @@ pub fn stringify_runtime_file_state(
state : RuntimeFileState,
pretty? : Bool = false,
) -> String {
@utils.stringify_runtime_file_state(state, pretty~)
}
///|
pub impl Sink for RuntimeSink with fn write(self, rec) {
match self {
Console(sink) => sink.write(rec)
JsonConsole(sink) => sink.write(rec)
TextConsole(sink) => sink.write(rec)
File(sink) => sink.write(rec)
QueuedConsole(sink) => sink.write(rec)
QueuedJsonConsole(sink) => sink.write(rec)
QueuedTextConsole(sink) => sink.write(rec)
QueuedFile(sink) => sink.write(rec)
}
}
///|
fn[S : Sink] queued_sink_close_internal(sink : QueuedSink[S]) -> Bool {
ignore(sink.flush())
sink.pending_count() == 0
}
///|
fn runtime_sink_progress_compat_count(progress : RuntimeSinkProgress) -> Int {
progress.queue_advanced_count + progress.file_flush_step_count
}
///|
pub fn RuntimeSink::flush_progress(self : RuntimeSink) -> RuntimeSinkProgress {
match self {
Console(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
JsonConsole(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
TextConsole(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
File(sink) => {
queue_advanced_count: 0,
file_flush_step_count: if sink.flush() { 1 } else { 0 },
queue_backed: false,
file_backed: true,
}
QueuedConsole(sink) => {
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedJsonConsole(sink) => {
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedTextConsole(sink) => {
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedFile(sink) => {
queue_advanced_count: sink.flush(),
file_flush_step_count: 0,
queue_backed: true,
file_backed: true,
}
}
}
///|
pub fn RuntimeSink::flush(self : RuntimeSink) -> Int {
runtime_sink_progress_compat_count(self.flush_progress())
}
///|
pub fn RuntimeSink::drain_progress(
self : RuntimeSink,
max_items? : Int = -1,
) -> RuntimeSinkProgress {
match self {
Console(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
JsonConsole(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
TextConsole(_) => {
queue_advanced_count: 0,
file_flush_step_count: 0,
queue_backed: false,
file_backed: false,
}
File(sink) => {
queue_advanced_count: 0,
file_flush_step_count: if sink.flush() { 1 } else { 0 },
queue_backed: false,
file_backed: true,
}
QueuedConsole(sink) => {
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedJsonConsole(sink) => {
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedTextConsole(sink) => {
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: false,
}
QueuedFile(sink) => {
queue_advanced_count: sink.drain(max_items~),
file_flush_step_count: 0,
queue_backed: true,
file_backed: true,
}
}
}
///|
pub fn RuntimeSink::drain(self : RuntimeSink, max_items? : Int = -1) -> Int {
runtime_sink_progress_compat_count(self.drain_progress(max_items~))
}
///|
pub fn RuntimeSink::close(self : RuntimeSink) -> Bool {
match self {
Console(_) => true
JsonConsole(_) => true
TextConsole(_) => true
File(sink) => sink.close()
QueuedConsole(sink) => queued_sink_close_internal(sink)
QueuedJsonConsole(sink) => queued_sink_close_internal(sink)
QueuedTextConsole(sink) => queued_sink_close_internal(sink)
QueuedFile(sink) => queued_file_close_internal(sink)
}
}
///|
pub fn RuntimeSink::pending_count(self : RuntimeSink) -> Int {
match self {
Console(_) => 0
JsonConsole(_) => 0
TextConsole(_) => 0
File(_) => 0
QueuedConsole(sink) => sink.pending_count()
QueuedJsonConsole(sink) => sink.pending_count()
QueuedTextConsole(sink) => sink.pending_count()
QueuedFile(sink) => sink.pending_count()
}
}
///|
pub fn RuntimeSink::dropped_count(self : RuntimeSink) -> Int {
match self {
Console(_) => 0
JsonConsole(_) => 0
TextConsole(_) => 0
File(_) => 0
QueuedConsole(sink) => sink.dropped_count()
QueuedJsonConsole(sink) => sink.dropped_count()
QueuedTextConsole(sink) => sink.dropped_count()
QueuedFile(sink) => sink.dropped_count()
}
@file_model.stringify_runtime_file_state(state, pretty~)
}
///|
pub type ConfiguredLogger = Logger[RuntimeSink]
///|
pub fn ConfiguredLogger::flush_progress(
self : ConfiguredLogger,
pub fn Logger::flush_progress(
self : Logger[RuntimeSink],
) -> RuntimeSinkProgress {
self.sink.flush_progress()
@runtime.RuntimeSink::flush_progress(self.sink)
}
///|
pub fn ConfiguredLogger::flush(self : ConfiguredLogger) -> Int {
self.sink.flush()
pub fn Logger::flush(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::flush(self.sink)
}
///|
pub fn ConfiguredLogger::drain_progress(
self : ConfiguredLogger,
pub fn Logger::drain_progress(
self : Logger[RuntimeSink],
max_items? : Int = -1,
) -> RuntimeSinkProgress {
self.sink.drain_progress(max_items~)
@runtime.RuntimeSink::drain_progress(self.sink, max_items~)
}
///|
pub fn ConfiguredLogger::drain(
self : ConfiguredLogger,
pub fn Logger::drain(
self : Logger[RuntimeSink],
max_items? : Int = -1,
) -> Int {
self.sink.drain(max_items~)
@runtime.RuntimeSink::drain(self.sink, max_items~)
}
///|
pub fn ConfiguredLogger::close(self : ConfiguredLogger) -> Bool {
self.sink.close()
pub fn Logger::close(self : Logger[RuntimeSink]) -> Bool {
@runtime.RuntimeSink::close(self.sink)
}
///|
pub fn ConfiguredLogger::pending_count(self : ConfiguredLogger) -> Int {
self.sink.pending_count()
pub fn Logger::pending_count(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::pending_count(self.sink)
}
///|
pub fn ConfiguredLogger::dropped_count(self : ConfiguredLogger) -> Int {
self.sink.dropped_count()
pub fn Logger::dropped_count(self : Logger[RuntimeSink]) -> Int {
@runtime.RuntimeSink::dropped_count(self.sink)
}
///|
fn build_runtime_sink(config : SinkConfig) -> RuntimeSink {
match config.kind {
SinkKind::Console => RuntimeSink::Console(console_sink())
SinkKind::JsonConsole => RuntimeSink::JsonConsole(json_console_sink())
SinkKind::TextConsole =>
RuntimeSink::TextConsole(
text_console_sink(config.text_formatter.to_formatter()),
)
SinkKind::File =>
RuntimeSink::File(
file_sink(
config.path,
append=config.append,
auto_flush=config.auto_flush,
rotation=config.rotation,
formatter=fn(rec) {
format_text(rec, formatter=config.text_formatter.to_formatter())
},
),
)
}
}
///|
fn apply_queue_config(sink : RuntimeSink, queue : QueueConfig) -> RuntimeSink {
match sink {
Console(inner) =>
RuntimeSink::QueuedConsole(
queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
JsonConsole(inner) =>
RuntimeSink::QueuedJsonConsole(
queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
TextConsole(inner) =>
RuntimeSink::QueuedTextConsole(
queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
File(inner) =>
RuntimeSink::QueuedFile(
queued_sink(
inner,
max_pending=queue.max_pending,
overflow=queue.overflow,
),
)
QueuedConsole(_) => sink
QueuedJsonConsole(_) => sink
QueuedTextConsole(_) => sink
QueuedFile(_) => sink
}
}
///|
pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {
let sink = build_runtime_sink(config.sink)
pub fn build_logger(config : LoggerConfig) -> Logger[RuntimeSink] {
let sink = @runtime.build_runtime_sink(config.sink)
let actual_sink = match config.queue {
None => sink
Some(queue) => apply_queue_config(sink, queue)
Some(queue) => @runtime.apply_queue_config(sink, queue)
}
Logger::new(actual_sink, min_level=config.min_level, target=config.target).with_timestamp(
enabled=config.timestamp,
@@ -379,6 +83,6 @@ pub fn build_logger(config : LoggerConfig) -> ConfiguredLogger {
///|
pub fn parse_and_build_logger(
input : String,
) -> ConfiguredLogger raise ConfigError {
) -> Logger[RuntimeSink] raise ConfigError {
build_logger(parse_logger_config_text(input))
}
+8
View File
@@ -0,0 +1,8 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/queue_model",
"Nanaloveyuki/BitLogger/src/record_ops",
"moonbitlang/core/queue",
"moonbitlang/core/ref",
}
+132
View File
@@ -0,0 +1,132 @@
// Generated using `moon info`, DON'T EDIT IT
package "Nanaloveyuki/BitLogger/src/sink_graph"
import {
"Nanaloveyuki/BitLogger/src/core",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/queue_model",
"moonbitlang/core/queue",
"moonbitlang/core/ref",
}
// Values
pub fn[S] buffered_sink(S, flush_limit? : Int) -> BufferedSink[S]
pub fn callback_sink((@core.Record) -> Unit) -> CallbackSink
pub fn console_sink() -> ConsoleSink
pub fn[S] context_sink(S, Array[@core.Field]) -> ContextSink[S]
pub fn[A, B] fanout_sink(A, B) -> FanoutSink[A, B]
pub fn[S] filter_sink(S, (@core.Record) -> Bool) -> FilterSink[S]
pub fn formatted_callback_sink((@core.Record) -> String, (String) -> Unit) -> FormattedCallbackSink
pub fn formatted_console_sink((@core.Record) -> String) -> FormattedConsoleSink
pub fn json_console_sink() -> JsonConsoleSink
pub fn[S] patch_sink(S, (@core.Record) -> @core.Record) -> PatchSink[S]
pub fn[S] queued_sink(S, max_pending? : Int, overflow? : @queue_model.QueueOverflowPolicy) -> QueuedSink[S]
pub fn[A, B] split_by_level(A, B, min_level? : @core.Level) -> SplitSink[A, B]
pub fn[A, B] split_sink(A, B, (@core.Record) -> Bool) -> SplitSink[A, B]
pub fn text_callback_sink(@formatting.TextFormatter, (String) -> Unit) -> FormattedCallbackSink
pub fn text_console_sink(@formatting.TextFormatter) -> FormattedConsoleSink
// Errors
// Types and methods
pub struct BufferedSink[S] {
sink : S
buffer : @ref.Ref[Array[@core.Record]]
flush_limit : Int
}
pub fn[S : Sink] BufferedSink::flush(Self[S]) -> Unit
pub fn[S] BufferedSink::pending_count(Self[S]) -> Int
pub impl[S : Sink] Sink for BufferedSink[S]
pub struct CallbackSink {
callback : (@core.Record) -> Unit
}
pub impl Sink for CallbackSink
pub struct ConsoleSink {
_dummy : Unit
}
pub impl Sink for ConsoleSink
pub struct ContextSink[S] {
sink : S
context_fields : Array[@core.Field]
}
pub impl[S : Sink] Sink for ContextSink[S]
pub struct FanoutSink[A, B] {
left : A
right : B
}
pub impl[A : Sink, B : Sink] Sink for FanoutSink[A, B]
pub struct FilterSink[S] {
sink : S
predicate : (@core.Record) -> Bool
}
pub impl[S : Sink] Sink for FilterSink[S]
pub struct FormattedCallbackSink {
formatter : (@core.Record) -> String
callback : (String) -> Unit
}
pub impl Sink for FormattedCallbackSink
pub struct FormattedConsoleSink {
formatter : (@core.Record) -> String
}
pub impl Sink for FormattedConsoleSink
pub struct JsonConsoleSink {
_dummy : Unit
}
pub impl Sink for JsonConsoleSink
pub struct PatchSink[S] {
sink : S
patch : (@core.Record) -> @core.Record
}
pub impl[S : Sink] Sink for PatchSink[S]
pub struct QueuedSink[S] {
sink : S
queue : @queue.Queue[@core.Record]
max_pending : Int
overflow : @queue_model.QueueOverflowPolicy
dropped_count : @ref.Ref[Int]
}
pub fn[S : Sink] QueuedSink::drain(Self[S], max_items? : Int) -> Int
pub fn[S] QueuedSink::dropped_count(Self[S]) -> Int
pub fn[S : Sink] QueuedSink::flush(Self[S]) -> Int
pub fn[S] QueuedSink::pending_count(Self[S]) -> Int
pub impl[S] Sink for QueuedSink[S]
pub struct SplitSink[A, B] {
left : A
right : B
predicate : (@core.Record) -> Bool
}
pub impl[A : Sink, B : Sink] Sink for SplitSink[A, B]
// Type aliases
pub using @queue_model {type QueueOverflowPolicy}
// Traits
pub(open) trait Sink {
fn write(Self, @core.Record) -> Unit
}
+352
View File
@@ -0,0 +1,352 @@
///|
type Record = @core.Record
///|
type Field = @core.Field
///|
type Level = @core.Level
///|
type RecordFormatter = @formatting.RecordFormatter
///|
type TextFormatter = @formatting.TextFormatter
///|
type RecordPatch = @record_ops.RecordPatch
///|
pub(open) trait Sink {
fn write(Self, Record) -> Unit
}
///|
pub struct ConsoleSink {
_dummy : Unit
}
///|
pub fn console_sink() -> ConsoleSink {
{ _dummy: () }
}
///|
pub impl Sink for ConsoleSink with fn write(self, rec) {
ignore(self)
println(@formatting.format_text(rec))
}
///|
pub struct ContextSink[S] {
sink : S
context_fields : Array[Field]
}
///|
pub fn[S] context_sink(
sink : S,
context_fields : Array[Field],
) -> ContextSink[S] {
{ sink, context_fields }
}
///|
pub impl[S : Sink] Sink for ContextSink[S] with fn write(self, rec) {
let merged = if self.context_fields.length() == 0 {
rec.fields
} else if rec.fields.length() == 0 {
self.context_fields
} else {
self.context_fields + rec.fields
}
self.sink.write(rec.with_fields(merged))
}
///|
pub struct JsonConsoleSink {
_dummy : Unit
}
///|
pub fn json_console_sink() -> JsonConsoleSink {
{ _dummy: () }
}
///|
pub impl Sink for JsonConsoleSink with fn write(self, rec) {
ignore(self)
println(@formatting.format_json(rec))
}
///|
pub struct FormattedConsoleSink {
formatter : RecordFormatter
}
///|
pub fn formatted_console_sink(
formatter : RecordFormatter,
) -> FormattedConsoleSink {
{ formatter, }
}
///|
pub fn text_console_sink(formatter : TextFormatter) -> FormattedConsoleSink {
formatted_console_sink(fn(rec) { @formatting.format_text(rec, formatter~) })
}
///|
pub impl Sink for FormattedConsoleSink with fn write(self, rec) {
println((self.formatter)(rec))
}
///|
pub struct FormattedCallbackSink {
formatter : RecordFormatter
callback : (String) -> Unit
}
///|
pub fn formatted_callback_sink(
formatter : RecordFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
{ formatter, callback }
}
///|
pub fn text_callback_sink(
formatter : TextFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
formatted_callback_sink(
fn(rec) { @formatting.format_text(rec, formatter~) },
callback,
)
}
///|
pub impl Sink for FormattedCallbackSink with fn write(self, rec) {
(self.callback)((self.formatter)(rec))
}
///|
pub struct FanoutSink[A, B] {
left : A
right : B
}
///|
pub fn[A, B] fanout_sink(left : A, right : B) -> FanoutSink[A, B] {
{ left, right }
}
///|
pub impl[A : Sink, B : Sink] Sink for FanoutSink[A, B] with fn write(self, rec) {
self.left.write(rec)
self.right.write(rec.copy())
}
///|
pub struct SplitSink[A, B] {
left : A
right : B
predicate : (Record) -> Bool
}
///|
pub fn[A, B] split_sink(
left : A,
right : B,
predicate : (Record) -> Bool,
) -> SplitSink[A, B] {
{ left, right, predicate }
}
///|
pub fn[A, B] split_by_level(
left : A,
right : B,
min_level? : Level = Level::Warn,
) -> SplitSink[A, B] {
split_sink(left, right, fn(rec) { rec.level.enabled(min_level) })
}
///|
pub impl[A : Sink, B : Sink] Sink for SplitSink[A, B] with fn write(self, rec) {
if (self.predicate)(rec) {
self.left.write(rec)
} else {
self.right.write(rec)
}
}
///|
pub struct CallbackSink {
callback : (Record) -> Unit
}
///|
pub fn callback_sink(callback : (Record) -> Unit) -> CallbackSink {
{ callback, }
}
///|
pub impl Sink for CallbackSink with fn write(self, rec) {
(self.callback)(rec)
}
///|
pub struct BufferedSink[S] {
sink : S
buffer : Ref[Array[Record]]
flush_limit : Int
}
///|
pub fn[S] buffered_sink(sink : S, flush_limit? : Int = 1) -> BufferedSink[S] {
let actual_limit = if flush_limit <= 0 { 1 } else { flush_limit }
{ sink, buffer: Ref([]), flush_limit: actual_limit }
}
///|
pub fn[S] BufferedSink::pending_count(self : BufferedSink[S]) -> Int {
self.buffer.val.length()
}
///|
pub fn[S : Sink] BufferedSink::flush(self : BufferedSink[S]) -> Unit {
if self.buffer.val.length() == 0 {
()
} else {
let pending = self.buffer.val
self.buffer.val = []
for rec in pending {
self.sink.write(rec)
}
}
}
///|
pub impl[S : Sink] Sink for BufferedSink[S] with fn write(self, rec) {
self.buffer.val.push(rec)
if self.buffer.val.length() >= self.flush_limit {
self.flush()
}
}
///|
pub type QueueOverflowPolicy = @queue_model.QueueOverflowPolicy
///|
pub struct QueuedSink[S] {
sink : S
queue : @queue.Queue[Record]
max_pending : Int
overflow : QueueOverflowPolicy
dropped_count : Ref[Int]
}
///|
pub fn[S] queued_sink(
sink : S,
max_pending? : Int = 0,
overflow? : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
) -> QueuedSink[S] {
{
sink,
queue: @queue.Queue([]),
max_pending,
overflow,
dropped_count: Ref(0),
}
}
///|
pub fn[S] QueuedSink::pending_count(self : QueuedSink[S]) -> Int {
self.queue.length()
}
///|
pub fn[S] QueuedSink::dropped_count(self : QueuedSink[S]) -> Int {
self.dropped_count.val
}
///|
pub fn[S : Sink] QueuedSink::drain(
self : QueuedSink[S],
max_items? : Int = -1,
) -> Int {
if max_items == 0 {
return 0
}
let limit = if max_items < 0 { self.pending_count() } else { max_items }
for drained = 0; drained < limit; {
match self.queue.pop() {
None => break drained
Some(rec) => {
self.sink.write(rec)
continue drained + 1
}
}
} nobreak {
limit
}
}
///|
pub fn[S : Sink] QueuedSink::flush(self : QueuedSink[S]) -> Int {
self.drain()
}
///|
pub impl[S] Sink for QueuedSink[S] with fn write(self, rec) {
let full = self.max_pending > 0 && self.pending_count() >= self.max_pending
if !full {
self.queue.push(rec)
} else {
self.dropped_count.val += 1
match self.overflow {
QueueOverflowPolicy::DropNewest => ()
QueueOverflowPolicy::DropOldest => {
ignore(self.queue.pop())
self.queue.push(rec)
}
}
}
}
///|
pub struct FilterSink[S] {
sink : S
predicate : (Record) -> Bool
}
///|
pub fn[S] filter_sink(sink : S, predicate : (Record) -> Bool) -> FilterSink[S] {
{ sink, predicate }
}
///|
pub impl[S : Sink] Sink for FilterSink[S] with fn write(self, rec) {
if (self.predicate)(rec) {
self.sink.write(rec)
}
}
///|
pub struct PatchSink[S] {
sink : S
patch : RecordPatch
}
///|
pub fn[S] patch_sink(sink : S, patch : RecordPatch) -> PatchSink[S] {
{ sink, patch }
}
///|
pub impl[S : Sink] Sink for PatchSink[S] with fn write(self, rec) {
self.sink.write((self.patch)(rec))
}
+35 -219
View File
@@ -1,92 +1,57 @@
///|
pub trait Sink {
fn write(Self, Record) -> Unit
}
pub using @sink_graph {trait Sink}
///|
pub struct ConsoleSink {
_dummy : Unit
}
pub type ConsoleSink = @sink_graph.ConsoleSink
///|
pub fn console_sink() -> ConsoleSink {
{ _dummy: () }
@sink_graph.console_sink()
}
///|
pub impl Sink for ConsoleSink with fn write(self, rec) {
ignore(self)
println(format_text(rec))
pub type ContextSink[S] = @sink_graph.ContextSink[S]
///|
pub fn[S] context_sink(
sink : S,
context_fields : Array[Field],
) -> ContextSink[S] {
@sink_graph.context_sink(sink, context_fields)
}
///|
pub struct ContextSink[S] {
sink : S
context_fields : Array[Field]
}
///|
pub impl[S : Sink] Sink for ContextSink[S] with fn write(self, rec) {
let merged = if self.context_fields.length() == 0 {
rec.fields
} else if rec.fields.length() == 0 {
self.context_fields
} else {
self.context_fields + rec.fields
}
self.sink.write(rec.with_fields(merged))
}
///|
pub struct JsonConsoleSink {
_dummy : Unit
}
pub type JsonConsoleSink = @sink_graph.JsonConsoleSink
///|
pub fn json_console_sink() -> JsonConsoleSink {
{ _dummy: () }
@sink_graph.json_console_sink()
}
///|
pub impl Sink for JsonConsoleSink with fn write(self, rec) {
ignore(self)
println(format_json(rec))
}
///|
pub struct FormattedConsoleSink {
formatter : RecordFormatter
}
pub type FormattedConsoleSink = @sink_graph.FormattedConsoleSink
///|
pub fn formatted_console_sink(
formatter : RecordFormatter,
) -> FormattedConsoleSink {
{ formatter, }
@sink_graph.formatted_console_sink(formatter)
}
///|
pub fn text_console_sink(formatter : TextFormatter) -> FormattedConsoleSink {
formatted_console_sink(fn(rec) { format_text(rec, formatter~) })
@sink_graph.text_console_sink(formatter)
}
///|
pub impl Sink for FormattedConsoleSink with fn write(self, rec) {
println((self.formatter)(rec))
}
///|
pub struct FormattedCallbackSink {
formatter : RecordFormatter
callback : (String) -> Unit
}
pub type FormattedCallbackSink = @sink_graph.FormattedCallbackSink
///|
pub fn formatted_callback_sink(
formatter : RecordFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
{ formatter, callback }
@sink_graph.formatted_callback_sink(formatter, callback)
}
///|
@@ -94,37 +59,19 @@ pub fn text_callback_sink(
formatter : TextFormatter,
callback : (String) -> Unit,
) -> FormattedCallbackSink {
formatted_callback_sink(fn(rec) { format_text(rec, formatter~) }, callback)
@sink_graph.text_callback_sink(formatter, callback)
}
///|
pub impl Sink for FormattedCallbackSink with fn write(self, rec) {
(self.callback)((self.formatter)(rec))
}
///|
pub struct FanoutSink[A, B] {
left : A
right : B
}
pub type FanoutSink[A, B] = @sink_graph.FanoutSink[A, B]
///|
pub fn[A, B] fanout_sink(left : A, right : B) -> FanoutSink[A, B] {
{ left, right }
@sink_graph.fanout_sink(left, right)
}
///|
pub impl[A : Sink, B : Sink] Sink for FanoutSink[A, B] with fn write(self, rec) {
self.left.write(rec)
self.right.write(rec.copy())
}
///|
pub struct SplitSink[A, B] {
left : A
right : B
predicate : (Record) -> Bool
}
pub type SplitSink[A, B] = @sink_graph.SplitSink[A, B]
///|
pub fn[A, B] split_sink(
@@ -132,7 +79,7 @@ pub fn[A, B] split_sink(
right : B,
predicate : (Record) -> Bool,
) -> SplitSink[A, B] {
{ left, right, predicate }
@sink_graph.split_sink(left, right, predicate)
}
///|
@@ -141,83 +88,30 @@ pub fn[A, B] split_by_level(
right : B,
min_level? : Level = Level::Warn,
) -> SplitSink[A, B] {
split_sink(left, right, fn(rec) { rec.level.enabled(min_level) })
@sink_graph.split_by_level(left, right, min_level~)
}
///|
pub impl[A : Sink, B : Sink] Sink for SplitSink[A, B] with fn write(self, rec) {
if (self.predicate)(rec) {
self.left.write(rec)
} else {
self.right.write(rec)
}
}
///|
pub struct CallbackSink {
callback : (Record) -> Unit
}
pub type CallbackSink = @sink_graph.CallbackSink
///|
pub fn callback_sink(callback : (Record) -> Unit) -> CallbackSink {
{ callback, }
@sink_graph.callback_sink(callback)
}
///|
pub impl Sink for CallbackSink with fn write(self, rec) {
(self.callback)(rec)
}
///|
pub struct BufferedSink[S] {
sink : S
buffer : Ref[Array[Record]]
flush_limit : Int
}
pub type BufferedSink[S] = @sink_graph.BufferedSink[S]
///|
pub fn[S] buffered_sink(sink : S, flush_limit? : Int = 1) -> BufferedSink[S] {
let actual_limit = if flush_limit <= 0 { 1 } else { flush_limit }
{ sink, buffer: Ref([]), flush_limit: actual_limit }
@sink_graph.buffered_sink(sink, flush_limit~)
}
///|
pub fn[S] BufferedSink::pending_count(self : BufferedSink[S]) -> Int {
self.buffer.val.length()
}
pub type QueueOverflowPolicy = @queue_model.QueueOverflowPolicy
///|
pub fn[S : Sink] BufferedSink::flush(self : BufferedSink[S]) -> Unit {
if self.buffer.val.length() == 0 {
()
} else {
let pending = self.buffer.val
self.buffer.val = []
for rec in pending {
self.sink.write(rec)
}
}
}
///|
pub impl[S : Sink] Sink for BufferedSink[S] with fn write(self, rec) {
self.buffer.val.push(rec)
if self.buffer.val.length() >= self.flush_limit {
self.flush()
}
}
///|
pub type QueueOverflowPolicy = @utils.QueueOverflowPolicy
///|
pub struct QueuedSink[S] {
sink : S
queue : @queue.Queue[Record]
max_pending : Int
overflow : QueueOverflowPolicy
dropped_count : Ref[Int]
}
pub type QueuedSink[S] = @sink_graph.QueuedSink[S]
///|
pub fn[S] queued_sink(
@@ -225,99 +119,21 @@ pub fn[S] queued_sink(
max_pending? : Int = 0,
overflow? : QueueOverflowPolicy = QueueOverflowPolicy::DropNewest,
) -> QueuedSink[S] {
{
sink,
queue: @queue.Queue([]),
max_pending,
overflow,
dropped_count: Ref(0),
}
@sink_graph.queued_sink(sink, max_pending~, overflow~)
}
///|
pub fn[S] QueuedSink::pending_count(self : QueuedSink[S]) -> Int {
self.queue.length()
}
///|
pub fn[S] QueuedSink::dropped_count(self : QueuedSink[S]) -> Int {
self.dropped_count.val
}
///|
pub fn[S : Sink] QueuedSink::drain(
self : QueuedSink[S],
max_items? : Int = -1,
) -> Int {
if max_items == 0 {
return 0
}
let limit = if max_items < 0 { self.pending_count() } else { max_items }
for drained = 0; drained < limit; {
match self.queue.pop() {
None => break drained
Some(rec) => {
self.sink.write(rec)
continue drained + 1
}
}
} nobreak {
limit
}
}
///|
pub fn[S : Sink] QueuedSink::flush(self : QueuedSink[S]) -> Int {
self.drain()
}
///|
pub impl[S] Sink for QueuedSink[S] with fn write(self, rec) {
let full = self.max_pending > 0 && self.pending_count() >= self.max_pending
if !full {
self.queue.push(rec)
} else {
self.dropped_count.val += 1
match self.overflow {
QueueOverflowPolicy::DropNewest => ()
QueueOverflowPolicy::DropOldest => {
ignore(self.queue.pop())
self.queue.push(rec)
}
}
}
}
///|
pub struct FilterSink[S] {
sink : S
predicate : (Record) -> Bool
}
pub type FilterSink[S] = @sink_graph.FilterSink[S]
///|
pub fn[S] filter_sink(sink : S, predicate : (Record) -> Bool) -> FilterSink[S] {
{ sink, predicate }
@sink_graph.filter_sink(sink, predicate)
}
///|
pub impl[S : Sink] Sink for FilterSink[S] with fn write(self, rec) {
if (self.predicate)(rec) {
self.sink.write(rec)
}
}
///|
pub struct PatchSink[S] {
sink : S
patch : RecordPatch
}
pub type PatchSink[S] = @sink_graph.PatchSink[S]
///|
pub fn[S] patch_sink(sink : S, patch : RecordPatch) -> PatchSink[S] {
{ sink, patch }
}
///|
pub impl[S : Sink] Sink for PatchSink[S] with fn write(self, rec) {
self.sink.write((self.patch)(rec))
@sink_graph.patch_sink(sink, patch)
}
+36 -353
View File
@@ -1,32 +1,18 @@
///|
pub struct FileSink {
path : String
append : Ref[Bool]
default_append : Bool
handle : Ref[FileHandle?]
formatter : RecordFormatter
auto_flush : Ref[Bool]
default_auto_flush : Bool
rotation : Ref[FileRotation?]
default_rotation : FileRotation?
open_failures : Ref[Int]
write_failures : Ref[Int]
flush_failures : Ref[Int]
rotation_failures : Ref[Int]
}
pub type FileSink = @file_runtime.FileSink
///|
pub type FileRotation = @utils.FileRotation
pub type FileRotation = @file_model.FileRotation
///|
pub type FileSinkState = @utils.FileSinkState
pub type FileSinkState = @file_model.FileSinkState
///|
pub type FileSinkPolicy = @utils.FileSinkPolicy
pub type FileSinkPolicy = @file_model.FileSinkPolicy
///|
pub fn file_rotation(max_bytes : Int, max_backups? : Int = 1) -> FileRotation {
@utils.file_rotation(max_bytes, max_backups~)
@file_model.file_rotation(max_bytes, max_backups~)
}
///|
@@ -34,12 +20,40 @@ pub fn file_rotation_i64(
max_bytes : Int64,
max_backups? : Int = 1,
) -> FileRotation {
@utils.file_rotation_i64(max_bytes, max_backups~)
@file_model.file_rotation_i64(max_bytes, max_backups~)
}
///|
pub fn native_files_supported() -> Bool {
native_files_supported_internal()
@file_runtime.native_files_supported()
}
///|
pub fn file_sink_policy_to_json(
policy : FileSinkPolicy,
) -> @json_parser.JsonValue {
@file_model.file_sink_policy_to_json(policy)
}
///|
pub fn stringify_file_sink_policy(
policy : FileSinkPolicy,
pretty? : Bool = false,
) -> String {
@file_model.stringify_file_sink_policy(policy, pretty~)
}
///|
pub fn file_sink_state_to_json(state : FileSinkState) -> @json_parser.JsonValue {
@file_model.file_sink_state_to_json(state)
}
///|
pub fn stringify_file_sink_state(
state : FileSinkState,
pretty? : Bool = false,
) -> String {
@file_model.stringify_file_sink_state(state, pretty~)
}
///|
@@ -50,336 +64,5 @@ pub fn file_sink(
rotation? : FileRotation? = None,
formatter? : RecordFormatter = fn(rec) { format_text(rec) },
) -> FileSink {
let handle = open_file_handle_internal(path, append)
{
path,
append: Ref(append),
default_append: append,
handle: Ref(handle),
formatter,
auto_flush: Ref(auto_flush),
default_auto_flush: auto_flush,
rotation: Ref(rotation),
default_rotation: rotation,
open_failures: Ref(if handle is Some(_) { 0 } else { 1 }),
write_failures: Ref(0),
flush_failures: Ref(0),
rotation_failures: Ref(0),
}
}
///|
pub fn FileSink::is_available(self : FileSink) -> Bool {
self.handle.val is Some(_)
}
///|
pub fn FileSink::flush(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = flush_file_handle_internal(handle)
if !ok {
self.flush_failures.val += 1
}
ok
}
}
}
///|
pub fn FileSink::append_mode(self : FileSink) -> Bool {
self.append.val
}
///|
pub fn FileSink::set_append_mode(self : FileSink, append : Bool) -> Unit {
self.append.val = append
}
///|
pub fn FileSink::path(self : FileSink) -> String {
self.path
}
///|
pub fn FileSink::auto_flush_enabled(self : FileSink) -> Bool {
self.auto_flush.val
}
///|
pub fn FileSink::rotation_enabled(self : FileSink) -> Bool {
self.rotation.val is Some(_)
}
///|
pub fn FileSink::rotation_config(self : FileSink) -> FileRotation? {
self.rotation.val
}
///|
pub fn FileSink::set_auto_flush(self : FileSink, enabled : Bool) -> Unit {
self.auto_flush.val = enabled
}
///|
pub fn FileSink::set_policy(self : FileSink, policy : FileSinkPolicy) -> Unit {
self.append.val = policy.append
self.auto_flush.val = policy.auto_flush
self.rotation.val = policy.rotation
}
///|
pub fn FileSink::set_rotation(
self : FileSink,
rotation : FileRotation?,
) -> Unit {
self.rotation.val = rotation
}
///|
pub fn FileSink::clear_rotation(self : FileSink) -> Unit {
self.rotation.val = None
}
///|
pub fn FileSink::close(self : FileSink) -> Bool {
match self.handle.val {
None => false
Some(handle) => {
let ok = close_file_handle_internal(handle)
self.handle.val = None
ok
}
}
}
///|
pub fn FileSink::rotation_failures(self : FileSink) -> Int {
self.rotation_failures.val
}
///|
pub fn FileSink::open_failures(self : FileSink) -> Int {
self.open_failures.val
}
///|
pub fn FileSink::write_failures(self : FileSink) -> Int {
self.write_failures.val
}
///|
pub fn FileSink::flush_failures(self : FileSink) -> Int {
self.flush_failures.val
}
///|
pub fn FileSink::reset_failure_counters(self : FileSink) -> Unit {
self.open_failures.val = 0
self.write_failures.val = 0
self.flush_failures.val = 0
self.rotation_failures.val = 0
}
///|
pub fn FileSink::reset_policy(self : FileSink) -> Unit {
self.append.val = self.default_append
self.auto_flush.val = self.default_auto_flush
self.rotation.val = self.default_rotation
}
///|
pub fn FileSink::policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.append.val,
auto_flush=self.auto_flush.val,
rotation=self.rotation.val,
)
}
///|
pub fn FileSink::default_policy(self : FileSink) -> FileSinkPolicy {
FileSinkPolicy::new(
append=self.default_append,
auto_flush=self.default_auto_flush,
rotation=self.default_rotation,
)
}
///|
pub fn FileSink::policy_matches_default(self : FileSink) -> Bool {
let current = self.policy()
let default = self.default_policy()
current.append == default.append &&
current.auto_flush == default.auto_flush &&
policy_rotation_equals_internal(current.rotation, default.rotation)
}
///|
fn policy_rotation_equals_internal(
left : FileRotation?,
right : FileRotation?,
) -> Bool {
match (left, right) {
(None, None) => true
(Some(a), Some(b)) =>
a.max_bytes == b.max_bytes &&
a.max_backups == b.max_backups &&
a.native_wide_max_bytes == b.native_wide_max_bytes
_ => false
}
}
///|
fn rotation_max_bytes_internal(rotation : FileRotation) -> Int64 {
match rotation.native_wide_max_bytes {
Some(value) => value
None => rotation.max_bytes.to_int64()
}
}
///|
pub fn FileSink::state(self : FileSink) -> FileSinkState {
FileSinkState::new(
self.path,
available=self.is_available(),
append=self.append.val,
auto_flush=self.auto_flush.val,
rotation=self.rotation.val,
open_failures=self.open_failures.val,
write_failures=self.write_failures.val,
flush_failures=self.flush_failures.val,
rotation_failures=self.rotation_failures.val,
)
}
///|
pub fn FileSink::reopen(self : FileSink, append? : Bool? = None) -> Bool {
let append_mode = append.unwrap_or(self.append.val)
self.append.val = append_mode
match self.handle.val {
None => ()
Some(handle) => {
ignore(close_file_handle_internal(handle))
self.handle.val = None
}
}
let reopened = open_file_handle_internal(self.path, append_mode)
self.handle.val = reopened
if reopened is Some(_) {
true
} else {
self.open_failures.val += 1
false
}
}
///|
pub fn FileSink::reopen_with_current_policy(self : FileSink) -> Bool {
self.reopen()
}
///|
pub fn FileSink::reopen_append(self : FileSink) -> Bool {
self.reopen(append=Some(true))
}
///|
pub fn FileSink::reopen_truncate(self : FileSink) -> Bool {
self.reopen(append=Some(false))
}
///|
fn rotated_file_path(path : String, index : Int) -> String {
"\{path}.\{index}"
}
///|
fn rotate_file_sink_internal(sink : FileSink, rotation : FileRotation) -> Bool {
let closed = match sink.handle.val {
None => true
Some(handle) => {
let ok = close_file_handle_internal(handle)
sink.handle.val = None
ok
}
}
if !closed {
return false
}
if rotation.max_backups > 0 {
ignore(
remove_file_internal(rotated_file_path(sink.path, rotation.max_backups)),
)
for index = rotation.max_backups - 1; index >= 1; {
let from_path = rotated_file_path(sink.path, index)
let to_path = rotated_file_path(sink.path, index + 1)
ignore(rename_file_internal(from_path, to_path))
continue index - 1
}
ignore(rename_file_internal(sink.path, rotated_file_path(sink.path, 1)))
} else {
ignore(remove_file_internal(sink.path))
}
sink.handle.val = open_file_handle_internal(sink.path, false)
sink.handle.val is Some(_)
}
///|
fn rotate_if_needed_internal(sink : FileSink, next_line_bytes : Int) -> Bool {
match sink.rotation.val {
None => true
Some(rotation) =>
match sink.handle.val {
None => false
Some(handle) => {
let size = file_size_i64_internal(handle)
let next_line = next_line_bytes.to_int64()
if size + next_line <= rotation_max_bytes_internal(rotation) {
true
} else {
let rotated = rotate_file_sink_internal(sink, rotation)
if !rotated {
sink.rotation_failures.val += 1
}
rotated
}
}
}
}
}
///|
pub impl Sink for FileSink with fn write(self, rec) {
match self.handle.val {
None => self.write_failures.val += 1
Some(_) => {
let line = "\{(self.formatter)(rec)}\n"
let can_write = rotate_if_needed_internal(
self,
string_byte_length_internal(line),
)
if can_write {
match self.handle.val {
None => self.write_failures.val += 1
Some(active) => {
let wrote = write_file_handle_internal(active, line)
if wrote {
if self.auto_flush.val {
let flushed = flush_file_handle_internal(active)
if !flushed {
self.flush_failures.val += 1
}
}
} else {
self.write_failures.val += 1
}
}
}
} else {
self.write_failures.val += 1
}
}
}
@file_runtime.file_sink(path, append~, auto_flush~, rotation~, formatter~)
}
+11
View File
@@ -93,6 +93,17 @@ pub fn open_file_handle_internal(path : String, append : Bool) -> FileHandle? {
}
}
///|
pub fn file_exists_internal(path : String) -> Bool {
let raw = file_open_ffi(string_to_c_bytes(path), string_to_c_bytes("rb"))
if file_is_null_ffi(raw) {
false
} else {
ignore(file_close_ffi(raw))
true
}
}
///|
pub fn write_file_handle_internal(
handle : FileHandle,
+10 -38
View File
@@ -1,75 +1,47 @@
///|
pub type RecordPredicate = (@core.Record) -> Bool
pub type RecordPredicate = @record_ops.RecordPredicate
///|
pub fn level_at_least(min_level : @core.Level) -> RecordPredicate {
fn(rec) { rec.level.priority() >= min_level.priority() }
@record_ops.level_at_least(min_level)
}
///|
pub fn target_is(target : String) -> RecordPredicate {
fn(rec) { rec.target == target }
@record_ops.target_is(target)
}
///|
pub fn target_has_prefix(prefix : String) -> RecordPredicate {
fn(rec) { rec.target.has_prefix(prefix) }
@record_ops.target_has_prefix(prefix)
}
///|
pub fn message_contains(fragment : String) -> RecordPredicate {
fn(rec) { rec.message.contains(fragment) }
@record_ops.message_contains(fragment)
}
///|
pub fn has_field(key : String) -> RecordPredicate {
fn(rec) {
for field in rec.fields {
if field.key == key {
return true
}
}
false
}
@record_ops.has_field(key)
}
///|
pub fn field_equals(key : String, value : String) -> RecordPredicate {
fn(rec) {
for field in rec.fields {
if field.key == key && field.value == value {
return true
}
}
false
}
@record_ops.field_equals(key, value)
}
///|
pub fn not_(predicate : RecordPredicate) -> RecordPredicate {
fn(rec) { !predicate(rec) }
@record_ops.not_(predicate)
}
///|
pub fn all_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
fn(rec) {
for predicate in predicates {
if !predicate(rec) {
return false
}
}
true
}
@record_ops.all_of(predicates)
}
///|
pub fn any_of(predicates : Array[RecordPredicate]) -> RecordPredicate {
fn(rec) {
for predicate in predicates {
if predicate(rec) {
return true
}
}
false
}
@record_ops.any_of(predicates)
}
+33 -1062
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,6 +1,7 @@
import {
"Nanaloveyuki/BitLogger/src/core",
"maria/json_parser",
"Nanaloveyuki/BitLogger/src/formatting",
"Nanaloveyuki/BitLogger/src/record_ops",
"moonbitlang/core/env",
"moonbitlang/core/json",
"moonbitlang/core/ref",
+8 -42
View File
@@ -1,47 +1,29 @@
///|
pub type RecordPatch = (@core.Record) -> @core.Record
pub type RecordPatch = @record_ops.RecordPatch
///|
pub fn identity_patch() -> RecordPatch {
fn(rec) { rec }
@record_ops.identity_patch()
}
///|
pub fn set_target(target : String) -> RecordPatch {
fn(rec) { rec.with_target(target) }
@record_ops.set_target(target)
}
///|
pub fn prefix_message(prefix : String) -> RecordPatch {
fn(rec) { rec.with_message("\{prefix}\{rec.message}") }
@record_ops.prefix_message(prefix)
}
///|
pub fn append_fields(extra_fields : Array[@core.Field]) -> RecordPatch {
fn(rec) {
if extra_fields.length() == 0 {
rec
} else if rec.fields.length() == 0 {
rec.with_fields(extra_fields)
} else {
rec.with_fields(rec.fields + extra_fields)
}
}
@record_ops.append_fields(extra_fields)
}
///|
pub fn redact_field(key : String, placeholder? : String = "***") -> RecordPatch {
fn(rec) {
rec.with_fields(
rec.fields.map(fn(field) {
if field.key == key {
field.with_value(placeholder)
} else {
field
}
}),
)
}
@record_ops.redact_field(key, placeholder~)
}
///|
@@ -49,26 +31,10 @@ pub fn redact_fields(
keys : Array[String],
placeholder? : String = "***",
) -> RecordPatch {
fn(rec) {
rec.with_fields(
rec.fields.map(fn(field) {
if keys.contains(field.key) {
field.with_value(placeholder)
} else {
field
}
}),
)
}
@record_ops.redact_fields(keys, placeholder~)
}
///|
pub fn compose_patches(patches : Array[RecordPatch]) -> RecordPatch {
fn(rec) {
let mut current = rec
for patch in patches {
current = patch(current)
}
current
}
@record_ops.compose_patches(patches)
}
-104
View File
@@ -1,105 +1 @@
///|
pub struct FileRotation {
max_bytes : Int
max_backups : Int
native_wide_max_bytes : Int64?
}
///|
pub struct FileSinkState {
path : String
available : Bool
append : Bool
auto_flush : Bool
rotation : FileRotation?
open_failures : Int
write_failures : Int
flush_failures : Int
rotation_failures : Int
}
///|
pub struct FileSinkPolicy {
append : Bool
auto_flush : Bool
rotation : FileRotation?
}
///|
pub fn FileSinkPolicy::new(
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
) -> FileSinkPolicy {
{ append, auto_flush, rotation }
}
///|
pub fn FileSinkState::new(
path : String,
available? : Bool = false,
append? : Bool = true,
auto_flush? : Bool = true,
rotation? : FileRotation? = None,
open_failures? : Int = 0,
write_failures? : Int = 0,
flush_failures? : Int = 0,
rotation_failures? : Int = 0,
) -> FileSinkState {
{
path,
available,
append,
auto_flush,
rotation,
open_failures,
write_failures,
flush_failures,
rotation_failures,
}
}
///|
pub fn file_rotation(max_bytes : Int, max_backups? : Int = 1) -> FileRotation {
{
max_bytes: if max_bytes <= 0 {
1
} else {
max_bytes
},
max_backups: if max_backups <= 0 {
1
} else {
max_backups
},
native_wide_max_bytes: None,
}
}
///|
pub fn file_rotation_i64(
max_bytes : Int64,
max_backups? : Int = 1,
) -> FileRotation {
let normalized = if max_bytes <= 0L { 1L } else { max_bytes }
let clamped_max_bytes = if normalized > 2147483647L {
2147483647
} else {
normalized.to_int()
}
{
max_bytes: clamped_max_bytes,
max_backups: if max_backups <= 0 {
1
} else {
max_backups
},
native_wide_max_bytes: Some(normalized),
}
}
///|
pub(all) enum QueueOverflowPolicy {
DropNewest
DropOldest
}