pub struct EventLog { /* private fields */ }Expand description
Append-only event log with optional JSONL journal.
Implementations§
Source§impl EventLog
impl EventLog
pub fn new() -> EventLog
pub fn with_journal(path: PathBuf) -> EventLog
Sourcepub fn with_journal_failure_injector(
path: PathBuf,
failures: JournalFailureInjector,
) -> EventLog
pub fn with_journal_failure_injector( path: PathBuf, failures: JournalFailureInjector, ) -> EventLog
Test/embedder seam for deterministic journal write/flush/fsync faults.
Sourcepub fn with_private_path_failure_injector(
path: PathBuf,
failures: PrivatePathDurabilityFailureInjector,
) -> EventLog
pub fn with_private_path_failure_injector( path: PathBuf, failures: PrivatePathDurabilityFailureInjector, ) -> EventLog
Test/embedder seam for deterministic first-use directory-entry faults.
Sourcepub fn bind_run(&mut self, run_id: &str, client_id: &str) -> Result<(), String>
pub fn bind_run(&mut self, run_id: &str, client_id: &str) -> Result<(), String>
Bind this log to one authenticated active run. Exact repeat binding is idempotent; a different run/client is rejected instead of silently re-attributing later action events.
Sourcepub fn bind_policy_session(
&mut self,
policy_session_id: &str,
) -> Result<(), String>
pub fn bind_policy_session( &mut self, policy_session_id: &str, ) -> Result<(), String>
Attach a CAR-minted policy session to the currently bound proposal.
pub fn clear_policy_session( &mut self, policy_session_id: &str, ) -> Result<(), String>
pub fn clear_run_binding( &mut self, run_id: &str, client_id: &str, ) -> Result<(), String>
pub fn active_run_binding(&self) -> Option<(&str, &str, Option<&str>)>
Sourcepub fn with_hash_chaining(self) -> EventLog
pub fn with_hash_chaining(self) -> EventLog
Enable tamper-evident hash chaining for events appended from now on
(EPIC A / A9). The chain continues from the last already-appended
event’s hash if one exists (re-enabling after a load), else from the
genesis link. Returns self for builder-style use.
Sourcepub fn enable_hash_chaining(&mut self)
pub fn enable_hash_chaining(&mut self)
Turn on hash chaining in place. Idempotent.
Sourcepub fn hash_chaining_enabled(&self) -> bool
pub fn hash_chaining_enabled(&self) -> bool
Whether hash chaining is currently enabled.
pub fn append( &mut self, kind: EventKind, action_id: Option<&str>, proposal_id: Option<&str>, data: HashMap<String, Value>, ) -> &Event
Sourcepub fn append_critical(
&mut self,
kind: EventKind,
action_id: Option<&str>,
proposal_id: Option<&str>,
data: HashMap<String, Value>,
) -> Result<&Event, String>
pub fn append_critical( &mut self, kind: EventKind, action_id: Option<&str>, proposal_id: Option<&str>, data: HashMap<String, Value>, ) -> Result<&Event, String>
Append a lifecycle-critical event and return only after its exact JSONL row and all earlier queued rows have been flushed and fsynced. If a write/flush/fsync attempt fails, the exact event remains pending in memory so an identical retry finishes the same row rather than minting a second terminal with a new timestamp.
This compatibility API performs an unbounded blocking acknowledgement
wait and is intended only for genuinely synchronous callers. Async
callers must use Self::append_critical_async so a stalled filesystem
cannot occupy an executor worker indefinitely.
Sourcepub fn append_critical_bounded(
&mut self,
kind: EventKind,
action_id: Option<&str>,
proposal_id: Option<&str>,
data: HashMap<String, Value>,
acknowledgement_timeout: Duration,
) -> Result<&Event, CriticalAppendError>
pub fn append_critical_bounded( &mut self, kind: EventKind, action_id: Option<&str>, proposal_id: Option<&str>, data: HashMap<String, Value>, acknowledgement_timeout: Duration, ) -> Result<&Event, CriticalAppendError>
Synchronous critical append with a hard acknowledgement bound.
This is the startup-thread counterpart of Self::append_critical_async.
The exact serialized row is installed in critical_pending before the
bounded wait begins, so timeout cannot claim success or authorize a
different lifecycle event. An identical later startup replay safely
reconciles the row whether or not the writer completed before timeout.
Sourcepub async fn append_critical_async(
&mut self,
kind: EventKind,
action_id: Option<&str>,
proposal_id: Option<&str>,
data: HashMap<String, Value>,
acknowledgement_timeout: Duration,
) -> Result<&Event, CriticalAppendError>
pub async fn append_critical_async( &mut self, kind: EventKind, action_id: Option<&str>, proposal_id: Option<&str>, data: HashMap<String, Value>, acknowledgement_timeout: Duration, ) -> Result<&Event, CriticalAppendError>
Append a lifecycle-critical event without blocking an async executor worker on filesystem acknowledgement.
The acknowledgement wait is bounded by acknowledgement_timeout. Once
the writer accepts the message, the exact serialized row is marked
pending before this future can yield. Timeout, cancellation by an outer
request deadline, writer failure, and acknowledgement-coordinator shutdown
therefore leave an identical retry safe: it reuses the original event
timestamp/hash and the writer suppresses a duplicate row. Until that
exact retry reconciles the pending row, a different critical event is
rejected before enqueue.
Sourcepub fn verify_chain(&self) -> Result<usize, usize>
pub fn verify_chain(&self) -> Result<usize, usize>
Verify the tamper-evidence hash chain over the currently-loaded
events (EPIC A / A9). Walks every event that carries a hash,
recomputing it from its content + the running prev_hash and
checking the links join up. Returns Ok(n) with the number of
chained events verified, or Err(index) naming the first event
whose hash or linkage doesn’t match — i.e. the point at which a
chained event was edited, or an interior event was deleted or
reordered.
Scope of the guarantee: the chain detects interior
edits/reorderings/deletions only. It cannot detect truncation at
either end: there is no anchored head hash, so the first chained
event’s prev_hash is taken on trust (dropping a prefix goes
unnoticed), and nothing pins the tail (dropping a suffix goes
unnoticed). Detecting head/tail truncation requires anchoring the
chain head (and a trusted latest-hash witness), which is out of
scope until that anchor exists.
Events without a hash (appended before chaining was enabled) are
skipped, so a partially-chained log verifies its chained suffix.
Sourcepub fn append_metered(
&mut self,
kind: EventKind,
action_id: Option<&str>,
proposal_id: Option<&str>,
data: HashMap<String, Value>,
metrics: Metrics,
) -> &Event
pub fn append_metered( &mut self, kind: EventKind, action_id: Option<&str>, proposal_id: Option<&str>, data: HashMap<String, Value>, metrics: Metrics, ) -> &Event
Append an event with cross-cutting Metrics (duration, tokens,
cost) merged into its data under metric_keys. Use this for any
event whose latency or token cost should feed trajectory-level
aggregation (metrics_totals) — the deep-telemetry substrate of
§3.5.1. Metric keys present in both data and metrics take the
metrics value (the metrics argument wins).
Sourcepub fn metrics_totals(&self) -> MetricsTotals
pub fn metrics_totals(&self) -> MetricsTotals
Sum the telemetry metrics across every event in the log — the trajectory-level totals (tokens, cost, wall-clock) that harness-level evaluation (§5.2.1) and the Evolution Agent (§3.5.2) reason over.
Contract: this sums every event carrying a metric_keys value,
regardless of which append path emitted it. A duration recorded once
per action (e.g. ActionSucceeded) is counted once; the standardized
keys mean there is a single value per metric per event, so there is no
double-count as long as each unit of work meters itself once. Token
metrics from InferenceMetered and latency from action events sum
into the same totals — that is intended (total cost = model + tools).
Sourcepub fn cost_by_agent(&self) -> Vec<AgentCost>
pub fn cost_by_agent(&self) -> Vec<AgentCost>
Per-agent cost/token report (EPIC G / G3) — see cost_by_agent_of.
pub fn events(&self) -> &[Event]
pub fn len(&self) -> usize
pub fn span_len(&self) -> usize
pub fn is_empty(&self) -> bool
pub fn stats(&self) -> EventLogStats
pub fn truncate_events_keep_last(&mut self, keep_last: usize) -> usize
pub fn truncate_spans_keep_last(&mut self, keep_last: usize) -> usize
Sourcepub fn clear(&mut self) -> EventLogStats
pub fn clear(&mut self) -> EventLogStats
Drop every retained event and span, releasing their memory. The
JSONL journal is left untouched (it is the audit trail); the
monotonic counters (trimmed_events, cumulative_cost_usd) are
preserved — clear frees memory, it doesn’t reset the log’s history.
Sourcepub fn trimmed_events(&self) -> u64
pub fn trimmed_events(&self) -> u64
Total events ever dropped from the in-memory log (retention trims,
manual truncation, clear). Monotonic; > 0 means the retained
window is incomplete — consumers projecting over Self::events
(e.g. the A6 tool-receipt verifier) must treat an absent event as
possibly-evicted, not as never-happened.
Sourcepub fn cumulative_cost_usd(&self) -> f64
pub fn cumulative_cost_usd(&self) -> f64
Monotonic cumulative cost (USD) across every event ever appended
(EPIC G / G1). Unlike folding cost_usd over Self::events — which
slides backward when retention trims metered events — this counter
only grows, so it is the correct denominator for a cumulative budget
(AlertThresholds::max_cost_usd). Seeded from the journal on
Self::load; survives trims and Self::clear.
Sourcepub fn journal_size_bytes(&self) -> Option<u64>
pub fn journal_size_bytes(&self) -> Option<u64>
Current size of the JSONL journal file in bytes, if a journal is configured and stat-able. The background writer batches, so this may momentarily lag the last few appends.
Sourcepub fn compact_journal(&mut self) -> bool
pub fn compact_journal(&mut self) -> bool
Rewrite the JSONL journal to contain exactly the currently-retained events (G2 journal compaction — before this, retention trimmed the in-memory log only and the journal grew unbounded). Atomic: writes a sibling temp file and renames it over the journal. The background writer is joined first (draining its backlog and closing its handle — renaming under a live append-mode handle would orphan subsequent writes to the old inode), then respawned on the compacted file.
Hash chaining (A9) survives: Self::verify_chain anchors the first
hashed event on its stored prev_hash, so the retained tail of a
chained log still verifies after a compact + reload. Corollary: a
head-trim by retention is indistinguishable from compaction — tamper
evidence covers the retained tail only.
Returns true if the journal was rewritten. Failure is best-effort
like the journal itself: a warning is logged, the old (uncompacted)
journal stays in place, and appending resumes against it.
Sourcepub fn query(&self, query: &EventQuery) -> Vec<&Event>
pub fn query(&self, query: &EventQuery) -> Vec<&Event>
Run a structured audit EventQuery, returning matching events
most-recent-first, capped at query.limit (EPIC G / G2).
Sourcepub fn set_retention(&mut self, policy: Option<RetentionPolicy>)
pub fn set_retention(&mut self, policy: Option<RetentionPolicy>)
Install an auto-retention policy (EPIC G / G2). max_events is then
enforced on every append; call Self::enforce_retention to also
apply the age bound.
Sourcepub fn retention(&self) -> Option<&RetentionPolicy>
pub fn retention(&self) -> Option<&RetentionPolicy>
The active retention policy, if any.
Sourcepub fn enforce_retention(
&mut self,
policy: &RetentionPolicy,
now: DateTime<Utc>,
) -> usize
pub fn enforce_retention( &mut self, policy: &RetentionPolicy, now: DateTime<Utc>, ) -> usize
Apply a retention policy now: drop events older than max_age_secs
and cap the count at max_events (keeping the most recent). Returns
the number of events removed. Independent of the installed policy, so a
caller can run a one-off sweep. When a journal is configured, a trim
also triggers the throttled journal compaction (see
Self::compact_journal) so the JSONL file tracks retention instead
of growing unbounded.
pub fn filter( &self, kind: Option<&EventKind>, action_id: Option<&str>, ) -> Vec<&Event>
Sourcepub fn begin_span(
&mut self,
name: &str,
trace_id: &str,
parent_span_id: Option<&str>,
attributes: HashMap<String, Value>,
) -> String
pub fn begin_span( &mut self, name: &str, trace_id: &str, parent_span_id: Option<&str>, attributes: HashMap<String, Value>, ) -> String
Begin a new trace span. Returns the generated span_id.
Sourcepub fn end_span(&mut self, span_id: &str, status: SpanStatus)
pub fn end_span(&mut self, span_id: &str, status: SpanStatus)
End an open span by setting its status and end time.
Sourcepub fn export_traces(&self) -> String
pub fn export_traces(&self) -> String
Export traces as OTLP-compatible JSON.
Sourcepub fn load(path: &Path) -> Result<EventLog, Error>
pub fn load(path: &Path) -> Result<EventLog, Error>
Load an event log from a JSONL journal file.
Sourcepub fn load_read_only(path: &Path) -> Result<EventLog, Error>
pub fn load_read_only(path: &Path) -> Result<EventLog, Error>
Load and validate an event log without attaching a writer or modifying
the source journal. An unterminated final row is reported as
std::io::ErrorKind::UnexpectedEof; callers that own live append
recovery should use EventLog::load instead.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for EventLog
impl !RefUnwindSafe for EventLog
impl !UnwindSafe for EventLog
impl Send for EventLog
impl Sync for EventLog
impl Unpin for EventLog
impl UnsafeUnpin for EventLog
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more