Files
ruvnet--RuView/docs/observability.md
T
ruv 9fb5af7cf2 feat(sensing-server): add secure opt-in OTLP log export
Import and harden the OpenTelemetry logging work from #1382. Preserve default stderr behavior, register and validate RuView semantic conventions, attach a published schema, enable TLS roots, pin demo images, and fix first-CSI node lifecycle reporting.

Supersedes #1382
Closes #1460

Co-authored-by: Jens Holdgaard Pedersen <jens@holdgaard.org>
2026-07-28 22:42:18 -04:00

4.5 KiB

Observability: OTLP log export

The sensing server can export every tracing log event as an OpenTelemetry log record over OTLP, with a curated set of sensing events (presence transitions, vitals estimates, node online/offline, fall detections, CSI capture stats, MQTT errors, model loads) carrying registry-backed event names and attributes under the ruview.* namespace.

The event registry

The names are not ad hoc: they are defined in a weaver-validated semantic-conventions registry at semconv/registry/ (attributes and log event names, OpenTelemetry registry format). The Rust constants module v2/crates/wifi-densepose-sensing-server/src/semconv.rs is generated from that registry (weaver registry generate, template under templates/registry/rust/) and CI (.github/workflows/semconv.yml) fails if either the registry stops validating or the generated module drifts. Executed Rust tests additionally reject any hard-coded ruview.* instrumentation key that is absent from the generated registry. Exported resources carry the registry's schema URL so downstream consumers can identify the exact conventions version.

Curated events:

Event Emitted when
ruview.node.online first frame from a sensing node (CSI or edge vitals)
ruview.node.offline node evicted after 60 s without frames
ruview.presence.changed smoothed presence classification flips (transition-only)
ruview.vitals.estimate periodic breathing / heart-rate estimate (every 100 ticks)
ruview.fall.detected edge-vitals fall flag rising edge, per node
ruview.csi.stats periodic capture snapshot: frames processed, active nodes
ruview.mqtt.error MQTT publish/connection error in the HA publisher
ruview.model.loaded inference model loaded via the model API

Enabling export

Export is doubly gated so the default build and the default runtime are both unaffected:

  1. Build with the otel cargo feature (compiles in the OTLP exporter stack, same gating principle as mqtt):

    cargo build --release -p wifi-densepose-sensing-server --features mqtt,otel
    
  2. Run with OTEL_EXPORTER_OTLP_ENDPOINT set (unset ⇒ the OTLP pipeline is never constructed and logging behaves exactly as before):

    OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \
    ./target/release/sensing-server --source simulated
    

Use an https:// collector endpoint outside a trusted local network. The otel feature includes Rustls and native certificate roots; standard OTLP environment variables can supply authentication headers. The Compose example uses plaintext only for container-to-container traffic on its private network.

Logs export with resource attribute service.name = "ruview" and schema URL https://raw.githubusercontent.com/ruvnet/RuView/main/semconv/schema/ruview-0.1.0.yaml. Curated sensing events are emitted only after the configured exporter initializes successfully; without it, the pre-existing stderr output is unchanged.

Full stack: docker compose

docker/otel-compose.yml brings up the whole pipeline — sensing server (synthetic CSI by default) → OpenTelemetry Collector → Ourios, an OTLP-native log backend built on Parquet + online log-template mining + DataFusion:

docker compose -f docker/otel-compose.yml up

The collector and backend image tags are pinned to immutable multi-platform digests so the demo resolves to the reviewed images.

Ourios derives the tenant from service.name, so all RuView logs land in tenant ruview.

Example queries

Ourios mines every log line into a stable template_id online at ingest, which makes template-level questions cheap. Its query endpoint speaks a small logs DSL:

Which log templates dominate RuView's output?

curl -s http://localhost:4319/v1/query \
  -H 'X-Ourios-Tenant: ruview' \
  -H 'Content-Type: text/plain' \
  -d 'severity >= trace | range(-1h, now) | count by template_id | sort count desc | limit 10'

Recent warnings and errors (fall detections, MQTT failures):

curl -s http://localhost:4319/v1/query \
  -H 'X-Ourios-Tenant: ruview' \
  -H 'Content-Type: text/plain' \
  -d 'severity >= warn | limit 50'

Did a RuView deploy change what the service logs? Template drift between two time windows (new / vanished / changed templates):

curl -s http://localhost:4319/v1/query \
  -H 'X-Ourios-Tenant: ruview' \
  -H 'Content-Type: text/plain' \
  -d 'drift from -7d to now'