From eb9b685246e43fb30c0d811b3729ee2e1c645ea4 Mon Sep 17 00:00:00 2001 From: Max Lehuraux <72396955+LehMaxence@users.noreply.github.com> Date: Mon, 23 Aug 2021 17:21:51 -0400 Subject: [PATCH] opentelemetry: Add extension to link spans (#1516) 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`. --- tracing-opentelemetry/src/span_ext.rs | 71 ++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/tracing-opentelemetry/src/span_ext.rs b/tracing-opentelemetry/src/span_ext.rs index 5b8ac7a02c..77448b0408 100644 --- a/tracing-opentelemetry/src/span_ext.rs +++ b/tracing-opentelemetry/src/span_ext.rs @@ -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. @@ -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); + /// Extracts an OpenTelemetry [`Context`] from `self`. /// /// [`Context`]: opentelemetry::Context @@ -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) { + 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::() { + 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)| {