From 7dd1c5d3bd4c6e94268f46365343b58cb848fce1 Mon Sep 17 00:00:00 2001 From: Christopher Holmes Date: Tue, 2 Jul 2024 19:24:35 +0200 Subject: [PATCH] feat: make the install of rack instrumentation by grape instrumentation optional (#1043) * feat: optional rack instrumentation install by grape instrumentation In some circumstances we may want to defer the installation of the rack instrumentation that is orchestrated during the grape instrumentation installation. For example, we may want to customise the installation of the rack instrumentation or we can rely on the installation being orchestrated by another framework's instrumentation (such as when Grape is mounted with Rails). This change allows for Grape to skip the installation of the Rack instrumentation so that it can manually installed at a later time. * move the comments above the class name for RubyDocs --- .../instrumentation/grape/instrumentation.rb | 13 +++++ .../instrumentation/grape_test.rb | 51 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/instrumentation/grape/lib/opentelemetry/instrumentation/grape/instrumentation.rb b/instrumentation/grape/lib/opentelemetry/instrumentation/grape/instrumentation.rb index d31def440..66c89c23a 100644 --- a/instrumentation/grape/lib/opentelemetry/instrumentation/grape/instrumentation.rb +++ b/instrumentation/grape/lib/opentelemetry/instrumentation/grape/instrumentation.rb @@ -8,6 +8,16 @@ module OpenTelemetry module Instrumentation module Grape # The Instrumentation class contains logic to detect and install the Grape instrumentation + # # Configuration keys and options + # ## `:ignored_events` + # + # Default is `[]`. Specifies which ActiveSupport::Notifications events published by Grape to ignore. + # Ignored events will not be published as Span events. + # + # ## `:install_rack` + # + # Default is `true`. Specifies whether or not to install the Rack instrumentation as part of installing the Grape instrumentation. + # This is useful in cases where you have multiple Rack applications but want to manually specify where to insert the tracing middleware. class Instrumentation < OpenTelemetry::Instrumentation::Base # Minimum Grape version needed for compatibility with this instrumentation MINIMUM_VERSION = Gem::Version.new('1.2.0') @@ -27,6 +37,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base end option :ignored_events, default: [], validate: :array + option :install_rack, default: true, validate: :boolean private @@ -35,6 +46,8 @@ def gem_version end def install_rack_instrumentation + return unless config[:install_rack] + OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.install({}) end diff --git a/instrumentation/grape/test/opentelemetry/instrumentation/grape_test.rb b/instrumentation/grape/test/opentelemetry/instrumentation/grape_test.rb index f9d6744cd..782dcc8d5 100644 --- a/instrumentation/grape/test/opentelemetry/instrumentation/grape_test.rb +++ b/instrumentation/grape/test/opentelemetry/instrumentation/grape_test.rb @@ -418,5 +418,56 @@ class IgnoredEventAPI < Grape::API _(events_per_name('grape.endpoint_render').length).must_equal 0 end end + + describe 'when install_rack is set to false' do + class BasicAPI < Grape::API + format :json + get :hello do + { message: 'Hello, world!' } + end + end + + let(:config) { { install_rack: false } } + + let(:app) do + builder = Rack::Builder.app do + run BasicAPI + end + Rack::MockRequest.new(builder) + end + + let(:request_path) { '/hello' } + let(:expected_span_name) { 'HTTP GET /hello' } + + describe 'missing rack installation' do + it 'disables tracing' do + app.get request_path + _(exporter.finished_spans).must_be_empty + end + end + + describe 'when rack is manually installed' do + let(:app) do + build_rack_app(BasicAPI) + end + + before do + OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.install + end + + it 'creates a span' do + app.get request_path + _(exporter.finished_spans.first.attributes).must_equal( + 'code.namespace' => 'BasicAPI', + 'http.method' => 'GET', + 'http.host' => 'unknown', + 'http.scheme' => 'http', + 'http.target' => '/hello', + 'http.route' => '/hello', + 'http.status_code' => 200 + ) + end + end + end end end