1
1
use crate :: { OtelData , PreSampledTracer } ;
2
2
use once_cell:: unsync;
3
3
use opentelemetry:: {
4
- trace:: { self as otel, noop, TraceContextExt } ,
4
+ trace:: { self as otel, noop, Status , TraceContextExt } ,
5
5
Context as OtelContext , Key , KeyValue , StringValue , Value ,
6
6
} ;
7
7
use std:: any:: TypeId ;
@@ -117,9 +117,15 @@ fn str_to_status(s: &str) -> otel::Status {
117
117
}
118
118
}
119
119
120
+ #[ derive( Default ) ]
121
+ struct ErrorState {
122
+ status : Option < Status > ,
123
+ attributes : Option < Vec < KeyValue > > ,
124
+ }
125
+
120
126
struct SpanEventVisitor < ' a , ' b > {
121
127
event_builder : & ' a mut otel:: Event ,
122
- span_builder : Option < & ' b mut otel :: SpanBuilder > ,
128
+ error_state : & ' b mut Option < ErrorState > ,
123
129
sem_conv_config : SemConvConfig ,
124
130
}
125
131
@@ -186,9 +192,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
186
192
// In both cases, an event with an empty name and with an error attribute is created.
187
193
"error" if self . event_builder . name . is_empty ( ) => {
188
194
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) ) ) ;
192
199
}
193
200
if self . sem_conv_config . error_events_to_exceptions {
194
201
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
@@ -225,9 +232,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
225
232
// In both cases, an event with an empty name and with an error attribute is created.
226
233
"error" if self . event_builder . name . is_empty ( ) => {
227
234
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) ) ) ;
231
239
}
232
240
if self . sem_conv_config . error_events_to_exceptions {
233
241
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
@@ -288,25 +296,27 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
288
296
}
289
297
290
298
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) ;
297
304
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
+ ) ) ;
310
320
}
311
321
312
322
self . event_builder
@@ -1023,24 +1033,24 @@ where
1023
1033
#[ cfg( not( feature = "tracing-log" ) ) ]
1024
1034
let target = target. string ( meta. target ( ) ) ;
1025
1035
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
-
1030
1036
let mut otel_event = otel:: Event :: new (
1031
1037
String :: new ( ) ,
1032
1038
crate :: time:: now ( ) ,
1033
1039
vec ! [ Key :: new( "level" ) . string( meta. level( ) . as_str( ) ) , target] ,
1034
1040
0 ,
1035
1041
) ;
1036
1042
1043
+ let mut error_state = None ;
1037
1044
event. record ( & mut SpanEventVisitor {
1038
1045
event_builder : & mut otel_event,
1039
- span_builder ,
1046
+ error_state : & mut error_state ,
1040
1047
sem_conv_config : self . sem_conv_config ,
1041
1048
} ) ;
1042
1049
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 {
1044
1054
let builder = & mut otel_data. builder ;
1045
1055
1046
1056
if builder. status == otel:: Status :: Unset
@@ -1049,6 +1059,18 @@ where
1049
1059
builder. status = otel:: Status :: error ( "" )
1050
1060
}
1051
1061
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
+
1052
1074
if self . location {
1053
1075
#[ cfg( not( feature = "tracing-log" ) ) ]
1054
1076
let normalized_meta: Option < tracing_core:: Metadata < ' _ > > = None ;
@@ -1085,8 +1107,6 @@ where
1085
1107
} else {
1086
1108
builder. events = Some ( vec ! [ otel_event] ) ;
1087
1109
}
1088
-
1089
- span. extensions_mut ( ) . replace ( otel_data) ;
1090
1110
}
1091
1111
} ;
1092
1112
}
0 commit comments