Skip to content

Commit bda9f4d

Browse files
committed
chore: update SpanRef context access based on feedback
1 parent f62c2bf commit bda9f4d

File tree

6 files changed

+120
-168
lines changed

6 files changed

+120
-168
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ opentelemetry_sdk = { version = "0.31.0", default-features = false, features = [
2929
] }
3030
tracing = { version = "0.1.35", default-features = false, features = ["std"] }
3131
tracing-core = "0.1.28"
32-
tracing-subscriber = { version = "0.3.0", default-features = false, features = [
32+
# The 0.3.22 version contains the fix for on_register_dispatch in Layer
33+
tracing-subscriber = { version = "0.3.22", default-features = false, features = [
3334
"registry",
3435
"std",
3536
] }

examples/otel_context.rs

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,23 @@
44
use opentelemetry::trace::{TraceContextExt, TracerProvider as _};
55
use opentelemetry_sdk::trace::SdkTracerProvider;
66
use opentelemetry_stdout as stdout;
7-
use std::sync::{Arc, Mutex, RwLock};
7+
use std::sync::{Arc, Mutex, OnceLock};
88
use tracing::{debug, info, span, warn, Subscriber};
99
use tracing::{dispatcher::WeakDispatch, level_filters::LevelFilter, Dispatch};
10-
use tracing_opentelemetry::{layer, OpenTelemetryContext};
10+
use tracing_opentelemetry::{get_otel_context, layer};
1111
use tracing_subscriber::layer::Context;
1212
use tracing_subscriber::prelude::*;
1313
use tracing_subscriber::registry::LookupSpan;
1414
use tracing_subscriber::Layer;
1515

1616
/// A custom layer that demonstrates how to use OpenTelemetryContext
1717
/// to extract OpenTelemetry contexts from span extensions.
18-
#[derive(Clone)]
18+
#[derive(Clone, Default)]
1919
struct SpanAnalysisLayer {
2020
/// Store span analysis results for demonstration
2121
analysis_results: Arc<Mutex<Vec<SpanAnalysis>>>,
2222
/// Weak reference to the dispatcher for context extraction
23-
dispatch: Arc<RwLock<Option<WeakDispatch>>>,
23+
dispatch: Arc<OnceLock<WeakDispatch>>,
2424
}
2525

2626
#[derive(Debug, Clone)]
@@ -32,13 +32,6 @@ struct SpanAnalysis {
3232
}
3333

3434
impl SpanAnalysisLayer {
35-
fn new() -> Self {
36-
Self {
37-
analysis_results: Arc::new(Mutex::new(Vec::new())),
38-
dispatch: Arc::new(RwLock::new(None)),
39-
}
40-
}
41-
4235
fn get_analysis_results(&self) -> Vec<SpanAnalysis> {
4336
self.analysis_results.lock().unwrap().clone()
4437
}
@@ -68,46 +61,23 @@ impl SpanAnalysisLayer {
6861
}
6962
}
7063
}
71-
72-
fn get_weak_dispatch(&self, get_default: bool) -> Option<WeakDispatch> {
73-
let read_guard = self.dispatch.read().unwrap();
74-
match read_guard.as_ref() {
75-
Some(weak_dispatch) => Some(weak_dispatch.clone()),
76-
// Note: This workaround is needed until https://github.com/tokio-rs/tracing/pull/3379
77-
// is merged and released. It should really be handled in on_register_dispatch
78-
None => {
79-
if !get_default {
80-
None
81-
} else {
82-
drop(read_guard);
83-
let mut dispatch = self.dispatch.write().unwrap();
84-
let weak_dispatch = Dispatch::default().downgrade();
85-
*dispatch = Some(weak_dispatch.clone());
86-
Some(weak_dispatch)
87-
}
88-
}
89-
}
90-
}
9164
}
9265

