Add OTLP logs signal to the Rust observability SDK#71
Merged
Conversation
Emit LOGS into the SmooAI observability product's /v1/logs OTLP endpoint, correlated to traces — the Rust/flagship dogfood reference for the SDK logs signal (th-5dca7d), matching ADR-100. Reuses the existing traces/metrics OTLP setup as the template: same endpoint (SMOOAI_OBSERVABILITY_ENDPOINT, logs -> /v1/logs), same auth (static token or M2M token provider), same enable/init path, no new config keys. Built with the standard OTel appender: opentelemetry-appender-tracing (tracing->OTel log bridge) + opentelemetry-otlp LogExporter (HTTP/JSON, matching the traces exporter transport) + opentelemetry_sdk SdkLoggerProvider. Uses the async-runtime BatchLogProcessor for the same reactor-panic reason as traces/metrics (SMOODEV-2045). trace_id/span_id correlation: the SDK logger enriches each record from the active opentelemetry::Context at emit time, which is the OTel-native context this crate's tower/gen_ai/reqwest spans already make current — so a log fired inside one of those spans carries its real W3C ids. The host installs the bridge via the new OtelSdkHandle::tracing_appender_layer(). Graceful no-op when no logs endpoint is configured; traces/metrics behavior unchanged. Tests (tests/logs_signal.rs): a log emitted inside an active span carries that span's trace_id/span_id (asserted against the real ids), and a log with no active span carries no fabricated trace context. Disabled-pipeline no-op covered in otel.rs unit tests. Follow-up now unblocked: dogfood wiring (api-prime / workers installing the appender layer and emitting logs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S2bM94GAnVjYSSv1x7HKRB
|
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.
Problem
The SmooAI observability SDK emits traces + metrics into the product's OTLP endpoints, but not logs — so we can't join a service's log lines to the traces they belong to. This is the Rust/flagship dogfood reference for the SDK logs signal (pearls th-5dca7d / th-de3805, ADR-100); the other four languages follow this pattern.
Solution
Add the LOGS signal, sending into
/v1/logscorrelated to traces, reusing the existing traces/metrics OTLP setup as the template:SMOOAI_OBSERVABILITY_ENDPOINT→/v1/logs), same auth (static_TOKENor M2M_AUTH_URL/_CLIENT_ID/_CLIENT_SECRETvia the per-requestAuthInjectingHttpClient), same enable/init path. Per-signal override honored viaOTEL_EXPORTER_OTLP_LOGS_ENDPOINT.opentelemetry-appender-tracing(tracing→OTel log bridge) +opentelemetry-otlpLogExporter(HTTP/JSON, matching the traces exporter's transport) +opentelemetry_sdkSdkLoggerProvider. Matched the exact0.32versions already pinned; the only new crate isopentelemetry-appender-tracing 0.32.BatchLogProcessor— same reactor-panic rationale as traces/metrics (SMOODEV-2045): the default thread-based processorblock_ons on a bare OS thread where our reqwest-backed exporter has no Tokio reactor and aborts underpanic = "abort".tracing_appender_layer()returnsNone; traces/metrics behavior unchanged.Trace/span correlation (the whole point)
severity←tracing level,body←message,resource service.name→service_name, event fields→attributes. Fortrace_id/span_id: the SDK logger enriches each record from the activeopentelemetry::Contextat emit time — which is the OTel-native context this crate's tower/gen_ai/reqwest spans already make current. So a log fired inside one of those spans carries its real W3C ids, automatically. Hosts install the bridge via the newOtelSdkHandle::tracing_appender_layer().Verification
cargo build,cargo clippy --all-targets(clean),cargo fmt --check(clean),cargo test— 60 passed. Newtests/logs_signal.rs:log_within_active_span_carries_trace_and_span_id— asserts the emitted record'strace_id/span_idequal the active span's real ids. Correlation was actually verified, not just compiled: mutating the expectedtrace_idfails the test (real captured id1732c136…vs the injected wrong value), confirming the assertion bites.log_without_active_span_has_no_trace_context— no active span → no fabricated trace context.otel.rsunit tests (build_log_exporterisNone; a fully-disabled handle yields no appender layer).Wire format is OTLP/HTTP/JSON, matching the traces/metrics bytes the product already ingests, but the end-to-end product ingest was not exercised here (offline). No deploy.
Follow-up (now unblocked)
Dogfood wiring — api-prime / workers installing the appender layer and emitting logs through this pipeline.
🤖 Generated with Claude Code