diff --git a/api-docs/observability.md b/api-docs/observability.md new file mode 100644 index 00000000..52b01bc3 --- /dev/null +++ b/api-docs/observability.md @@ -0,0 +1,117 @@ +# 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`): + + ```sh + 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): + + ```sh + 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](https://github.com/jensholdgaard/ourios), an OTLP-native log +backend built on Parquet + online log-template mining + DataFusion: + +```sh +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? + +```sh +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): + +```sh +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): + +```sh +curl -s http://localhost:4319/v1/query \ + -H 'X-Ourios-Tenant: ruview' \ + -H 'Content-Type: text/plain' \ + -d 'drift from -7d to now' +```