refactor(otel): idiomatic abstractions, self-telemetry, SARIF SLO wiring#66
Merged
Merged
Conversation
ledger-core::observability: - Add TryFrom<u8> for OTelSeverityNumber (OTLP range 1-24) eliminating duplicated severity mapping in rotel-visual. - Add otlp_json module with LogsRequest, LogRecord, AnyValue, Attribute wire-format types for shared OTLP JSON deserialization. - Add TryFrom<&otlp_json::LogRecord> for OTelLogRecord bridge. - Add LogShapeClassifier::with_builtin_rules() constructor so callers don't hardcode operational rules. rotel-visual: - Refactor to use new ledger-core abstractions: otlp_json types, OTelSeverityNumber::try_from(), LogShapeClassifier::with_builtin_rules(). - Add SurfaceMetrics with AtomicU64 counters for logs/metrics/traces ingested, classified, and WebSocket connections. - Add GET /metrics endpoint returning self-telemetry snapshot. - Add POST /rotel/evaluate endpoint wiring b00t-iface SARIF SLO evaluation (check_otel_logic_slo_as_sarif) — previously tested but never invoked in production code. - Fix error handling: create_app() and run_server() return Result instead of unwrap/expect. - Add b00t-iface dependency. Tests: - ledger-core: 4 new tests (severity mapping, OTLP deserialization, log record conversion, builtin rules) - rotel-visual: 4 new tests (/metrics, /metrics increment, /rotel/evaluate success, /rotel/evaluate SLO failure detection) - All crates pass: ledger-core 51, b00t-iface 51, ledgerr-host 20, rotel-visual 12
| let replay = state.replay_buffer().await; | ||
| for batch in replay { | ||
| let msg = axum::extract::ws::Message::Text( | ||
| serde_json::to_string(&batch).unwrap().into() |
| let replay = state.replay_buffer().await; | ||
| for batch in replay { | ||
| let msg = axum::extract::ws::Message::Text( | ||
| serde_json::to_string(&batch).unwrap().into() |
| match rx.recv().await { | ||
| Ok(telemetry) => { | ||
| let msg = axum::extract::ws::Message::Text( | ||
| serde_json::to_string(&telemetry).unwrap().into() |
| match rx.recv().await { | ||
| Ok(telemetry) => { | ||
| let msg = axum::extract::ws::Message::Text( | ||
| serde_json::to_string(&telemetry).unwrap().into() |
Comment on lines
+539
to
+541
| let status = if report.has_errors() { | ||
| axum::http::StatusCode::OK | ||
| } else { |
Comment on lines
+539
to
+541
| let status = if report.has_errors() { | ||
| axum::http::StatusCode::OK | ||
| } else { |
| @@ -0,0 +1,10 @@ | |||
| use tracing_subscriber; | |||
| @@ -0,0 +1,10 @@ | |||
| use tracing_subscriber; | |||
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors OpenTelemetry support so shared OTLP/log-classification primitives live in ledger-core, while rotel-visual grows into a more complete OTLP-facing surface with self-telemetry and a production SARIF SLO evaluation endpoint.
Changes:
- Extracts reusable OTLP severity and JSON wire-format abstractions into
ledger-core::observability, plus a built-in log-shape classifier constructor. - Expands
rotel-visualwith OTLP ingest routes, self-telemetry counters, replay/broadcast plumbing, and a/rotel/evaluateSARIF endpoint. - Adds/updates integration tests and wires the crate as an explicit binary with new dependencies.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
crates/rotel-visual/tests/health_dashboard_tests.rs |
Adds endpoint and self-telemetry tests for the expanded rotel-visual surface. |
crates/rotel-visual/src/main.rs |
Introduces the binary entrypoint and tracing initialization. |
crates/rotel-visual/src/lib.rs |
Implements OTLP ingest handlers, websocket replay/broadcast, metrics snapshotting, dashboard updates, and SARIF evaluation routing. |
crates/rotel-visual/Cargo.toml |
Declares the binary target and new crate dependencies needed for observability/SARIF work. |
crates/ledger-core/src/observability.rs |
Adds shared OTLP severity conversion, OTLP JSON types, record conversion, and built-in classifier rules. |
Cargo.lock |
Locks the newly added rotel-visual dependencies. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+169
to
+182
| let response = app() | ||
| .clone() | ||
| .oneshot( | ||
| Request::builder() | ||
| .uri("/v1/logs") | ||
| .method("POST") | ||
| .header("content-type", "application/json") | ||
| .body(Body::from(body.to_string())) | ||
| .unwrap(), | ||
| ) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| assert_eq!(response.status(), StatusCode::ACCEPTED); |
Comment on lines
+408
to
+412
| let log = OTelLogRecord::new( | ||
| record.time_unix_nano.parse().unwrap_or(0), | ||
| severity, | ||
| body.clone(), | ||
| ); |
Comment on lines
+530
to
+537
| let report = check_otel_logic_slo_as_sarif( | ||
| &mut registry, | ||
| &req.gate_name, | ||
| &req.expression, | ||
| req.log_shape_observed, | ||
| req.metric_observed, | ||
| req.slo_expected, | ||
| ); |
Comment on lines
+153
to
+160
| pub struct Resource { | ||
| pub attributes: Vec<Attribute>, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize)] | ||
| pub struct ScopeLogs { | ||
| #[serde(rename = "logRecords")] | ||
| pub log_records: Vec<LogRecord>, |
Comment on lines
+342
to
+346
| let replay = state.replay_buffer().await; | ||
| for batch in replay { | ||
| let msg = axum::extract::ws::Message::Text( | ||
| serde_json::to_string(&batch).unwrap().into() | ||
| ); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Member
Author
Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses non-obvious synergistic gaps by extracting shared OTLP abstractions into ledger-core, adding self-telemetry to rotel-visual, and wiring the previously orphan SARIF SLO evaluation into a production endpoint.
Changes
ledger-core::observability
rotel-visual
Test Evidence
Checklist