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, SpanBuilder , SpanKind , Status , TraceContextExt } ,
5
5
Context as OtelContext , Key , KeyValue , StringValue , Value ,
6
6
} ;
7
- use std:: any:: TypeId ;
8
7
use std:: fmt;
9
8
use std:: marker;
10
9
use std:: thread;
11
10
#[ cfg( not( target_arch = "wasm32" ) ) ]
12
11
use std:: time:: Instant ;
12
+ use std:: { any:: TypeId , borrow:: Cow } ;
13
13
use tracing_core:: span:: { self , Attributes , Id , Record } ;
14
14
use tracing_core:: { field, Event , Subscriber } ;
15
15
#[ cfg( feature = "tracing-log" ) ]
@@ -117,9 +117,45 @@ fn str_to_status(s: &str) -> otel::Status {
117
117
}
118
118
}
119
119
120
+ #[ derive( Default ) ]
121
+ struct SpanBuilderUpdates {
122
+ name : Option < Cow < ' static , str > > ,
123
+ span_kind : Option < SpanKind > ,
124
+ status : Option < Status > ,
125
+ attributes : Option < Vec < KeyValue > > ,
126
+ }
127
+
128
+ impl SpanBuilderUpdates {
129
+ fn update ( self , span_builder : & mut SpanBuilder ) {
130
+ let Self {
131
+ name,
132
+ span_kind,
133
+ status,
134
+ attributes,
135
+ } = self ;
136
+
137
+ if let Some ( name) = name {
138
+ span_builder. name = name;
139
+ }
140
+ if let Some ( span_kind) = span_kind {
141
+ span_builder. span_kind = Some ( span_kind) ;
142
+ }
143
+ if let Some ( status) = status {
144
+ span_builder. status = status;
145
+ }
146
+ if let Some ( attributes) = attributes {
147
+ if let Some ( builder_attributes) = & mut span_builder. attributes {
148
+ builder_attributes. extend ( attributes) ;
149
+ } else {
150
+ span_builder. attributes = Some ( attributes) ;
151
+ }
152
+ }
153
+ }
154
+ }
155
+
120
156
struct SpanEventVisitor < ' a , ' b > {
121
157
event_builder : & ' a mut otel:: Event ,
122
- span_builder : Option < & ' b mut otel :: SpanBuilder > ,
158
+ span_builder_updates : & ' b mut Option < SpanBuilderUpdates > ,
123
159
sem_conv_config : SemConvConfig ,
124
160
}
125
161
@@ -186,9 +222,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
186
222
// In both cases, an event with an empty name and with an error attribute is created.
187
223
"error" if self . event_builder . name . is_empty ( ) => {
188
224
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
- }
225
+ self . span_builder_updates
226
+ . get_or_insert_with ( SpanBuilderUpdates :: default)
227
+ . status
228
+ . replace ( otel:: Status :: error ( format ! ( "{:?}" , value) ) ) ;
192
229
}
193
230
if self . sem_conv_config . error_events_to_exceptions {
194
231
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
@@ -225,9 +262,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
225
262
// In both cases, an event with an empty name and with an error attribute is created.
226
263
"error" if self . event_builder . name . is_empty ( ) => {
227
264
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
- }
265
+ self . span_builder_updates
266
+ . get_or_insert_with ( SpanBuilderUpdates :: default)
267
+ . status
268
+ . replace ( otel:: Status :: error ( format ! ( "{:?}" , value) ) ) ;
231
269
}
232
270
if self . sem_conv_config . error_events_to_exceptions {
233
271
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
@@ -288,25 +326,27 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
288
326
}
289
327
290
328
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
- ) ) ;
329
+ let attributes = self
330
+ . span_builder_updates
331
+ . get_or_insert_with ( SpanBuilderUpdates :: default)
332
+ . attributes
333
+ . get_or_insert_with ( Vec :: new) ;
297
334
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
- }
335
+ attributes. push ( KeyValue :: new (
336
+ FIELD_EXCEPTION_MESSAGE ,
337
+ Value :: String ( error_msg. clone ( ) . into ( ) ) ,
338
+ ) ) ;
339
+
340
+ // NOTE: This is actually not the stacktrace of the exception. This is
341
+ // the "source chain". It represents the heirarchy of errors from the
342
+ // app level to the lowest level such as IO. It does not represent all
343
+ // of the callsites in the code that led to the error happening.
344
+ // `std::error::Error::backtrace` is a nightly-only API and cannot be
345
+ // used here until the feature is stabilized.
346
+ attributes. push ( KeyValue :: new (
347
+ FIELD_EXCEPTION_STACKTRACE ,
348
+ Value :: Array ( chain. clone ( ) . into ( ) ) ,
349
+ ) ) ;
310
350
}
311
351
312
352
self . event_builder
@@ -354,16 +394,16 @@ struct SemConvConfig {
354
394
}
355
395
356
396
struct SpanAttributeVisitor < ' a > {
357
- span_builder : & ' a mut otel :: SpanBuilder ,
397
+ span_builder_updates : & ' a mut SpanBuilderUpdates ,
358
398
sem_conv_config : SemConvConfig ,
359
399
}
360
400
361
401
impl < ' a > SpanAttributeVisitor < ' a > {
362
402
fn record ( & mut self , attribute : KeyValue ) {
363
- debug_assert ! ( self . span_builder . attributes . is_some ( ) ) ;
364
- if let Some ( v ) = self . span_builder . attributes . as_mut ( ) {
365
- v . push ( KeyValue :: new ( attribute . key , attribute . value ) ) ;
366
- }
403
+ self . span_builder_updates
404
+ . attributes
405
+ . get_or_insert_with ( Vec :: new)
406
+ . push ( KeyValue :: new ( attribute . key , attribute . value ) ) ;
367
407
}
368
408
}
369
409
@@ -394,11 +434,11 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
394
434
/// [`Span`]: opentelemetry::trace::Span
395
435
fn record_str ( & mut self , field : & field:: Field , value : & str ) {
396
436
match field. name ( ) {
397
- SPAN_NAME_FIELD => self . span_builder . name = value. to_string ( ) . into ( ) ,
398
- SPAN_KIND_FIELD => self . span_builder . span_kind = str_to_span_kind ( value) ,
399
- SPAN_STATUS_CODE_FIELD => self . span_builder . status = str_to_status ( value) ,
437
+ SPAN_NAME_FIELD => self . span_builder_updates . name = Some ( value. to_string ( ) . into ( ) ) ,
438
+ SPAN_KIND_FIELD => self . span_builder_updates . span_kind = str_to_span_kind ( value) ,
439
+ SPAN_STATUS_CODE_FIELD => self . span_builder_updates . status = Some ( str_to_status ( value) ) ,
400
440
SPAN_STATUS_MESSAGE_FIELD => {
401
- self . span_builder . status = otel:: Status :: error ( value. to_string ( ) )
441
+ self . span_builder_updates . status = Some ( otel:: Status :: error ( value. to_string ( ) ) )
402
442
}
403
443
_ => self . record ( KeyValue :: new ( field. name ( ) , value. to_string ( ) ) ) ,
404
444
}
@@ -410,15 +450,15 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
410
450
/// [`Span`]: opentelemetry::trace::Span
411
451
fn record_debug ( & mut self , field : & field:: Field , value : & dyn fmt:: Debug ) {
412
452
match field. name ( ) {
413
- SPAN_NAME_FIELD => self . span_builder . name = format ! ( "{:?}" , value) . into ( ) ,
453
+ SPAN_NAME_FIELD => self . span_builder_updates . name = Some ( format ! ( "{:?}" , value) . into ( ) ) ,
414
454
SPAN_KIND_FIELD => {
415
- self . span_builder . span_kind = str_to_span_kind ( & format ! ( "{:?}" , value) )
455
+ self . span_builder_updates . span_kind = str_to_span_kind ( & format ! ( "{:?}" , value) )
416
456
}
417
457
SPAN_STATUS_CODE_FIELD => {
418
- self . span_builder . status = str_to_status ( & format ! ( "{:?}" , value) )
458
+ self . span_builder_updates . status = Some ( str_to_status ( & format ! ( "{:?}" , value) ) )
419
459
}
420
460
SPAN_STATUS_MESSAGE_FIELD => {
421
- self . span_builder . status = otel:: Status :: error ( format ! ( "{:?}" , value) )
461
+ self . span_builder_updates . status = Some ( otel:: Status :: error ( format ! ( "{:?}" , value) ) )
422
462
}
423
463
_ => self . record ( Key :: new ( field. name ( ) ) . string ( format ! ( "{:?}" , value) ) ) ,
424
464
}
@@ -904,10 +944,13 @@ where
904
944
}
905
945
}
906
946
947
+ let mut updates = SpanBuilderUpdates :: default ( ) ;
907
948
attrs. record ( & mut SpanAttributeVisitor {
908
- span_builder : & mut builder ,
949
+ span_builder_updates : & mut updates ,
909
950
sem_conv_config : self . sem_conv_config ,
910
951
} ) ;
952
+
953
+ updates. update ( & mut builder) ;
911
954
extensions. insert ( OtelData { builder, parent_cx } ) ;
912
955
}
913
956
@@ -946,12 +989,14 @@ where
946
989
/// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes
947
990
fn on_record ( & self , id : & Id , values : & Record < ' _ > , ctx : Context < ' _ , S > ) {
948
991
let span = ctx. span ( id) . expect ( "Span not found, this is a bug" ) ;
992
+ let mut updates = SpanBuilderUpdates :: default ( ) ;
993
+ values. record ( & mut SpanAttributeVisitor {
994
+ span_builder_updates : & mut updates,
995
+ sem_conv_config : self . sem_conv_config ,
996
+ } ) ;
949
997
let mut extensions = span. extensions_mut ( ) ;
950
998
if let Some ( data) = extensions. get_mut :: < OtelData > ( ) {
951
- values. record ( & mut SpanAttributeVisitor {
952
- span_builder : & mut data. builder ,
953
- sem_conv_config : self . sem_conv_config ,
954
- } ) ;
999
+ updates. update ( & mut data. builder ) ;
955
1000
}
956
1001
}
957
1002
@@ -1023,24 +1068,24 @@ where
1023
1068
#[ cfg( not( feature = "tracing-log" ) ) ]
1024
1069
let target = target. string ( meta. target ( ) ) ;
1025
1070
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
1071
let mut otel_event = otel:: Event :: new (
1031
1072
String :: new ( ) ,
1032
1073
crate :: time:: now ( ) ,
1033
1074
vec ! [ Key :: new( "level" ) . string( meta. level( ) . as_str( ) ) , target] ,
1034
1075
0 ,
1035
1076
) ;
1036
1077
1078
+ let mut builder_updates = None ;
1037
1079
event. record ( & mut SpanEventVisitor {
1038
1080
event_builder : & mut otel_event,
1039
- span_builder ,
1081
+ span_builder_updates : & mut builder_updates ,
1040
1082
sem_conv_config : self . sem_conv_config ,
1041
1083
} ) ;
1042
1084
1043
- if let Some ( mut otel_data) = otel_data {
1085
+ let mut extensions = span. extensions_mut ( ) ;
1086
+ let otel_data = extensions. get_mut :: < OtelData > ( ) ;
1087
+
1088
+ if let Some ( otel_data) = otel_data {
1044
1089
let builder = & mut otel_data. builder ;
1045
1090
1046
1091
if builder. status == otel:: Status :: Unset
@@ -1049,6 +1094,10 @@ where
1049
1094
builder. status = otel:: Status :: error ( "" )
1050
1095
}
1051
1096
1097
+ if let Some ( builder_updates) = builder_updates {
1098
+ builder_updates. update ( builder) ;
1099
+ }
1100
+
1052
1101
if self . location {
1053
1102
#[ cfg( not( feature = "tracing-log" ) ) ]
1054
1103
let normalized_meta: Option < tracing_core:: Metadata < ' _ > > = None ;
@@ -1085,8 +1134,6 @@ where
1085
1134
} else {
1086
1135
builder. events = Some ( vec ! [ otel_event] ) ;
1087
1136
}
1088
-
1089
- span. extensions_mut ( ) . replace ( otel_data) ;
1090
1137
}
1091
1138
} ;
1092
1139
}
0 commit comments