Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

opentelemetry: Add extension to link spans #1516

Merged
merged 4 commits into from
Aug 23, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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::subscriber::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_collector(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_collector(|(id, collector)| {
Expand Down