Skip to content

Commit 64db454

Browse files
committed
layer: Fix race condition in on_event
1 parent c76edee commit 64db454

File tree

1 file changed

+54
-34
lines changed

1 file changed

+54
-34
lines changed

src/layer.rs

+54-34
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{OtelData, PreSampledTracer};
22
use once_cell::unsync;
33
use opentelemetry::{
4-
trace::{self as otel, noop, TraceContextExt},
4+
trace::{self as otel, noop, Status, TraceContextExt},
55
Context as OtelContext, Key, KeyValue, StringValue, Value,
66
};
77
use std::any::TypeId;
@@ -117,9 +117,15 @@ fn str_to_status(s: &str) -> otel::Status {
117117
}
118118
}
119119

120+
#[derive(Default)]
121+
struct ErrorState {
122+
status: Option<Status>,
123+
attributes: Option<Vec<KeyValue>>,
124+
}
125+
120126
struct SpanEventVisitor<'a, 'b> {
121127
event_builder: &'a mut otel::Event,
122-
span_builder: Option<&'b mut otel::SpanBuilder>,
128+
error_state: &'b mut Option<ErrorState>,
123129
sem_conv_config: SemConvConfig,
124130
}
125131

@@ -186,9 +192,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
186192
// In both cases, an event with an empty name and with an error attribute is created.
187193
"error" if self.event_builder.name.is_empty() => {
188194
if self.sem_conv_config.error_events_to_status {
189-
if let Some(span) = &mut self.span_builder {
190-
span.status = otel::Status::error(format!("{:?}", value));
191-
}
195+
self.error_state
196+
.get_or_insert_with(ErrorState::default)
197+
.status
198+
.replace(otel::Status::error(format!("{:?}", value)));
192199
}
193200
if self.sem_conv_config.error_events_to_exceptions {
194201
self.event_builder.name = EVENT_EXCEPTION_NAME.into();
@@ -225,9 +232,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
225232
// In both cases, an event with an empty name and with an error attribute is created.
226233
"error" if self.event_builder.name.is_empty() => {
227234
if self.sem_conv_config.error_events_to_status {
228-
if let Some(span) = &mut self.span_builder {
229-
span.status = otel::Status::error(format!("{:?}", value));
230-
}
235+
self.error_state
236+
.get_or_insert_with(ErrorState::default)
237+
.status
238+
.replace(otel::Status::error(format!("{:?}", value)));
231239
}
232240
if self.sem_conv_config.error_events_to_exceptions {
233241
self.event_builder.name = EVENT_EXCEPTION_NAME.into();
@@ -288,25 +296,27 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
288296
}
289297

290298
if self.sem_conv_config.error_records_to_exceptions {
291-
if let Some(span) = &mut self.span_builder {
292-
if let Some(attrs) = span.attributes.as_mut() {
293-
attrs.push(KeyValue::new(
294-
FIELD_EXCEPTION_MESSAGE,
295-
Value::String(error_msg.clone().into()),
296-
));
299+
let attributes = self
300+
.error_state
301+
.get_or_insert_with(ErrorState::default)
302+
.attributes
303+
.get_or_insert_with(Vec::new);
297304

298-
// NOTE: This is actually not the stacktrace of the exception. This is
299-
// the "source chain". It represents the heirarchy of errors from the
300-
// app level to the lowest level such as IO. It does not represent all
301-
// of the callsites in the code that led to the error happening.
302-
// `std::error::Error::backtrace` is a nightly-only API and cannot be
303-
// used here until the feature is stabilized.
304-
attrs.push(KeyValue::new(
305-
FIELD_EXCEPTION_STACKTRACE,
306-
Value::Array(chain.clone().into()),
307-
));
308-
}
309-
}
305+
attributes.push(KeyValue::new(
306+
FIELD_EXCEPTION_MESSAGE,
307+
Value::String(error_msg.clone().into()),
308+
));
309+
310+
// NOTE: This is actually not the stacktrace of the exception. This is
311+
// the "source chain". It represents the heirarchy of errors from the
312+
// app level to the lowest level such as IO. It does not represent all
313+
// of the callsites in the code that led to the error happening.
314+
// `std::error::Error::backtrace` is a nightly-only API and cannot be
315+
// used here until the feature is stabilized.
316+
attributes.push(KeyValue::new(
317+
FIELD_EXCEPTION_STACKTRACE,
318+
Value::Array(chain.clone().into()),
319+
));
310320
}
311321

312322
self.event_builder
@@ -1023,24 +1033,24 @@ where
10231033
#[cfg(not(feature = "tracing-log"))]
10241034
let target = target.string(meta.target());
10251035

1026-
// Move out extension data to not hold the extensions lock across the event.record() call, which could result in a deadlock
1027-
let mut otel_data = span.extensions_mut().remove::<OtelData>();
1028-
let span_builder = otel_data.as_mut().map(|data| &mut data.builder);
1029-
10301036
let mut otel_event = otel::Event::new(
10311037
String::new(),
10321038
crate::time::now(),
10331039
vec![Key::new("level").string(meta.level().as_str()), target],
10341040
0,
10351041
);
10361042

1043+
let mut error_state = None;
10371044
event.record(&mut SpanEventVisitor {
10381045
event_builder: &mut otel_event,
1039-
span_builder,
1046+
error_state: &mut error_state,
10401047
sem_conv_config: self.sem_conv_config,
10411048
});
10421049

1043-
if let Some(mut otel_data) = otel_data {
1050+
let mut extensions = span.extensions_mut();
1051+
let otel_data = extensions.get_mut::<OtelData>();
1052+
1053+
if let Some(otel_data) = otel_data {
10441054
let builder = &mut otel_data.builder;
10451055

10461056
if builder.status == otel::Status::Unset
@@ -1049,6 +1059,18 @@ where
10491059
builder.status = otel::Status::error("")
10501060
}
10511061

1062+
if let Some(error_state) = error_state {
1063+
if let Some(status) = error_state.status {
1064+
builder.status = status;
1065+
}
1066+
if let Some(attributes) = error_state.attributes {
1067+
builder
1068+
.attributes
1069+
.get_or_insert_with(Vec::new)
1070+
.extend(attributes);
1071+
}
1072+
}
1073+
10521074
if self.location {
10531075
#[cfg(not(feature = "tracing-log"))]
10541076
let normalized_meta: Option<tracing_core::Metadata<'_>> = None;
@@ -1085,8 +1107,6 @@ where
10851107
} else {
10861108
builder.events = Some(vec![otel_event]);
10871109
}
1088-
1089-
span.extensions_mut().replace(otel_data);
10901110
}
10911111
};
10921112
}

0 commit comments

Comments
 (0)