feat(sensing-server): opt-in OpenTelemetry log export with a weaver-checked ruview.* registry - #1382
feat(sensing-server): opt-in OpenTelemetry log export with a weaver-checked ruview.* registry#1382jensholdgaard wants to merge 6 commits into
Conversation
… CI gate Add a weaver-validated (0.23, `registry check --future`) OpenTelemetry semantic-conventions registry at semconv/registry/ defining the `ruview.*` telemetry namespace: 13 attributes (node id, CSI source/stats, presence state, motion level, inference confidence, person count, vitals rates + confidences, model id) and 8 curated log event names (node online/offline, presence.changed, vitals.estimate, fall.detected, csi.stats, mqtt.error, model.loaded). Everything is `stability: development` with requirement levels on the event attribute refs, and enums mirror the code's actual value sets (the adaptive classifier's class list, the sensing source labels). templates/registry/rust/ holds the weaver-forge template that generates the Rust constants module (landed with its first consumer in a follow-up commit). The new semconv workflow pins weaver v0.23.0 by sha256, validates the registry, regenerates the module, and fails on drift (codegen no-diff). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Add a `telemetry` module that installs the tracing subscriber, with optional OpenTelemetry log export doubly gated so both the default build and the default runtime are byte-identical to before: - build: a new `otel` cargo feature (same gating principle as `mqtt`) keeps the opentelemetry 0.32 SDK/OTLP/appender stack out of the default binary; without it the module is exactly the long-standing stderr fmt subscriber - runtime: even with the feature, export only activates when OTEL_EXPORTER_OTLP_ENDPOINT is set — unset means the OTLP pipeline is never constructed When enabled, every tracing event exports as an OTel log record over OTLP/gRPC with resource service.name = "ruview", through a batch exporter, with a telemetry-induced-telemetry loop guard muting the exporter's own crates (hyper/tonic/h2/tower/opentelemetry*) so a failed export can never feed back into the exporter. src/semconv.rs is the module GENERATED from semconv/registry/ by `weaver registry generate` (see the module header for the exact command); the semconv CI workflow keeps it in sync. Call sites adopt the constants in the next commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
…names Emit the highest-value sensing signals as tracing events carrying the generated `ruview.*` event names and attribute keys (semconv/registry/), and switch main() to telemetry::init() so they export over OTLP when enabled: - ruview.node.online / ruview.node.offline — first frame per node (both the CSI and edge-vitals UDP paths) and the 60 s staleness eviction, which now logs each evicted node id - ruview.presence.changed — smoothed presence transitions, emitted from every SensingUpdate publish path (esp32 CSI, edge vitals, wifi, multi-BSSID, simulated) via one observe_sensing_update helper; transition-only, never per frame - ruview.vitals.estimate + ruview.csi.stats — cadenced snapshots every 100 sensing ticks - ruview.fall.detected — edge-vitals fall flag, edge-triggered per node against the previously stored packet - ruview.mqtt.error — the HA publisher's publish/connect failure sites - ruview.model.loaded — the model-management API load handler Rate discipline: nothing new logs at frame rate; all per-frame paths only emit on transitions or the 100-tick cadence. Stderr output is unchanged apart from the new lines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
docker/otel-compose.yml brings up sensing-server (synthetic CSI by default, built with `mqtt,otel` via a new SENSING_FEATURES build arg in Dockerfile.rust) → an OpenTelemetry Collector → an OTLP-native log backend, giving a queryable log pipeline from a single `docker compose -f docker/otel-compose.yml up`. docs/observability.md documents the ruview.* event registry, the double gating (build feature + endpoint env var), and example queries against the bundled backend (template frequency, warning/error filter, log-template drift after a deploy). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Resolve two conflicts against upstream's ADR-185 (AETHER hoist) and
Cognitum OAuth work:
- v2/crates/wifi-densepose-sensing-server/src/lib.rs: keep the new
semconv/telemetry modules (this PR's OTel feature, used by main.rs);
drop the sona/sparse_inference local `pub mod`s — upstream moved them
(with embedding/graph_transformer) to the
`pub use wifi_densepose_aether::{...}` re-export, so crate::sona etc.
still resolve and the deleted local files no longer exist.
- v2/Cargo.lock: union the sensing-server deps — this PR's opentelemetry-*
alongside upstream's p256.
Signed-off-by: Jens Holdgaard Pedersen <jens@holdgaard.org>
ruvnet
left a comment
There was a problem hiding this comment.
The opt-in exporter and registry direction looks useful, but I found three correctness/contract gaps that should be addressed before merge:
- The default behavior is not actually unchanged. The new
info!/warn!call sites are compiled and sent through the existing fmt subscriber even without theotelfeature or endpoint. That means the issue's “nothing is emitted unless the exporter is configured” promise is false and default stderr volume/behavior changes. Either gate the curated emissions with the opt-in feature/runtime state, or revise the contract and explicitly test/document the default logging change. ruview.node.onlinecan be skipped for normal CSI nodes. The SyncPacket handler createsnode_states[node_id]atmain.rs:6076; the first CSI frame later emits online only when!node_states.contains_key(&node_id)atmain.rs:6225. A node that sends sync before CSI therefore never emits the promised first-frame online event. Track first data-frame observation separately (or testlast_frame_time/an explicit flag) and add a regression test for sync → first CSI.- The claimed semconv enforcement is incomplete. Event names use generated constants, but attribute keys remain hard-coded string literals at the call sites, and generated
SCHEMA_URLis never attached to the OTel resource (telemetry.rs:88only setsservice.name). Weaver/codegen can therefore stay green while emitted attributes drift from the registry, and consumers receive no schema URL. Please wire or otherwise validate the emitted keys and attach the registry schema URL (or narrow the documented contract if the SDK cannot represent it).
I have not treated the contributor-hosted demo backend as a blocker, but pinning demo container images by digest would make the one-command pipeline more reproducible and reduce tag-mutation risk.
Imports and hardens #1382 with opt-in OTLP logging, registry-validated semantic conventions, a published schema, TLS roots, digest-pinned demo images, and corrected first-CSI lifecycle reporting. Co-authored by Jens Holdgaard Pedersen.
|
Thank you @jensholdgaard — this work has been imported, security-hardened, and merged via #1465. Your authorship is preserved in the merged commit. The replacement keeps OTLP export explicitly opt-in, adds TLS roots and a resolvable schema contract, fixes first-CSI lifecycle reporting, pins demo images, and adds registry/codegen drift checks. Closing this PR as superseded by #1465. |
Import and harden the OpenTelemetry logging work from ruvnet#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 ruvnet#1382 Closes ruvnet#1460 Co-authored-by: Jens Holdgaard Pedersen <jens@holdgaard.org>
What
Opt-in OpenTelemetry logs export for the sensing server, with a curated set of sensing events under a
ruview.*namespace whose names and attribute keys are defined in a semantic-conventions registry and enforced in CI:semconv/registry/— a weaver-validated (0.23,registry check --future) OTel semantic-conventions registry: 13 attributes and 8 log event names, allstability: development, with requirement levels on the event attribute refs and enums mirroring the code's actual value sets (the adaptive classifier's class list, the sensing source labels).src/semconv.rs— Rust constants generated from the registry (weaver registry generate, template undertemplates/registry/rust/). The newsemconvworkflow pins weaver v0.23.0 by sha256, validates the registry, regenerates the module, and fails on drift (codegen no-diff).src/telemetry.rs— tracing bootstrap with optional OTLP export, doubly gated:otelcargo feature (same gating principle asmqtt) keeps the opentelemetry 0.32 stack out of the default binary;OTEL_EXPORTER_OTLP_ENDPOINTis set.Unset ⇒ the OTLP pipeline is never constructed and behavior is byte-identical to today's stderr
fmtsubscriber. When enabled, every tracing event exports as an OTel log record (service.name = "ruview") with a telemetry-induced-telemetry loop guard muting the exporter's own crates.docker/otel-compose.yml+docs/observability.md— a one-command demo pipeline (sensing server on synthetic CSI → OTel Collector → an OTLP log backend) and the guide.Why
The sensing server already narrates the interesting moments (presence flips, vitals, node churn, falls, MQTT failures) as free-text log lines. Giving the highest-value ones stable, registry-backed event names and attributes makes them consumable by any OTLP collector/backend — dashboards, alerting on
ruview.fall.detected, per-node filtering — without scraping stderr, and the weaver CI gate keeps the schema from drifting silently.The events
ruview.node.onlineruview.node.offlineruview.presence.changedSensingUpdatepublish paths via one helperruview.vitals.estimateruview.csi.statsruview.fall.detectedruview.mqtt.errorruview.model.loadedRate discipline: nothing new logs at frame rate — transitions and the 100-tick cadence only.
Deliberate omissions:
ruview.roomandruview.mesh.channelwere considered but not added — the registry only defines attributes for data this crate actually has (rooms live in the calibration crate; channel isn't in the frame structs).Verification
cargo check -p wifi-densepose-sensing-server— default features,--features otel,mqtt, and--all-targets: clean (only pre-existing warnings, verified against the base commit). Each commit in this PR builds independently (default features checked at every commit boundary).weaver registry check --future: pass; the generate + rustfmt cycle is hash-stable, so the CI no-diff gate is sound.--source simulated) against a real OTLP logs backend — we dogfood with Ourios, but the export is backend-neutral and works with any OTLP collector/backend. Logs arrived underservice.name = ruview, the curated events came through with their registry names/attributes, and template-level queries (event frequency by mined log template,severity >= warnfiltering, log-template drift between time windows) answered correctly.Caveats (honest limits)
Dropnever runs on SIGKILL/SIGTERM); the periodic batch export bounds the loss to the last export interval.docker/otel-compose.ymlanddocker/otel-collector.yamlare parser-validated only (no docker CLI on the dev machine); the same env/feature path was verified end-to-end outside compose as described above.🤖 Generated with Claude Code
https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y