Skip to content

Commit 83cbc22

Browse files
committed
enhancement: honor span limits when collecting events
1 parent 6cdd4d7 commit 83cbc22

File tree

2 files changed

+148
-59
lines changed

2 files changed

+148
-59
lines changed

src/layer.rs

+138-59
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use opentelemetry::{
44
trace::{self as otel, noop, SpanBuilder, SpanKind, Status, TraceContextExt},
55
Context as OtelContext, Key, KeyValue, StringValue, Value,
66
};
7+
use opentelemetry_sdk::trace::SpanLimits;
78
use std::fmt;
89
use std::marker;
910
use std::thread;
@@ -126,7 +127,7 @@ struct SpanBuilderUpdates {
126127
}
127128

128129
impl SpanBuilderUpdates {
129-
fn update(self, span_builder: &mut SpanBuilder) {
130+
fn update(self, span_builder: &mut SpanBuilder, limits: Option<SpanLimits>) {
130131
let Self {
131132
name,
132133
span_kind,
@@ -143,20 +144,46 @@ impl SpanBuilderUpdates {
143144
if let Some(status) = status {
144145
span_builder.status = status;
145146
}
146-
if let Some(attributes) = attributes {
147+
if let Some(mut attributes) = attributes {
147148
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+
}
148155
builder_attributes.extend(attributes);
149156
} else {
157+
if let Some(limits) = limits {
158+
attributes.truncate(limits.max_attributes_per_span as usize);
159+
}
150160
span_builder.attributes = Some(attributes);
151161
}
152162
}
153163
}
154164
}
155165

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+
156172
struct SpanEventVisitor<'a, 'b> {
157173
event_builder: &'a mut otel::Event,
158174
span_builder_updates: &'b mut Option<SpanBuilderUpdates>,
159175
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+
}
160187
}
161188

162189
impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
@@ -170,9 +197,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
170197
#[cfg(feature = "tracing-log")]
171198
name if name.starts_with("log.") => (),
172199
name => {
173-
self.event_builder
174-
.attributes
175-
.push(KeyValue::new(name, value));
200+
self.push_event_attribute(KeyValue::new(name, value));
176201
}
177202
}
178203
}
@@ -187,9 +212,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
187212
#[cfg(feature = "tracing-log")]
188213
name if name.starts_with("log.") => (),
189214
name => {
190-
self.event_builder
191-
.attributes
192-
.push(KeyValue::new(name, value));
215+
self.push_event_attribute(KeyValue::new(name, value));
193216
}
194217
}
195218
}
@@ -204,9 +227,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
204227
#[cfg(feature = "tracing-log")]
205228
name if name.starts_with("log.") => (),
206229
name => {
207-
self.event_builder
208-
.attributes
209-
.push(KeyValue::new(name, value));
230+
self.push_event_attribute(KeyValue::new(name, value));
210231
}
211232
}
212233
}
@@ -229,23 +250,19 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
229250
}
230251
if self.sem_conv_config.error_events_to_exceptions {
231252
self.event_builder.name = EVENT_EXCEPTION_NAME.into();
232-
self.event_builder.attributes.push(KeyValue::new(
253+
self.push_event_attribute(KeyValue::new(
233254
FIELD_EXCEPTION_MESSAGE,
234255
format!("{:?}", value),
235256
));
236257
} else {
237-
self.event_builder
238-
.attributes
239-
.push(KeyValue::new("error", format!("{:?}", value)));
258+
self.push_event_attribute(KeyValue::new("error", format!("{:?}", value)));
240259
}
241260
}
242261
// Skip fields that are actually log metadata that have already been handled
243262
#[cfg(feature = "tracing-log")]
244263
name if name.starts_with("log.") => (),
245264
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()));
249266
}
250267
}
251268
}
@@ -269,23 +286,19 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
269286
}
270287
if self.sem_conv_config.error_events_to_exceptions {
271288
self.event_builder.name = EVENT_EXCEPTION_NAME.into();
272-
self.event_builder.attributes.push(KeyValue::new(
289+
self.push_event_attribute(KeyValue::new(
273290
FIELD_EXCEPTION_MESSAGE,
274291
format!("{:?}", value),
275292
));
276293
} else {
277-
self.event_builder
278-
.attributes
279-
.push(KeyValue::new("error", format!("{:?}", value)));
294+
self.push_event_attribute(KeyValue::new("error", format!("{:?}", value)));
280295
}
281296
}
282297
// Skip fields that are actually log metadata that have already been handled
283298
#[cfg(feature = "tracing-log")]
284299
name if name.starts_with("log.") => (),
285300
name => {
286-
self.event_builder
287-
.attributes
288-
.push(KeyValue::new(name, format!("{:?}", value)));
301+
self.push_event_attribute(KeyValue::new(name, format!("{:?}", value)));
289302
}
290303
}
291304
}
@@ -310,19 +323,15 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
310323
let error_msg = value.to_string();
311324

