From 779e2bb0fc86c1539689dfb66a8c2898db62383e Mon Sep 17 00:00:00 2001 From: Hannah Ramadan Date: Tue, 14 Apr 2026 13:54:31 -0700 Subject: [PATCH 1/4] fix: Handle unconfigured middleware when OTEL_SDK_DISABLED=true --- .../instrumentation/rack/middlewares/dup/tracer_middleware.rb | 4 ++-- .../instrumentation/rack/middlewares/old/tracer_middleware.rb | 4 ++-- .../rack/middlewares/stable/tracer_middleware.rb | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb index ff1bbbd1ab..69739afd5a 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = config[:untraced_endpoints] + @untraced_endpoints = Array(config[:untraced_endpoints]) end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - config[:response_propagators].each { |propagator| propagator.inject(headers) } + Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } end end end diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb index 2b515c8697..c53efce96f 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = config[:untraced_endpoints] + @untraced_endpoints = Array(config[:untraced_endpoints]) end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - config[:response_propagators].each { |propagator| propagator.inject(headers) } + Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } end end end diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb index f14e819fcc..c1759144df 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = config[:untraced_endpoints] + @untraced_endpoints = Array(config[:untraced_endpoints]) end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - config[:response_propagators].each { |propagator| propagator.inject(headers) } + Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } end end end From 8a360fde97d200e332a0200cdc655445662add10 Mon Sep 17 00:00:00 2001 From: Hannah Ramadan Date: Wed, 15 Apr 2026 08:57:43 -0700 Subject: [PATCH 2/4] Add empty config test --- .../middlewares/dup/tracer_middleware_test.rb | 17 +++++++++++++++++ .../middlewares/old/tracer_middleware_test.rb | 17 +++++++++++++++++ .../stable/tracer_middleware_test.rb | 17 +++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb index bca2c0683f..d33c87aafd 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb @@ -415,4 +415,21 @@ class SimulatedError < StandardError _(first_span.status.code).must_equal OpenTelemetry::Trace::Status::ERROR end end + + # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. + describe 'when config is empty' do + let(:empty_config_rack_builder) { Rack::Builder.new } + + before do + instrumentation.instance_variable_set(:@config, {}) + described_class.send(:clear_cached_config) + empty_config_rack_builder.run app + empty_config_rack_builder.use described_class + end + + it 'handles requests without raising an error' do + response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + _(response.status).must_equal 200 + end + end end diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb index c1e2b2423a..e118899836 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb @@ -387,4 +387,21 @@ class SimulatedError < StandardError _(first_span.status.code).must_equal OpenTelemetry::Trace::Status::ERROR end end + + # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. + describe 'when config is empty' do + let(:empty_config_rack_builder) { Rack::Builder.new } + + before do + instrumentation.instance_variable_set(:@config, {}) + described_class.send(:clear_cached_config) + empty_config_rack_builder.run app + empty_config_rack_builder.use described_class + end + + it 'handles requests without raising an error' do + response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + _(response.status).must_equal 200 + end + end end diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb index 8691b43e9d..c76f2c9d1e 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb @@ -386,4 +386,21 @@ class SimulatedError < StandardError _(first_span.status.code).must_equal OpenTelemetry::Trace::Status::ERROR end end + + # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. + describe 'when config is empty' do + let(:empty_config_rack_builder) { Rack::Builder.new } + + before do + instrumentation.instance_variable_set(:@config, {}) + described_class.send(:clear_cached_config) + empty_config_rack_builder.run app + empty_config_rack_builder.use described_class + end + + it 'handles requests without raising an error' do + response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + _(response.status).must_equal 200 + end + end end From 97f434c43621b832a516abd4cd42e103c3ccce86 Mon Sep 17 00:00:00 2001 From: Hannah Ramadan Date: Wed, 15 Apr 2026 21:16:17 -0700 Subject: [PATCH 3/4] Update base case, revert rack changes --- .../base/lib/opentelemetry/instrumentation/base.rb | 4 ++-- .../base/test/instrumentation/base_test.rb | 9 +++++++++ .../rack/middlewares/dup/tracer_middleware.rb | 4 ++-- .../rack/middlewares/old/tracer_middleware.rb | 4 ++-- .../rack/middlewares/stable/tracer_middleware.rb | 4 ++-- .../rack/middlewares/dup/tracer_middleware_test.rb | 13 ++++++------- .../rack/middlewares/old/tracer_middleware_test.rb | 13 ++++++------- .../middlewares/stable/tracer_middleware_test.rb | 13 ++++++------- 8 files changed, 35 insertions(+), 29 deletions(-) diff --git a/instrumentation/base/lib/opentelemetry/instrumentation/base.rb b/instrumentation/base/lib/opentelemetry/instrumentation/base.rb index 9dd36d95e6..681d0bd83e 100644 --- a/instrumentation/base/lib/opentelemetry/instrumentation/base.rb +++ b/instrumentation/base/lib/opentelemetry/instrumentation/base.rb @@ -201,9 +201,9 @@ def initialize(name, version, install_blk, present_blk, @install_blk = install_blk @present_blk = present_blk @compatible_blk = compatible_blk - @config = {} - @installed = false @options = options + @config = config_options({}) + @installed = false @tracer = OpenTelemetry::Trace::Tracer.new end # rubocop:enable Metrics/ParameterLists diff --git a/instrumentation/base/test/instrumentation/base_test.rb b/instrumentation/base/test/instrumentation/base_test.rb index ef11119777..4893c947d0 100644 --- a/instrumentation/base/test/instrumentation/base_test.rb +++ b/instrumentation/base/test/instrumentation/base_test.rb @@ -345,6 +345,15 @@ def initialize(*args) end end + describe '#config' do + describe 'before install' do + it 'returns default values for defined options' do + instance = instrumentation_with_callbacks.instance + _(instance.config[:max_count]).must_equal(5) + end + end + end + describe '#enabled?' do describe 'with env var' do it 'is disabled when false' do diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb index 69739afd5a..ff1bbbd1ab 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = Array(config[:untraced_endpoints]) + @untraced_endpoints = config[:untraced_endpoints] end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } + config[:response_propagators].each { |propagator| propagator.inject(headers) } end end end diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb index c53efce96f..2b515c8697 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = Array(config[:untraced_endpoints]) + @untraced_endpoints = config[:untraced_endpoints] end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } + config[:response_propagators].each { |propagator| propagator.inject(headers) } end end end diff --git a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb index c1759144df..f14e819fcc 100644 --- a/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb +++ b/instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb @@ -54,7 +54,7 @@ def clear_cached_config def initialize(app) @app = app - @untraced_endpoints = Array(config[:untraced_endpoints]) + @untraced_endpoints = config[:untraced_endpoints] end def call(env) @@ -83,7 +83,7 @@ def call(env) OpenTelemetry::Instrumentation::Rack.with_span(request_span) do @app.call(env).tap do |status, headers, response| set_attributes_after_request(request_span, status, headers, response) - Array(config[:response_propagators]).each { |propagator| propagator.inject(headers) } + config[:response_propagators].each { |propagator| propagator.inject(headers) } end end end diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb index d33c87aafd..fa1023ee9e 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware_test.rb @@ -416,19 +416,18 @@ class SimulatedError < StandardError end end - # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. - describe 'when config is empty' do - let(:empty_config_rack_builder) { Rack::Builder.new } + describe 'when SDK is disabled' do + let(:disabled_rack_builder) { Rack::Builder.new } before do - instrumentation.instance_variable_set(:@config, {}) + instrumentation.instance_variable_set(:@installed, false) described_class.send(:clear_cached_config) - empty_config_rack_builder.run app - empty_config_rack_builder.use described_class + disabled_rack_builder.run app + disabled_rack_builder.use described_class end it 'handles requests without raising an error' do - response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + response = Rack::MockRequest.new(disabled_rack_builder).get('/ping', env) _(response.status).must_equal 200 end end diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb index e118899836..3da44b83d5 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware_test.rb @@ -388,19 +388,18 @@ class SimulatedError < StandardError end end - # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. - describe 'when config is empty' do - let(:empty_config_rack_builder) { Rack::Builder.new } + describe 'when SDK is disabled' do + let(:disabled_rack_builder) { Rack::Builder.new } before do - instrumentation.instance_variable_set(:@config, {}) + instrumentation.instance_variable_set(:@installed, false) described_class.send(:clear_cached_config) - empty_config_rack_builder.run app - empty_config_rack_builder.use described_class + disabled_rack_builder.run app + disabled_rack_builder.use described_class end it 'handles requests without raising an error' do - response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + response = Rack::MockRequest.new(disabled_rack_builder).get('/ping', env) _(response.status).must_equal 200 end end diff --git a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb index c76f2c9d1e..f90ad8e6eb 100644 --- a/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb +++ b/instrumentation/rack/test/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware_test.rb @@ -387,19 +387,18 @@ class SimulatedError < StandardError end end - # When OTEL_SDK_DISABLED=true, the SDK skips installation and config remains empty. - describe 'when config is empty' do - let(:empty_config_rack_builder) { Rack::Builder.new } + describe 'when SDK is disabled' do + let(:disabled_rack_builder) { Rack::Builder.new } before do - instrumentation.instance_variable_set(:@config, {}) + instrumentation.instance_variable_set(:@installed, false) described_class.send(:clear_cached_config) - empty_config_rack_builder.run app - empty_config_rack_builder.use described_class + disabled_rack_builder.run app + disabled_rack_builder.use described_class end it 'handles requests without raising an error' do - response = Rack::MockRequest.new(empty_config_rack_builder).get('/ping', env) + response = Rack::MockRequest.new(disabled_rack_builder).get('/ping', env) _(response.status).must_equal 200 end end From a5ce8f6c5303aa982fa9667c578e09def775d78c Mon Sep 17 00:00:00 2001 From: Hannah Ramadan Date: Wed, 15 Apr 2026 22:00:42 -0700 Subject: [PATCH 4/4] grpc, gruf - return if not installed --- .../instrumentation/grpc/interceptors/client_tracer.rb | 2 +- .../opentelemetry/instrumentation/gruf/interceptors/client.rb | 2 +- .../opentelemetry/instrumentation/gruf/interceptors/server.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/grpc/lib/opentelemetry/instrumentation/grpc/interceptors/client_tracer.rb b/instrumentation/grpc/lib/opentelemetry/instrumentation/grpc/interceptors/client_tracer.rb index 364dbe2637..5c1139e212 100644 --- a/instrumentation/grpc/lib/opentelemetry/instrumentation/grpc/interceptors/client_tracer.rb +++ b/instrumentation/grpc/lib/opentelemetry/instrumentation/grpc/interceptors/client_tracer.rb @@ -29,7 +29,7 @@ def bidi_streamer(requests: nil, call: nil, method: nil, metadata: nil, &) private def call(type:, requests: nil, call: nil, method: nil, metadata: nil) - return yield if instrumentation_config.empty? + return yield unless Grpc::Instrumentation.instance.installed? method_parts = method.to_s.split('/') service = method_parts[1] diff --git a/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/client.rb b/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/client.rb index 2e86226e44..7c5d7a535e 100644 --- a/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/client.rb +++ b/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/client.rb @@ -10,7 +10,7 @@ module Gruf module Interceptors class Client < ::Gruf::Interceptors::ClientInterceptor def call(request_context:) - return yield if instrumentation_config.empty? + return yield unless Gruf::Instrumentation.instance.installed? service = request_context.method.split('/')[1] method = request_context.method_name diff --git a/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/server.rb b/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/server.rb index 231a5f4a42..eedd4c2456 100644 --- a/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/server.rb +++ b/instrumentation/gruf/lib/opentelemetry/instrumentation/gruf/interceptors/server.rb @@ -10,7 +10,7 @@ module Gruf module Interceptors class Server < ::Gruf::Interceptors::ServerInterceptor def call - return yield if instrumentation_config.empty? + return yield unless Gruf::Instrumentation.instance.installed? method = request.method_name