9366
impl<S> Layer<S> for SpanAnalysisLayer
9467
where
9568
S: Subscriber + for<'span> LookupSpan<'span>,
9669
{
70+
fn on_register_dispatch(&self, subscriber: &Dispatch) {
71+
let _ = self.dispatch.set(subscriber.downgrade());
72+
}
73+
9774
fn on_new_span(
9875
&self,
9976
attrs: &tracing::span::Attributes<'_>,
10077
id: &tracing::span::Id,
10178
ctx: Context<'_, S>,
10279
) {
103-
// Get the weak dispatch reference.
104-
//
105-
// Note: We can't use the Dispatch::default() workaround described above here since this
106-
// method is called from inside a dispatcher::get_default block, and such calls can't be
107-
// nested so we would get the global dispatcher instead, which can't downcast to the right
108-
// types when extracting the OpenTelemetry context. This also means that we will miss
109-
// analyzing the first span that is created 🤷🏼‍♂️
110-
let Some(weak_dispatch) = self.get_weak_dispatch(false) else {
80+
let Some(weak_dispatch) = self.dispatch.get() else {
11181
return;
11282
};
11383

@@ -116,8 +86,7 @@ where
11686
// This is the key functionality: using OpenTelemetryContext
11787
// to extract the OpenTelemetry context from span extensions
11888
let mut extensions = span_ref.extensions_mut();
119-
if let Some(otel_context) =
120-
OpenTelemetryContext::context(&mut extensions, &weak_dispatch.upgrade())
89+
if let Some(otel_context) = get_otel_context(&mut extensions, &weak_dispatch.upgrade())
12190
{
12291
self.analyze_span_context(attrs.metadata().name(), &otel_context);
12392
} else {
@@ -130,11 +99,11 @@ where
13099
}
131100

132101
fn on_enter(&self, id: &tracing::span::Id, ctx: Context<'_, S>) {
133-
if let Some(weak_dispatch) = self.get_weak_dispatch(true) {
102+
if let Some(weak_dispatch) = self.dispatch.get() {
134103
if let Some(span_ref) = ctx.span(id) {
135104
let mut extensions = span_ref.extensions_mut();
136105
if let Some(otel_context) =
137-
OpenTelemetryContext::context(&mut extensions, &weak_dispatch.upgrade())
106+
get_otel_context(&mut extensions, &weak_dispatch.upgrade())
138107
{
139108
let span = otel_context.span();
140109
let span_context = span.span_context();
@@ -149,13 +118,6 @@ where
149118
}
150119
}
151120
}
152-
153-
fn on_register_dispatch(&self, subscriber: &tracing::Dispatch) {
154-
// Note: This does not work for Layer until https://github.com/tokio-rs/tracing/pull/3379
155-
// is merged and released, since `on_register_dispatch` is never called.
156-
let mut dispatch = self.dispatch.write().unwrap();
157-
*dispatch = Some(subscriber.clone().downgrade());
158-
}
159121
}
160122

161123
fn setup_tracing() -> (impl Subscriber, SdkTracerProvider, SpanAnalysisLayer) {
@@ -166,7 +128,7 @@ fn setup_tracing() -> (impl Subscriber, SdkTracerProvider, SpanAnalysisLayer) {
166128
let tracer = provider.tracer("span_ref_ext_example");
167129

168130
// Create our custom analysis layer
169-
let analysis_layer = SpanAnalysisLayer::new();
131+
let analysis_layer = SpanAnalysisLayer::default();
170132

171133
// Build the subscriber with multiple layers:
172134
// 1. OpenTelemetry layer for trace export

src/layer.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ pub(crate) struct WithContext {
115115
/// and had its SpanBuilder consumed - and then provides access to the OtelData associated with it.
116116
///
117117
#[allow(clippy::type_complexity)]
118-
pub(crate) with_activated_context_extensions:
119-
fn(&tracing::Dispatch, &mut ExtensionsMut<'_>, f: &mut dyn FnMut(&mut OtelData)),
118+
pub(crate) with_activated_otel_context:
119+
fn(&tracing::Dispatch, &mut ExtensionsMut<'_>, f: &mut dyn FnMut(&OtelContext)),
120120
}
121121

122122
impl WithContext {
@@ -152,13 +152,13 @@ impl WithContext {
152152
/// and had its SpanBuilder consumed - and then provides access to the OtelData associated with it.
153153
///
154154
#[allow(clippy::type_complexity)]
155-
pub(crate) fn with_activated_context_extensions(
155+
pub(crate) fn with_activated_otel_context(
156156
&self,
157157
dispatch: &tracing::Dispatch,
158158
extensions: &mut ExtensionsMut<'_>,
159-
mut f: impl FnMut(&mut OtelData),
159+
mut f: impl FnMut(&OtelContext),
160160
) {
161-
(self.with_activated_context_extensions)(dispatch, extensions, &mut f)
161+
(self.with_activated_otel_context)(dispatch, extensions, &mut f)
162162
}
163163
}
164164

@@ -652,7 +652,7 @@ where
652652
with_context: WithContext {
653653
with_context: Self::get_context,
654654
with_activated_context: Self::get_activated_context,
655-
with_activated_context_extensions: Self::get_activated_context_extensions,
655+
with_activated_otel_context: Self::get_activated_otel_context,
656656
},
657657
_registry: marker::PhantomData,
658658
}
@@ -708,8 +708,8 @@ where
708708
with_context: WithContext {
709709
with_context: OpenTelemetryLayer::<S, Tracer>::get_context,
710710
with_activated_context: OpenTelemetryLayer::<S, Tracer>::get_activated_context,
711-
with_activated_context_extensions:
712-
OpenTelemetryLayer::<S, Tracer>::get_activated_context_extensions,
711+
with_activated_otel_context:
712+
OpenTelemetryLayer::<S, Tracer>::get_activated_otel_context,
713713
},
714714
_registry: self._registry,
715715
// cannot use ``..self` here due to different generics
@@ -984,6 +984,22 @@ where
984984
Self::get_activated_context_extensions(dispatch, &mut extensions, f)
985985
}
986986

987+
fn get_activated_otel_context(
988+
dispatch: &tracing::Dispatch,
989+
extensions: &mut ExtensionsMut<'_>,
990+
f: &mut dyn FnMut(&OtelContext),
991+
) {
992+
Self::get_activated_context_extensions(
993+
dispatch,
994+
extensions,
995+
&mut |otel_data: &mut OtelData| {
996+
if let OtelDataState::Context { current_cx } = &otel_data.state {
997+
f(&current_cx)
998+
}
999+
},
1000+
);
1001+
}
1002+
9871003
fn get_activated_context_extensions(
9881004
dispatch: &tracing::Dispatch,
9891005
extensions: &mut ExtensionsMut<'_>,

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ mod metrics;
115115

116116
/// Implementation of the trace::Layer as a source of OpenTelemetry data.
117117
mod layer;
118-
/// OpenTelemetryContext which enables OpenTelemetry context extraction from span extensions.
118+
/// Function which enables OpenTelemetry context extraction from span extensions.
119119
mod otel_context;
120120
/// Span extension which enables OpenTelemetry context management.
121121
mod span_ext;
@@ -129,7 +129,7 @@ pub use layer::{layer, FilteredOpenTelemetryLayer, OpenTelemetryLayer};
129129
#[cfg(feature = "metrics")]
130130
pub use metrics::MetricsLayer;
131131
use opentelemetry::trace::TraceContextExt as _;
132-
pub use otel_context::OpenTelemetryContext;
132+
pub use otel_context::get_otel_context;
133133
pub use span_ext::{OpenTelemetrySpanExt, SetParentError};
134134

135135
/// Per-span OpenTelemetry data tracked by this crate.

src/otel_context.rs

Lines changed: 58 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{layer::WithContext, OtelData, OtelDataState};
1+
use crate::layer::WithContext;
22
use tracing::Dispatch;
33
use tracing_subscriber::registry::ExtensionsMut;
44

@@ -8,65 +8,61 @@ use tracing_subscriber::registry::ExtensionsMut;
88
/// [`ExtensionsMut`]: tracing_subscriber::registry::ExtensionsMut
99
/// [OpenTelemetry]: https://opentelemetry.io
1010
/// [`Context`]: opentelemetry::Context
11-
pub struct OpenTelemetryContext {}
12-
impl OpenTelemetryContext {
13-
/// Extracts the OpenTelemetry [`Context`] associated with this span extensions.
14-
///
15-
/// This method retrieves the OpenTelemetry context data that has been stored
16-
/// for the span by the OpenTelemetry layer. The context includes the span's
17-
/// OpenTelemetry span context, which contains trace ID, span ID, and other
18-
/// trace-related metadata.
19-
///
20-
/// [`Context`]: opentelemetry::Context
21-
///
22-
/// # Examples
23-
///
24-
/// ```rust
25-
/// use tracing_opentelemetry::OpenTelemetryContext;
26-
/// use tracing::dispatcher::WeakDispatch;
27-
/// use tracing_subscriber::registry::LookupSpan;
28-
/// use opentelemetry::trace::TraceContextExt;
29-
///
30-
/// fn do_things_with_otel_context<'a, D>(
31-
/// span_ref: &tracing_subscriber::registry::SpanRef<'a, D>,
32-
/// weak_dispatch: &WeakDispatch
33-
/// ) where
34-
/// D: LookupSpan<'a>,
35-
/// {
36-
/// if let Some(otel_context) = OpenTelemetryContext::context(&mut span_ref.extensions_mut(), &weak_dispatch.upgrade()) {
37-
/// // Process the extracted context
38-
/// let span = otel_context.span();
39-
/// let span_context = span.span_context();
40-
/// if span_context.is_valid() {
41-
/// // Handle the valid context...
42-
/// }
43-
/// }
44-
/// }
45-
/// ```
46-
///
47-
/// # Use Cases
48-
///
49-
/// - When working with multiple subscriber configurations
50-
/// - When implementing advanced tracing middleware that manages multiple dispatches
51-
pub fn context(
52-
extensions: &mut ExtensionsMut<'_>,
53-
dispatch: &Option<Dispatch>,
54-
) -> Option<opentelemetry::Context> {
55-
dispatch.as_ref().and_then(|dispatch| {
56-
let mut cx = None;
57-
if let Some(get_context) = dispatch.downcast_ref::<WithContext>() {
58-
// If our span hasn't been built, we should build it and get the context in one call
59-
get_context.with_activated_context_extensions(
60-
dispatch,
61-
extensions,
62-
|data: &mut OtelData| {
63-
if let OtelDataState::Context { current_cx } = &data.state {
64-
cx = Some(current_cx.clone());
65-
}
66-
},
67-
);
68-
}
69-
cx
70-
})
71-
}
11+
///
12+
/// Extracts the OpenTelemetry [`Context`] associated with this span extensions.
13+
///
14+
/// This method retrieves the OpenTelemetry context data that has been stored
15+
/// for the span by the OpenTelemetry layer. The context includes the span's
16+
/// OpenTelemetry span context, which contains trace ID, span ID, and other
17+
/// trace-related metadata.
18+
///
19+
/// [`Context`]: opentelemetry::Context
20+
///
21+
/// # Examples
22+
///
23+
/// ```rust
24+
/// use tracing_opentelemetry::get_otel_context;
25+
/// use tracing::dispatcher::WeakDispatch;
26+
/// use tracing_subscriber::registry::LookupSpan;
27+
/// use opentelemetry::trace::TraceContextExt;
28+
///
29+
/// fn do_things_with_otel_context<'a, D>(
30+
/// span_ref: &tracing_subscriber::registry::SpanRef<'a, D>,
31+
/// weak_dispatch: &WeakDispatch
32+
/// ) where
33+
/// D: LookupSpan<'a>,
34+
/// {
35+
/// if let Some(otel_context) = get_otel_context(&mut span_ref.extensions_mut(), &weak_dispatch.upgrade()) {
36+
/// // Process the extracted context
37+
/// let span = otel_context.span();
38+
/// let span_context = span.span_context();
39+
/// if span_context.is_valid() {
40+
/// // Handle the valid context...
41+
/// }
42+
/// }
43+
/// }
44+
/// ```
45+
///
46+
/// # Use Cases
47+
///
48+
/// - When working with multiple subscriber configurations
49+
/// - When implementing advanced tracing middleware that manages multiple dispatches
50+
pub fn get_otel_context(
51+
extensions: &mut ExtensionsMut<'_>,
52+
dispatch: &Option<Dispatch>,
53+
) -> Option<opentelemetry::Context> {
54+
dispatch.as_ref().and_then(|dispatch| {
55+
let mut cx = None;
56+
if let Some(get_context) = dispatch.downcast_ref::<WithContext>() {
57+
// If our span hasn't been built, we should build it and get the context in one call
58+
get_context.with_activated_otel_context(
59+
dispatch,
60+
extensions,
61+
|current_cx: &opentelemetry::Context| {
62+
cx = Some(current_cx.clone());
63+
},
64+
);
65+
}
66+
cx
67+
})
7268
}

0 commit comments

Comments
 (0)