Skip to content

Commit 9c78908

Browse files
committed
adding support for setting the status of the span through an extension
1 parent 515fe00 commit 9c78908

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

src/span_ext.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::layer::WithContext;
2-
use opentelemetry::{trace::SpanContext, Context, Key, KeyValue, Value};
2+
use opentelemetry::{trace::SpanContext, trace::Status, Context, Key, KeyValue, Value};
33

44
/// Utility functions to allow tracing [`Span`]s to accept and return
55
/// [OpenTelemetry] [`Context`]s.
@@ -133,6 +133,25 @@ pub trait OpenTelemetrySpanExt {
133133
/// app_root.set_attribute("http.request.header.x_forwarded_for", "example");
134134
/// ```
135135
fn set_attribute(&self, key: impl Into<Key>, value: impl Into<Value>);
136+
137+
/// Sets an OpenTelemetry status for this span.
138+
/// This is useful for setting the status of a span that was created by a library that does not declare
139+
/// the otel.status_code field of the span in advance.
140+
/// # Examples
141+
///
142+
/// ```rust
143+
/// use opentelemetry::trace::Status;
144+
/// use tracing_opentelemetry::OpenTelemetrySpanExt;
145+
/// use tracing::Span;
146+
///
147+
/// /// // Generate a tracing span as usual
148+
/// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
149+
///
150+
/// // Set the Status of the span to `Status::Ok`.
151+
/// app_root.set_status(Status::Ok);
152+
///
153+
/// ```
154+
fn set_status(&self, status: Status);
136155
}
137156

138157
impl OpenTelemetrySpanExt for tracing::Span {
@@ -207,4 +226,15 @@ impl OpenTelemetrySpanExt for tracing::Span {
207226
}
208227
});
209228
}
229+
230+
fn set_status(&self, status: Status) {
231+
self.with_subscriber(move |(id, subscriber)| {
232+
let mut status = Some(status);
233+
if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
234+
get_context.with_context(subscriber, id, move |builder, _| {
235+
builder.builder.status = status.take().unwrap();
236+
});
237+
}
238+
});
239+
}
210240
}

tests/span_ext.rs

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use futures_util::future::BoxFuture;
2+
use opentelemetry::trace::{Status, TracerProvider as _};
3+
use opentelemetry_sdk::{
4+
export::trace::{ExportResult, SpanData, SpanExporter},
5+
trace::{Tracer, TracerProvider},
6+
};
7+
use std::sync::{Arc, Mutex};
8+
use tracing::level_filters::LevelFilter;
9+
use tracing::Subscriber;
10+
use tracing_opentelemetry::{layer, OpenTelemetrySpanExt};
11+
use tracing_subscriber::prelude::*;
12+
13+
#[derive(Clone, Default, Debug)]
14+
struct TestExporter(Arc<Mutex<Vec<SpanData>>>);
15+
16+
impl SpanExporter for TestExporter {
17+
fn export(&mut self, mut batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
18+
let spans = self.0.clone();
19+
Box::pin(async move {
20+
if let Ok(mut inner) = spans.lock() {
21+
inner.append(&mut batch);
22+
}
23+
Ok(())
24+
})
25+
}
26+
}
27+
28+
fn test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Subscriber) {
29+
let exporter = TestExporter::default();
30+
let provider = TracerProvider::builder()
31+
.with_simple_exporter(exporter.clone())
32+
.build();
33+
let tracer = provider.tracer("test");
34+
35+
let subscriber = tracing_subscriber::registry()
36+
.with(
37+
layer()
38+
.with_tracer(tracer.clone())
39+
.with_filter(LevelFilter::DEBUG),
40+
)
41+
.with(tracing_subscriber::fmt::layer().with_filter(LevelFilter::TRACE));
42+
43+
(tracer, provider, exporter, subscriber)
44+
}
45+
46+
#[test]
47+
fn set_status_ok() {
48+
let root_span = set_status_helper(Status::Ok);
49+
assert_eq!(Status::Ok, root_span.status);
50+
}
51+
52+
#[test]
53+
fn set_status_error() {
54+
let expected_error = Status::Error { description: std::borrow::Cow::Borrowed("Elon put in too much fuel in his rocket!") };
55+
let root_span = set_status_helper(expected_error.clone());
56+
assert_eq!(expected_error, root_span.status);
57+
}
58+
59+
fn set_status_helper(status: Status) -> SpanData
60+
{
61+
let (_tracer, provider, exporter, subscriber) = test_tracer();
62+
63+
tracing::subscriber::with_default(subscriber, || {
64+
let root = tracing::debug_span!("root").entered();
65+
66+
root.set_status(status);
67+
});
68+
69+
drop(provider); // flush all spans
70+
let spans = exporter.0.lock().unwrap();
71+
72+
assert_eq!(spans.len(), 1);
73+
74+
spans.iter().find(|s| s.name == "root").unwrap().clone()
75+
}

0 commit comments

Comments
 (0)