diff --git a/opentelemetry-sdk/src/trace/links.rs b/opentelemetry-sdk/src/trace/links.rs index 1810c46367..33d8074fa3 100644 --- a/opentelemetry-sdk/src/trace/links.rs +++ b/opentelemetry-sdk/src/trace/links.rs @@ -29,3 +29,9 @@ impl IntoIterator for SpanLinks { self.links.into_iter() } } + +impl SpanLinks { + pub(crate) fn add_link(&mut self, link: Link) { + self.links.push(link); + } +} diff --git a/opentelemetry-sdk/src/trace/mod.rs b/opentelemetry-sdk/src/trace/mod.rs index 13b5818fdb..63354a871c 100644 --- a/opentelemetry-sdk/src/trace/mod.rs +++ b/opentelemetry-sdk/src/trace/mod.rs @@ -181,16 +181,13 @@ mod tests { let mut links = Vec::new(); for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) { - links.push(Link::new( - SpanContext::new( - TraceId::from_u128(12), - SpanId::from_u64(12), - TraceFlags::default(), - false, - Default::default(), - ), - Vec::new(), - )) + links.push(Link::with_context(SpanContext::new( + TraceId::from_u128(12), + SpanId::from_u64(12), + TraceFlags::default(), + false, + Default::default(), + ))) } let span_builder = SpanBuilder::from_name("span_name").with_links(links); diff --git a/opentelemetry-sdk/src/trace/span.rs b/opentelemetry-sdk/src/trace/span.rs index ab27f8b0c0..2872363aa5 100644 --- a/opentelemetry-sdk/src/trace/span.rs +++ b/opentelemetry-sdk/src/trace/span.rs @@ -10,7 +10,7 @@ //! These cannot be changed after the `Span`'s end time has been set. use crate::trace::SpanLimits; use crate::Resource; -use opentelemetry::trace::{Event, SpanContext, SpanId, SpanKind, Status}; +use opentelemetry::trace::{Event, Link, SpanContext, SpanId, SpanKind, Status}; use opentelemetry::KeyValue; use std::borrow::Cow; use std::time::SystemTime; @@ -170,6 +170,28 @@ impl opentelemetry::trace::Span for Span { }); } + /// Add `Link` to this `Span` + /// + fn add_link(&mut self, span_context: SpanContext, attributes: Vec) { + let span_links_limit = self.span_limits.max_links_per_span as usize; + let link_attributes_limit = self.span_limits.max_attributes_per_link as usize; + self.with_data(|data| { + if data.links.links.len() < span_links_limit { + let dropped_attributes_count = + attributes.len().saturating_sub(link_attributes_limit); + let mut attributes = attributes; + attributes.truncate(link_attributes_limit); + data.links.add_link(Link::new( + span_context, + attributes, + dropped_attributes_count as u32, + )); + } else { + data.links.dropped_count += 1; + } + }); + } + /// Finishes the span with given timestamp. fn end_with_timestamp(&mut self, timestamp: SystemTime) { self.ensure_ended_and_exported(Some(timestamp)); @@ -595,16 +617,13 @@ mod tests { let provider = provider_builder.build(); let tracer = provider.tracer("opentelemetry-test"); - let mut link = Link::new( - SpanContext::new( - TraceId::from_u128(12), - SpanId::from_u64(12), - TraceFlags::default(), - false, - Default::default(), - ), - Vec::new(), - ); + let mut link = Link::with_context(SpanContext::new( + TraceId::from_u128(12), + SpanId::from_u64(12), + TraceFlags::default(), + false, + Default::default(), + )); for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_LINK * 2) { link.attributes .push(KeyValue::new(format!("key {}", i), i.to_string())); @@ -632,20 +651,29 @@ mod tests { let mut links = Vec::new(); for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) { - links.push(Link::new( - SpanContext::new( - TraceId::from_u128(12), - SpanId::from_u64(12), - TraceFlags::default(), - false, - Default::default(), - ), - Vec::new(), - )) + links.push(Link::with_context(SpanContext::new( + TraceId::from_u128(12), + SpanId::from_u64(12), + TraceFlags::default(), + false, + Default::default(), + ))) } let span_builder = tracer.span_builder("test").with_links(links); - let span = tracer.build(span_builder); + let mut span = tracer.build(span_builder); + + // add links using span api after building the span + span.add_link( + SpanContext::new( + TraceId::from_u128(12), + SpanId::from_u64(12), + TraceFlags::default(), + false, + Default::default(), + ), + vec![], + ); let link_queue = span .data .clone() diff --git a/opentelemetry/src/global/trace.rs b/opentelemetry/src/global/trace.rs index 7449019235..d3058dc533 100644 --- a/opentelemetry/src/global/trace.rs +++ b/opentelemetry/src/global/trace.rs @@ -86,6 +86,10 @@ pub trait ObjectSafeSpan { /// filtering decisions made previously depending on the implementation. fn update_name(&mut self, new_name: Cow<'static, str>); + /// Adds a link to this span + /// + fn add_link(&mut self, span_context: SpanContext, attributes: Vec); + /// Finishes the `Span`. /// /// Implementations MUST ignore all subsequent calls to `end` (there might be @@ -138,6 +142,10 @@ impl ObjectSafeSpan for T { self.update_name(new_name) } + fn add_link(&mut self, span_context: SpanContext, attributes: Vec) { + self.add_link(span_context, attributes) + } + fn end_with_timestamp(&mut self, timestamp: SystemTime) { self.end_with_timestamp(timestamp) } @@ -216,6 +224,12 @@ impl trace::Span for BoxedSpan { self.0.update_name(new_name.into()) } + /// Adds a link to this span + /// + fn add_link(&mut self, span_context: trace::SpanContext, attributes: Vec) { + self.0.add_link(span_context, attributes) + } + /// Finishes the span with given timestamp. fn end_with_timestamp(&mut self, timestamp: SystemTime) { self.0.end_with_timestamp(timestamp); diff --git a/opentelemetry/src/testing/trace.rs b/opentelemetry/src/testing/trace.rs index 065b056398..040d22d314 100644 --- a/opentelemetry/src/testing/trace.rs +++ b/opentelemetry/src/testing/trace.rs @@ -30,6 +30,8 @@ impl Span for TestSpan { T: Into>, { } + + fn add_link(&mut self, _span_context: SpanContext, _attributes: Vec) {} fn end_with_timestamp(&mut self, _timestamp: std::time::SystemTime) {} } diff --git a/opentelemetry/src/trace/mod.rs b/opentelemetry/src/trace/mod.rs index 3b959d3e0f..434c6ca85d 100644 --- a/opentelemetry/src/trace/mod.rs +++ b/opentelemetry/src/trace/mod.rs @@ -302,11 +302,24 @@ pub struct Link { } impl Link { - /// Create a new link. - pub fn new(span_context: SpanContext, attributes: Vec) -> Self { + /// Create new `Link` + pub fn new( + span_context: SpanContext, + attributes: Vec, + dropped_attributes_count: u32, + ) -> Self { Link { span_context, attributes, + dropped_attributes_count, + } + } + + /// Create new `Link` with given context + pub fn with_context(span_context: SpanContext) -> Self { + Link { + span_context, + attributes: Vec::new(), dropped_attributes_count: 0, } } diff --git a/opentelemetry/src/trace/noop.rs b/opentelemetry/src/trace/noop.rs index 4110209889..74bc81d9a2 100644 --- a/opentelemetry/src/trace/noop.rs +++ b/opentelemetry/src/trace/noop.rs @@ -51,7 +51,7 @@ impl trace::Span for NoopSpan { where T: Into>, { - // Ignore + // Ignored } /// Ignores all events with timestamps @@ -94,6 +94,10 @@ impl trace::Span for NoopSpan { // Ignored } + fn add_link(&mut self, _span_context: trace::SpanContext, _attributes: Vec) { + // Ignored + } + /// Ignores `Span` endings fn end_with_timestamp(&mut self, _timestamp: SystemTime) { // Ignored diff --git a/opentelemetry/src/trace/span.rs b/opentelemetry/src/trace/span.rs index 97d9578565..8b54fffb53 100644 --- a/opentelemetry/src/trace/span.rs +++ b/opentelemetry/src/trace/span.rs @@ -154,6 +154,22 @@ pub trait Span { where T: Into>; + /// Adds [`Link`] to another [`SpanContext`]. + /// + /// This method allows linking the current span to another span, identified by its `SpanContext`. Links can be used + /// to connect spans from different traces or within the same trace. Attributes can be attached to the link to + /// provide additional context or metadata. + /// + /// # Arguments + /// + /// * `span_context` - The `SpanContext` of the span to link to. This represents the target span's unique identifiers + /// and trace information. + /// * `attributes` - A vector of `KeyValue` pairs that describe additional attributes of the link. These attributes + /// can include any contextual information relevant to the link between the spans. + /// + /// [`Link`]: crate::trace::Link + fn add_link(&mut self, span_context: SpanContext, attributes: Vec); + /// Signals that the operation described by this span has now ended. fn end(&mut self) { self.end_with_timestamp(crate::time::now());