allframe_core/otel/
mod.rs

1//! OpenTelemetry automatic instrumentation
2//!
3//! This module provides automatic distributed tracing, metrics, and context
4//! propagation for AllFrame applications.
5//!
6//! # Features
7//!
8//! - `otel` - Basic tracing support with the `#[traced]` macro
9//! - `otel-otlp` - Full OpenTelemetry integration with OTLP export
10//!
11//! # Quick Start
12//!
13//! ```rust,ignore
14//! use allframe_core::otel::Observability;
15//!
16//! let _guard = Observability::builder("my-service")
17//!     .service_version(env!("CARGO_PKG_VERSION"))
18//!     .environment_from_env()
19//!     .otlp_endpoint_from_env()
20//!     .json_logging()
21//!     .log_level_from_env()
22//!     .build()?;
23//!
24//! // Guard keeps the subscriber active
25//! // When dropped, flushes pending spans
26//! ```
27
28mod builder;
29mod testing;
30
31// Re-export the traced macro
32// Legacy placeholder functions - kept for backwards compatibility
33// These will be removed in a future version
34use std::collections::HashMap;
35
36#[cfg(feature = "otel")]
37pub use allframe_macros::traced;
38// Re-export builder types
39pub use builder::{Observability, ObservabilityBuilder, ObservabilityError, ObservabilityGuard};
40// Re-export testing utilities
41pub use testing::{Histogram, MetricsRecorder, Span, SpanContext, SpanRecorder};
42
43/// Get the current span ID (placeholder - use tracing for real spans)
44#[deprecated(since = "0.2.0", note = "Use tracing::Span::current() instead")]
45pub fn current_span_id() -> String {
46    "span-placeholder".to_string()
47}
48
49/// Get the current trace ID (placeholder - use tracing for real traces)
50#[deprecated(since = "0.2.0", note = "Use tracing::Span::current() instead")]
51pub fn current_trace_id() -> String {
52    "trace-placeholder".to_string()
53}
54
55/// Start a new trace (placeholder)
56#[deprecated(since = "0.2.0", note = "Use tracing spans instead")]
57pub fn start_trace(_trace_id: &str) {
58    // Placeholder for backwards compatibility
59}
60
61/// Set baggage value (placeholder)
62#[deprecated(since = "0.2.0", note = "Use opentelemetry baggage API instead")]
63pub fn set_baggage(_key: &str, _value: &str) {
64    // Placeholder for backwards compatibility
65}
66
67/// Get baggage value (placeholder)
68#[deprecated(since = "0.2.0", note = "Use opentelemetry baggage API instead")]
69pub fn get_baggage(_key: &str) -> Option<String> {
70    None
71}
72
73/// Inject context into headers (placeholder)
74#[deprecated(since = "0.2.0", note = "Use opentelemetry propagator API instead")]
75pub fn inject_context(_context: &SpanContext) -> HashMap<String, String> {
76    let mut headers = HashMap::new();
77    headers.insert("traceparent".to_string(), "placeholder".to_string());
78    headers
79}
80
81/// Extract context from headers (placeholder)
82#[deprecated(since = "0.2.0", note = "Use opentelemetry propagator API instead")]
83pub fn extract_context(headers: &HashMap<String, String>) -> Option<SpanContext> {
84    headers.get("traceparent").map(|_| SpanContext {
85        trace_id: "extracted-trace".to_string(),
86        parent_span_id: "extracted-span".to_string(),
87        sampled: true,
88    })
89}
90
91/// Exporter type (legacy)
92#[derive(Debug, Clone, PartialEq)]
93#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
94pub enum ExporterType {
95    /// Stdout console exporter
96    Stdout,
97    /// Jaeger exporter
98    Jaeger {
99        /// Jaeger endpoint URL
100        endpoint: String,
101    },
102    /// OTLP exporter
103    Otlp {
104        /// OTLP endpoint URL
105        endpoint: String,
106    },
107}
108
109/// Configure exporter (placeholder)
110#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
111#[allow(deprecated)]
112pub fn configure_exporter(_exporter: ExporterType) {
113    // Placeholder for backwards compatibility
114}
115
116/// Configure batch export (placeholder)
117#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
118pub fn configure_batch_export(_batch_size: usize, _flush_interval_ms: u64) {
119    // Placeholder for backwards compatibility
120}
121
122/// Get export count (placeholder)
123#[deprecated(since = "0.2.0", note = "This function will be removed")]
124pub fn get_export_count() -> usize {
125    0
126}
127
128/// Configure sampling rate (placeholder)
129#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
130pub fn configure_sampling(_rate: f64) {
131    // Placeholder for backwards compatibility
132}
133
134/// Enable tracing (placeholder)
135#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder::build() instead")]
136pub fn enable_tracing() {
137    // Placeholder for backwards compatibility
138}
139
140/// Disable tracing (placeholder)
141#[deprecated(since = "0.2.0", note = "Drop the ObservabilityGuard instead")]
142pub fn disable_tracing() {
143    // Placeholder for backwards compatibility
144}
145
146/// OTel configuration (legacy)
147#[derive(Debug, Clone)]
148#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
149pub struct OtelConfig {
150    /// Service name
151    pub service_name: String,
152    /// Exporter type
153    pub exporter_type: String,
154    /// Sampling rate
155    pub sampling_rate: f64,
156    /// Batch size
157    pub batch_size: usize,
158}
159
160/// Configure from file (placeholder)
161#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
162pub async fn configure_from_file(_path: &str) -> Result<(), String> {
163    Ok(())
164}
165
166/// Get current config (placeholder)
167#[deprecated(since = "0.2.0", note = "Use ObservabilityBuilder instead")]
168#[allow(deprecated)]
169pub fn get_config() -> OtelConfig {
170    OtelConfig {
171        service_name: "allframe".to_string(),
172        exporter_type: "stdout".to_string(),
173        sampling_rate: 1.0,
174        batch_size: 512,
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_span_recorder() {
184        let recorder = SpanRecorder::new();
185
186        let span = Span {
187            span_id: "span-1".to_string(),
188            parent_span_id: None,
189            trace_id: "trace-1".to_string(),
190            name: "test".to_string(),
191            attributes: std::collections::HashMap::new(),
192            status: "ok".to_string(),
193            error_message: String::new(),
194            duration_ms: 100.0,
195            layer: String::new(),
196        };
197
198        recorder.record(span.clone());
199        let spans = recorder.spans();
200
201        assert_eq!(spans.len(), 1);
202        assert_eq!(spans[0].span_id, "span-1");
203    }
204
205    #[test]
206    fn test_histogram() {
207        let hist = Histogram::new(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
208
209        assert_eq!(hist.count(), 5);
210        assert_eq!(hist.sum(), 15.0);
211        assert_eq!(hist.p50(), 3.0);
212    }
213
214    #[test]
215    fn test_builder_creation() {
216        // Just verify we can create the builder - fields are tested in builder.rs
217        let _builder = ObservabilityBuilder::new("test-service");
218    }
219}