From 9b3f59ef0f03ab553f9845760deaaf4186aec7ce Mon Sep 17 00:00:00 2001 From: Maggie Spletzer Date: Thu, 14 Aug 2025 16:59:27 +0000 Subject: [PATCH 1/3] capture exceptions and close span in perform --- .../instrumentation/ethon/patches/dup/easy.rb | 11 ++++++++-- .../instrumentation/ethon/patches/old/easy.rb | 11 ++++++++-- .../ethon/patches/stable/easy.rb | 11 ++++++++-- .../ethon/dup/instrumentation_test.rb | 20 +++++++++++++++++++ .../ethon/old/instrumentation_test.rb | 18 +++++++++++++++++ .../ethon/stable/instrumentation_test.rb | 17 ++++++++++++++++ 6 files changed, 82 insertions(+), 6 deletions(-) diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb index 16bcdeadfe..8357afabd7 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb @@ -36,8 +36,15 @@ def headers=(headers) end def perform - otel_before_request - super + begin + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil + end end def complete diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb index aa83ddffff..f2a9876085 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb @@ -33,8 +33,15 @@ def headers=(headers) end def perform - otel_before_request - super + begin + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil + end end def complete diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb index 4af3ebf9cb..d6070c96a0 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb @@ -36,8 +36,15 @@ def headers=(headers) end def perform - otel_before_request - super + begin + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil + end end def complete diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb index 3da341d966..66f869355b 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb @@ -82,6 +82,26 @@ end end end + + it 'when the perform fails before complete with an exception' do + Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + easy.perform + + # NOTE: check the finished spans since we expect to have closed it + span = exporter.finished_spans.first + _(span.name).must_equal 'HTTP' + _(span.attributes['http.method']).must_equal 'N/A' + _(span.attributes['http.status_code']).must_be_nil + _(span.attributes['http.url']).must_equal 'http://example.com/test' + _(span.attributes['http.request.method']).must_equal '_OTHER' + _(span.attributes['http.response.status_code']).must_be_nil + _(span.attributes['url.full']).must_equal 'http://example.com/test' + _(span.status.code).must_equal( + OpenTelemetry::Trace::Status::ERROR + ) + _(easy.instance_eval { @otel_span }).must_be_nil + end + end end describe '#complete' do diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb index 0daf151f73..42ff1504c5 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb @@ -77,6 +77,23 @@ end end end + + it 'when the perform fails before complete with an exception' do + Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + easy.perform + + # NOTE: check the finished spans since we expect to have closed it + span = exporter.finished_spans.first + _(span.name).must_equal 'HTTP N/A' + _(span.attributes['http.method']).must_equal 'N/A' + _(span.attributes['http.status_code']).must_be_nil + _(span.attributes['http.url']).must_equal 'http://example.com/test' + _(span.status.code).must_equal( + OpenTelemetry::Trace::Status::ERROR + ) + _(easy.instance_eval { @otel_span }).must_be_nil + end + end end describe '#complete' do @@ -105,6 +122,7 @@ def stub_response(options) it 'when response is not successful' do stub_response(response_code: 500) do + binding.pry _(span.name).must_equal 'HTTP N/A' _(span.attributes['http.method']).must_equal 'N/A' _(span.attributes['http.status_code']).must_equal 500 diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb index c787a78d3e..ec5dba7fc8 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb @@ -78,6 +78,23 @@ end end end + + it 'when the perform fails before complete with an exception' do + Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + easy.perform + + # NOTE: check the finished spans since we expect to have closed it + span = exporter.finished_spans.first + _(span.name).must_equal 'HTTP' + _(span.attributes['http.request.method']).must_equal '_OTHER' + _(span.attributes['http.response.status_code']).must_be_nil + _(span.attributes['url.full']).must_equal 'http://example.com/test' + _(span.status.code).must_equal( + OpenTelemetry::Trace::Status::ERROR + ) + _(easy.instance_eval { @otel_span }).must_be_nil + end + end end describe '#complete' do From a45437a2009859569d4f75871720c3f23462d2e4 Mon Sep 17 00:00:00 2001 From: Maggie Spletzer Date: Thu, 14 Aug 2025 17:08:45 +0000 Subject: [PATCH 2/3] remove binding.pry --- .../instrumentation/ethon/old/instrumentation_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb index 42ff1504c5..11b7ea5ba9 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb @@ -122,7 +122,6 @@ def stub_response(options) it 'when response is not successful' do stub_response(response_code: 500) do - binding.pry _(span.name).must_equal 'HTTP N/A' _(span.attributes['http.method']).must_equal 'N/A' _(span.attributes['http.status_code']).must_equal 500 From b9b369a10311d336de32407628401b66e3521bf3 Mon Sep 17 00:00:00 2001 From: Maggie Spletzer Date: Mon, 25 Aug 2025 13:54:49 +0000 Subject: [PATCH 3/3] fix rubocop errors --- .../instrumentation/ethon/patches/dup/easy.rb | 16 +++++++--------- .../instrumentation/ethon/patches/old/easy.rb | 16 +++++++--------- .../instrumentation/ethon/patches/stable/easy.rb | 16 +++++++--------- .../ethon/dup/instrumentation_test.rb | 4 ++-- .../ethon/old/instrumentation_test.rb | 4 ++-- .../ethon/stable/instrumentation_test.rb | 4 ++-- 6 files changed, 27 insertions(+), 33 deletions(-) diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb index 8357afabd7..8099eca033 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb @@ -36,15 +36,13 @@ def headers=(headers) end def perform - begin - otel_before_request - super - rescue StandardError => e - # If an exception occurs before we can call `complete`, we should add and error status and close the span - @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") - @otel_span&.finish - @otel_span = nil - end + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil end def complete diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb index f2a9876085..7d72e2ed69 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb @@ -33,15 +33,13 @@ def headers=(headers) end def perform - begin - otel_before_request - super - rescue StandardError => e - # If an exception occurs before we can call `complete`, we should add and error status and close the span - @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") - @otel_span&.finish - @otel_span = nil - end + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil end def complete diff --git a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb index d6070c96a0..1306183ccb 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb @@ -36,15 +36,13 @@ def headers=(headers) end def perform - begin - otel_before_request - super - rescue StandardError => e - # If an exception occurs before we can call `complete`, we should add and error status and close the span - @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") - @otel_span&.finish - @otel_span = nil - end + otel_before_request + super + rescue StandardError => e + # If an exception occurs before we can call `complete`, we should add and error status and close the span + @otel_span&.status = OpenTelemetry::Trace::Status.error("Request threw an exception: #{e.message}") + @otel_span&.finish + @otel_span = nil end def complete diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb index 66f869355b..76603741c8 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/dup/instrumentation_test.rb @@ -84,7 +84,7 @@ end it 'when the perform fails before complete with an exception' do - Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + Ethon::Curl.stub(:easy_perform, ->(_handle) { raise StandardError, 'Connection failed' }) do easy.perform # NOTE: check the finished spans since we expect to have closed it @@ -100,7 +100,7 @@ OpenTelemetry::Trace::Status::ERROR ) _(easy.instance_eval { @otel_span }).must_be_nil - end + end end end diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb index 11b7ea5ba9..4c9b5cb360 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/old/instrumentation_test.rb @@ -79,7 +79,7 @@ end it 'when the perform fails before complete with an exception' do - Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + Ethon::Curl.stub(:easy_perform, ->(_handle) { raise StandardError, 'Connection failed' }) do easy.perform # NOTE: check the finished spans since we expect to have closed it @@ -92,7 +92,7 @@ OpenTelemetry::Trace::Status::ERROR ) _(easy.instance_eval { @otel_span }).must_be_nil - end + end end end diff --git a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb index ec5dba7fc8..4179072ec2 100644 --- a/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb +++ b/instrumentation/ethon/test/opentelemetry/instrumentation/ethon/stable/instrumentation_test.rb @@ -80,7 +80,7 @@ end it 'when the perform fails before complete with an exception' do - Ethon::Curl.stub(:easy_perform, ->(handle) { raise StandardError, "Connection failed" }) do + Ethon::Curl.stub(:easy_perform, ->(_handle) { raise StandardError, 'Connection failed' }) do easy.perform # NOTE: check the finished spans since we expect to have closed it @@ -93,7 +93,7 @@ OpenTelemetry::Trace::Status::ERROR ) _(easy.instance_eval { @otel_span }).must_be_nil - end + end end end