From f14bdac50f0734f38a7150cf6b1e6274e1102075 Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Tue, 9 Apr 2024 17:27:48 +0200 Subject: [PATCH] feat: Add add_link to span api/sdk (#1623) * add add_link to span api/sdk * Update api/lib/opentelemetry/trace/span.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * Update sdk/lib/opentelemetry/sdk/trace/span.rb Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> * only freeze links when the span is ended --------- Co-authored-by: Kayla Reopelle (she/her) <87386821+kaylareopelle@users.noreply.github.com> --- api/lib/opentelemetry/trace/span.rb | 22 ++++++++++ api/test/opentelemetry/trace/span_test.rb | 6 +++ sdk/lib/opentelemetry/sdk/trace/span.rb | 36 +++++++++++++++- sdk/test/opentelemetry/sdk/trace/span_test.rb | 41 +++++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) diff --git a/api/lib/opentelemetry/trace/span.rb b/api/lib/opentelemetry/trace/span.rb index 17109e892c..1d0b0b4ff5 100644 --- a/api/lib/opentelemetry/trace/span.rb +++ b/api/lib/opentelemetry/trace/span.rb @@ -78,6 +78,28 @@ def add_attributes(attributes) self end + # Add a link to a {Span}. + # + # Adding links at span creation using the `links` option is preferred + # to calling add_link later, because head sampling decisions can only + # consider information present during span creation. + # + # Example: + # + # span.add_link(OpenTelemetry::Trace::Link.new(span_to_link_from.context)) + # + # Note that the OpenTelemetry project + # {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md + # documents} certain "standard attributes" that have prescribed semantic + # meanings. + # + # @param [OpenTelemetry::Trace::Link] the link object to add on the {Span}. + # + # @return [self] returns itself + def add_link(link) + self + end + # Add an event to a {Span}. # # Example: diff --git a/api/test/opentelemetry/trace/span_test.rb b/api/test/opentelemetry/trace/span_test.rb index 99af11eb9e..dea290b4aa 100644 --- a/api/test/opentelemetry/trace/span_test.rb +++ b/api/test/opentelemetry/trace/span_test.rb @@ -36,6 +36,12 @@ end end + describe '#add_link' do + it 'returns self' do + _(span.add_link(OpenTelemetry::Trace::Link.new(span_context))).must_equal(span) + end + end + describe '#add_event' do it 'returns self' do _(span.add_event('event-name')).must_equal(span) diff --git a/sdk/lib/opentelemetry/sdk/trace/span.rb b/sdk/lib/opentelemetry/sdk/trace/span.rb index 3221b8ccdd..274ac4c24d 100644 --- a/sdk/lib/opentelemetry/sdk/trace/span.rb +++ b/sdk/lib/opentelemetry/sdk/trace/span.rb @@ -118,6 +118,37 @@ def add_attributes(attributes) self end + # Add a link to a {Span}. + # + # Adding links at span creation using the `links` option is preferred + # to calling add_link later, because head sampling decisions can only + # consider information present during span creation. + # + # Example: + # + # span.add_link(OpenTelemetry::Trace::Link.new(span_to_link_from.context)) + # + # Note that the OpenTelemetry project + # {https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md + # documents} certain "standard attributes" that have prescribed semantic + # meanings. + # + # @param [OpenTelemetry::Trace::Link] the link object to add on the {Span}. + # + # @return [self] returns itself + def add_link(link) + @mutex.synchronize do + if @ended + OpenTelemetry.logger.warn('Calling add_link on an ended Span.') + else + @links ||= [] + @links = trim_links(@links << link, @span_limits.link_count_limit, @span_limits.link_attribute_count_limit) + @total_recorded_links += 1 + end + end + self + end + # Add an Event to a {Span}. # # Example: @@ -242,6 +273,7 @@ def finish(end_timestamp: nil) @end_timestamp = relative_timestamp(end_timestamp) @attributes = validated_attributes(@attributes).freeze @events.freeze + @links.freeze @ended = true end @span_processors.each { |processor| processor.on_finish(self) } @@ -373,7 +405,7 @@ def trim_links(links, link_count_limit, link_attribute_count_limit) # rubocop:di if links.size <= link_count_limit && links.all? { |link| link.span_context.valid? && link.attributes.size <= link_attribute_count_limit && Internal.valid_attributes?(name, 'link', link.attributes) } - return links.frozen? ? links : links.clone.freeze + return links end # Slow path: trim attributes for each Link. @@ -386,7 +418,7 @@ def trim_links(links, link_count_limit, link_attribute_count_limit) # rubocop:di excess = attrs.size - link_attribute_count_limit excess.times { attrs.shift } if excess.positive? OpenTelemetry::Trace::Link.new(link.span_context, attrs) - end.freeze + end end def append_event(events, event) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity diff --git a/sdk/test/opentelemetry/sdk/trace/span_test.rb b/sdk/test/opentelemetry/sdk/trace/span_test.rb index f9a08126c0..6d56a77484 100644 --- a/sdk/test/opentelemetry/sdk/trace/span_test.rb +++ b/sdk/test/opentelemetry/sdk/trace/span_test.rb @@ -167,6 +167,47 @@ end end + describe '#add_link' do + it 'does not add a link if span is ended' do + span.finish + span.add_link(OpenTelemetry::Trace::Link.new(context)) + _(span.links).must_be_nil + end + + it 'adds a link' do + span.add_link(OpenTelemetry::Trace::Link.new(context)) + links = span.links + _(links.size).must_equal(1) + _(links.first.span_context).must_equal(context) + end + + it 'adds a link with attributes' do + attrs = { 'foo' => 'bar' } + span.add_link(OpenTelemetry::Trace::Link.new(context, attrs)) + links = span.links + _(links.size).must_equal(1) + _(links.first.attributes).must_equal(attrs) + end + + it 'updates the links count' do + span.add_link(OpenTelemetry::Trace::Link.new(context)) + span.add_link(OpenTelemetry::Trace::Link.new(context)) + _(span.to_span_data.total_recorded_links).must_equal(2) + end + + it 'trims excess links' do + span.add_link(OpenTelemetry::Trace::Link.new(context)) + span.add_link(OpenTelemetry::Trace::Link.new(context)) + _(span.links.size).must_equal(1) + end + + it 'prunes invalid links' do + invalid_context = OpenTelemetry::Trace::SpanContext.new(trace_id: OpenTelemetry::Trace::INVALID_TRACE_ID) + span.add_link(OpenTelemetry::Trace::Link.new(invalid_context)) + _(span.links.size).must_equal(0) + end + end + describe '#add_event' do it 'add a named event' do span.add_event('added')