@@ -4,6 +4,7 @@ use opentelemetry::{
4
4
trace:: { self as otel, noop, SpanBuilder , SpanKind , Status , TraceContextExt } ,
5
5
Context as OtelContext , Key , KeyValue , StringValue , Value ,
6
6
} ;
7
+ use opentelemetry_sdk:: trace:: SpanLimits ;
7
8
use std:: fmt;
8
9
use std:: marker;
9
10
use std:: thread;
@@ -126,7 +127,7 @@ struct SpanBuilderUpdates {
126
127
}
127
128
128
129
impl SpanBuilderUpdates {
129
- fn update ( self , span_builder : & mut SpanBuilder ) {
130
+ fn update ( self , span_builder : & mut SpanBuilder , limits : Option < SpanLimits > ) {
130
131
let Self {
131
132
name,
132
133
span_kind,
@@ -143,20 +144,46 @@ impl SpanBuilderUpdates {
143
144
if let Some ( status) = status {
144
145
span_builder. status = status;
145
146
}
146
- if let Some ( attributes) = attributes {
147
+ if let Some ( mut attributes) = attributes {
147
148
if let Some ( builder_attributes) = & mut span_builder. attributes {
149
+ if let Some ( limits) = limits {
150
+ attributes. truncate (
151
+ ( limits. max_attributes_per_span as usize )
152
+ . saturating_sub ( builder_attributes. len ( ) ) ,
153
+ ) ;
154
+ }
148
155
builder_attributes. extend ( attributes) ;
149
156
} else {
157
+ if let Some ( limits) = limits {
158
+ attributes. truncate ( limits. max_attributes_per_span as usize ) ;
159
+ }
150
160
span_builder. attributes = Some ( attributes) ;
151
161
}
152
162
}
153
163
}
154
164
}
155
165
166
+ fn push_unless_limit_reached < T > ( attributes_or_events : & mut Vec < T > , limit : Option < u32 > , value : T ) {
167
+ if limit. map_or ( true , |limit| ( limit as usize ) > attributes_or_events. len ( ) ) {
168
+ attributes_or_events. push ( value) ;
169
+ }
170
+ }
171
+
156
172
struct SpanEventVisitor < ' a , ' b > {
157
173
event_builder : & ' a mut otel:: Event ,
158
174
span_builder_updates : & ' b mut Option < SpanBuilderUpdates > ,
159
175
sem_conv_config : SemConvConfig ,
176
+ limits : Option < SpanLimits > ,
177
+ }
178
+
179
+ impl < ' a , ' b > SpanEventVisitor < ' a , ' b > {
180
+ fn push_event_attribute ( & mut self , attribute : KeyValue ) {
181
+ push_unless_limit_reached (
182
+ & mut self . event_builder . attributes ,
183
+ self . limits . map ( |limits| limits. max_attributes_per_event ) ,
184
+ attribute,
185
+ ) ;
186
+ }
160
187
}
161
188
162
189
impl < ' a , ' b > field:: Visit for SpanEventVisitor < ' a , ' b > {
@@ -170,9 +197,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
170
197
#[ cfg( feature = "tracing-log" ) ]
171
198
name if name. starts_with ( "log." ) => ( ) ,
172
199
name => {
173
- self . event_builder
174
- . attributes
175
- . push ( KeyValue :: new ( name, value) ) ;
200
+ self . push_event_attribute ( KeyValue :: new ( name, value) ) ;
176
201
}
177
202
}
178
203
}
@@ -187,9 +212,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
187
212
#[ cfg( feature = "tracing-log" ) ]
188
213
name if name. starts_with ( "log." ) => ( ) ,
189
214
name => {
190
- self . event_builder
191
- . attributes
192
- . push ( KeyValue :: new ( name, value) ) ;
215
+ self . push_event_attribute ( KeyValue :: new ( name, value) ) ;
193
216
}
194
217
}
195
218
}
@@ -204,9 +227,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
204
227
#[ cfg( feature = "tracing-log" ) ]
205
228
name if name. starts_with ( "log." ) => ( ) ,
206
229
name => {
207
- self . event_builder
208
- . attributes
209
- . push ( KeyValue :: new ( name, value) ) ;
230
+ self . push_event_attribute ( KeyValue :: new ( name, value) ) ;
210
231
}
211
232
}
212
233
}
@@ -229,23 +250,19 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
229
250
}
230
251
if self . sem_conv_config . error_events_to_exceptions {
231
252
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
232
- self . event_builder . attributes . push ( KeyValue :: new (
253
+ self . push_event_attribute ( KeyValue :: new (
233
254
FIELD_EXCEPTION_MESSAGE ,
234
255
format ! ( "{:?}" , value) ,
235
256
) ) ;
236
257
} else {
237
- self . event_builder
238
- . attributes
239
- . push ( KeyValue :: new ( "error" , format ! ( "{:?}" , value) ) ) ;
258
+ self . push_event_attribute ( KeyValue :: new ( "error" , format ! ( "{:?}" , value) ) ) ;
240
259
}
241
260
}
242
261
// Skip fields that are actually log metadata that have already been handled
243
262
#[ cfg( feature = "tracing-log" ) ]
244
263
name if name. starts_with ( "log." ) => ( ) ,
245
264
name => {
246
- self . event_builder
247
- . attributes
248
- . push ( KeyValue :: new ( name, value. to_string ( ) ) ) ;
265
+ self . push_event_attribute ( KeyValue :: new ( name, value. to_string ( ) ) ) ;
249
266
}
250
267
}
251
268
}
@@ -269,23 +286,19 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
269
286
}
270
287
if self . sem_conv_config . error_events_to_exceptions {
271
288
self . event_builder . name = EVENT_EXCEPTION_NAME . into ( ) ;
272
- self . event_builder . attributes . push ( KeyValue :: new (
289
+ self . push_event_attribute ( KeyValue :: new (
273
290
FIELD_EXCEPTION_MESSAGE ,
274
291
format ! ( "{:?}" , value) ,
275
292
) ) ;
276
293
} else {
277
- self . event_builder
278
- . attributes
279
- . push ( KeyValue :: new ( "error" , format ! ( "{:?}" , value) ) ) ;
294
+ self . push_event_attribute ( KeyValue :: new ( "error" , format ! ( "{:?}" , value) ) ) ;
280
295
}
281
296
}
282
297
// Skip fields that are actually log metadata that have already been handled
283
298
#[ cfg( feature = "tracing-log" ) ]
284
299
name if name. starts_with ( "log." ) => ( ) ,
285
300
name => {
286
- self . event_builder
287
- . attributes
288
- . push ( KeyValue :: new ( name, format ! ( "{:?}" , value) ) ) ;
301
+ self . push_event_attribute ( KeyValue :: new ( name, format ! ( "{:?}" , value) ) ) ;
289
302
}
290
303
}
291
304
}
@@ -310,19 +323,15 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
310
323
let error_msg = value. to_string ( ) ;
311
324
312
325
if self . sem_conv_config . error_fields_to_exceptions {
313
- self . event_builder
314
- . attributes
315
- . push ( Key :: new ( FIELD_EXCEPTION_MESSAGE ) . string ( error_msg. clone ( ) ) ) ;
326
+ self . push_event_attribute ( Key :: new ( FIELD_EXCEPTION_MESSAGE ) . string ( error_msg. clone ( ) ) ) ;
316
327
317
328
// NOTE: This is actually not the stacktrace of the exception. This is
318
329
// the "source chain". It represents the heirarchy of errors from the
319
330
// app level to the lowest level such as IO. It does not represent all
320
331
// of the callsites in the code that led to the error happening.
321
332
// `std::error::Error::backtrace` is a nightly-only API and cannot be
322
333
// used here until the feature is stabilized.
323
- self . event_builder
324
- . attributes
325
- . push ( Key :: new ( FIELD_EXCEPTION_STACKTRACE ) . array ( chain. clone ( ) ) ) ;
334
+ self . push_event_attribute ( Key :: new ( FIELD_EXCEPTION_STACKTRACE ) . array ( chain. clone ( ) ) ) ;
326
335
}
327
336
328
337
if self . sem_conv_config . error_records_to_exceptions {
@@ -349,12 +358,8 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
349
358
) ) ;
350
359
}
351
360
352
- self . event_builder
353
- . attributes
354
- . push ( Key :: new ( field. name ( ) ) . string ( error_msg) ) ;
355
- self . event_builder
356
- . attributes
357
- . push ( Key :: new ( format ! ( "{}.chain" , field. name( ) ) ) . array ( chain) ) ;
361
+ self . push_event_attribute ( Key :: new ( field. name ( ) ) . string ( error_msg) ) ;
362
+ self . push_event_attribute ( Key :: new ( format ! ( "{}.chain" , field. name( ) ) ) . array ( chain) ) ;
358
363
}
359
364
}
360
365
@@ -913,34 +918,62 @@ where
913
918
builder. trace_id = Some ( self . tracer . new_trace_id ( ) ) ;
914
919
}
915
920
921
+ let limits = self . tracer . span_limits ( ) ;
922
+ let attribute_limit = limits. map ( |limits| limits. max_attributes_per_span ) ;
916
923
let builder_attrs = builder. attributes . get_or_insert ( Vec :: with_capacity (
917
- attrs. fields ( ) . len ( ) + self . extra_span_attrs ( ) ,
924
+ ( attrs. fields ( ) . len ( ) + self . extra_span_attrs ( ) ) . min (
925
+ attribute_limit
926
+ . map ( |limit| limit as usize )
927
+ . unwrap_or ( usize:: MAX ) ,
928
+ ) ,
918
929
) ) ;
919
930
920
931
if self . location {
921
932
let meta = attrs. metadata ( ) ;
922
933
923
934
if let Some ( filename) = meta. file ( ) {
924
- builder_attrs. push ( KeyValue :: new ( "code.filepath" , filename) ) ;
935
+ push_unless_limit_reached (
936
+ builder_attrs,
937
+ attribute_limit,
938
+ KeyValue :: new ( "code.filepath" , filename) ,
939
+ ) ;
925
940
}
926
941
927
942
if let Some ( module) = meta. module_path ( ) {
928
- builder_attrs. push ( KeyValue :: new ( "code.namespace" , module) ) ;
943
+ push_unless_limit_reached (
944
+ builder_attrs,
945
+ attribute_limit,
946
+ KeyValue :: new ( "code.namespace" , module) ,
947
+ ) ;
929
948
}
930
949
931
950
if let Some ( line) = meta. line ( ) {
932
- builder_attrs. push ( KeyValue :: new ( "code.lineno" , line as i64 ) ) ;
951
+ push_unless_limit_reached (
952
+ builder_attrs,
953
+ attribute_limit,
954
+ KeyValue :: new ( "code.lineno" , line as i64 ) ,
955
+ ) ;
933
956
}
934
957
}
935
958
936
959
if self . with_threads {
937
- THREAD_ID . with ( |id| builder_attrs. push ( KeyValue :: new ( "thread.id" , * * id as i64 ) ) ) ;
960
+ THREAD_ID . with ( |id| {
961
+ push_unless_limit_reached (
962
+ builder_attrs,
963
+ attribute_limit,
964
+ KeyValue :: new ( "thread.id" , * * id as i64 ) ,
965
+ )
966
+ } ) ;
938
967
if let Some ( name) = std:: thread:: current ( ) . name ( ) {
939
968
// TODO(eliza): it's a bummer that we have to allocate here, but
940
969
// we can't easily get the string as a `static`. it would be
941
970
// nice if `opentelemetry` could also take `Arc<str>`s as
942
971
// `String` values...
943
- builder_attrs. push ( KeyValue :: new ( "thread.name" , name. to_string ( ) ) ) ;
972
+ push_unless_limit_reached (
973
+ builder_attrs,
974
+ attribute_limit,
975
+ KeyValue :: new ( "thread.name" , name. to_string ( ) ) ,
976
+ ) ;
944
977
}
945
978
}
946
979
@@ -950,7 +983,7 @@ where
950
983
sem_conv_config : self . sem_conv_config ,
951
984
} ) ;
952
985
953
- updates. update ( & mut builder) ;
986
+ updates. update ( & mut builder, limits ) ;
954
987
extensions. insert ( OtelData { builder, parent_cx } ) ;
955
988
}
956
989
@@ -996,7 +1029,7 @@ where
996
1029
} ) ;
997
1030
let mut extensions = span. extensions_mut ( ) ;
998
1031
if let Some ( data) = extensions. get_mut :: < OtelData > ( ) {
999
- updates. update ( & mut data. builder ) ;
1032
+ updates. update ( & mut data. builder , self . tracer . span_limits ( ) ) ;
1000
1033
}
1001
1034
}
1002
1035
@@ -1024,8 +1057,18 @@ where
1024
1057
. clone ( ) ;
1025
1058
let follows_link = otel:: Link :: new ( follows_context, Vec :: new ( ) ) ;
1026
1059
if let Some ( ref mut links) = data. builder . links {
1027
- links. push ( follows_link) ;
1028
- } else {
1060
+ push_unless_limit_reached (
1061
+ links,
1062
+ self . tracer
1063
+ . span_limits ( )
1064
+ . map ( |limits| limits. max_links_per_span ) ,
1065
+ follows_link,
1066
+ ) ;
1067
+ } else if self
1068
+ . tracer
1069
+ . span_limits ( )
1070
+ . map_or ( true , |limits| limits. max_links_per_span > 0 )
1071
+ {
1029
1072
data. builder . links = Some ( vec ! [ follows_link] ) ;
1030
1073
}
1031
1074
}
@@ -1068,6 +1111,8 @@ where
1068
1111
#[ cfg( not( feature = "tracing-log" ) ) ]
1069
1112
let target = target. string ( meta. target ( ) ) ;
1070
1113
1114
+ let limits = self . tracer . span_limits ( ) ;
1115
+
1071
1116
let mut otel_event = otel:: Event :: new (
1072
1117
String :: new ( ) ,
1073
1118
crate :: time:: now ( ) ,
@@ -1080,6 +1125,7 @@ where
1080
1125
event_builder : & mut otel_event,
1081
1126
span_builder_updates : & mut builder_updates,
1082
1127
sem_conv_config : self . sem_conv_config ,
1128
+ limits,
1083
1129
} ) ;
1084
1130
1085
1131
let mut extensions = span. extensions_mut ( ) ;
@@ -1095,7 +1141,21 @@ where
1095
1141
}
1096
1142
1097
1143
if let Some ( builder_updates) = builder_updates {
1098
- builder_updates. update ( builder) ;
1144
+ builder_updates. update ( builder, limits) ;
1145
+ }
1146
+
1147
+ if builder
1148
+ . events
1149
+ . as_ref ( )
1150
+ . map ( |events| events. len ( ) )
1151
+ . zip ( limits)
1152
+ . map_or ( false , |( current_length, limits) | {
1153
+ current_length >= limits. max_events_per_span as usize
1154
+ } )
1155
+ {
1156
+ // We have reached the configured limit for events so there is no point in storing any more.
1157
+ // This is however the earliest we can abort this as the event can change e.g. the span's status.
1158
+ return ;
1099
1159
}
1100
1160
1101
1161
if self . location {
@@ -1112,20 +1172,30 @@ where
1112
1172
) ,
1113
1173
} ;
1114
1174
1175
+ let event_attributes_limit =
1176
+ limits. map ( |limits| limits. max_attributes_per_event ) ;
1177
+ let event_attributes = & mut otel_event. attributes ;
1178
+
1115
1179
if let Some ( file) = file {
1116
- otel_event
1117
- . attributes
1118
- . push ( KeyValue :: new ( "code.filepath" , file) ) ;
1180
+ push_unless_limit_reached (
1181
+ event_attributes,
1182
+ event_attributes_limit,
1183
+ KeyValue :: new ( "code.filepath" , file) ,
1184
+ ) ;
1119
1185
}
1120
1186
if let Some ( module) = module {
1121
- otel_event
1122
- . attributes
1123
- . push ( KeyValue :: new ( "code.namespace" , module) ) ;
1187
+ push_unless_limit_reached (
1188
+ event_attributes,
1189
+ event_attributes_limit,
1190
+ KeyValue :: new ( "code.namespace" , module) ,
1191
+ ) ;
1124
1192
}
1125
1193
if let Some ( line) = meta. line ( ) {
1126
- otel_event
1127
- . attributes
1128
- . push ( KeyValue :: new ( "code.lineno" , line as i64 ) ) ;
1194
+ push_unless_limit_reached (
1195
+ event_attributes,
1196
+ event_attributes_limit,
1197
+ KeyValue :: new ( "code.lineno" , line as i64 ) ,
1198
+ ) ;
1129
1199
}
1130
1200
}
1131
1201
@@ -1159,8 +1229,17 @@ where
1159
1229
let attributes = builder
1160
1230
. attributes
1161
1231
. get_or_insert_with ( || Vec :: with_capacity ( 2 ) ) ;
1162
- attributes. push ( KeyValue :: new ( busy_ns, timings. busy ) ) ;
1163
- attributes. push ( KeyValue :: new ( idle_ns, timings. idle ) ) ;
1232
+ let limits = self . tracer . span_limits ( ) ;
1233
+ push_unless_limit_reached (
1234
+ attributes,
1235
+ limits. map ( |limits| limits. max_attributes_per_span ) ,
1236
+ KeyValue :: new ( busy_ns, timings. busy ) ,
1237
+ ) ;
1238
+ push_unless_limit_reached (
1239
+ attributes,
1240
+ limits. map ( |limits| limits. max_attributes_per_span ) ,
1241
+ KeyValue :: new ( idle_ns, timings. idle ) ,
1242
+ ) ;
1164
1243
}
1165
1244
}
1166
1245
0 commit comments