lc_callbacks/tracing/
backend.rs1use std::sync::Mutex;
2
3#[cfg(feature = "opentelemetry")]
4use std::collections::HashMap;
5
6use super::span::{build_tree, TraceSpan};
7use super::TracingBackend;
8
9pub struct InMemoryTracingBackend {
15 spans: Mutex<Vec<TraceSpan>>,
16}
17
18impl InMemoryTracingBackend {
19 pub fn new() -> Self {
21 Self {
22 spans: Mutex::new(Vec::new()),
23 }
24 }
25
26 pub fn spans(&self) -> Vec<TraceSpan> {
28 self.spans.lock().unwrap_or_else(|e| e.into_inner()).clone()
29 }
30
31 pub fn clear(&self) {
33 self.spans.lock().unwrap_or_else(|e| e.into_inner()).clear();
34 }
35
36 pub fn trace_tree(&self, root_id: &str) -> Option<super::TraceNode> {
40 let spans = self.spans.lock().unwrap_or_else(|e| e.into_inner());
41 let root = spans.iter().find(|s| s.id == root_id)?;
42 Some(build_tree(root, &spans))
43 }
44}
45
46impl Default for InMemoryTracingBackend {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl TracingBackend for InMemoryTracingBackend {
53 fn start_span(&self, span: &TraceSpan) {
54 self.spans
55 .lock()
56 .unwrap_or_else(|e| e.into_inner())
57 .push(span.clone());
58 }
59
60 fn end_span(&self, span: &TraceSpan) {
61 let mut spans = self.spans.lock().unwrap_or_else(|e| e.into_inner());
62 if let Some(existing) = spans.iter_mut().find(|s| s.id == span.id) {
63 *existing = span.clone();
64 }
65 }
66
67 fn flush(&self) {
68 }
70}
71
72pub struct ConsoleTracingBackend;
78
79impl TracingBackend for ConsoleTracingBackend {
80 fn start_span(&self, span: &TraceSpan) {
81 println!("[TRACE START] {} ({})", span.name, span.kind);
82 }
83
84 fn end_span(&self, span: &TraceSpan) {
85 let latency = span.latency_ms.unwrap_or(0);
86 let status_str = match &span.status {
87 super::SpanStatus::Ok => "OK".to_string(),
88 super::SpanStatus::Error(e) => format!("ERROR: {}", e),
89 };
90 println!(
91 "[TRACE END] {} latency={}ms status={}",
92 span.name, latency, status_str
93 );
94 }
95
96 fn flush(&self) {}
97}
98
99#[cfg(feature = "opentelemetry")]
107pub struct OtelTracingBackend {
108 tracer: opentelemetry::global::BoxedTracer,
109 spans: Mutex<HashMap<String, opentelemetry::global::BoxedSpan>>,
111}
112
113#[cfg(feature = "opentelemetry")]
114impl OtelTracingBackend {
115 pub fn new(tracer: opentelemetry::global::BoxedTracer) -> Self {
117 Self {
118 tracer,
119 spans: Mutex::new(HashMap::new()),
120 }
121 }
122
123 pub fn from_global(name: &str) -> Self {
125 Self::new(opentelemetry::global::tracer(name.to_string()))
126 }
127}
128
129#[cfg(feature = "opentelemetry")]
130impl TracingBackend for OtelTracingBackend {
131 fn start_span(&self, span: &TraceSpan) {
132 use opentelemetry::trace::Tracer as OtelTracer;
133 let otel_span = OtelTracer::start(&self.tracer, span.name.clone());
134 self.spans
135 .lock()
136 .unwrap_or_else(|e| e.into_inner())
137 .insert(span.id.clone(), otel_span);
138 }
139
140 fn end_span(&self, span: &TraceSpan) {
141 use opentelemetry::trace::Span as OtelSpan;
142 if let Some(mut s) = self
143 .spans
144 .lock()
145 .unwrap_or_else(|e| e.into_inner())
146 .remove(&span.id)
147 {
148 OtelSpan::end(&mut s);
149 }
150 }
151
152 fn flush(&self) {}
153}