Skip to content

Commit bee0d48

Browse files
authored
opentelemetry: update to otel 0.14.x (#1394)
## Motivation Support the latest OpenTelemetry specification. ## Solution Update `opentelemetry` to the latest `0.14.x` release. Despite breaking changes upstream, no additional public API or behavioral changes are necessary in `tracing-opentelemetry`, but simplifications in the propagation of span information have removed the need for the current internal `CompatSpan` and custom parent context construction. Performance has improved by ~30% on current benchmarks.
1 parent 72a9656 commit bee0d48

File tree

6 files changed

+49
-123
lines changed

6 files changed

+49
-123
lines changed

examples/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@ inferno = "0.10.0"
5151
tempdir = "0.3.7"
5252

5353
# opentelemetry example
54-
opentelemetry = { version = "0.13", default-features = false, features = ["trace"] }
55-
opentelemetry-jaeger = "0.12"
54+
opentelemetry = { version = "0.14", default-features = false, features = ["trace"] }
55+
opentelemetry-jaeger = "0.13"

tracing-opentelemetry/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ edition = "2018"
2222
default = ["tracing-log"]
2323

2424
[dependencies]
25-
opentelemetry = { version = "0.13", default-features = false, features = ["trace"] }
25+
opentelemetry = { version = "0.14", default-features = false, features = ["trace"] }
2626
tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] }
2727
tracing-core = { path = "../tracing-core", version = "0.2" }
2828
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry"] }
@@ -31,7 +31,7 @@ tracing-log = { path = "../tracing-log", version = "0.2", default-features = fal
3131
[dev-dependencies]
3232
async-trait = "0.1"
3333
criterion = { version = "0.3", default_features = false }
34-
opentelemetry-jaeger = "0.12"
34+
opentelemetry-jaeger = "0.13"
3535

3636
[lib]
3737
bench = false

tracing-opentelemetry/src/span_ext.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ impl OpenTelemetrySpanExt for tracing::Span {
7878
self.with_collector(move |(id, collector)| {
7979
if let Some(get_context) = collector.downcast_ref::<WithContext>() {
8080
get_context.with_context(collector, id, move |builder, _tracer| {
81-
builder.parent_context = cx.take();
81+
if let Some(cx) = cx.take() {
82+
builder.parent_context = cx;
83+
}
8284
});
8385
}
8486
});

tracing-opentelemetry/src/subscriber.rs

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
210210
SPAN_NAME_FIELD => self.0.name = value.to_string().into(),
211211
SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(value),
212212
SPAN_STATUS_CODE_FIELD => self.0.status_code = str_to_status_code(value),
213-
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(value.to_owned()),
213+
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(value.to_owned().into()),
214214
_ => {
215215
let attribute = KeyValue::new(field.name(), value.to_string());
216216
if let Some(attributes) = &mut self.0.attributes {
@@ -233,7 +233,9 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
233233
SPAN_STATUS_CODE_FIELD => {
234234
self.0.status_code = str_to_status_code(&format!("{:?}", value))
235235
}
236-
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(format!("{:?}", value)),
236+
SPAN_STATUS_MESSAGE_FIELD => {
237+
self.0.status_message = Some(format!("{:?}", value).into())
238+
}
237239
_ => {
238240
let attribute = Key::new(field.name()).string(format!("{:?}", value));
239241
if let Some(attributes) = &mut self.0.attributes {
@@ -410,25 +412,12 @@ where
410412
.tracer
411413
.span_builder(attrs.metadata().name())
412414
.with_start_time(SystemTime::now())
415+
.with_parent_context(self.parent_context(attrs, &ctx))
413416
// Eagerly assign span id so children have stable parent id
414417
.with_span_id(self.tracer.new_span_id());
415418

416-
// Set optional parent span context from attrs
417-
builder.parent_context = Some(self.parent_context(attrs, &ctx));
418-
419-
// Ensure trace id exists so spans are associated with the proper trace.
420-
//
421-
// Parent contexts are in 4 possible states, first two require a new
422-
// trace ids, second two have existing trace ids:
423-
// * Empty - explicit new tracing root span, needs new id
424-
// * A parent context containing no active or remote span, needs new id
425-
// * A parent context containing an active span, defer to that span's trace
426-
// * A parent context containing a remote span context, defer to remote trace
427-
let needs_trace_id = builder.parent_context.as_ref().map_or(true, |cx| {
428-
!cx.has_active_span() && cx.remote_span_context().is_none()
429-
});
430-
431-
if needs_trace_id {
419+
// Record new trace id if there is no active parent span
420+
if !builder.parent_context.has_active_span() {
432421
builder.trace_id = Some(self.tracer.new_trace_id());
433422
}
434423

@@ -532,6 +521,7 @@ where
532521
Key::new("level").string(meta.level().to_string()),
533522
Key::new("target").string(meta.target().to_string()),
534523
],
524+
0,
535525
);
536526
event.record(&mut SpanEventVisitor(&mut otel_event));
537527

@@ -541,10 +531,10 @@ where
541531
builder.status_code = Some(otel::StatusCode::Error);
542532
}
543533

544-
if let Some(ref mut events) = builder.message_events {
534+
if let Some(ref mut events) = builder.events {
545535
events.push(otel_event);
546536
} else {
547-
builder.message_events = Some(vec![otel_event]);
537+
builder.events = Some(vec![otel_event]);
548538
}
549539
}
550540
};
@@ -611,6 +601,7 @@ impl Timings {
611601
mod tests {
612602
use super::*;
613603
use opentelemetry::trace::SpanKind;
604+
use std::borrow::Cow;
614605
use std::sync::{Arc, Mutex};
615606
use std::time::SystemTime;
616607
use tracing_subscriber::prelude::*;
@@ -622,11 +613,17 @@ mod tests {
622613
fn invalid(&self) -> Self::Span {
623614
otel::NoopSpan::new()
624615
}
625-
fn start_with_context(&self, _name: &str, _context: OtelContext) -> Self::Span {
616+
fn start_with_context<T>(&self, _name: T, _context: OtelContext) -> Self::Span
617+
where
618+
T: Into<Cow<'static, str>>,
619+
{
626620
self.invalid()
627621
}
628-
fn span_builder(&self, name: &str) -> otel::SpanBuilder {
629-
otel::SpanBuilder::from_name(name.to_string())
622+
fn span_builder<T>(&self, name: T) -> otel::SpanBuilder
623+
where
624+
T: Into<Cow<'static, str>>,
625+
{
626+
otel::SpanBuilder::from_name(name)
630627
}
631628
fn build(&self, builder: otel::SpanBuilder) -> Self::Span {
632629
*self.0.lock().unwrap() = Some(builder);
@@ -649,17 +646,17 @@ mod tests {
649646
#[derive(Debug, Clone)]
650647
struct TestSpan(otel::SpanContext);
651648
impl otel::Span for TestSpan {
652-
fn add_event_with_timestamp(&self, _: String, _: SystemTime, _: Vec<KeyValue>) {}
649+
fn add_event_with_timestamp(&mut self, _: String, _: SystemTime, _: Vec<KeyValue>) {}
653650
fn span_context(&self) -> &otel::SpanContext {
654651
&self.0
655652
}
656653
fn is_recording(&self) -> bool {
657654
false
658655
}
659-
fn set_attribute(&self, _attribute: KeyValue) {}
660-
fn set_status(&self, _code: otel::StatusCode, _message: String) {}
661-
fn update_name(&self, _new_name: String) {}
662-
fn end_with_timestamp(&self, _timestamp: SystemTime) {}
656+
fn set_attribute(&mut self, _attribute: KeyValue) {}
657+
fn set_status(&mut self, _code: otel::StatusCode, _message: String) {}
658+
fn update_name(&mut self, _new_name: String) {}
659+
fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
663660
}
664661

665662
#[test]
@@ -725,7 +722,7 @@ mod tests {
725722
.status_message
726723
.clone();
727724

728-
assert_eq!(recorded_status_message, Some(message.to_string()))
725+
assert_eq!(recorded_status_message, Some(message.into()))
729726
}
730727

731728
#[test]
@@ -754,8 +751,6 @@ mod tests {
754751
.as_ref()
755752
.unwrap()
756753
.parent_context
757-
.as_ref()
758-
.unwrap()
759754
.span()
760755
.span_context()
761756
.trace_id();

tracing-opentelemetry/src/tracer.rs

Lines changed: 10 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ use opentelemetry::{
55
SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceId, TraceState,
66
TRACE_FLAG_SAMPLED,
77
},
8-
Context as OtelContext, KeyValue,
8+
Context as OtelContext,
99
};
10-
use std::time::SystemTime;
1110

1211
/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers.
1312
///
@@ -51,10 +50,7 @@ pub trait PreSampledTracer {
5150

5251
impl PreSampledTracer for otel::NoopTracer {
5352
fn sampled_context(&self, builder: &mut otel::SpanBuilder) -> OtelContext {
54-
builder
55-
.parent_context
56-
.clone()
57-
.unwrap_or_else(OtelContext::new)
53+
builder.parent_context.clone()
5854
}
5955

6056
fn new_trace_id(&self) -> otel::TraceId {
@@ -73,9 +69,7 @@ impl PreSampledTracer for Tracer {
7369
return OtelContext::new();
7470
}
7571
let provider = self.provider().unwrap();
76-
77-
// Ensure parent context exists and contains data necessary for sampling
78-
let parent_cx = build_parent_context(&builder);
72+
let parent_cx = &builder.parent_context;
7973

8074
// Gather trace state
8175
let (no_parent, trace_id, remote_parent, parent_trace_flags) =
@@ -85,7 +79,7 @@ impl PreSampledTracer for Tracer {
8579
let (flags, trace_state) = if let Some(result) = &builder.sampling_result {
8680
process_sampling_result(result, parent_trace_flags)
8781
} else if no_parent || remote_parent {
88-
builder.sampling_result = Some(provider.config().default_sampler.should_sample(
82+
builder.sampling_result = Some(provider.config().sampler.should_sample(
8983
Some(&parent_cx),
9084
trace_id,
9185
&builder.name,
@@ -109,7 +103,7 @@ impl PreSampledTracer for Tracer {
109103

110104
let span_id = builder.span_id.unwrap_or_else(SpanId::invalid);
111105
let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state);
112-
parent_cx.with_span(CompatSpan(span_context))
106+
parent_cx.with_remote_span_context(span_context)
113107
}
114108

115109
fn new_trace_id(&self) -> otel::TraceId {
@@ -125,31 +119,14 @@ impl PreSampledTracer for Tracer {
125119
}
126120
}
127121

128-
fn build_parent_context(builder: &SpanBuilder) -> OtelContext {
129-
builder
130-
.parent_context
131-
.as_ref()
132-
.map(|cx| {
133-
// Sampling expects to be able to access the parent span via `span` so wrap remote span
134-
// context in a wrapper span if necessary. Remote span contexts will be passed to
135-
// subsequent context's, so wrapping is only necessary if there is no active span.
136-
match cx.remote_span_context() {
137-
Some(remote_sc) if !cx.has_active_span() => {
138-
cx.with_span(CompatSpan(remote_sc.clone()))
139-
}
140-
_ => cx.clone(),
141-
}
142-
})
143-
.unwrap_or_default()
144-
}
145-
146122
fn current_trace_state(
147123
builder: &SpanBuilder,
148124
parent_cx: &OtelContext,
149125
provider: &TracerProvider,
150126
) -> (bool, TraceId, bool, u8) {
151127
if parent_cx.has_active_span() {
152-
let sc = parent_cx.span().span_context();
128+
let span = parent_cx.span();
129+
let sc = span.span_context();
153130
(false, sc.trace_id(), sc.is_remote(), sc.trace_flags())
154131
} else {
155132
(
@@ -185,58 +162,6 @@ fn process_sampling_result(
185162
}
186163
}
187164

188-
#[derive(Debug)]
189-
struct CompatSpan(otel::SpanContext);
190-
impl otel::Span for CompatSpan {
191-
fn add_event_with_timestamp(
192-
&self,
193-
_name: String,
194-
_timestamp: std::time::SystemTime,
195-
_attributes: Vec<KeyValue>,
196-
) {
197-
#[cfg(debug_assertions)]
198-
panic!(
199-
"OpenTelemetry and tracing APIs cannot be mixed, use `tracing::event!` macro instead."
200-
);
201-
}
202-
203-
/// This method is used by OpenTelemetry propagators to inject span context
204-
/// information into [`Injector`]s.
205-
///
206-
/// [`Injector`]: opentelemetry::propagation::Injector
207-
fn span_context(&self) -> &otel::SpanContext {
208-
&self.0
209-
}
210-
211-
fn is_recording(&self) -> bool {
212-
#[cfg(debug_assertions)]
213-
panic!("cannot record via OpenTelemetry API when using extracted span in tracing");
214-
215-
#[cfg(not(debug_assertions))]
216-
false
217-
}
218-
219-
fn set_attribute(&self, _attribute: KeyValue) {
220-
#[cfg(debug_assertions)]
221-
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` instead.");
222-
}
223-
224-
fn set_status(&self, _code: otel::StatusCode, _message: String) {
225-
#[cfg(debug_assertions)]
226-
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` with `otel.status_code` and `otel.status_message` instead.");
227-
}
228-
229-
fn update_name(&self, _new_name: String) {
230-
#[cfg(debug_assertions)]
231-
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `span.record()` with `otel.name` instead.");
232-
}
233-
234-
fn end_with_timestamp(&self, _timestamp: SystemTime) {
235-
#[cfg(debug_assertions)]
236-
panic!("OpenTelemetry and tracing APIs cannot be mixed, span end times are set when the underlying tracing span closes.");
237-
}
238-
}
239-
240165
#[cfg(test)]
241166
mod tests {
242167
use super::*;
@@ -251,7 +176,8 @@ mod tests {
251176
builder.span_id = Some(SpanId::from_u64(1));
252177
builder.trace_id = None;
253178
let cx = tracer.sampled_context(&mut builder);
254-
let span_context = cx.span().span_context();
179+
let span = cx.span();
180+
let span_context = span.span_context();
255181

256182
assert!(span_context.is_valid());
257183
}
@@ -287,7 +213,7 @@ mod tests {
287213
.build();
288214
let tracer = provider.get_tracer("test", None);
289215
let mut builder = SpanBuilder::from_name("parent".to_string());
290-
builder.parent_context = Some(parent_cx);
216+
builder.parent_context = parent_cx;
291217
builder.sampling_result = previous_sampling_result;
292218
let sampled = tracer.sampled_context(&mut builder);
293219

tracing-opentelemetry/tests/trace_state_propagation.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ use tracing_subscriber::prelude::*;
1717

1818
#[test]
1919
fn trace_with_active_otel_context() {
20-
let (cx, subscriber, exporter, _provider) = build_sampled_context();
20+
let (cx, subscriber, exporter, provider) = build_sampled_context();
2121
let attached = cx.attach();
2222

2323
tracing::collect::with_default(subscriber, || {
2424
tracing::debug_span!("child");
2525
});
2626

2727
drop(attached); // end implicit parent
28+
drop(provider); // flush all spans
2829

2930
let spans = exporter.0.lock().unwrap();
3031
assert_eq!(spans.len(), 2);
@@ -33,28 +34,30 @@ fn trace_with_active_otel_context() {
3334

3435
#[test]
3536
fn trace_with_assigned_otel_context() {
36-
let (cx, subscriber, exporter, _provider) = build_sampled_context();
37+
let (cx, subscriber, exporter, provider) = build_sampled_context();
3738

3839
tracing::collect::with_default(subscriber, || {
3940
let child = tracing::debug_span!("child");
4041
child.set_parent(cx);
4142
});
4243

44+
drop(provider); // flush all spans
4345
let spans = exporter.0.lock().unwrap();
4446
assert_eq!(spans.len(), 2);
4547
assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
4648
}
4749

4850
#[test]
4951
fn trace_root_with_children() {
50-
let (_tracer, _provider, exporter, subscriber) = test_tracer();
52+
let (_tracer, provider, exporter, subscriber) = test_tracer();
5153

5254
tracing::collect::with_default(subscriber, || {
5355
// Propagate trace information through tracing parent -> child
5456
let root = tracing::debug_span!("root");
5557
root.in_scope(|| tracing::debug_span!("child"));
5658
});
5759

60+
drop(provider); // flush all spans
5861
let spans = exporter.0.lock().unwrap();
5962
assert_eq!(spans.len(), 2);
6063
assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);

0 commit comments

Comments
 (0)