Skip to content

Commit 9954488

Browse files
Merge pull request #3454 from newrelic/otel_bridge_bugfix_unfinished_segment
Finish OTel span before forcing finish
2 parents a1e5a98 + f687185 commit 9954488

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file is distributed under New Relic's license terms.
2+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3+
# frozen_string_literal: true
4+
5+
module NewRelic
6+
module Agent
7+
module OpenTelemetry
8+
module AbstractSegmentPatch
9+
def force_finish
10+
if instance_variable_defined?(:@otel_span)
11+
otel_span = instance_variable_get(:@otel_span)
12+
if otel_span.respond_to?(:finish) && !otel_span.instance_variable_get(:@finished)
13+
begin
14+
otel_span.finish
15+
16+
return if finished?
17+
rescue => e
18+
NewRelic::Agent.logger.debug("Error finishing OpenTelemetry span during force_finish: #{e}")
19+
end
20+
end
21+
end
22+
23+
super
24+
end
25+
end
26+
end
27+
end
28+
end

lib/new_relic/agent/opentelemetry_bridge.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ def self.install
6969
require_relative 'opentelemetry/transaction_patch'
7070
require_relative 'opentelemetry/context'
7171
require_relative 'opentelemetry/trace_patch'
72+
require_relative 'opentelemetry/abstract_segment_patch'
7273

7374
NewRelic::Agent.logger.warn('OpenTelemetry SDK gem is installed. This may interfere with New Relic instrumentation.') if defined?(OpenTelemetry::SDK)
7475

7576
::OpenTelemetry::Trace.singleton_class.prepend(NewRelic::Agent::OpenTelemetry::TracePatch)
7677
Transaction.prepend(OpenTelemetry::TransactionPatch)
78+
Transaction::AbstractSegment.prepend(OpenTelemetry::AbstractSegmentPatch)
7779

7880
::OpenTelemetry.tracer_provider = OpenTelemetry::Trace::TracerProvider.new
7981
::OpenTelemetry.propagation = OpenTelemetry::Context::Propagation::TracePropagator.new
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# This file is distributed under New Relic's license terms.
2+
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3+
# frozen_string_literal: true
4+
5+
module NewRelic
6+
module Agent
7+
module OpenTelemetry
8+
class AbstractSegmentPatchTest < Minitest::Test
9+
def setup
10+
harvest_transaction_events!
11+
harvest_span_events!
12+
end
13+
14+
def test_force_finish_with_otel_span_that_cannot_finish_segment
15+
in_transaction do |txn|
16+
txn.stubs(:sampled?).returns(true)
17+
segment = Tracer.start_segment(name: 'test_segment')
18+
otel_span = segment.instance_variable_get(:@otel_span)
19+
20+
otel_span.stubs(:instance_variable_get).with(:@finished).returns(false)
21+
otel_span.stubs(:finish).returns(nil)
22+
23+
segment.force_finish
24+
25+
assert_predicate segment, :finished?
26+
end
27+
end
28+
29+
def test_force_finish_with_successful_otel_span_finish
30+
in_transaction do |txn|
31+
txn.stubs(:sampled?).returns(true)
32+
segment = Tracer.start_segment(name: 'test_segment')
33+
otel_span = segment.instance_variable_get(:@otel_span)
34+
35+
otel_span.stubs(:instance_variable_get).with(:@finished).returns(false)
36+
otel_span.stubs(:finish) do
37+
segment.finish
38+
end
39+
40+
refute_predicate segment, :finished?, 'Segment should start unfinished'
41+
42+
segment.force_finish
43+
44+
assert_predicate segment, :finished?, 'Segment should be finished by span.finish'
45+
end
46+
end
47+
48+
def test_force_finish_handles_otel_span_exceptions_gracefully
49+
in_transaction do |txn|
50+
txn.stubs(:sampled?).returns(true)
51+
segment = Tracer.start_segment(name: 'test_segment')
52+
otel_span = segment.instance_variable_get(:@otel_span)
53+
54+
otel_span.stubs(:instance_variable_get).with(:@finished).returns(false)
55+
otel_span.stubs(:finish).raises(StandardError.new('Test exception'))
56+
57+
logger_mock = mock()
58+
logger_mock.expects(:debug).with(regexp_matches(/Error finishing OpenTelemetry span during force_finish.*Test exception/))
59+
NewRelic::Agent.stubs(:logger).returns(logger_mock)
60+
61+
segment.force_finish
62+
63+
assert_predicate segment, :finished?, 'Segment should still be finished via fallback after exception'
64+
end
65+
end
66+
end
67+
end
68+
end
69+
end

0 commit comments

Comments
 (0)