Skip to content

Commit 7074891

Browse files
authored
fix(otel): fix span and trace ids for distributed tracing (#801)
* fix(otel): fix span and trace ids for distributed tracing * formatting * remove prints * changelog * doc * dot * default calls new
1 parent 69ce481 commit 7074891

File tree

4 files changed

+42
-18
lines changed

4 files changed

+42
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ An OpenTelemetry integration has been released. Please refer to the changelog en
5959
- build: add `sentry-opentelemetry` to workspace (#789) by @lcian
6060
- docs: update docs including OTEL and other integrations (#790) by @lcian
6161
- fix(otel): fix doctests (#794) by @lcian
62+
- fix(otel): fix span and trace ids for distributed tracing (#801) by @lcian
6263

6364
## 0.37.0
6465

sentry-core/src/performance.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ impl TransactionContext {
180180
headers: I,
181181
) -> Self {
182182
parse_headers(headers)
183-
.map(|sentry_trace| Self::continue_from_sentry_trace(name, op, &sentry_trace))
183+
.map(|sentry_trace| Self::continue_from_sentry_trace(name, op, &sentry_trace, None))
184184
.unwrap_or_else(|| Self {
185185
name: name.into(),
186186
op: op.into(),
@@ -192,17 +192,23 @@ impl TransactionContext {
192192
})
193193
}
194194

195-
/// Creates a new Transaction Context based on the provided distributed tracing data.
196-
pub fn continue_from_sentry_trace(name: &str, op: &str, sentry_trace: &SentryTrace) -> Self {
195+
/// Creates a new Transaction Context based on the provided distributed tracing data,
196+
/// optionally creating the `TransactionContext` with the provided `span_id`.
197+
pub fn continue_from_sentry_trace(
198+
name: &str,
199+
op: &str,
200+
sentry_trace: &SentryTrace,
201+
span_id: Option<SpanId>,
202+
) -> Self {
197203
let (trace_id, parent_span_id, sampled) =
198204
(sentry_trace.0, Some(sentry_trace.1), sentry_trace.2);
199205
Self {
200206
name: name.into(),
201207
op: op.into(),
202208
trace_id,
203209
parent_span_id,
204-
span_id: Default::default(),
205210
sampled,
211+
span_id: span_id.unwrap_or_default(),
206212
custom: None,
207213
}
208214
}

sentry-opentelemetry/src/processor.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! setting up both is provided in the [crate-level documentation](../).
1010
1111
use std::collections::HashMap;
12-
use std::sync::{Arc, Mutex};
12+
use std::sync::{Arc, LazyLock, Mutex};
1313
use std::time::SystemTime;
1414

1515
use opentelemetry::global::ObjectSafeSpan;
@@ -31,12 +31,12 @@ use crate::converters::{
3131
/// to track OTEL spans across start/end calls.
3232
type SpanMap = Arc<Mutex<HashMap<sentry_core::protocol::SpanId, TransactionOrSpan>>>;
3333

34+
static SPAN_MAP: LazyLock<SpanMap> = LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
35+
3436
/// An OpenTelemetry SpanProcessor that converts OTEL spans to Sentry spans/transactions and sends
3537
/// them to Sentry.
3638
#[derive(Debug, Clone)]
37-
pub struct SentrySpanProcessor {
38-
span_map: SpanMap,
39-
}
39+
pub struct SentrySpanProcessor {}
4040

4141
impl SentrySpanProcessor {
4242
/// Creates a new `SentrySpanProcessor`.
@@ -46,10 +46,23 @@ impl SentrySpanProcessor {
4646
// This works as long as all Sentry spans/transactions are managed exclusively through OTEL APIs.
4747
scope.add_event_processor(|mut event| {
4848
get_active_span(|otel_span| {
49-
let (span_id, trace_id) = (
50-
convert_span_id(&otel_span.span_context().span_id()),
51-
convert_trace_id(&otel_span.span_context().trace_id()),
52-
);
49+
let span_map = SPAN_MAP.lock().unwrap();
50+
51+
let Some(sentry_span) =
52+
span_map.get(&convert_span_id(&otel_span.span_context().span_id()))
53+
else {
54+
return;
55+
};
56+
57+
let (span_id, trace_id) = match sentry_span {
58+
TransactionOrSpan::Transaction(transaction) => (
59+
transaction.get_trace_context().span_id,
60+
transaction.get_trace_context().trace_id,
61+
),
62+
TransactionOrSpan::Span(span) => {
63+
(span.get_span_id(), span.get_trace_context().trace_id)
64+
}
65+
};
5366

5467
if let Some(sentry_core::protocol::Context::Trace(trace_context)) =
5568
event.contexts.get_mut("trace")
@@ -71,9 +84,7 @@ impl SentrySpanProcessor {
7184
Some(event)
7285
});
7386
});
74-
Self {
75-
span_map: Default::default(),
76-
}
87+
Self {}
7788
}
7889
}
7990

@@ -89,7 +100,7 @@ impl SpanProcessor for SentrySpanProcessor {
89100
let span_id = span.span_context().span_id();
90101
let trace_id = span.span_context().trace_id();
91102

92-
let mut span_map = self.span_map.lock().unwrap();
103+
let mut span_map = SPAN_MAP.lock().unwrap();
93104

94105
let mut span_description = String::new();
95106
let mut span_op = String::new();
@@ -123,6 +134,7 @@ impl SpanProcessor for SentrySpanProcessor {
123134
span_description,
124135
span_op,
125136
sentry_trace,
137+
Some(convert_span_id(&span_id)),
126138
)
127139
} else {
128140
// start a new trace
@@ -146,7 +158,7 @@ impl SpanProcessor for SentrySpanProcessor {
146158
fn on_end(&self, data: SpanData) {
147159
let span_id = data.span_context.span_id();
148160

149-
let mut span_map = self.span_map.lock().unwrap();
161+
let mut span_map = SPAN_MAP.lock().unwrap();
150162

151163
let Some(sentry_span) = span_map.remove(&convert_span_id(&span_id)) else {
152164
return;

sentry-opentelemetry/tests/creates_distributed_trace.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ fn test_creates_distributed_trace() {
3232
});
3333

3434
// Now simulate the second service receiving the headers and continuing the trace
35+
let tracer_provider = SdkTracerProvider::builder()
36+
.with_span_processor(SentrySpanProcessor::new())
37+
.build();
38+
let tracer = tracer_provider.tracer("test_2".to_string());
39+
let propagator = SentryPropagator::new();
3540
let second_service_ctx =
36-
propagator.extract_with_context(&Context::current(), &TestExtractor(&headers));
41+
propagator.extract_with_context(&Context::new(), &TestExtractor(&headers));
3742

3843
// Create a second service span that continues the trace
3944
// We need to use start_with_context here to connect with the previous context

0 commit comments

Comments
 (0)