use crate::error::{OTelSdkError, OTelSdkResult};
use crate::resource::Resource;
use crate::trace::{SpanData, SpanExporter};
use crate::InMemoryExporterError;
use std::sync::{atomic::AtomicBool, Arc, Mutex};
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct InMemorySpanExporter {
spans: Arc<Mutex<Vec<SpanData>>>,
resource: Arc<Mutex<Resource>>,
should_reset_on_shutdown: bool,
shutdown_called: Arc<AtomicBool>,
}
impl Default for InMemorySpanExporter {
fn default() -> Self {
InMemorySpanExporterBuilder::new().build()
}
}
#[derive(Clone, Debug)]
pub struct InMemorySpanExporterBuilder {
reset_on_shutdown: bool,
}
impl Default for InMemorySpanExporterBuilder {
fn default() -> Self {
Self::new()
}
}
impl InMemorySpanExporterBuilder {
pub fn new() -> Self {
Self {
reset_on_shutdown: true,
}
}
pub fn build(&self) -> InMemorySpanExporter {
InMemorySpanExporter {
spans: Arc::new(Mutex::new(Vec::new())),
resource: Arc::new(Mutex::new(Resource::builder().build())),
should_reset_on_shutdown: self.reset_on_shutdown,
shutdown_called: Arc::new(AtomicBool::new(false)),
}
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) fn keep_records_on_shutdown(self) -> Self {
Self {
reset_on_shutdown: false,
}
}
}
impl InMemorySpanExporter {
pub fn is_shutdown_called(&self) -> bool {
self.shutdown_called
.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn get_finished_spans(&self) -> Result<Vec<SpanData>, InMemoryExporterError> {
let spans = self
.spans
.lock()
.map(|spans_guard| spans_guard.iter().cloned().collect())
.map_err(InMemoryExporterError::from)?;
Ok(spans)
}
pub fn reset(&self) {
let _ = self.spans.lock().map(|mut spans_guard| spans_guard.clear());
}
}
impl SpanExporter for InMemorySpanExporter {
async fn export(&self, batch: Vec<SpanData>) -> OTelSdkResult {
let result = self
.spans
.lock()
.map(|mut spans_guard| spans_guard.append(&mut batch.clone()))
.map_err(|err| OTelSdkError::InternalFailure(format!("Failed to lock spans: {err:?}")));
result
}
fn shutdown_with_timeout(&mut self, _timeout: Duration) -> OTelSdkResult {
self.shutdown_called
.store(true, std::sync::atomic::Ordering::Relaxed);
if self.should_reset_on_shutdown {
self.reset();
}
Ok(())
}
fn set_resource(&mut self, resource: &Resource) {
self.resource
.lock()
.map(|mut res_guard| *res_guard = resource.clone())
.expect("Resource lock poisoned");
}
}