Download all docs

Recipe: Event-Sourced Audit Log

This recipe records everything that happens as an immutable, queryable event stream. Events flow through a queue for ordering and buffering, land in a timeseries store keyed by time, and a document renders human-readable audit reports from the same data. Append-only in, time-ordered out.

The problem it solves

“Who did what, when?” is a question every serious system has to answer, and the worst time to discover you cannot is during an incident or an audit. Sprinkling log statements around does not give you an ordered, queryable, retention-managed record. This recipe makes the audit trail a deliberate pipeline: every event is captured, time-stamped, and kept exactly as long as you decide.

Elements

ElementRole
queueOrders and buffers the incoming event stream.
timeseriesAppend-only, time-keyed store of every event.
documentRenders human-readable audit reports from the stored events.

Flow

  1. Create a queue as the ingest point. As things happen across your circle, publish an event to the queue; it orders and buffers them so a burst of activity never drops or reorders the record. Watch depth with queue_stats.
  2. Create a timeseries store. As events drain from the queue, insert each one keyed by its timestamp — this is your append-only log.
  3. Query the history. Use range for the raw events in a window (the audit detail), query for an aggregated view (“how many of event X per hour”), and downsample to roll older data into coarser buckets.
  4. Set how long the record lives with the timeseries retention operation — your compliance window, declared once, enforced automatically.
  5. Produce reports with a document: write a page that summarizes a query result, then render it to HTML for a readable, shareable audit report.

What this shows

The two halves of an audit trail want different stores, and this recipe uses the right one for each: the queue guarantees ordered, lossless ingest, and the timeseries element is purpose-built for append-only, time-keyed data with range/query/downsample access and a real retention policy — so the log is queryable and bounded, not an ever-growing text file. Rendering reports from a document over the same data means the human view and the machine record never diverge. The whole trail is event-sourced: you never mutate history, you only append to it and read it back.

Next pages