opentelemetry: update to latest otel release version (#1049) (#1099)

This backports  #1049, which was already approved on master.

## Motivation

Support the latest OpenTelemetry specification

## Solution

In order to support the latest spec, this patch makes the following
breaking API changes:

* Update `opentelemetry` to 0.10.x
* Update `CompatSpan` to reflect changes to `Span` trait
* Record `u64` values as strings as they are no longer supported in
  OpenTelemetry.

Additionally the following non-public api, doc, and example changes:

* Update examples and docs to use new simplified pipeline builder.
* As `opentelemetry::api` no longer exports trace types, internally use
`opentelemetry::trace as otel` to disambiguate from tracing types.
* Remove `rand` dependency as it is no longer needed

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
# Conflicts:
#	examples/examples/opentelemetry-remote-context.rs
#	tracing-opentelemetry/Cargo.toml
#	tracing-opentelemetry/src/layer.rs
#	tracing-opentelemetry/src/span_ext.rs
#	tracing-opentelemetry/src/tracer.rs
This commit is contained in:
Julian Tescher 2020-11-13 12:16:22 -08:00 committed by GitHub
parent 692c56f630
commit 642ad19d62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 219 additions and 280 deletions

View File

@ -52,5 +52,5 @@ inferno = "0.10.0"
tempdir = "0.3.7"
# opentelemetry example
opentelemetry = { version = "0.8", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.7"
opentelemetry = { version = "0.10", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.9"

View File

@ -1,13 +1,14 @@
use opentelemetry::{api, api::HttpTextFormat};
use opentelemetry::sdk::propagation::B3Propagator;
use opentelemetry::{global, Context};
use std::collections::HashMap;
use tracing::span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
fn make_request(_cx: api::Context) {
fn make_request(_cx: Context) {
// perform external request after injecting context
// e.g. if there are request headers that impl `opentelemetry::api::Carrier`
// e.g. if there are request headers that impl `opentelemetry::propagation::Injector`
// then `propagator.inject_context(cx, request.headers_mut())`
}
@ -22,14 +23,15 @@ fn build_example_carrier() -> HashMap<String, String> {
}
fn main() {
// Set a format for propagating context. This MUST be provided, as the default is a no-op.
global::set_text_map_propagator(B3Propagator::new());
let subscriber = Registry::default().with(tracing_opentelemetry::layer());
// Propagator can be swapped with trace context propagator binary propagator, etc.
let propagator = api::B3Propagator::new();
tracing::subscriber::with_default(subscriber, || {
// Extract from request headers, or any type that impls `opentelemetry::api::Carrier`
let parent_context = propagator.extract(&build_example_carrier());
// Extract context from request headers
let parent_context = global::get_text_map_propagator(|propagator| {
propagator.extract(&build_example_carrier())
});
// Generate tracing span as usual
let app_root = span!(tracing::Level::INFO, "app_start");

View File

@ -1,6 +1,4 @@
use opentelemetry::api::Provider;
use opentelemetry::sdk;
use std::{thread, time::Duration};
use std::{error::Error, thread, time::Duration};
use tracing::{span, trace, warn};
use tracing_attributes::instrument;
use tracing_subscriber::prelude::*;
@ -16,34 +14,15 @@ fn expensive_work() -> &'static str {
"success"
}
fn init_tracer() -> Result<(), Box<dyn std::error::Error>> {
let exporter = opentelemetry_jaeger::Exporter::builder()
.with_agent_endpoint("127.0.0.1:6831".parse().unwrap())
.with_process(opentelemetry_jaeger::Process {
service_name: "report_example".to_string(),
tags: Vec::new(),
})
.init()?;
let provider = sdk::Provider::builder()
.with_simple_exporter(exporter)
.with_config(sdk::Config {
default_sampler: Box::new(sdk::Sampler::AlwaysOn),
..Default::default()
})
.build();
let tracer = provider.get_tracer("tracing");
fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
.with_service_name("report_example")
.install()?;
let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
tracing_subscriber::registry()
.with(opentelemetry)
.try_init()?;
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
init_tracer()?;
let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
let _enter = root.enter();

View File

@ -22,12 +22,11 @@ edition = "2018"
default = ["tracing-log"]
[dependencies]
opentelemetry = { version = "0.8", default-features = false, features = ["trace"] }
rand = "0.7"
opentelemetry = { version = "0.10", default-features = false, features = ["trace"] }
tracing = { path = "../tracing", version = "0.1" }
tracing-core = { path = "../tracing-core", version = "0.1" }
tracing-subscriber = { path = "../tracing-subscriber", version = "0.2", default-features = false, features = ["registry"] }
tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true }
[dev-dependencies]
opentelemetry-jaeger = "0.7"
opentelemetry-jaeger = "0.9"

View File

@ -59,22 +59,25 @@ The crate provides the following types:
### Basic Usage
```rust
use opentelemetry::{api::Provider, sdk};
use opentelemetry::exporter::trace::stdout;
use tracing::{error, span};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
fn main() {
// Create a new tracer
let tracer = sdk::Provider::default().get_tracer("component_name");
// Install a new OpenTelemetry trace pipeline
let (tracer, _uninstall) = stdout::new_pipeline().install();
// Create a new OpenTelemetry tracing layer
// Create a tracing layer with the configured tracer
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
// Use the tracing subscriber `Registry`, or any other subscriber
// that impls `LookupSpan`
let subscriber = Registry::default().with(telemetry);
// Trace executed code
tracing::subscriber::with_default(subscriber, || {
// Spans will be sent to the configured OpenTelemetry exporter
let root = span!(tracing::Level::TRACE, "app_start", work_units = 2);
let _enter = root.enter();

View File

@ -1,5 +1,5 @@
use crate::PreSampledTracer;
use opentelemetry::api::{self, Context as OtelContext, TraceContextExt};
use opentelemetry::{trace as otel, trace::TraceContextExt, Context as OtelContext, Key, KeyValue};
use std::any::TypeId;
use std::fmt;
use std::marker;
@ -27,12 +27,12 @@ pub struct OpenTelemetryLayer<S, T> {
_registry: marker::PhantomData<S>,
}
impl<S> Default for OpenTelemetryLayer<S, api::NoopTracer>
impl<S> Default for OpenTelemetryLayer<S, otel::NoopTracer>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
fn default() -> Self {
OpenTelemetryLayer::new(api::NoopTracer {})
OpenTelemetryLayer::new(otel::NoopTracer::new())
}
}
@ -51,7 +51,7 @@ where
/// let subscriber = Registry::default().with(tracing_opentelemetry::layer());
/// # drop(subscriber);
/// ```
pub fn layer<S>() -> OpenTelemetryLayer<S, api::NoopTracer>
pub fn layer<S>() -> OpenTelemetryLayer<S, otel::NoopTracer>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
@ -67,7 +67,7 @@ pub(crate) struct WithContext(
fn(
&tracing::Dispatch,
&span::Id,
f: &mut dyn FnMut(&mut api::SpanBuilder, &dyn PreSampledTracer),
f: &mut dyn FnMut(&mut otel::SpanBuilder, &dyn PreSampledTracer),
),
);
@ -78,34 +78,34 @@ impl WithContext {
&self,
dispatch: &'a tracing::Dispatch,
id: &span::Id,
mut f: impl FnMut(&mut api::SpanBuilder, &dyn PreSampledTracer),
mut f: impl FnMut(&mut otel::SpanBuilder, &dyn PreSampledTracer),
) {
(self.0)(dispatch, id, &mut f)
}
}
fn str_to_span_kind(s: &str) -> Option<api::SpanKind> {
fn str_to_span_kind(s: &str) -> Option<otel::SpanKind> {
if s.eq_ignore_ascii_case("SERVER") {
Some(api::SpanKind::Server)
Some(otel::SpanKind::Server)
} else if s.eq_ignore_ascii_case("CLIENT") {
Some(api::SpanKind::Client)
Some(otel::SpanKind::Client)
} else if s.eq_ignore_ascii_case("PRODUCER") {
Some(api::SpanKind::Producer)
Some(otel::SpanKind::Producer)
} else if s.eq_ignore_ascii_case("CONSUMER") {
Some(api::SpanKind::Consumer)
Some(otel::SpanKind::Consumer)
} else if s.eq_ignore_ascii_case("INTERNAL") {
Some(api::SpanKind::Internal)
Some(otel::SpanKind::Internal)
} else {
None
}
}
struct SpanEventVisitor<'a>(&'a mut api::Event);
struct SpanEventVisitor<'a>(&'a mut otel::Event);
impl<'a> field::Visit for SpanEventVisitor<'a> {
/// Record events on the underlying OpenTelemetry [`Span`] from `bool` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_bool(&mut self, field: &field::Field, value: bool) {
match field.name() {
"message" => self.0.name = value.to_string(),
@ -113,14 +113,14 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => (),
name => {
self.0.attributes.push(api::KeyValue::new(name, value));
self.0.attributes.push(KeyValue::new(name, value));
}
}
}
/// Record events on the underlying OpenTelemetry [`Span`] from `i64` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_i64(&mut self, field: &field::Field, value: i64) {
match field.name() {
"message" => self.0.name = value.to_string(),
@ -128,29 +128,14 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => (),
name => {
self.0.attributes.push(api::KeyValue::new(name, value));
}
}
}
/// Record events on the underlying OpenTelemetry [`Span`] from `u64` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
fn record_u64(&mut self, field: &field::Field, value: u64) {
match field.name() {
"message" => self.0.name = value.to_string(),
// Skip fields that are actually log metadata that have already been handled
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => (),
name => {
self.0.attributes.push(api::KeyValue::new(name, value));
self.0.attributes.push(KeyValue::new(name, value));
}
}
}
/// Record events on the underlying OpenTelemetry [`Span`] from `&str` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_str(&mut self, field: &field::Field, value: &str) {
match field.name() {
"message" => self.0.name = value.to_string(),
@ -158,7 +143,9 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
#[cfg(feature = "tracing-log")]
name if name.starts_with("log.") => (),
name => {
self.0.attributes.push(api::KeyValue::new(name, value));
self.0
.attributes
.push(KeyValue::new(name, value.to_string()));
}
}
}
@ -166,7 +153,7 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
/// Record events on the underlying OpenTelemetry [`Span`] from values that
/// implement Debug.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
match field.name() {
"message" => self.0.name = format!("{:?}", value),
@ -176,20 +163,20 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
name => {
self.0
.attributes
.push(api::KeyValue::new(name, format!("{:?}", value)));
.push(KeyValue::new(name, format!("{:?}", value)));
}
}
}
}
struct SpanAttributeVisitor<'a>(&'a mut api::SpanBuilder);
struct SpanAttributeVisitor<'a>(&'a mut otel::SpanBuilder);
impl<'a> field::Visit for SpanAttributeVisitor<'a> {
/// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_bool(&mut self, field: &field::Field, value: bool) {
let attribute = api::KeyValue::new(field.name(), value);
let attribute = KeyValue::new(field.name(), value);
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
@ -199,21 +186,9 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
/// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_i64(&mut self, field: &field::Field, value: i64) {
let attribute = api::KeyValue::new(field.name(), value);
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
self.0.attributes = Some(vec![attribute]);
}
}
/// Set attributes on the underlying OpenTelemetry [`Span`] from `u64` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
fn record_u64(&mut self, field: &field::Field, value: u64) {
let attribute = api::KeyValue::new(field.name(), value);
let attribute = KeyValue::new(field.name(), value);
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
@ -223,14 +198,14 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
/// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_str(&mut self, field: &field::Field, value: &str) {
if field.name() == SPAN_NAME_FIELD {
self.0.name = value.to_string();
} else if field.name() == SPAN_KIND_FIELD {
self.0.span_kind = str_to_span_kind(value);
} else {
let attribute = api::KeyValue::new(field.name(), value);
let attribute = KeyValue::new(field.name(), value.to_string());
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
@ -242,14 +217,14 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
/// Set attributes on the underlying OpenTelemetry [`Span`] from values that
/// implement Debug.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
if field.name() == SPAN_NAME_FIELD {
self.0.name = format!("{:?}", value);
} else if field.name() == SPAN_KIND_FIELD {
self.0.span_kind = str_to_span_kind(&format!("{:?}", value));
} else {
let attribute = api::Key::new(field.name()).string(format!("{:?}", value));
let attribute = Key::new(field.name()).string(format!("{:?}", value));
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
@ -262,42 +237,25 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
impl<S, T> OpenTelemetryLayer<S, T>
where
S: Subscriber + for<'span> LookupSpan<'span>,
T: api::Tracer + PreSampledTracer + 'static,
T: otel::Tracer + PreSampledTracer + 'static,
{
/// Set the [`Tracer`] that this layer will use to produce and track
/// OpenTelemetry [`Span`]s.
///
/// [`Tracer`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/tracer/trait.Tracer.html
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Tracer`]: opentelemetry::trace::Tracer
/// [`Span`]: opentelemetry::trace::Span
///
/// # Examples
///
/// ```rust,no_run
/// use opentelemetry::{api::Provider, sdk};
/// ```no_run
/// use tracing_opentelemetry::OpenTelemetryLayer;
/// use tracing_subscriber::layer::SubscriberExt;
/// use tracing_subscriber::Registry;
///
/// // Create a jaeger exporter for a `trace-demo` service.
/// let exporter = opentelemetry_jaeger::Exporter::builder()
/// .with_agent_endpoint("127.0.0.1:6831".parse().unwrap())
/// .with_process(opentelemetry_jaeger::Process {
/// service_name: "trace_demo".to_string(),
/// tags: Vec::new(),
/// })
/// .init().expect("Error initializing Jaeger exporter");
///
/// // Build a provider from the jaeger exporter that always samples.
/// let provider = sdk::Provider::builder()
/// .with_simple_exporter(exporter)
/// .with_config(sdk::Config {
/// default_sampler: Box::new(sdk::Sampler::AlwaysOn),
/// ..Default::default()
/// })
/// .build();
///
/// // Get a tracer from the provider for a component
/// let tracer = provider.get_tracer("component-name");
/// // Create a jaeger exporter pipeline for a `trace_demo` service.
/// let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
/// .with_service_name("trace_demo")
/// .install().expect("Error initializing Jaeger exporter");
///
/// // Create a layer with the configured tracer
/// let otel_layer = OpenTelemetryLayer::new(tracer);
@ -318,49 +276,31 @@ where
/// Set the [`Tracer`] that this layer will use to produce and track
/// OpenTelemetry [`Span`]s.
///
/// [`Tracer`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/tracer/trait.Tracer.html
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Tracer`]: opentelemetry::trace::Tracer
/// [`Span`]: opentelemetry::trace::Span
///
/// # Examples
///
/// ```rust,no_run
/// use opentelemetry::{api::Provider, sdk};
/// ```no_run
/// use tracing_subscriber::layer::SubscriberExt;
/// use tracing_subscriber::Registry;
///
/// // Create a jaeger exporter for a `trace-demo` service.
/// let exporter = opentelemetry_jaeger::Exporter::builder()
/// .with_agent_endpoint("127.0.0.1:6831".parse().unwrap())
/// .with_process(opentelemetry_jaeger::Process {
/// service_name: "trace_demo".to_string(),
/// tags: Vec::new(),
/// })
/// .init().expect("Error initializing Jaeger exporter");
///
/// // Build a provider from the jaeger exporter that always samples.
/// let provider = sdk::Provider::builder()
/// .with_simple_exporter(exporter)
/// .with_config(sdk::Config {
/// default_sampler: Box::new(sdk::Sampler::AlwaysOn),
/// ..Default::default()
/// })
/// .build();
///
/// // Get a tracer from the provider for a component
/// let tracer = provider.get_tracer("component-name");
/// // Create a jaeger exporter pipeline for a `trace_demo` service.
/// let (tracer, _uninstall) = opentelemetry_jaeger::new_pipeline()
/// .with_service_name("trace_demo")
/// .install().expect("Error initializing Jaeger exporter");
///
/// // Create a layer with the configured tracer
/// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer);
///
/// // Use the tracing subscriber `Registry`, or any other subscriber
/// // that impls `LookupSpan`
/// let subscriber = Registry::default()
/// .with(otel_layer);
/// let subscriber = Registry::default().with(otel_layer);
/// # drop(subscriber);
/// ```
pub fn with_tracer<Tracer>(self, tracer: Tracer) -> OpenTelemetryLayer<S, Tracer>
where
Tracer: api::Tracer + PreSampledTracer + 'static,
Tracer: otel::Tracer + PreSampledTracer + 'static,
{
OpenTelemetryLayer {
tracer,
@ -373,27 +313,27 @@ where
/// tracing [`span`] through the [`Registry`]. This [`SpanContext`]
/// links spans to their parent for proper hierarchical visualization.
///
/// [`SpanContext`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span_context/struct.SpanContext.html
/// [`span`]: https://docs.rs/tracing/latest/tracing/struct.Span.html
/// [`Registry`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/struct.Registry.html
/// [`SpanContext`]: opentelemetry::trace::SpanContext
/// [`span`]: tracing::Span
/// [`Registry`]: tracing_subscriber::Registry
fn parent_span_context(
&self,
attrs: &Attributes<'_>,
ctx: &Context<'_, S>,
) -> Option<api::SpanContext> {
) -> Option<otel::SpanContext> {
// If a span is specified, it _should_ exist in the underlying `Registry`.
if let Some(parent) = attrs.parent() {
let span = ctx.span(parent).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
extensions
.get_mut::<api::SpanBuilder>()
.get_mut::<otel::SpanBuilder>()
.map(|builder| self.tracer.sampled_span_context(builder))
// Else if the span is inferred from context, look up any available current span.
} else if attrs.is_contextual() {
ctx.lookup_current().and_then(|span| {
let mut extensions = span.extensions_mut();
extensions
.get_mut::<api::SpanBuilder>()
.get_mut::<otel::SpanBuilder>()
.map(|builder| self.tracer.sampled_span_context(builder))
})
// Explicit root spans should have no parent context.
@ -405,7 +345,7 @@ where
fn get_context(
dispatch: &tracing::Dispatch,
id: &span::Id,
f: &mut dyn FnMut(&mut api::SpanBuilder, &dyn PreSampledTracer),
f: &mut dyn FnMut(&mut otel::SpanBuilder, &dyn PreSampledTracer),
) {
let subscriber = dispatch
.downcast_ref::<S>()
@ -418,7 +358,7 @@ where
.expect("layer should downcast to expected type; this is a bug!");
let mut extensions = span.extensions_mut();
if let Some(builder) = extensions.get_mut::<api::SpanBuilder>() {
if let Some(builder) = extensions.get_mut::<otel::SpanBuilder>() {
f(builder, &layer.tracer);
}
}
@ -427,12 +367,12 @@ where
impl<S, T> Layer<S> for OpenTelemetryLayer<S, T>
where
S: Subscriber + for<'span> LookupSpan<'span>,
T: api::Tracer + PreSampledTracer + 'static,
T: otel::Tracer + PreSampledTracer + 'static,
{
/// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`].
///
/// [OpenTelemetry `Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [tracing `Span`]: https://docs.rs/tracing/latest/tracing/struct.Span.html
/// [OpenTelemetry `Span`]: opentelemetry::trace::Span
/// [tracing `Span`]: tracing::Span
fn new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
@ -449,7 +389,8 @@ where
// Ensure trace id exists so children are matched properly.
if builder.parent_context.is_none() {
let existing_otel_span_context = OtelContext::current().span().span_context();
let cx = OtelContext::current();
let existing_otel_span_context = cx.span().span_context();
if existing_otel_span_context.is_valid() {
builder.trace_id = Some(existing_otel_span_context.trace_id());
} else {
@ -463,11 +404,11 @@ where
/// Record OpenTelemetry [`attributes`] for the given values.
///
/// [`attributes`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/tracer/struct.SpanBuilder.html#structfield.attributes
/// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes
fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
if let Some(builder) = extensions.get_mut::<api::SpanBuilder>() {
if let Some(builder) = extensions.get_mut::<otel::SpanBuilder>() {
values.record(&mut SpanAttributeVisitor(builder));
}
}
@ -476,7 +417,7 @@ where
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
let builder = extensions
.get_mut::<api::SpanBuilder>()
.get_mut::<otel::SpanBuilder>()
.expect("Missing SpanBuilder span extensions");
let follows_span = ctx
@ -484,11 +425,11 @@ where
.expect("Span to follow not found, this is a bug");
let mut follows_extensions = follows_span.extensions_mut();
let follows_builder = follows_extensions
.get_mut::<api::SpanBuilder>()
.get_mut::<otel::SpanBuilder>()
.expect("Missing SpanBuilder span extensions");
let follows_context = self.tracer.sampled_span_context(follows_builder);
let follows_link = api::Link::new(follows_context, Vec::new());
let follows_link = otel::Link::new(follows_context, Vec::new());
if let Some(ref mut links) = builder.links {
links.push(follows_link);
} else {
@ -499,11 +440,11 @@ where
/// Records OpenTelemetry [`Event`] data on event.
///
/// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to
/// [`Unknown`], signaling that an error has occurred.
/// [`Error`], signaling that an error has occurred.
///
/// [`Event`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/event/struct.Event.html
/// [`ERROR`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.ERROR
/// [`Unknown`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/enum.StatusCode.html#variant.Unknown
/// [`Event`]: opentelemetry::trace::Event
/// [`ERROR`]: tracing::Level::ERROR
/// [`Error`]: opentelemetry::trace::StatusCode::Error
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
// Ignore events that are not in the context of a span
if let Some(span) = ctx.lookup_current() {
@ -515,20 +456,20 @@ where
let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
#[cfg(not(feature = "tracing-log"))]
let meta = event.metadata();
let mut otel_event = api::Event::new(
let mut otel_event = otel::Event::new(
String::new(),
SystemTime::now(),
vec![
api::Key::new("level").string(meta.level().to_string()),
api::Key::new("target").string(meta.target()),
Key::new("level").string(meta.level().to_string()),
Key::new("target").string(meta.target().to_string()),
],
);
event.record(&mut SpanEventVisitor(&mut otel_event));
let mut extensions = span.extensions_mut();
if let Some(builder) = extensions.get_mut::<api::SpanBuilder>() {
if let Some(builder) = extensions.get_mut::<otel::SpanBuilder>() {
if builder.status_code.is_none() && *meta.level() == tracing_core::Level::ERROR {
builder.status_code = Some(api::StatusCode::Unknown);
builder.status_code = Some(otel::StatusCode::Error);
}
if let Some(ref mut events) = builder.message_events {
@ -542,11 +483,11 @@ where
/// Exports an OpenTelemetry [`Span`] on close.
///
/// [`Span`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span/trait.Span.html
/// [`Span`]: opentelemetry::trace::Span
fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
let span = ctx.span(&id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
if let Some(builder) = extensions.remove::<api::SpanBuilder>() {
if let Some(builder) = extensions.remove::<otel::SpanBuilder>() {
// Assign end time, build and start span, drop span to export
builder.with_end_time(SystemTime::now()).start(&self.tracer);
}
@ -568,57 +509,55 @@ where
#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::api;
use opentelemetry::api::TraceContextExt;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use tracing_subscriber::prelude::*;
#[derive(Debug, Clone)]
struct TestTracer(Arc<Mutex<Option<api::SpanBuilder>>>);
impl api::Tracer for TestTracer {
type Span = api::NoopSpan;
struct TestTracer(Arc<Mutex<Option<otel::SpanBuilder>>>);
impl otel::Tracer for TestTracer {
type Span = otel::NoopSpan;
fn invalid(&self) -> Self::Span {
api::NoopSpan::new()
otel::NoopSpan::new()
}
fn start_from_context(&self, _name: &str, _context: &api::Context) -> Self::Span {
fn start_from_context(&self, _name: &str, _context: &OtelContext) -> Self::Span {
self.invalid()
}
fn span_builder(&self, name: &str) -> api::SpanBuilder {
api::SpanBuilder::from_name(name.to_string())
fn span_builder(&self, name: &str) -> otel::SpanBuilder {
otel::SpanBuilder::from_name(name.to_string())
}
fn build_with_context(&self, builder: api::SpanBuilder, _cx: &api::Context) -> Self::Span {
fn build_with_context(&self, builder: otel::SpanBuilder, _cx: &OtelContext) -> Self::Span {
*self.0.lock().unwrap() = Some(builder);
self.invalid()
}
}
impl PreSampledTracer for TestTracer {
fn sampled_span_context(&self, _builder: &mut api::SpanBuilder) -> api::SpanContext {
api::SpanContext::empty_context()
fn sampled_span_context(&self, _builder: &mut otel::SpanBuilder) -> otel::SpanContext {
otel::SpanContext::empty_context()
}
fn new_trace_id(&self) -> api::TraceId {
api::TraceId::invalid()
fn new_trace_id(&self) -> otel::TraceId {
otel::TraceId::invalid()
}
fn new_span_id(&self) -> api::SpanId {
api::SpanId::invalid()
fn new_span_id(&self) -> otel::SpanId {
otel::SpanId::invalid()
}
}
#[derive(Debug, Clone)]
struct TestSpan(api::SpanContext);
impl api::Span for TestSpan {
fn add_event_with_timestamp(&self, _: String, _: SystemTime, _: Vec<api::KeyValue>) {}
fn span_context(&self) -> api::SpanContext {
self.0.clone()
struct TestSpan(otel::SpanContext);
impl otel::Span for TestSpan {
fn add_event_with_timestamp(&self, _: String, _: SystemTime, _: Vec<KeyValue>) {}
fn span_context(&self) -> &otel::SpanContext {
&self.0
}
fn is_recording(&self) -> bool {
false
}
fn set_attribute(&self, _attribute: api::KeyValue) {}
fn set_status(&self, _code: api::StatusCode, _message: String) {}
fn set_attribute(&self, _attribute: KeyValue) {}
fn set_status(&self, _code: otel::StatusCode, _message: String) {}
fn update_name(&self, _new_name: String) {}
fn end(&self) {}
fn end_with_timestamp(&self, _timestamp: SystemTime) {}
}
#[test]
@ -645,19 +584,20 @@ mod tests {
});
let recorded_kind = tracer.0.lock().unwrap().as_ref().unwrap().span_kind.clone();
assert_eq!(recorded_kind, Some(api::SpanKind::Server))
assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
}
#[test]
fn trace_id_from_existing_context() {
let tracer = TestTracer(Arc::new(Mutex::new(None)));
let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
let trace_id = api::TraceId::from_u128(42);
let existing_cx = api::Context::current_with_span(TestSpan(api::SpanContext::new(
let trace_id = otel::TraceId::from_u128(42);
let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
trace_id,
api::SpanId::from_u64(1),
otel::SpanId::from_u64(1),
0,
false,
Default::default(),
)));
let _g = existing_cx.attach();

View File

@ -43,25 +43,27 @@
//! ### Stability Status
//!
//! The OpenTelemetry specification is currently in beta so some breaking
//! may still occur on the path to 1.0. You can follow the changes via the
//! [spec repository] to track progress toward stabilization.
//! changes may still occur on the path to 1.0. You can follow the changes via
//! the [spec repository] to track progress toward stabilization.
//!
//! [spec repository]: https://github.com/open-telemetry/opentelemetry-specification
//!
//! ## Examples
//!
//! ```
//! use opentelemetry::{api::Provider, sdk};
//! use opentelemetry::exporter::trace::stdout;
//! use tracing::{error, span};
//! use tracing_subscriber::layer::SubscriberExt;
//! use tracing_subscriber::Registry;
//!
//! // Create a new tracer
//! let tracer = sdk::Provider::default().get_tracer("service_name");
//! // Create a new OpenTelemetry pipeline
//! let (tracer, _uninstall) = stdout::new_pipeline().install();
//!
//! // Create a new OpenTelemetry tracing layer
//! // Create a tracing layer with the configured tracer
//! let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
//!
//! // Use the tracing subscriber `Registry`, or any other subscriber
//! // that impls `LookupSpan`
//! let subscriber = Registry::default().with(telemetry);
//!
//! // Trace executed code

View File

@ -1,32 +1,33 @@
use crate::layer::WithContext;
use opentelemetry::api;
use opentelemetry::api::TraceContextExt;
use opentelemetry::{trace as otel, trace::TraceContextExt, Context, KeyValue};
use std::time::SystemTime;
/// Utility functions to allow tracing [`Span`]s to accept and return
/// [OpenTelemetry] [`Context`]s.
///
/// [`Span`]: https://docs.rs/tracing/latest/tracing/struct.Span.html
/// [OpenTelemetry]: https://opentelemetry.io
/// [`Context`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/context/struct.Context.html
/// [`Context`]: opentelemetry::Context
pub trait OpenTelemetrySpanExt {
/// Associates `self` with a given OpenTelemetry trace, using the provided
/// parent [`Context`].
///
/// [`Context`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/context/struct.Context.html
/// [`Context`]: opentelemetry::Context
///
/// # Examples
///
/// ```rust
/// use opentelemetry::api::{self, HttpTextFormat, TraceContextExt};
/// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
/// use opentelemetry::sdk::propagation::B3Propagator;
/// use tracing_opentelemetry::OpenTelemetrySpanExt;
/// use std::collections::HashMap;
/// use tracing::Span;
///
/// // Example carrier, could be a framework header map that impls `api::Carrier`.
/// // Example carrier, could be a framework header map that impls otel's `Extract`.
/// let mut carrier = HashMap::new();
///
/// // Propagator can be swapped with trace context propagator binary propagator, etc.
/// let propagator = api::B3Propagator::new();
/// // Propagator can be swapped with trace context propagator, binary propagator, etc.
/// let propagator = B3Propagator::new();
///
/// // Extract otel parent context via the chosen propagator
/// let parent_context = propagator.extract(&carrier);
@ -40,22 +41,22 @@ pub trait OpenTelemetrySpanExt {
/// // Or if the current span has been created elsewhere:
/// Span::current().set_parent(&parent_context);
/// ```
fn set_parent(&self, span_context: &api::Context);
fn set_parent(&self, cx: &Context);
/// Extracts an OpenTelemetry [`Context`] from `self`.
///
/// [`Context`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/context/struct.Context.html
/// [`Context`]: opentelemetry::Context
///
/// # Examples
///
/// ```rust
/// use opentelemetry::api;
/// use opentelemetry::Context;
/// use tracing_opentelemetry::OpenTelemetrySpanExt;
/// use tracing::Span;
///
/// fn make_request(cx: api::Context) {
/// fn make_request(cx: Context) {
/// // perform external request after injecting context
/// // e.g. if there are request headers that impl `opentelemetry::api::Carrier`
/// // e.g. if the request's headers impl `opentelemetry::propagation::Injector`
/// // then `propagator.inject_context(cx, request.headers_mut())`
/// }
///
@ -69,21 +70,21 @@ pub trait OpenTelemetrySpanExt {
/// // Or if the current span has been created elsewhere:
/// make_request(Span::current().context())
/// ```
fn context(&self) -> api::Context;
fn context(&self) -> Context;
}
impl OpenTelemetrySpanExt for tracing::Span {
fn set_parent(&self, parent_context: &api::Context) {
fn set_parent(&self, cx: &Context) {
self.with_subscriber(move |(id, subscriber)| {
if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
get_context.with_context(subscriber, id, move |builder, _tracer| {
builder.parent_context = parent_context.remote_span_context().cloned()
builder.parent_context = cx.remote_span_context().cloned()
});
}
});
}
fn context(&self) -> api::Context {
fn context(&self) -> Context {
let mut span_context = None;
self.with_subscriber(|(id, subscriber)| {
if let Some(get_context) = subscriber.downcast_ref::<WithContext>() {
@ -93,20 +94,21 @@ impl OpenTelemetrySpanExt for tracing::Span {
}
});
let compat_span = CompatSpan(span_context.unwrap_or_else(api::SpanContext::empty_context));
api::Context::current_with_span(compat_span)
let span_context = span_context.unwrap_or_else(otel::SpanContext::empty_context);
let compat_span = CompatSpan(span_context);
Context::current_with_span(compat_span)
}
}
/// A compatibility wrapper for an injectable OpenTelemetry span context.
#[derive(Debug)]
struct CompatSpan(api::SpanContext);
impl api::Span for CompatSpan {
struct CompatSpan(otel::SpanContext);
impl otel::Span for CompatSpan {
fn add_event_with_timestamp(
&self,
_name: String,
_timestamp: std::time::SystemTime,
_attributes: Vec<api::KeyValue>,
_attributes: Vec<KeyValue>,
) {
#[cfg(debug_assertions)]
panic!(
@ -115,11 +117,11 @@ impl api::Span for CompatSpan {
}
/// This method is used by OpenTelemetry propagators to inject span context
/// information into [`Carrier`]s.
/// information into [`Injector`]s.
///
/// [`Carrier`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/context/propagation/trait.Carrier.html
fn span_context(&self) -> api::SpanContext {
self.0.clone()
/// [`Injector`]: opentelemetry::propagation::Injector
fn span_context(&self) -> &otel::SpanContext {
&self.0
}
fn is_recording(&self) -> bool {
@ -130,12 +132,12 @@ impl api::Span for CompatSpan {
false
}
fn set_attribute(&self, _attribute: api::KeyValue) {
fn set_attribute(&self, _attribute: KeyValue) {
#[cfg(debug_assertions)]
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` instead.");
}
fn set_status(&self, _code: api::StatusCode, _message: String) {
fn set_status(&self, _code: otel::StatusCode, _message: String) {
#[cfg(debug_assertions)]
panic!("OpenTelemetry and tracing APIs cannot be mixed, use `tracing::span!` macro or `span.record()` instead.");
}
@ -145,7 +147,7 @@ impl api::Span for CompatSpan {
panic!("OpenTelemetry and tracing APIs cannot be mixed, span names are not mutable.");
}
fn end(&self) {
fn end_with_timestamp(&self, _timestamp: SystemTime) {
#[cfg(debug_assertions)]
panic!("OpenTelemetry and tracing APIs cannot be mixed, span end times are set when the underlying tracing span closes.");
}

View File

@ -1,4 +1,5 @@
use opentelemetry::{api, sdk};
use opentelemetry::sdk::trace::{SamplingDecision, Tracer};
use opentelemetry::trace as otel;
/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers.
///
@ -20,67 +21,71 @@ use opentelemetry::{api, sdk};
/// See the [`OpenTelemetrySpanExt::set_parent`] and
/// [`OpenTelemetrySpanExt::context`] methods for example usage.
///
/// [`Tracer`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/tracer/trait.Tracer.html
/// [`SpanBuilder`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/tracer/struct.SpanBuilder.html
/// [`SpanContext`]: https://docs.rs/opentelemetry/latest/opentelemetry/api/trace/span_context/struct.SpanContext.html
/// [`PreSampledTracer::sampled_span_context`]: trait.PreSampledTracer.html#tymethod.sampled_span_context
/// [`OpenTelemetrySpanExt::set_parent`]: trait.OpenTelemetrySpanExt.html#tymethod.set_parent
/// [`OpenTelemetrySpanExt::context`]: trait.OpenTelemetrySpanExt.html#tymethod.context
/// [`Tracer`]: opentelemetry::trace::Tracer
/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder
/// [`SpanContext`]: opentelemetry::trace::SpanContext
/// [`PreSampledTracer::sampled_span_context`]: crate::PreSampledTracer::sampled_span_context
/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent
/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context
pub trait PreSampledTracer {
/// Produce a pre-sampled span context for the given span builder.
fn sampled_span_context(&self, builder: &mut api::SpanBuilder) -> api::SpanContext;
fn sampled_span_context(&self, builder: &mut otel::SpanBuilder) -> otel::SpanContext;
/// Generate a new trace id.
fn new_trace_id(&self) -> api::TraceId;
fn new_trace_id(&self) -> otel::TraceId;
/// Generate a new span id.
fn new_span_id(&self) -> api::SpanId;
fn new_span_id(&self) -> otel::SpanId;
}
impl PreSampledTracer for api::NoopTracer {
fn sampled_span_context(&self, builder: &mut api::SpanBuilder) -> api::SpanContext {
impl PreSampledTracer for otel::NoopTracer {
fn sampled_span_context(&self, builder: &mut otel::SpanBuilder) -> otel::SpanContext {
builder
.parent_context
.clone()
.unwrap_or_else(api::SpanContext::empty_context)
.unwrap_or_else(otel::SpanContext::empty_context)
}
fn new_trace_id(&self) -> api::TraceId {
api::TraceId::invalid()
fn new_trace_id(&self) -> otel::TraceId {
otel::TraceId::invalid()
}
fn new_span_id(&self) -> api::SpanId {
api::SpanId::invalid()
fn new_span_id(&self) -> otel::SpanId {
otel::SpanId::invalid()
}
}
impl PreSampledTracer for sdk::Tracer {
fn sampled_span_context(&self, builder: &mut api::SpanBuilder) -> api::SpanContext {
let span_id = builder
.span_id
.unwrap_or_else(|| self.provider().config().id_generator.new_span_id());
impl PreSampledTracer for Tracer {
fn sampled_span_context(&self, builder: &mut otel::SpanBuilder) -> otel::SpanContext {
let span_id = builder.span_id.unwrap_or_else(|| {
self.provider()
.map(|provider| provider.config().id_generator.new_span_id())
.unwrap_or_else(otel::SpanId::invalid)
});
let (trace_id, trace_flags) = builder
.parent_context
.as_ref()
.filter(|parent_context| parent_context.is_valid())
.map(|parent_context| (parent_context.trace_id(), parent_context.trace_flags()))
.unwrap_or_else(|| {
let trace_id = builder
.trace_id
.unwrap_or_else(|| self.provider().config().id_generator.new_trace_id());
let trace_id = builder.trace_id.unwrap_or_else(|| {
self.provider()
.map(|provider| provider.config().id_generator.new_trace_id())
.unwrap_or_else(otel::TraceId::invalid)
});
// ensure sampling decision is recorded so all span contexts have consistent flags
let sampling_decision = if let Some(result) = builder.sampling_result.as_ref() {
result.decision.clone()
} else {
let mut result = self.provider().config().default_sampler.should_sample(
} else if let Some(provider) = self.provider().as_ref() {
let mut result = provider.config().default_sampler.should_sample(
builder.parent_context.as_ref(),
trace_id,
&builder.name,
builder
.span_kind
.as_ref()
.unwrap_or(&api::SpanKind::Internal),
.unwrap_or(&otel::SpanKind::Internal),
builder.attributes.as_ref().unwrap_or(&Vec::new()),
builder.links.as_ref().unwrap_or(&Vec::new()),
);
@ -93,10 +98,12 @@ impl PreSampledTracer for sdk::Tracer {
}
result.decision
} else {
SamplingDecision::Drop
};
let trace_flags = if sampling_decision == sdk::SamplingDecision::RecordAndSampled {
api::TRACE_FLAG_SAMPLED
let trace_flags = if sampling_decision == SamplingDecision::RecordAndSample {
otel::TRACE_FLAG_SAMPLED
} else {
0
};
@ -104,27 +111,32 @@ impl PreSampledTracer for sdk::Tracer {
(trace_id, trace_flags)
});
api::SpanContext::new(trace_id, span_id, trace_flags, false)
otel::SpanContext::new(trace_id, span_id, trace_flags, false, Default::default())
}
fn new_trace_id(&self) -> api::TraceId {
self.provider().config().id_generator.new_trace_id()
fn new_trace_id(&self) -> otel::TraceId {
self.provider()
.map(|provider| provider.config().id_generator.new_trace_id())
.unwrap_or_else(otel::TraceId::invalid)
}
fn new_span_id(&self) -> api::SpanId {
self.provider().config().id_generator.new_span_id()
fn new_span_id(&self) -> otel::SpanId {
self.provider()
.map(|provider| provider.config().id_generator.new_span_id())
.unwrap_or_else(otel::SpanId::invalid)
}
}
#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::api::{Provider, SpanBuilder};
use opentelemetry::sdk;
use opentelemetry::trace::{SpanBuilder, TracerProvider};
#[test]
fn assigns_default_ids_if_missing() {
let tracer = sdk::Provider::default().get_tracer("test");
let provider = sdk::trace::TracerProvider::default();
let tracer = provider.get_tracer("test", None);
let mut builder = SpanBuilder::from_name("empty".to_string());
builder.trace_id = None;
builder.span_id = None;