312325
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()));
316327

317328
// NOTE: This is actually not the stacktrace of the exception. This is
318329
// the "source chain". It represents the heirarchy of errors from the
319330
// app level to the lowest level such as IO. It does not represent all
320331
// of the callsites in the code that led to the error happening.
321332
// `std::error::Error::backtrace` is a nightly-only API and cannot be
322333
// 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()));
326335
}
327336

328337
if self.sem_conv_config.error_records_to_exceptions {
@@ -349,12 +358,8 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
349358
));
350359
}
351360

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));
358363
}
359364
}
360365

@@ -913,34 +918,62 @@ where
913918
builder.trace_id = Some(self.tracer.new_trace_id());
914919
}
915920

921+
let limits = self.tracer.span_limits();
922+
let attribute_limit = limits.map(|limits| limits.max_attributes_per_span);
916923
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+
),
918929
));
919930

920931
if self.location {
921932
let meta = attrs.metadata();
922933

923934
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+
);
925940
}
926941

927942
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+
);
929948
}
930949

931950
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+
);
933956
}
934957
}
935958

936959
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+
});
938967
if let Some(name) = std::thread::current().name() {
939968
// TODO(eliza): it's a bummer that we have to allocate here, but
940969
// we can't easily get the string as a `static`. it would be
941970
// nice if `opentelemetry` could also take `Arc<str>`s as
942971
// `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+
);
944977
}
945978
}
946979

@@ -950,7 +983,7 @@ where
950983
sem_conv_config: self.sem_conv_config,
951984
});
952985

953-
updates.update(&mut builder);
986+
updates.update(&mut builder, limits);
954987
extensions.insert(OtelData { builder, parent_cx });
955988
}
956989

@@ -996,7 +1029,7 @@ where
9961029
});
9971030
let mut extensions = span.extensions_mut();
9981031
if let Some(data) = extensions.get_mut::<OtelData>() {
999-
updates.update(&mut data.builder);
1032+
updates.update(&mut data.builder, self.tracer.span_limits());
10001033
}
10011034
}
10021035

@@ -1024,8 +1057,18 @@ where
10241057
.clone();
10251058
let follows_link = otel::Link::new(follows_context, Vec::new());
10261059
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+
{
10291072
data.builder.links = Some(vec![follows_link]);
10301073
}
10311074
}
@@ -1068,6 +1111,8 @@ where
10681111
#[cfg(not(feature = "tracing-log"))]
10691112
let target = target.string(meta.target());
10701113

1114+
let limits = self.tracer.span_limits();
1115+
10711116
let mut otel_event = otel::Event::new(
10721117
String::new(),
10731118
crate::time::now(),
@@ -1080,6 +1125,7 @@ where
10801125
event_builder: &mut otel_event,
10811126
span_builder_updates: &mut builder_updates,
10821127
sem_conv_config: self.sem_conv_config,
1128+
limits,
10831129
});
10841130

10851131
let mut extensions = span.extensions_mut();
@@ -1095,7 +1141,21 @@ where
10951141
}
10961142

10971143
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;
10991159
}
11001160

11011161
if self.location {
@@ -1112,20 +1172,30 @@ where
11121172
),
11131173
};
11141174

1175+
let event_attributes_limit =
1176+
limits.map(|limits| limits.max_attributes_per_event);
1177+
let event_attributes = &mut otel_event.attributes;
1178+
11151179
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+
);
11191185
}
11201186
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+
);
11241192
}
11251193
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+
);
11291199
}
11301200
}
11311201

@@ -1159,8 +1229,17 @@ where
11591229
let attributes = builder
11601230
.attributes
11611231
.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+
);
11641243
}
11651244
}
11661245

0 commit comments

Comments
 (0)