use opentelemetry::trace::TracerProvider as _;
use opentelemetry_sdk::error::OTelSdkResult;
use opentelemetry_sdk::trace::{SdkTracerProvider, SpanData, SpanExporter, Tracer};
use std::sync::{Arc, Mutex};
use tracing::{instrument, Subscriber};
use tracing_opentelemetry::layer;
use tracing_subscriber::prelude::*;
#[test]
fn map_error_event_to_status_description() {
let (_tracer, provider, exporter, subscriber) = test_tracer(Some(false), None);
#[instrument(err)]
fn test_fn() -> Result<(), &'static str> {
Err("test error")
}
tracing::subscriber::with_default(subscriber, || {
let _ = test_fn();
});
drop(provider);
let spans = exporter.0.lock().unwrap();
let span = spans.iter().find(|s| s.name == "test_fn").unwrap();
assert!(span.status == opentelemetry::trace::Status::error("test error"));
}
#[test]
fn error_mapping_disabled() {
let (_tracer, provider, exporter, subscriber) = test_tracer(Some(false), Some(false));
#[instrument(err)]
fn test_fn() -> Result<(), &'static str> {
Err("test error")
}
tracing::subscriber::with_default(subscriber, || {
let _ = test_fn();
});
drop(provider);
let spans = exporter.0.lock().unwrap();
let span = spans.iter().find(|s| s.name == "test_fn").unwrap();
assert!(span.status == opentelemetry::trace::Status::error(""));
let exception_event = span.events.iter().any(|e| e.name == "exception");
assert!(!exception_event);
}
#[test]
fn transform_error_event_to_exception_event() {
let (_tracer, provider, exporter, subscriber) = test_tracer(None, Some(false));
#[instrument(err)]
fn test_fn() -> Result<(), &'static str> {
Err("test error")
}
tracing::subscriber::with_default(subscriber, || {
let _ = test_fn();
});
drop(provider);
let spans = exporter.0.lock().unwrap();
let span = spans.iter().find(|s| s.name == "test_fn").unwrap();
let exception_event = span.events.iter().find(|e| e.name == "exception").unwrap();
let exception_attribute = exception_event
.attributes
.iter()
.find(|a| a.key.as_str() == "exception.message")
.unwrap();
assert!(exception_attribute.value.as_str() == "test error");
}
fn test_tracer(
error_event_exceptions: Option<bool>,
error_event_status: Option<bool>,
) -> (Tracer, SdkTracerProvider, TestExporter, impl Subscriber) {
let exporter = TestExporter::default();
let provider = SdkTracerProvider::builder()
.with_simple_exporter(exporter.clone())
.build();
let tracer = provider.tracer("test");
let mut layer = layer().with_tracer(tracer.clone());
if let Some(error_event_exceptions) = error_event_exceptions {
layer = layer.with_error_events_to_exceptions(error_event_exceptions)
}
if let Some(error_event_status) = error_event_status {
layer = layer.with_error_events_to_status(error_event_status)
}
let subscriber = tracing_subscriber::registry().with(layer);
(tracer, provider, exporter, subscriber)
}
#[derive(Clone, Default, Debug)]
struct TestExporter(Arc<Mutex<Vec<SpanData>>>);
impl SpanExporter for TestExporter {
async fn export(&self, mut batch: Vec<SpanData>) -> OTelSdkResult {
let spans = self.0.clone();
if let Ok(mut inner) = spans.lock() {
inner.append(&mut batch);
}
Ok(())
}
}