feat(telemetry): export gateway health diagnostics - #61252
feat(telemetry): export gateway health diagnostics#61252victor-kyriazakos wants to merge 1 commit into
Conversation
fb7dc65 to
2785c36
Compare
3d3f824 to
3e39b39
Compare
Local smoke test: call flow and reproductionVerified end to end on this branch: the in-process emitter sent real OTLP protobuf to a local capture collector on :4318 and all three routes arrived. Decoded payloads showed 8 gauge series on Who calls who, when, with whatsequenceDiagram
autonumber
participant Probe as gateway_health_export_probe.py
participant Runtime as start_gateway_health_export()<br/>(gateway_health_export.py)
participant Status as write_runtime_status()<br/>(gateway/status.py)
participant Emitter as telemetry emitter<br/>(agent/telemetry/emitter.py)
participant SDK as OTel SDK 1.39.1<br/>(MeterProvider / OTLPStreamer / LoggerProvider)
participant Collector as capture collector :4318<br/>(otel_capture_collector.py)
Note over Collector: started first in tmux, writes each POST to JSONL
Probe->>Runtime: start (config: enabled=true,<br/>endpoint=http://127.0.0.1:4318/v1/traces)
Runtime->>SDK: load SDK, build MeterProvider (observable gauges),<br/>span streamer (event_filter=gateway_health),<br/>log streamer (gateway_diagnostic -> /v1/logs)
Runtime->>Emitter: subscribe streamers to gateway events
Probe->>Status: write_runtime_status(starting -> running,<br/>agents=2, busy, drainable)
Status->>Emitter: emit_runtime_status_transition(prev, curr)<br/>(fail-open, content-free)
Probe->>Status: write_runtime_status(slack platform -> fatal,<br/>error=auth_failed)
Status->>Emitter: platform.state_change + platform.fatal events
Probe->>Emitter: gateway logger warning<br/>(raw line contains token + e-mail)
Note over Emitter: redaction rewrites secrets to [redacted]/[email]<br/>BEFORE anything leaves the process
Emitter->>SDK: gateway_health events -> span projection
Emitter->>SDK: gateway_diagnostic events -> log records<br/>(INVALID_TRACE_ID/INVALID_SPAN_ID, uncorrelated)
SDK->>Collector: POST /v1/metrics (periodic reader, gauges)
SDK->>Collector: POST /v1/traces (batch span processor)
SDK->>Collector: POST /v1/logs (batch log processor)
Probe->>Runtime: shutdown() (flush + unsubscribe)
Probe->>Collector: read JSONL, assert all three paths present
Note over Probe: exit 0 = /v1/metrics, /v1/traces, /v1/logs all received
The collector is a dev/test receiver only. In production the same emitter points at a customer-owned OTEL Collector ( Reproduce (no Docker)# branch feat/gateway-health-otel, needs the [otlp] extra installed
# terminal 1: local capture collector
tmux new-session -d -s otel-smoke \
"python scripts/observability/otel_capture_collector.py \
--host 127.0.0.1 --port 4318 --log /tmp/hermes_otel_capture.jsonl"
# terminal 2: drive the real exporter through lifecycle transitions,
# a fatal slack platform, and a redacted warning log, then flush
python scripts/observability/gateway_health_export_probe.py \
--endpoint http://127.0.0.1:4318/v1/traces \
--log /tmp/hermes_otel_capture.jsonl --wait 8
# exit 0 prints: {"requests": 6, "paths": ["/v1/logs", "/v1/metrics", "/v1/traces"]} |
3e39b39 to
f89bc68
Compare
|
Superseded by PR #64536, which landed on main (merge commit 7de33cc) as the standalone successor to this work — same gateway health & diagnostics OTLP export, extended with cron scheduler/execution health, background-work signals, and the durable execution ledger. All of the design here carried forward into the merged version. Thanks @victor-kyriazakos! |
Summary
Taxonomy alignment
This is Service Health Monitoring plus Operational Diagnostics only. It does not export prompts, messages, tool args/results, session history, product usage analytics, governance/audit logs, quality reports, or trajectories.
Scope additions
write_runtime_status()transitions/v1/logsscripts/observability/Review fixes
Addressed independent review blockers:
OTLPStreamer.shutdown()unsubscribes itself from the emittergateway/gateway.*onlyVerification
python -m pytest tests/telemetry/test_gateway_health_export.py -q -o 'addopts='-> 12 passedpython -m pytest tests/telemetry -q -o 'addopts='-> 88 passedpython -m pytest tests/gateway/test_status.py tests/gateway/test_status_command.py tests/gateway/test_version_command.py tests/cli/test_version_command.py -q -o 'addopts='-> 112 passedpython -m py_compile agent/telemetry/gateway_health.py agent/telemetry/gateway_health_export.py agent/telemetry/events.py agent/telemetry/otlp_exporter.py gateway/status.py gateway/run.py hermes_cli/config.py hermes_cli/main.py scripts/observability/otel_capture_collector.py scripts/observability/gateway_health_export_probe.pygit diff --check/v1/logs,/v1/metrics, and/v1/tracescapturedStacked on #51714; should merge after or into that telemetry PR.
CI triage note
Compared #61252 with base PR #51714. The broad failing test/build jobs were inherited from #51714:
hermes-agent[otlp]pinned OpenTelemetry1.30.0, whilemistralai==2.4.8requiresopentelemetry-api>=1.33.1. This branch now bumps the OTLP extra andtools.lazy_depsto OpenTelemetry1.39.1, matching the existinguv.lockpackage set, and reranuv lock.The only #61252-specific failure found was ruff
unspecified-encodingin the local OTLP probe script. Fixed by adding explicit UTF-8 decoding.