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