Module telemetry

Module telemetry 

Source
Expand description

Telemetry — and the question of what it is for, in an architecture that already has a log.

§Why this is not the usual answer

Distributed tracing exists because in a system of services nobody knows what happened. You reconstruct causality after the fact from correlated, sampled spans, and you accept that the reconstruction is partial.

Beck already has something strictly stronger for the part tracing usually covers: a durable total order of every state transition. state = fold(f, init, log[..seq]) is not a sample and not a reconstruction — it is the actual history, and crate::replay_to will rebuild any state the system was ever in. Tracing the fold’s internal call tree as spans would re-record, lossily and at cost, what the log records exactly and for free.

So the division of labour is specific:

questionanswered by
what happened, in what order, and what state did it producethe log
what state was the system in at 14:02the log, by replay
why did this command produce that eventthe log, by replay
how long did the fold takehere
how long did the append wait on Postgreshere
what was rejected, and never became an eventhere
how many sessions are connectedhere
what the maintained views cost, shared and per sessionhere
did the pod get killed mid-batchhere

Everything in the right column is either wall-clock, resource use, or a non-event: something the log deliberately does not record, because §4.8 requires the fold to be replay-pure and a fold that recorded its own duration would not replay identically. Telemetry is not a weaker substitute for the log here; it is the complement of it, and the boundary between them is exactly the boundary of determinism.

§Correlation is seq, not a trace id

A random trace id identifies a request. seq identifies a state: given one, beck replay --to <seq> reproduces the system exactly as it was. So every record that has a sequence number carries beck.seq, and a span in any OTel backend is one command away from a reproducible debugging session. That is a property this architecture has and a microservice fleet does not.

§Is OpenTelemetry valid here?

Yes, for the right column, and this module speaks it: Telemetry::otlp_metrics and Telemetry::otlp_logs produce OTLP/HTTP JSON, which is a first-class encoding in the OTLP specification — same field names and semantics as the protobuf form, with no tonic, no prost, and no code generation. Export is pull-only: the dashboard serves this data from memory at /_beck/otlp/metrics and /_beck/otlp/logs, and nothing pushes to a collector (a push exporter is scheduled, not built — docs/101 §101.8).

What Beck should not do is adopt OTel’s model as its own. Spans belong at the boundaries — ingress, validate, append, fold, view, patch — and not inside the fold, where the log is the better instrument.

§Cost

Counters and histogram buckets are AtomicU64: recording is one relaxed fetch-add and no allocation, so instrumenting the fold does not perturb what it measures. Histograms are fixed power-of-two buckets, so the bucket index is a leading_zeros. The log ring is bounded and overwrites oldest-first, so a process that runs for a month does not accumulate.

Structs§

Counter
Gauge
A gauge — a value that goes up and down, like the number of connected sessions.
Histogram
A histogram of durations in microseconds.
Record
One log record, kept in memory for the dashboard and exported as an OTLP log record.
Telemetry
The instruments.

Functions§

now_unix_nanos
telemetry
The process-wide instruments.
timed
Time a block and record it, returning what the block returned.