diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 2879c51790..ac67d2fae0 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -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 @@ -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 diff --git a/tracing-opentelemetry/src/span_ext.rs b/tracing-opentelemetry/src/span_ext.rs index 7140372ccf..800c63335b 100644 --- a/tracing-opentelemetry/src/span_ext.rs +++ b/tracing-opentelemetry/src/span_ext.rs @@ -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. @@ -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_subscriber(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_subscriber(|(id, subscriber)| {