feat: ability to link other spans in span context

This can be used when aggregating from multiple parent spans, e.g. when
we want to implement #1473.
pull/24376/head
Marco Neumann 2021-10-12 11:10:40 +02:00
parent c31bcbced5
commit 173f9aefcf
4 changed files with 64 additions and 6 deletions

View File

@ -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 <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#links-between-spans>.
pub links: Vec<(TraceId, SpanId)>,
pub collector: Option<Arc<dyn TraceCollector>>,
}
@ -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,

View File

@ -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);

View File

@ -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<Span> 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<Span> 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,

View File

@ -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)),
}))
}