Telemetry
Wide events: each call is a span you enrich, stages are child spans, calls and stages are metrics, and one trace follows a caller into the calls it makes.
What it is and when to use it#
architect logs in wide events: one span per unit of work, carrying everything
known about it, instead of many log lines. Every vox call already runs inside
a span named Service/method; a handler adds fields to it with
architect_telemetry::wide::set. Stages times named steps inside the work,
each as a child span and an app.stage.duration sample. With the otel
feature, OTLP exports traces, logs and metrics, including
rpc.server.duration per service and method and process metrics, and a W3C
trace context rides each vox call so a caller’s span and the server work it
causes land in one trace.
Use it in every server and app instead of println! or scattered info!
lines. There is no architecture page for the wide-event model; the rules are
in skills/architect/references/telemetry.md. The crash log is
The flight recorder, and per-action
timings are in The Action List.
A minimal example#
architect-telemetry with feature otel (plus flight for the recorder);
architect with host or server-axum, which turn on its telemetry feature.
fn main() {
let (_sentry, otel) =
architect_telemetry::init_tracing_full("greeter", "info,architect::timing=trace");
std::mem::forget(otel); // a UI loop never returns; a server keeps it for all of main
// serve ..
}
// In a handler, already inside the call's span:
architect_telemetry::wide::set("greet.name", name.clone());
let mut stages = architect_telemetry::timing::Stages::start();
let found = lookup(&name);
stages.mark("greet.lookup_ms");
if let Err(e) = &found {
architect_telemetry::wide::set_display("greet.err", e);
}
OTLP starts only when OTEL_EXPORTER_OTLP_ENDPOINT is set. Clients built
with architect::clients! carry the trace context on their own; another
client adds architect::trace_context::TraceContextMiddleware with
with_middleware.
Code tour#
features/telemetry/src/lib.rsinit_tracing_full: Sentry, theEnvFilter, the fmt layer, then the OTLP layers.features/telemetry/src/lib.rsotelmodule,init: the OTLP exporters, the W3C propagator, the global meter and the process metrics.features/telemetry/src/lib.rswidemodule,set,set_display: an attribute on the current span; a no-op withoutotel.crates/architect/src/layer.rscall_span,record_call: the dispatch span (rpc.system,rpc.service,rpc.method, the scope) and therpc.server.durationhistogram.crates/architect/src/trace_context.rsTraceContextMiddleware,parent_of:traceparentandtracestatein the call’s metadata.features/telemetry/src/calls.rsQuietCalls: drops call spans that were fast, clean and not streaming; their timing stays in the metric.features/telemetry/src/timing.rsStages,record: stage events, child spans andapp.stage.duration.features/telemetry/src/flight.rsRingLayer,install_panic_recorder,restore_previous: the last run’s log and the crash log (featureflight).
A client call made inside a span goes out with that span’s context in its
metadata. On the server, LayerRouter reads it with parent_of and runs the
handler inside call_span, so wide::set and Stages::mark land on the
child of the caller’s span. When the call ends, record_call records its
duration. At export, QuietCalls keeps the span only if it was slow
(ARCHITECT_RPC_SPAN_MIN_MS, default 5 ms), failed or warned.
Tests to read#
crates/architect/tests/trace_context.rs: a call made in a span is dispatched as its child.crates/architect/tests/rpc_metrics.rs: each call records its duration by service and method.crates/architect/tests/rpc_spans.rs: the dispatch span names the method and carries the scope.features/telemetry/tests/calls.rs: only slow, failed and warning calls are exported.features/telemetry/tests/stages.rs: each stage is a span under its unit of work.features/telemetry/tests/metrics.rs: stages and the process are recorded.features/telemetry/tests/flight.rs: the crash and flight logs.
The three crates/architect tests need features local and telemetry.
Status#
Done in architect; trace context, rpc.server.duration and QuietCalls are
in the latest tags. Limits: telemetry is native only; wide::set is silent
without otel; a call span measures dispatch, not the reply; the flight log
is one hook per process with no size cap or upload.
- Task boots telemetry on the server and desktop
(
task:apps/server/src/main.rs,task:apps/desktop/src/main.rs) and useswide::setwidely. Its pinned tag predates trace context, the call metric andQuietCalls; it uses neitherStagesnorflight. - Studio uses
init_tracing_full,wide::setandStages(for examplestudio:apps/session/daw/src/perf.rs), and keeps its own log ring (studio:features/app/shell/src/log_ring.rs) instead offlight. Its tag predates trace context.