diff --git a/trace/src/ctx.rs b/trace/src/ctx.rs index 22a7739044..e3cb155aac 100644 --- a/trace/src/ctx.rs +++ b/trace/src/ctx.rs @@ -51,6 +51,11 @@ pub struct SpanContext { pub span_id: SpanId, + /// Link to other spans, can be cross-trace if this span aggregates multiple spans. + /// + /// See . + pub links: Vec<(TraceId, SpanId)>, + pub collector: Option>, } @@ -67,6 +72,7 @@ impl SpanContext { trace_id: TraceId(NonZeroU128::new(trace_id).unwrap()), parent_span_id: None, span_id: SpanId(NonZeroU64::new(span_id).unwrap()), + links: Vec::with_capacity(0), collector: Some(collector), } } @@ -79,6 +85,7 @@ impl SpanContext { trace_id: self.trace_id, span_id: SpanId::gen(), collector: self.collector.clone(), + links: Vec::with_capacity(0), parent_span_id: Some(self.span_id), }, start: None, diff --git a/trace_exporters/src/jaeger.rs b/trace_exporters/src/jaeger.rs index 6ae8427909..da05196d67 100644 --- a/trace_exporters/src/jaeger.rs +++ b/trace_exporters/src/jaeger.rs @@ -236,9 +236,14 @@ mod tests { trace_id: TraceId::new(43434).unwrap(), parent_span_id: None, span_id: SpanId::new(3495993).unwrap(), + links: vec![], collector: None, }; let mut span = ctx.child("foo"); + span.ctx.links = vec![ + (TraceId::new(12).unwrap(), SpanId::new(123).unwrap()), + (TraceId::new(45).unwrap(), SpanId::new(456).unwrap()), + ]; span.status = SpanStatus::Ok; span.events = vec![SpanEvent { time: Utc.timestamp_nanos(200000), @@ -283,6 +288,14 @@ mod tests { span.ctx.parent_span_id.unwrap().get() as i64 ); + // test links + let b1_s0_refs = b1_s0.references.as_ref().unwrap(); + assert_eq!(b1_s0_refs.len(), 2); + let b1_s0_r0 = &b1_s0_refs[0]; + let b1_s0_r1 = &b1_s0_refs[1]; + assert_eq!(b1_s0_r0.span_id, span.ctx.links[0].1.get() as i64); + assert_eq!(b1_s0_r1.span_id, span.ctx.links[1].1.get() as i64); + // microseconds not nanoseconds assert_eq!(b1_s0.start_time, 100); assert_eq!(b1_s0.duration, 200); diff --git a/trace_exporters/src/jaeger/span.rs b/trace_exporters/src/jaeger/span.rs index e3af3e45d3..73ed699de5 100644 --- a/trace_exporters/src/jaeger/span.rs +++ b/trace_exporters/src/jaeger/span.rs @@ -1,12 +1,21 @@ /// Contains the conversion logic from a `trace::span::Span` to `thrift::jaeger::Span` -use crate::thrift::jaeger; -use trace::span::{MetaValue, Span, SpanEvent, SpanStatus}; +use crate::thrift::jaeger::{self, SpanRef}; +use trace::{ + ctx::TraceId, + span::{MetaValue, Span, SpanEvent, SpanStatus}, +}; + +/// Split [`TraceId`] into high and low part. +fn split_trace_id(trace_id: TraceId) -> (i64, i64) { + let trace_id = trace_id.get(); + let trace_id_high = (trace_id >> 64) as i64; + let trace_id_low = trace_id as i64; + (trace_id_high, trace_id_low) +} impl From for jaeger::Span { fn from(mut s: Span) -> Self { - let trace_id = s.ctx.trace_id.get(); - let trace_id_high = (trace_id >> 64) as i64; - let trace_id_low = trace_id as i64; + let (trace_id_high, trace_id_low) = split_trace_id(s.ctx.trace_id); // A parent span id of 0 indicates no parent span ID (span IDs are non-zero) let parent_span_id = s.ctx.parent_span_id.map(|id| id.get()).unwrap_or_default() as i64; @@ -51,13 +60,34 @@ impl From for jaeger::Span { false => Some(s.events.into_iter().map(Into::into).collect()), }; + let references = if s.ctx.links.is_empty() { + None + } else { + Some( + s.ctx + .links + .into_iter() + .map(|(trace_id, span_id)| { + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md#links + let (trace_id_high, trace_id_low) = split_trace_id(trace_id); + SpanRef { + ref_type: jaeger::SpanRefType::FollowsFrom, + trace_id_high, + trace_id_low, + span_id: span_id.get() as i64, + } + }) + .collect(), + ) + }; + Self { trace_id_low, trace_id_high, span_id: s.ctx.span_id.get() as i64, parent_span_id, operation_name: s.name.to_string(), - references: None, + references, flags: 0, start_time, duration, diff --git a/trace_http/src/ctx.rs b/trace_http/src/ctx.rs index 9946786287..67c27d1bec 100644 --- a/trace_http/src/ctx.rs +++ b/trace_http/src/ctx.rs @@ -154,10 +154,14 @@ fn decode_b3( return Ok(None); } + // Links cannot be specified via the HTTP header + let links = Vec::with_capacity(0); + Ok(Some(SpanContext { trace_id: required_header(headers, B3_TRACE_ID_HEADER, parse_trace)?, parent_span_id: parsed_header(headers, B3_PARENT_SPAN_ID_HEADER, parse_span)?, span_id: required_header(headers, B3_SPAN_ID_HEADER, parse_span)?, + links, collector: Some(Arc::clone(collector)), })) } @@ -211,10 +215,14 @@ fn decode_jaeger( return Ok(None); } + // Links cannot be specified via the HTTP header + let links = Vec::with_capacity(0); + Ok(Some(SpanContext { trace_id: decoded.trace_id, parent_span_id: decoded.parent_span_id, span_id: decoded.span_id, + links, collector: Some(Arc::clone(collector)), })) }