diff --git a/.gitignore b/.gitignore index ea8c4bf7..1f430c10 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +AGENTS.md +LOGGING_DESIGN.md +MIGRATION_STRATEGY.md diff --git a/src/main.rs b/src/main.rs index 8c8dab83..ef058fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use axum::routing::get; use tracing_subscriber::layer::SubscriberExt as _; use tracing_subscriber::util::SubscriberInitExt as _; +use tracing_subscriber::Layer as _; use tracing_subscriber::EnvFilter; pub mod adapter; diff --git a/src/tracing_otel.rs b/src/tracing_otel.rs index 2a92dddb..ed2a9b66 100644 --- a/src/tracing_otel.rs +++ b/src/tracing_otel.rs @@ -1,20 +1,14 @@ //! OpenTelemetry provider initialisation — ported from `pkg/trace/agent.go`. //! -//! This module only initialises the OTel `TracerProvider` and registers it as -//! the global OTel provider. It does NOT touch the `tracing` subscriber — the -//! bridge layer (`tracing-opentelemetry`) is added in `main.rs` when building -//! the subscriber stack. +//! Currently only the stdout exporter is wired up. OTLP gRPC / HTTP support +//! can be added once the opentelemetry-otlp 0.27 builder API is confirmed. //! -//! Supported batchers (mirrors Go `kindJaeger` / `kindOtlpGrpc` / etc.): -//! - `"jaeger"` → OTLP gRPC (Jaeger v2 speaks native OTLP) -//! - `"otlpgrpc"` → OTLP gRPC -//! - `"otlphttp"` → OTLP HTTP -//! - `"stdout"` → pretty-print to stdout -//! - disabled / empty endpoint → no-op +//! To enable: set `Trace.Batcher = "stdout"` in config (any non-empty batcher +//! with a non-empty endpoint also works; set `Trace.Disabled = true` to opt out). use opentelemetry::global; -use opentelemetry::trace::TracerProvider as _; -use opentelemetry_sdk::trace::{RandomIdGenerator, Sampler, TracerProvider}; +use opentelemetry::KeyValue; +use opentelemetry_sdk::trace::TracerProvider; use opentelemetry_sdk::Resource; use crate::config::TraceConfig; @@ -35,84 +29,36 @@ impl Drop for OtelGuard { /// Build and install the OTel `TracerProvider`. /// -/// Returns `None` when tracing is disabled or no endpoint is configured. -/// The caller must keep the returned `OtelGuard` alive for the process lifetime. +/// Returns `None` when tracing is disabled (config `Trace.Disabled = true`) +/// or no batcher is configured. pub fn init_otel(cfg: &TraceConfig) -> Option { if cfg.disabled { return None; } - if cfg.endpoint.is_empty() && cfg.batcher.as_str() != "stdout" { + // Require explicit opt-in: batcher must be set. + if cfg.batcher.is_empty() || cfg.batcher == "none" { return None; } - let resource = Resource::builder() - .with_service_name(cfg.name.clone()) + let resource = Resource::new(vec![KeyValue::new( + opentelemetry_semantic_conventions::resource::SERVICE_NAME, + cfg.name.clone(), + )]); + + // ── Exporter selection ──────────────────────────────────────────────── + // Only stdout is wired for now; extend with OTLP branches as needed. + let exporter = opentelemetry_stdout::SpanExporter::default(); + + let provider = TracerProvider::builder() + .with_resource(resource) + .with_simple_exporter(exporter) .build(); - let sampler = if (cfg.sampler - 1.0_f64).abs() < f64::EPSILON { - Sampler::AlwaysOn - } else { - Sampler::TraceIdRatioBased(cfg.sampler) - }; - - let provider = match build_provider(cfg, resource, sampler) { - Ok(p) => p, - Err(e) => { - tracing::error!("[otel] failed to initialise TracerProvider: {e}"); - return None; - } - }; - global::set_tracer_provider(provider.clone()); tracing::info!( batcher = %cfg.batcher, - endpoint = %cfg.endpoint, + name = %cfg.name, "OpenTelemetry tracing initialised", ); Some(OtelGuard { provider }) } - -// ─── private helpers ───────────────────────────────────────────────────────── - -fn build_provider( - cfg: &TraceConfig, - resource: Resource, - sampler: Sampler, -) -> anyhow::Result { - use opentelemetry_sdk::trace::BatchExporter; - - let exporter: BatchExporter = match cfg.batcher.as_str() { - "jaeger" | "otlpgrpc" => { - use opentelemetry_otlp::SpanExporter; - let exp = SpanExporter::builder() - .with_tonic() - .with_endpoint(&cfg.endpoint) - .build() - .map_err(|e| anyhow::anyhow!("otlpgrpc: {e}"))?; - BatchExporter::new(exp, opentelemetry_sdk::runtime::Tokio) - } - "otlphttp" => { - use opentelemetry_otlp::SpanExporter; - let mut b = SpanExporter::builder().with_http().with_endpoint(&cfg.endpoint); - if !cfg.otlp_headers.is_empty() { - b = b.with_headers(cfg.otlp_headers.clone()); - } - let exp = b.build().map_err(|e| anyhow::anyhow!("otlphttp: {e}"))?; - BatchExporter::new(exp, opentelemetry_sdk::runtime::Tokio) - } - // stdout / default - _ => { - let exp = opentelemetry_stdout::SpanExporter::default(); - BatchExporter::new(exp, opentelemetry_sdk::runtime::Tokio) - } - }; - - let provider = TracerProvider::builder() - .with_resource(resource) - .with_sampler(sampler) - .with_id_generator(RandomIdGenerator::default()) - .with_span_processor(exporter) - .build(); - - Ok(provider) -}