opentelemetry_spanprocessor_any/testing/
trace.rs

1use crate::{
2    sdk::export::{
3        trace::{ExportResult, SpanData, SpanExporter},
4        ExportError,
5    },
6    sdk::{
7        trace::{Config, EvictedHashMap, EvictedQueue},
8        InstrumentationLibrary,
9    },
10    trace::{Span, SpanContext, SpanId, SpanKind, StatusCode, TraceId},
11    KeyValue,
12};
13use async_trait::async_trait;
14use std::borrow::Cow;
15use std::fmt::{Display, Formatter};
16use std::sync::mpsc::{channel, Receiver, Sender};
17
18#[derive(Debug)]
19pub struct TestSpan(pub SpanContext);
20
21impl Span for TestSpan {
22    fn add_event_with_timestamp<T>(
23        &mut self,
24        _name: T,
25        _timestamp: std::time::SystemTime,
26        _attributes: Vec<KeyValue>,
27    ) where
28        T: Into<Cow<'static, str>>,
29    {
30    }
31    fn span_context(&self) -> &SpanContext {
32        &self.0
33    }
34    fn is_recording(&self) -> bool {
35        false
36    }
37    fn set_attribute(&mut self, _attribute: KeyValue) {}
38    fn set_status(&mut self, _code: StatusCode, _message: String) {}
39    fn update_name<T>(&mut self, _new_name: T)
40    where
41        T: Into<Cow<'static, str>>,
42    {
43    }
44    fn end_with_timestamp(&mut self, _timestamp: std::time::SystemTime) {}
45}
46
47pub fn new_test_export_span_data() -> SpanData {
48    let config = Config::default();
49    SpanData {
50        span_context: SpanContext::empty_context(),
51        parent_span_id: SpanId::INVALID,
52        span_kind: SpanKind::Internal,
53        name: "opentelemetry".into(),
54        start_time: crate::time::now(),
55        end_time: crate::time::now(),
56        attributes: EvictedHashMap::new(config.span_limits.max_attributes_per_span, 0),
57        events: EvictedQueue::new(config.span_limits.max_events_per_span),
58        links: EvictedQueue::new(config.span_limits.max_links_per_span),
59        status_code: StatusCode::Unset,
60        status_message: "".into(),
61        resource: config.resource,
62        instrumentation_lib: InstrumentationLibrary::default(),
63    }
64}
65
66#[derive(Debug)]
67pub struct TestSpanExporter {
68    tx_export: Sender<SpanData>,
69    tx_shutdown: Sender<()>,
70}
71
72#[async_trait]
73impl SpanExporter for TestSpanExporter {
74    async fn export(&mut self, batch: Vec<SpanData>) -> ExportResult {
75        for span_data in batch {
76            self.tx_export
77                .send(span_data)
78                .map_err::<TestExportError, _>(Into::into)?;
79        }
80        Ok(())
81    }
82
83    fn shutdown(&mut self) {
84        self.tx_shutdown.send(()).unwrap();
85    }
86}
87
88pub fn new_test_exporter() -> (TestSpanExporter, Receiver<SpanData>, Receiver<()>) {
89    let (tx_export, rx_export) = channel();
90    let (tx_shutdown, rx_shutdown) = channel();
91    let exporter = TestSpanExporter {
92        tx_export,
93        tx_shutdown,
94    };
95    (exporter, rx_export, rx_shutdown)
96}
97
98#[derive(Debug)]
99pub struct TokioSpanExporter {
100    tx_export: tokio::sync::mpsc::UnboundedSender<SpanData>,
101    tx_shutdown: tokio::sync::mpsc::UnboundedSender<()>,
102}
103
104#[async_trait]
105impl SpanExporter for TokioSpanExporter {
106    async fn export(&mut self, batch: Vec<SpanData>) -> ExportResult {
107        for span_data in batch {
108            self.tx_export
109                .send(span_data)
110                .map_err::<TestExportError, _>(Into::into)?;
111        }
112        Ok(())
113    }
114
115    fn shutdown(&mut self) {
116        self.tx_shutdown.send(()).unwrap();
117    }
118}
119
120pub fn new_tokio_test_exporter() -> (
121    TokioSpanExporter,
122    tokio::sync::mpsc::UnboundedReceiver<SpanData>,
123    tokio::sync::mpsc::UnboundedReceiver<()>,
124) {
125    let (tx_export, rx_export) = tokio::sync::mpsc::unbounded_channel();
126    let (tx_shutdown, rx_shutdown) = tokio::sync::mpsc::unbounded_channel();
127    let exporter = TokioSpanExporter {
128        tx_export,
129        tx_shutdown,
130    };
131    (exporter, rx_export, rx_shutdown)
132}
133
134#[derive(Debug)]
135pub struct TestExportError(String);
136
137impl std::error::Error for TestExportError {}
138
139impl ExportError for TestExportError {
140    fn exporter_name(&self) -> &'static str {
141        "test"
142    }
143}
144
145impl Display for TestExportError {
146    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
147        write!(f, "{}", self.0)
148    }
149}
150
151impl<T> From<tokio::sync::mpsc::error::SendError<T>> for TestExportError {
152    fn from(err: tokio::sync::mpsc::error::SendError<T>) -> Self {
153        TestExportError(err.to_string())
154    }
155}
156
157impl<T> From<std::sync::mpsc::SendError<T>> for TestExportError {
158    fn from(err: std::sync::mpsc::SendError<T>) -> Self {
159        TestExportError(err.to_string())
160    }
161}
162
163// Helper to create trace ids for testing
164impl TraceId {
165    pub fn from_u128(num: u128) -> Self {
166        TraceId(num)
167    }
168}
169
170// Helper to create span ids for testing
171impl SpanId {
172    pub fn from_u64(num: u64) -> Self {
173        SpanId(num)
174    }
175}