claude_agent/observability/
mod.rs

1//! Observability module for Claude Agent SDK.
2//!
3//! Provides structured tracing, metrics collection, and telemetry integration.
4//!
5//! ## Features
6//!
7//! - **Built-in metrics**: Counter, Gauge, Histogram for local tracking
8//! - **Structured spans**: Tracing integration for request/tool execution
9//! - **OpenTelemetry** (optional): Export to OTLP-compatible backends
10//!
11//! ## OpenTelemetry Integration
12//!
13//! Enable the `otel` feature to export traces and metrics:
14//!
15//! ```toml
16//! claude-agent = { version = "0.2", features = ["otel"] }
17//! ```
18//!
19//! ```rust,ignore
20//! use claude_agent::observability::{OtelConfig, OtelRuntime};
21//!
22//! let config = OtelConfig::new("my-agent")
23//!     .with_endpoint("http://localhost:4317");
24//!
25//! let runtime = OtelRuntime::init(&config)?;
26//! // ... use the agent ...
27//! runtime.shutdown(); // Flush before exit
28//! ```
29
30mod metrics;
31#[cfg(feature = "otel")]
32mod otel;
33mod spans;
34
35pub use metrics::{AgentMetrics, Counter, Gauge, Histogram, MetricsConfig, MetricsRegistry};
36#[cfg(feature = "otel")]
37pub use otel::{
38    OtelConfig, OtelError, OtelRuntime, SERVICE_NAME_DEFAULT, init_tracing_subscriber, semantic,
39};
40pub use spans::{ApiCallSpan, SpanContext, TracingConfig, TracingLevel};
41
42use std::sync::Arc;
43
44/// Observability configuration combining tracing and metrics.
45#[derive(Clone, Default)]
46pub struct ObservabilityConfig {
47    pub tracing: TracingConfig,
48    pub metrics: MetricsConfig,
49    #[cfg(feature = "otel")]
50    pub otel: Option<OtelConfig>,
51}
52
53impl ObservabilityConfig {
54    pub fn new() -> Self {
55        Self::default()
56    }
57
58    pub fn with_tracing(mut self, config: TracingConfig) -> Self {
59        self.tracing = config;
60        self
61    }
62
63    pub fn with_metrics(mut self, config: MetricsConfig) -> Self {
64        self.metrics = config;
65        self
66    }
67
68    pub fn with_service_name(mut self, name: impl Into<String>) -> Self {
69        self.tracing.service_name = Some(name.into());
70        self
71    }
72
73    #[cfg(feature = "otel")]
74    pub fn with_otel(mut self, config: OtelConfig) -> Self {
75        self.otel = Some(config);
76        self
77    }
78
79    pub fn build_registry(&self) -> Arc<MetricsRegistry> {
80        #[cfg(feature = "otel")]
81        if let Some(ref otel_config) = self.otel {
82            return Arc::new(MetricsRegistry::with_otel(&self.metrics, otel_config));
83        }
84
85        Arc::new(MetricsRegistry::new(&self.metrics))
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_observability_config() {
95        let config = ObservabilityConfig::new()
96            .with_service_name("test-agent")
97            .with_metrics(MetricsConfig::default());
98
99        assert_eq!(config.tracing.service_name, Some("test-agent".to_string()));
100    }
101
102    #[cfg(feature = "otel")]
103    #[test]
104    fn test_observability_with_otel() {
105        let config = ObservabilityConfig::new()
106            .with_service_name("test-agent")
107            .with_otel(OtelConfig::new("test-agent"));
108
109        assert!(config.otel.is_some());
110    }
111}