Skip to main content

mem7_telemetry/
lib.rs

1use mem7_config::TelemetryConfig;
2use mem7_error::Result;
3use opentelemetry::trace::TracerProvider as _;
4use opentelemetry_otlp::{SpanExporter, WithExportConfig};
5use opentelemetry_sdk::{Resource, trace::SdkTracerProvider};
6use tracing_opentelemetry::OpenTelemetryLayer;
7use tracing_subscriber::{
8    EnvFilter, Layer, Registry, layer::SubscriberExt, util::SubscriberInitExt,
9};
10
11static PROVIDER: std::sync::OnceLock<SdkTracerProvider> = std::sync::OnceLock::new();
12
13/// Initialize OpenTelemetry tracing with OTLP export.
14///
15/// Sets up a global `tracing` subscriber that:
16/// 1. Emits human-readable logs to stderr (controlled by `RUST_LOG`).
17/// 2. Exports spans as OpenTelemetry traces via OTLP/gRPC.
18///
19/// Call [`shutdown`] before process exit to flush pending spans.
20pub fn init(config: &TelemetryConfig) -> Result<()> {
21    let exporter = SpanExporter::builder()
22        .with_tonic()
23        .with_endpoint(&config.otlp_endpoint)
24        .build()
25        .map_err(|e| mem7_error::Mem7Error::Config(format!("OTLP exporter: {e}")))?;
26
27    let resource = Resource::builder()
28        .with_service_name(config.service_name.clone())
29        .build();
30
31    let provider = SdkTracerProvider::builder()
32        .with_resource(resource)
33        .with_batch_exporter(exporter)
34        .build();
35
36    let tracer = provider.tracer("mem7");
37
38    PROVIDER
39        .set(provider)
40        .map_err(|_| mem7_error::Mem7Error::Config("telemetry already initialized".into()))?;
41
42    let otel_layer = OpenTelemetryLayer::new(tracer);
43
44    let fmt_layer = tracing_subscriber::fmt::layer()
45        .with_target(true)
46        .with_level(true)
47        .with_filter(EnvFilter::from_default_env());
48
49    Registry::default().with(fmt_layer).with(otel_layer).init();
50
51    Ok(())
52}
53
54/// Flush pending spans and shut down the OTLP exporter.
55pub fn shutdown() {
56    if let Some(provider) = PROVIDER.get()
57        && let Err(e) = provider.shutdown()
58    {
59        tracing::warn!(error = %e, "failed to shutdown telemetry provider");
60    }
61}