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..8099eca033 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/dup/easy.rb @@ -38,6 +38,11 @@ def headers=(headers) def perform 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 aa83ddffff..7d72e2ed69 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/old/easy.rb @@ -35,6 +35,11 @@ def headers=(headers) def perform 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 4af3ebf9cb..1306183ccb 100644 --- a/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb +++ b/instrumentation/ethon/lib/opentelemetry/instrumentation/ethon/patches/stable/easy.rb @@ -38,6 +38,11 @@ def headers=(headers) def perform 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 3da341d966..76603741c8 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..4c9b5cb360 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 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..4179072ec2 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