Skip to main content

cognee_observability/
lib.rs

1//! # cognee-observability
2//!
3//! OpenTelemetry tracing pipeline for Cognee-Rust. Bridges the existing
4//! `tracing` instrumentation (60+ `#[tracing::instrument]` sites across
5//! the workspace) into an OTLP exporter so spans flow to a collector.
6//!
7//! This crate is the single home for OTEL configuration, OTLP exporter
8//! construction, the `tracing-opentelemetry` bridge layer, and the RAII
9//! [`TelemetryGuard`] that flushes pending spans on drop.
10//!
11//! ## Activation
12//!
13//! Tracing is activated when **either** of:
14//! - `Settings.cognee_tracing_enabled == true`
15//!   (env: `COGNEE_TRACING_ENABLED=true`)
16//! - `Settings.otel_exporter_otlp_endpoint` is non-empty
17//!   (env: `OTEL_EXPORTER_OTLP_ENDPOINT=https://...`)
18//!
19//! Either path triggers the same provider setup. This mirrors Python's
20//! `is_tracing_enabled()` lazy-init semantics in
21//! `cognee/modules/observability/trace_context.py`.
22//!
23//! ## Programmatic init
24//!
25//! Drive [`init_telemetry`] from a [`SettingsView`] implementation. The
26//! [`EnvSettingsView`] adapter reads the standard env vars directly, so
27//! callers that don't want to depend on `cognee-lib` can still bring up
28//! the pipeline:
29//!
30//! ```ignore
31//! use cognee_observability::{init_telemetry, EnvSettingsView, TelemetryGuard};
32//! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Registry};
33//!
34//! let settings = EnvSettingsView::from_env();
35//!
36//! let (otel_layer, guard): (_, TelemetryGuard) =
37//!     init_telemetry::<Registry>(&settings).expect("telemetry init");
38//!
39//! Registry::default()
40//!     .with(otel_layer)
41//!     .with(tracing_subscriber::EnvFilter::from_default_env())
42//!     .with(tracing_subscriber::fmt::layer())
43//!     .init();
44//!
45//! // Hold `guard` for the lifetime of your process; dropping it
46//! // calls `force_flush()` then `shutdown()` on the OTEL provider.
47//! drop(guard);
48//! ```
49//!
50//! Embedders that already use `cognee_lib::config::Settings` can pass it
51//! directly — `Settings` implements [`SettingsView`].
52//!
53//! ## Configuration
54//!
55//! See [`docs/observability/opentelemetry.md`](https://github.com/topoteretes/cognee-rs/blob/main/docs/observability/opentelemetry.md)
56//! for the full env-var reference and deployment recipes (Tempo, Honeycomb,
57//! Dash0, in-cluster Collector).
58//!
59//! ## Feature flags
60//!
61//! - `telemetry` (off by default) — pulls in `opentelemetry`,
62//!   `opentelemetry_sdk`, `opentelemetry-otlp`,
63//!   `opentelemetry-semantic-conventions`, `tracing-opentelemetry`,
64//!   plus `tonic` and `http` for gRPC metadata construction. When
65//!   enabled, [`init_telemetry`] builds a real `SdkTracerProvider`,
66//!   installs it globally, and returns a guard that flushes on drop.
67//!   When disabled, [`init_telemetry`] still compiles but returns an
68//!   identity tracing layer plus a noop guard, so embedders can call it
69//!   unconditionally.
70//!
71//! ## Feature-state contract
72//!
73//! [`init_telemetry`] returns `Ok((noop_layer, TelemetryGuard::noop()))`
74//! whenever the process is not configured to export spans — specifically
75//! when **either** (1) the `telemetry` cargo feature is **off** at compile
76//! time, **or** (2) [`is_tracing_enabled`] returns `false` at runtime
77//! (`COGNEE_TRACING_ENABLED` is not truthy and
78//! `OTEL_EXPORTER_OTLP_ENDPOINT` is empty). On both paths the returned
79//! layer is a boxed [`tracing_subscriber::layer::Identity`] that observes
80//! nothing, and the guard's `Drop` runs no code. [`TelemetryGuard::noop`]
81//! is publicly constructible for tests and embedders that want the same
82//! shape without going through [`init_telemetry`]. This mirrors parent
83//! [decision 6](../../../docs/telemetry/01-otel-otlp-export.md#design-decisions-locked)
84//! (implicit activation: an endpoint alone is enough to opt in).
85
86#![deny(missing_docs)]
87
88mod guard;
89mod headers;
90mod init;
91mod settings;
92
93#[cfg(feature = "telemetry")]
94mod error;
95
96pub use guard::TelemetryGuard;
97pub use headers::parse_otlp_headers;
98pub use init::{BoxedTelemetryLayer, already_instrumented, init_telemetry, is_tracing_enabled};
99pub use settings::{EnvSettingsView, SettingsView};
100
101#[cfg(feature = "telemetry")]
102pub use error::TelemetryInitError;
103
104/// Stub `TelemetryInitError` exposed when the `telemetry` feature is off
105/// so that the public signature of [`init_telemetry`] does not change
106/// shape between builds. The variant is unreachable in practice — the
107/// noop path always returns `Ok`.
108#[cfg(not(feature = "telemetry"))]
109#[derive(Debug, thiserror::Error)]
110pub enum TelemetryInitError {
111    /// Placeholder ensuring the enum is non-empty without the feature.
112    #[error("cognee-observability built without `telemetry` feature")]
113    FeatureDisabled,
114}