Skip to content

Commit b776b1e

Browse files
authored
feat: add InMemorySpanExporter (#1216)
1 parent b6e91a9 commit b776b1e

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

opentelemetry-sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Add unit/doc tests for MeterProvider #1220
1212
- Changed dependency from `opentelemetry_api` to `opentelemetry` as the latter
1313
is now the API crate. [#1226](https://github.com/open-telemetry/opentelemetry-rust/pull/1226)
14+
- Add in memory span exporter [#1216](https://github.com/open-telemetry/opentelemetry-rust/pull/1216)
1415

1516
## v0.20.0
1617

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use crate::export::trace::{ExportResult, SpanData, SpanExporter};
2+
use futures_util::future::BoxFuture;
3+
use opentelemetry::trace::{TraceError, TraceResult};
4+
use std::sync::{Arc, Mutex};
5+
6+
/// An in-memory span exporter that stores span data in memory.
7+
///
8+
/// This exporter is useful for testing and debugging purposes. It stores
9+
/// metric data in a `Vec<SpanData>`. Metrics can be retrieved
10+
/// using the `get_finished_spans` method.
11+
/// # Example
12+
/// ```
13+
///# use opentelemetry::trace::{SpanKind, TraceContextExt};
14+
///# use opentelemetry::{global, trace::Tracer, Context};
15+
///# use opentelemetry_sdk::propagation::TraceContextPropagator;
16+
///# use opentelemetry_sdk::runtime;
17+
///# use opentelemetry_sdk::testing::trace::InMemorySpanExporterBuilder;
18+
///# use opentelemetry_sdk::trace::{BatchSpanProcessor, TracerProvider};
19+
///
20+
///# #[tokio::main]
21+
///# async fn main() {
22+
/// let exporter = InMemorySpanExporterBuilder::new().build();
23+
/// let provider = TracerProvider::builder()
24+
/// .with_span_processor(BatchSpanProcessor::builder(exporter.clone(), runtime::Tokio).build())
25+
/// .build();
26+
///
27+
/// global::set_tracer_provider(provider.clone());
28+
///
29+
/// let tracer = global::tracer("example/in_memory_exporter");
30+
/// let span = tracer
31+
/// .span_builder("say hello")
32+
/// .with_kind(SpanKind::Server)
33+
/// .start(&tracer);
34+
///
35+
/// let cx = Context::current_with_span(span);
36+
/// cx.span().add_event("handling this...", Vec::new());
37+
/// cx.span().end();
38+
///
39+
/// let results = provider.force_flush();
40+
/// for result in results {
41+
/// if let Err(e) = result {
42+
/// println!("{:?}", e)
43+
/// }
44+
/// }
45+
/// let spans = exporter.get_finished_spans().unwrap();
46+
/// for span in spans {
47+
/// println!("{:?}", span)
48+
/// }
49+
///# }
50+
/// ```
51+
#[derive(Clone, Debug)]
52+
pub struct InMemorySpanExporter {
53+
spans: Arc<Mutex<Vec<SpanData>>>,
54+
}
55+
56+
impl Default for InMemorySpanExporter {
57+
fn default() -> Self {
58+
InMemorySpanExporterBuilder::new().build()
59+
}
60+
}
61+
62+
/// Builder for [`InMemorySpanExporter`].
63+
/// # Example
64+
/// ```
65+
///# use opentelemetry_sdk::testing::trace::InMemorySpanExporterBuilder;
66+
///
67+
/// let exporter = InMemorySpanExporterBuilder::new().build();
68+
/// ```
69+
#[derive(Clone, Debug)]
70+
pub struct InMemorySpanExporterBuilder {}
71+
72+
impl Default for InMemorySpanExporterBuilder {
73+
fn default() -> Self {
74+
Self::new()
75+
}
76+
}
77+
78+
impl InMemorySpanExporterBuilder {
79+
/// Creates a new instance of the `InMemorySpanExporterBuilder`.
80+
pub fn new() -> Self {
81+
Self {}
82+
}
83+
84+
/// Creates a new instance of the `InMemorySpanExporter`.
85+
pub fn build(&self) -> InMemorySpanExporter {
86+
InMemorySpanExporter {
87+
spans: Arc::new(Mutex::new(Vec::new())),
88+
}
89+
}
90+
}
91+
92+
impl InMemorySpanExporter {
93+
/// Returns the finished span as a vector of `SpanData`.
94+
///
95+
/// # Errors
96+
///
97+
/// Returns a `TraceError` if the internal lock cannot be acquired.
98+
///
99+
/// # Example
100+
///
101+
/// ```
102+
/// # use opentelemetry_sdk::testing::trace::InMemorySpanExporter;
103+
///
104+
/// let exporter = InMemorySpanExporter::default();
105+
/// let finished_spans = exporter.get_finished_spans().unwrap();
106+
/// ```
107+
pub fn get_finished_spans(&self) -> TraceResult<Vec<SpanData>> {
108+
self.spans
109+
.lock()
110+
.map(|spans_guard| spans_guard.iter().cloned().collect())
111+
.map_err(TraceError::from)
112+
}
113+
114+
/// Clears the internal storage of finished spans.
115+
///
116+
/// # Example
117+
///
118+
/// ```
119+
/// # use opentelemetry_sdk::testing::trace::InMemorySpanExporter;
120+
///
121+
/// let exporter = InMemorySpanExporter::default();
122+
/// exporter.reset();
123+
/// ```
124+
pub fn reset(&self) {
125+
let _ = self.spans.lock().map(|mut spans_guard| spans_guard.clear());
126+
}
127+
}
128+
129+
impl SpanExporter for InMemorySpanExporter {
130+
fn export(&mut self, batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
131+
if let Err(err) = self
132+
.spans
133+
.lock()
134+
.map(|mut spans_guard| spans_guard.append(&mut batch.clone()))
135+
.map_err(TraceError::from)
136+
{
137+
return Box::pin(std::future::ready(Err(Into::into(err))));
138+
}
139+
Box::pin(std::future::ready(Ok(())))
140+
}
141+
142+
fn shutdown(&mut self) {
143+
self.reset()
144+
}
145+
}

opentelemetry-sdk/src/testing/trace.rs renamed to opentelemetry-sdk/src/testing/trace/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
pub use in_memory_exporter::{InMemorySpanExporter, InMemorySpanExporterBuilder};
2+
3+
mod in_memory_exporter;
4+
15
use crate::{
26
export::{
37
trace::{ExportResult, SpanData, SpanExporter},

opentelemetry/src/trace/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ pub use self::{
184184
};
185185
use crate::{ExportError, KeyValue};
186186
use std::collections::hash_map::RandomState;
187+
use std::sync::PoisonError;
187188

188189
/// re-export OrderMap to mitigate breaking change
189190
pub type OrderMap<K, V, S = RandomState> = crate::order_map::OrderMap<K, V, S>;
@@ -229,6 +230,12 @@ impl From<&'static str> for TraceError {
229230
}
230231
}
231232

233+
impl<T> From<PoisonError<T>> for TraceError {
234+
fn from(err: PoisonError<T>) -> Self {
235+
TraceError::Other(err.to_string().into())
236+
}
237+
}
238+
232239
/// Wrap type for string
233240
#[derive(Error, Debug)]
234241
#[error("{0}")]

0 commit comments

Comments
 (0)