Skip to content

Commit df95d06

Browse files
authored
opentelemetry: update to otel 0.14.x (#1394) (#1403)
## 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 614a327 commit df95d06

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
@@ -53,5 +53,5 @@ inferno = "0.10.0"
5353
tempdir = "0.3.7"
5454

5555
# opentelemetry example
56-
opentelemetry = { version = "0.13", default-features = false, features = ["trace"] }
57-
opentelemetry-jaeger = "0.12"
56+
opentelemetry = { version = "0.14", default-features = false, features = ["trace"] }
57+
opentelemetry-jaeger = "0.13"

tracing-opentelemetry/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ 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.1", default-features = false, features = ["std"] }
2727
tracing-core = { path = "../tracing-core", version = "0.1" }
2828
tracing-subscriber = { path = "../tracing-subscriber", version = "0.2", default-features = false, features = ["registry"] }
2929
tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true }
3030

3131
[dev-dependencies]
3232
async-trait = "0.1"
33-
opentelemetry-jaeger = "0.12"
33+
opentelemetry-jaeger = "0.13"
3434
criterion = { version = "0.3", default_features = false }
3535

3636
[lib]

tracing-opentelemetry/src/layer.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
};
@@ -610,6 +600,7 @@ impl Timings {
610600
mod tests {
611601
use super::*;
612602
use opentelemetry::trace::SpanKind;
603+
use std::borrow::Cow;
613604
use std::sync::{Arc, Mutex};
614605
use std::time::SystemTime;
615606
use tracing_subscriber::prelude::*;
@@ -621,11 +612,17 @@ mod tests {
621612
fn invalid(&self) -> Self::Span {
622613
otel::NoopSpan::new()
623614
}
624-
fn start_with_context(&self, _name: &str, _context: OtelContext) -> Self::Span {
615+
fn start_with_context<T>(&self, _name: T, _context: OtelContext) -> Self::Span
616+
where
617+
T: Into<Cow<'static, str>>,
618+
{
625619
self.invalid()
626620
}
627-
fn span_builder(&self, name: &str) -> otel::SpanBuilder {
628-
otel::SpanBuilder::from_name(name.to_string())
621+
fn span_builder<T>(&self, name: T) -> otel::SpanBuilder
622+
where
623+
T: Into<Cow<'static, str>>,
624+
{
625+
otel::SpanBuilder::from_name(name)
629626
}
630627
fn build(&self, builder: otel::SpanBuilder) -> Self::Span {
631628
*self.0.lock().unwrap() = Some(builder);
@@ -648,17 +645,17 @@ mod tests {
648645
#[derive(Debug, Clone)]
649646
struct TestSpan(otel::SpanContext);
650647
impl otel::Span for TestSpan {
651-
fn add_event_with_timestamp(&self, _: String, _: SystemTime, _: Vec<KeyValue>) {}
648+
fn add_event_with_timestamp(&mut self, _: String, _: SystemTime, _: Vec<KeyValue>) {}
652649
fn span_context(&self) -> &otel::SpanContext {
653650
&self.0
654651
}
655652
fn is_recording(&self) -> bool {
656653
false
657654
}
658-
fn set_attribute(&self, _attribute: KeyValue) {}
659-
fn set_status(&self, _code: otel::StatusCode, _message: String) {}
660-
fn update_name(&self, _new_name: String) {}
661-
fn end_with_timestamp(&self, _timestamp: SystemTime) {}
655+
fn set_attribute(&mut self, _attribute: KeyValue) {}
656+
fn set_status(&mut self, _code: otel::StatusCode, _message: String) {}
657+
fn update_name(&mut self, _new_name: String) {}
658+
fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
662659
}
663660

664661
#[test]
@@ -720,7 +717,7 @@ mod tests {
720717
.status_message
721718
.clone();
722719

723-
assert_eq!(recorded_status_message, Some(message.to_string()))
720+
assert_eq!(recorded_status_message, Some(message.into()))
724721
}
725722

726723
#[test]
@@ -748,8 +745,6 @@ mod tests {
748745
.as_ref()
749746
.unwrap()
750747
.parent_context
751-
.as_ref()
752-
.unwrap()
753748
.span()
754749
.span_context()
755750
.trace_id();

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_subscriber(move |(id, subscriber)| {
7979
if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
8080
get_context.with_context(subscriber, 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/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
///
@@ -52,10 +51,7 @@ pub trait PreSampledTracer {
5251

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

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

8175
// Gather trace state
8276
let (no_parent, trace_id, remote_parent, parent_trace_flags) =
@@ -86,7 +80,7 @@ impl PreSampledTracer for Tracer {
8680
let (flags, trace_state) = if let Some(result) = &builder.sampling_result {
8781
process_sampling_result(result, parent_trace_flags)
8882
} else if no_parent || remote_parent {
89-
builder.sampling_result = Some(provider.config().default_sampler.should_sample(
83+
builder.sampling_result = Some(provider.config().sampler.should_sample(
9084
Some(&parent_cx),
9185
trace_id,
9286
&builder.name,
@@ -110,7 +104,7 @@ impl PreSampledTracer for Tracer {
110104

111105
let span_id = builder.span_id.unwrap_or_else(SpanId::invalid);
112106
let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state);
113-
parent_cx.with_span(CompatSpan(span_context))
107+
parent_cx.with_remote_span_context(span_context)
114108
}
115109

116110
fn new_trace_id(&self) -> otel::TraceId {
@@ -126,31 +120,14 @@ impl PreSampledTracer for Tracer {
126120
}
127121
}
128122

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

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

257183
assert!(span_context.is_valid());
258184
}
@@ -288,7 +214,7 @@ mod tests {
288214
.build();
289215
let tracer = provider.get_tracer("test", None);
290216
let mut builder = SpanBuilder::from_name("parent".to_string());
291-
builder.parent_context = Some(parent_cx);
217+
builder.parent_context = parent_cx;
292218
builder.sampling_result = previous_sampling_result;
293219
let sampled = tracer.sampled_context(&mut builder);
294220

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::subscriber::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::subscriber::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::subscriber::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)