telemetry guidelines
Observability Architecture Assessment
spindle implements local-only, opt-in metric exposition and structured log filtering designed for production matrix server operations.
Key Components
-
Prometheus Metrics Listener:
- Metrics exposition is disabled by default and runs on an isolated opt-in loopback listener when configured (
[metrics] bind = "127.0.0.1:9090"in server configuration). - Exposes key performance, sync lag, append latency, and state resolution metrics including:
spindle_build_info: Gauge indicating build version.spindle_events_appended_total: Counter tracking events reaching room logs by origin (localvsfederated).spindle_fork_resolutions_total: Counter tracking SPEC §9.2 state resolution cases (case="1",case="2",case="3").spindle_append_duration_seconds: Histogram measuring durability commit latency.spindle_http_requests_total&spindle_http_request_duration_seconds: HTTP route metrics.spindle_sync_subscribers&spindle_sync_lag_seconds: Long-polling/syncsubscriber and event delivery lag indicators.
- Metrics exposition is disabled by default and runs on an isolated opt-in loopback listener when configured (
-
Structured Logging Posture:
- Utilizes
tracingandtracing-subscriberwithenv-filtersupport. - Verbosity controlled at runtime via
RUST_LOGenvironment variables (e.g.,spindle=debug,warn).
- Utilizes
-
Validation & CI Automation:
scripts/check-observability-pack.pyverifies metric registration against Prometheus alert definitions (deploy/prometheus/spindle-alerts.yaml) and Grafana dashboard schema (deploy/grafana/spindle.json).
Data Flow & Telemetry Boundary Rules
- Zero Exporter Policy: No default OpenTelemetry exporter, Jaeger agent, or external collector endpoint is wired.
- Local Network Boundary: All telemetry data stays strictly within local boundaries unless explicitly routed by network operators via local scrapers or reverse proxies.
- Cardinality Limits: Custom metrics must maintain bounded label dimensions (e.g., standard HTTP status codes, specific durability modes, or case types) to prevent memory expansion.
Traces
Distributed tracing exists and is off. It is switched on by naming the exporter in the config, and by nothing else:
[logging]
traces = "otlp"
With that line the server exports every span it records -- one per
request, named by the matched route, with OpenTelemetry's HTTP semantic
fields -- over OTLP/HTTP with protobuf bodies. The destination is not a
setting in the file: the SDK reads the standard OTEL_EXPORTER_OTLP_ENDPOINT
(default http://localhost:4318), OTEL_EXPORTER_OTLP_HEADERS and
OTEL_EXPORTER_OTLP_TIMEOUT, so no collector address is ever hardcoded
and the same config runs against any backend that speaks OTLP. Spans are
batched on a thread of the SDK's own: a slow collector delays no request,
and past the queue spans drop rather than back up.
Without the line, no exporter is built and nothing leaves the process, which keeps the two rules above true by default. A malformed OTLP environment fails the start with the SDK's error rather than starting a server that silently exports nothing.