Skip to content

Commit 7e86613

Browse files
committed
chore: address review comments
1 parent 0921962 commit 7e86613

File tree

4 files changed

+58
-30
lines changed

4 files changed

+58
-30
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ tracing-subscriber = { version = "0.3.0", default-features = false, features = [
2929
tracing-log = { version = "0.2.0", default-features = false, optional = true }
3030
rustversion = "1.0.9"
3131
smallvec = { version = "1.0", optional = true }
32+
thiserror = { version = "2", default-features = false }
3233

3334
# Fix minimal-versions
3435
lazy_static = { version = "1.0.2", optional = true }

src/span_ext.rs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use opentelemetry::{
66
Context, Key, KeyValue, Value,
77
};
88
use std::{borrow::Cow, time::SystemTime};
9+
use thiserror::Error;
910

1011
/// Utility functions to allow tracing [`Span`]s to accept and return
1112
/// [OpenTelemetry] [`Context`]s.
@@ -17,6 +18,13 @@ pub trait OpenTelemetrySpanExt {
1718
/// Associates `self` with a given OpenTelemetry trace, using the provided
1819
/// parent [`Context`].
1920
///
21+
/// This method exists primarily to make it possible to inject a _distributed_ incoming
22+
/// context, e.g. span IDs, etc.
23+
///
24+
/// A span's parent should only be set _once_, for the purpose described above.
25+
/// Additionally, once a span has been fully built - and the SpanBuilder has been
26+
/// consumed - the parent _cannot_ be mutated.
27+
///
2028
/// This method provides error handling for cases where the span context
2129
/// cannot be set, such as when the OpenTelemetry layer is not present
2230
/// or when the span has already been started.
@@ -50,7 +58,7 @@ pub trait OpenTelemetrySpanExt {
5058
/// // Or if the current span has been created elsewhere:
5159
/// let _ = Span::current().set_parent(parent_context);
5260
/// ```
53-
fn set_parent(&self, cx: Context) -> Result<(), &'static str>;
61+
fn set_parent(&self, cx: Context) -> Result<(), SetParentError>;
5462

5563
/// Associates `self` with a given OpenTelemetry trace, using the provided
5664
/// followed span [`SpanContext`].
@@ -218,29 +226,31 @@ pub trait OpenTelemetrySpanExt {
218226
);
219227
}
220228

229+
#[derive(Error, Debug)]
230+
pub enum SetParentError {
231+
#[error("OpenTelemetry layer not found")]
232+
LayerNotFound,
233+
#[error("Span has already been started, cannot set parent")]
234+
AlreadyStarted,
235+
#[error("Span disabled")]
236+
SpanDisabled,
237+
}
238+
221239
impl OpenTelemetrySpanExt for tracing::Span {
222-
///
223-
/// Allows us to set the parent context of this span. This method exists primarily to allow
224-
/// us to pull in distributed_ incoming context - e.g. span IDs, etc - that have been read
225-
/// into an existing context.
226-
///
227-
/// A span's parent should only be set _once_, for the purpose described above.
228-
/// Additionally, once a span has been fully built - and the SpanBuilder has been consumed -
229-
/// the parent _cannot_ be mutated.
230-
///
231-
fn set_parent(&self, cx: Context) -> Result<(), &'static str> {
240+
fn set_parent(&self, cx: Context) -> Result<(), SetParentError> {
232241
let mut cx = Some(cx);
233-
let mut result = Ok(());
234-
let result_ref = &mut result;
235242

236243
self.with_subscriber(move |(id, subscriber)| {
237244
let Some(get_context) = subscriber.downcast_ref::<WithContext>() else {
238-
*result_ref = Err("OpenTelemetry layer not found");
239-
return;
245+
return Err(SetParentError::LayerNotFound);
240246
};
247+
248+
let mut result = Ok(());
249+
let result_ref = &mut result;
241250
// Set the parent OTel for the current span
242251
get_context.with_context(subscriber, id, move |data| {
243252
let Some(new_cx) = cx.take() else {
253+
*result_ref = Err(SetParentError::AlreadyStarted);
244254
return;
245255
};
246256
// Create a new context with the new parent but preserve our span.
@@ -254,13 +264,13 @@ impl OpenTelemetrySpanExt for tracing::Span {
254264
*parent_cx = new_cx;
255265
}
256266
OtelDataState::Context { .. } => {
257-
*result_ref = Err("Span has already been started, cannot set parent");
267+
*result_ref = Err(SetParentError::AlreadyStarted);
258268
}
259269
}
260270
});
261-
});
262-
263-
result
271+
result
272+
})
273+
.unwrap_or(Err(SetParentError::SpanDisabled))
264274
}
265275

266276
fn add_link(&self, cx: SpanContext) {
@@ -324,8 +334,7 @@ impl OpenTelemetrySpanExt for tracing::Span {
324334
let Some(get_context) = subscriber.downcast_ref::<WithContext>() else {
325335
return;
326336
};
327-
let mut key = Some(key.into());
328-
let mut value = Some(value.into());
337+
let mut key_value = Some(KeyValue::new(key.into(), value.into()));
329338
get_context.with_context(subscriber, id, move |data| {
330339
match &mut data.state {
331340
OtelDataState::Builder { builder, .. } => {
@@ -336,12 +345,11 @@ impl OpenTelemetrySpanExt for tracing::Span {
336345
.attributes
337346
.as_mut()
338347
.unwrap()
339-
.push(KeyValue::new(key.take().unwrap(), value.take().unwrap()));
348+
.push(key_value.take().unwrap());
340349
}
341350
OtelDataState::Context { current_cx } => {
342351
let span = current_cx.span();
343-
let key_value = KeyValue::new(key.take().unwrap(), value.take().unwrap());
344-
span.set_attribute(key_value);
352+
span.set_attribute(key_value.take().unwrap());
345353
}
346354
};
347355
});

src/stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl<T: Debug> IdValueStack<T> {
3838
}
3939

4040
#[cfg(test)]
41-
pub(super) fn len(&self) -> usize {
41+
fn len(&self) -> usize {
4242
self.stack.len()
4343
}
4444
}

tests/trace_state_propagation.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn trace_with_assigned_otel_context() {
3737

3838
tracing::subscriber::with_default(subscriber, || {
3939
let child = tracing::debug_span!("child");
40-
let _ = child.set_parent(cx);
40+
let _ = child.set_parent(cx).unwrap();
4141
});
4242

4343
drop(provider); // flush all spans
@@ -122,10 +122,9 @@ fn sampling_decision_respects_new_parent() {
122122
tracing::subscriber::with_default(subscriber, || {
123123
let child = tracing::debug_span!("child");
124124

125-
// Observation: if you force the _child_ to materialize before the parent, e.g.,
126-
// if you swap these two lines - bad things will happen, and we shouldn't support
127-
// this.
128-
let _ = child.set_parent(Context::current_with_span(root_span));
125+
let _ = child
126+
.set_parent(Context::current_with_span(root_span))
127+
.unwrap();
129128
child.context(); // force a sampling decision
130129
});
131130

@@ -145,6 +144,26 @@ fn sampling_decision_respects_new_parent() {
145144
);
146145
}
147146

147+
#[test]
148+
fn set_parent_fails_after_span_entered() {
149+
let (_tracer, provider, _exporter, subscriber) = test_tracer();
150+
let (cx, _, _, _) = build_sampled_context();
151+
152+
tracing::subscriber::with_default(subscriber, || {
153+
let span = tracing::debug_span!("test_span");
154+
let _guard = span.enter(); // Enter the span first
155+
156+
// Attempting to set parent after entering should fail
157+
let result = span.set_parent(cx);
158+
assert!(
159+
result.is_err(),
160+
"set_parent should fail after span is entered"
161+
);
162+
});
163+
164+
drop(provider);
165+
}
166+
148167
fn assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext) {
149168
assert_eq!(sc_a.trace_id(), sc_b.trace_id());
150169
assert_eq!(sc_a.trace_state(), sc_b.trace_state());

0 commit comments

Comments
 (0)