Skip to content

Commit

Permalink
opentelemetry: Add extension to link spans (#1516)
Browse files Browse the repository at this point in the history
Repeat of #1480 to merge on master.

## Motivation

Discussed in #1121, the opentelemetry specifications allow adding a link
to a propagated span and/or a closed span. However, the implemented
`on_follows_from` of the `OpenTelemetryLayer` does not allow this.

## Solution

The solution follows the same model as the `set_parent` `Span` extension
function. A `add_link` function that takes the linked span context was
added to the `OpenTelemetrySpanExt`.
  • Loading branch information
LehMaxence authored and hawkw committed Sep 4, 2021
1 parent f21028f commit d394efa
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 1 deletion.
22 changes: 22 additions & 0 deletions tracing-opentelemetry/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {
}
}

/// Record events on the underlying OpenTelemetry [`Span`] from `f64` values.
///
/// [`Span`]: opentelemetry::trace::Span
fn record_f64(&mut self, field: &field::Field, value: f64) {
match field.name() {
"message" => self.0.name = value.to_string().into(),
// 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(KeyValue::new(name, value));
}
}
}

/// Record events on the underlying OpenTelemetry [`Span`] from `i64` values.
///
/// [`Span`]: opentelemetry::trace::Span
Expand Down Expand Up @@ -197,6 +212,13 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
self.record(KeyValue::new(field.name(), value));
}

/// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values.
///
/// [`Span`]: opentelemetry::trace::Span
fn record_f64(&mut self, field: &field::Field, value: f64) {
self.record(KeyValue::new(field.name(), value));
}

/// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values.
///
/// [`Span`]: opentelemetry::trace::Span
Expand Down
71 changes: 70 additions & 1 deletion tracing-opentelemetry/src/span_ext.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::layer::WithContext;
use opentelemetry::Context;
use opentelemetry::{trace::SpanContext, Context, KeyValue};

/// Utility functions to allow tracing [`Span`]s to accept and return
/// [OpenTelemetry] [`Context`]s.
Expand Down Expand Up @@ -42,6 +42,50 @@ pub trait OpenTelemetrySpanExt {
/// ```
fn set_parent(&self, cx: Context);

/// Associates `self` with a given OpenTelemetry trace, using the provided
/// followed span [`SpanContext`].
///
/// [`SpanContext`]: opentelemetry::trace::SpanContext
///
/// # Examples
///
/// ```rust
/// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
/// use opentelemetry::sdk::propagation::TraceContextPropagator;
/// use tracing_opentelemetry::OpenTelemetrySpanExt;
/// use std::collections::HashMap;
/// use tracing::Span;
///
/// // Example carrier, could be a framework header map that impls otel's `Extract`.
/// let mut carrier = HashMap::new();
///
/// // Propagator can be swapped with b3 propagator, jaeger propagator, etc.
/// let propagator = TraceContextPropagator::new();
///
/// // Extract otel context of linked span via the chosen propagator
/// let linked_span_otel_context = propagator.extract(&carrier);
///
/// // Extract the linked span context from the otel context
/// let linked_span_context = linked_span_otel_context.span().span_context().clone();
///
/// // Generate a tracing span as usual
/// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
///
/// // Assign linked trace from external context
/// app_root.add_link(linked_span_context);
///
/// // Or if the current span has been created elsewhere:
/// let linked_span_context = linked_span_otel_context.span().span_context().clone();
/// Span::current().add_link(linked_span_context);
/// ```
fn add_link(&self, cx: SpanContext);

/// Associates `self` with a given OpenTelemetry trace, using the provided
/// followed span [`SpanContext`] and attributes.
///
/// [`SpanContext`]: opentelemetry::trace::SpanContext
fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec<KeyValue>);

/// Extracts an OpenTelemetry [`Context`] from `self`.
///
/// [`Context`]: opentelemetry::Context
Expand Down Expand Up @@ -86,6 +130,31 @@ impl OpenTelemetrySpanExt for tracing::Span {
});
}

fn add_link(&self, cx: SpanContext) {
self.add_link_with_attributes(cx, Vec::new())
}

fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec<KeyValue>) {
if cx.is_valid() {
let mut cx = Some(cx);
let mut att = Some(attributes);
self.with_subscriber(move |(id, collector)| {
if let Some(get_context) = collector.downcast_ref::<WithContext>() {
get_context.with_context(collector, id, move |builder, _tracer| {
if let Some(cx) = cx.take() {
let attr = att.take().unwrap_or_default();
let follows_link = opentelemetry::trace::Link::new(cx, attr);
builder
.links
.get_or_insert_with(|| Vec::with_capacity(1))
.push(follows_link);
}
});
}
});
}
}

fn context(&self) -> Context {
let mut cx = None;
self.with_subscriber(|(id, subscriber)| {
Expand Down

0 comments on commit d394efa

Please sign in to comment.