Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Make Rack install optional for sinatra #1019

Merged
merged 12 commits into from
Jun 21, 2024

Conversation

arielvalentin
Copy link
Collaborator

@arielvalentin arielvalentin commented Jun 16, 2024

This allows for scenarios when there are multiple Rack Applications mounted in the same builder and you want to avoid installing the Rack Events middleware multiple times in the stack.

A common example is a Rails application that mounts a Sinatra App in the routes file:

Rails.application.routes.draw do
  mount Sinatra::Application, at: '/sinatra'
end

This results in the Rack middleware being installed multiple times.

Once by the Rails ActionPack instrumentation and then by the Sinatra application leading to multiple Rack spans being created in the same trace.

This change allows you to avoid this by setting the install_rack option to false when using Sinatra instrumentation and allowing users to manually configure the middleware in the stack as they see fit.

Example output

Manual configuration of Rack

I, [2024-06-20T16:01:42.758846 #83791]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Rack was successfully installed with the following options {:allowed_request_headers=>[], :allowed_response_headers=>[], :application=>nil, :record_frontend_span=>false, :untraced_endpoints=>[], :url_quantization=>nil, :untraced_requests=>nil, :response_propagators=>[], :use_rack_events=>true, :allowed_rack_request_headers=>{}, :allowed_rack_response_headers=>{}}
I, [2024-06-20T16:01:42.758907 #83791]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Sinatra was successfully installed with the following options {:install_rack=>false}

#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /example",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000010509e998 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917334154639000,
 end_timestamp=1718917334160898000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/example",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/example",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000132df4908
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84124,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="\xA6\xCB\fZU\xBB\x99\e",
 trace_id="\x95\xA0\xE7RgE\x85\x83j?\x9B,\xF5\x00i5",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x0000000130fd3230 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010505ce58 @hash={}>>
127.0.0.1 - - [20/Jun/2024:16:02:14 -0500] "GET /example HTTP/1.1" 200 31 0.0134
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000010509e998 @code=1, @description="">,
 parent_span_id="\xEC\xF7\xE2\x1D\x98\xA8\x9B\xA0",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917334171108000,
 end_timestamp=1718917334171161000,
 attributes={"sinatra.template_name"=>"layout"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000132df4908
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84124,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\xACR\x00\x10\x8D\x19m\xAA",
 trace_id="\x16;\xE2\xA9\xDD\x88\xBE\x03\x03\x83\xD8)\xEAt\x82\xBF",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x0000000130fd3230 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010505ce58 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000010509e998 @code=1, @description="">,
 parent_span_id="\xBA\x8F\xBD\xBAB<\xA1>",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917334168310000,
 end_timestamp=1718917334171909000,
 attributes={"sinatra.template_name"=>"example_render"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000132df4908
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84124,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\xEC\xF7\xE2\x1D\x98\xA8\x9B\xA0",
 trace_id="\x16;\xE2\xA9\xDD\x88\xBE\x03\x03\x83\xD8)\xEAt\x82\xBF",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x0000000130fd3230 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010505ce58 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /example_render",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000010509e998 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917334168223000,
 end_timestamp=1718917334172887000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/example_render",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/example_render",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000132df4908
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84124,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="\xBA\x8F\xBD\xBAB<\xA1>",
 trace_id="\x16;\xE2\xA9\xDD\x88\xBE\x03\x03\x83\xD8)\xEAt\x82\xBF",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x0000000130fd3230 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010505ce58 @hash={}>>
127.0.0.1 - - [20/Jun/2024:16:02:14 -0500] "GET /example_render HTTP/1.1" 200 14 0.0077
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /thing/:id",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000010509e998 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917334176212000,
 end_timestamp=1718917334176615000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/thing/12345",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/thing/:id",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000132df4908
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84124,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="J;\xF7*\xF68\xF7\x9A",
 trace_id="(\x93JW'L\x80\x86`\x80\xA4\x01\xD4\x1E\x19\xDD",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x0000000130fd3230 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010505ce58 @hash={}>>
127.0.0.1 - - [20/Jun/2024:16:02:14 -0500] "GET /thing/12345 HTTP/1.1" 200 7 0.0014

Automatic configuration of Rack

I, [2024-06-20T16:03:31.930735 #84994]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Rack was successfully installed with the following options {:allowed_request_headers=>[], :allowed_response_headers=>[], :application=>nil, :record_frontend_span=>false, :untraced_endpoints=>[], :url_quantization=>nil, :untraced_requests=>nil, :response_propagators=>[], :use_rack_events=>true, :allowed_rack_request_headers=>{}, :allowed_rack_response_headers=>{}}
I, [2024-06-20T16:03:31.930797 #84994]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Sinatra was successfully installed with the following options {:install_rack=>true}

#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /example",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000011e2ddc40 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917417080544000,
 end_timestamp=1718917417081867000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/example",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/example",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x000000011ef94f10
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84994,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="\xE2\x93\xE7\x9D \xC0X_",
 trace_id="\xCD9z\x8B~\xAFa\xDFN@\xFC\xE2\x81`\xB7T",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x000000011e6b4fd0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000011e6b0cf0 @hash={}>>
127.0.0.1 - - [20/Jun/2024:16:03:37 -0500] "GET /example HTTP/1.1" 200 31 0.0179
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000011e2ddc40 @code=1, @description="">,
 parent_span_id="\xE8\xB6\x97\x97\x12n\x8FT",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917417090861000,
 end_timestamp=1718917417091007000,
 attributes={"sinatra.template_name"=>"layout"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x000000011ef94f10
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84994,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\x8F\x15\xD6\\>\xC1\x16\xC1",
 trace_id="#\x96\xFC\xA3\x84U}I\xBA9\x85\xDD\x03\x95\xAC\xF9",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x000000011e6b4fd0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000011e6b0cf0 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000011e2ddc40 @code=1, @description="">,
 parent_span_id="NOOP\xFC\x81\xD3\xEB",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917417088657000,
 end_timestamp=1718917417092168000,
 attributes={"sinatra.template_name"=>"example_render"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x000000011ef94f10
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84994,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\xE8\xB6\x97\x97\x12n\x8FT",
 trace_id="#\x96\xFC\xA3\x84U}I\xBA9\x85\xDD\x03\x95\xAC\xF9",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x000000011e6b4fd0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000011e6b0cf0 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /example_render",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000011e2ddc40 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917417088583000,
 end_timestamp=1718917417095124000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/example_render",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/example_render",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x000000011ef94f10
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84994,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="NOOP\xFC\x81\xD3\xEB",
 trace_id="#\x96\xFC\xA3\x84U}I\xBA9\x85\xDD\x03\x95\xAC\xF9",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x000000011e6b4fd0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000011e6b0cf0 @hash={}>>
127.0.0.1 - - [20/Jun/2024:16:03:37 -0500] "GET /example_render HTTP/1.1" 200 14 0.0077
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="GET /thing/:id",
 kind=:server,
 status=#<OpenTelemetry::Trace::Status:0x000000011e2ddc40 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=7,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917417098258000,
 end_timestamp=1718917417098682000,
 attributes=
  {"http.method"=>"GET",
   "http.host"=>"0.0.0.0:4567",
   "http.scheme"=>"http",
   "http.target"=>"/thing/12345",
   "http.user_agent"=>"Ruby",
   "http.route"=>"/thing/:id",
   "http.status_code"=>200},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x000000011ef94f10
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>84994,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Rack",
   version="0.24.5">,
 span_id="\x12\xAC\xE0\xEAayO\x85",
 trace_id="\x11\xC8\t:\xA3\xF3\x99D-\xE9\x13~Af\xDFY",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x000000011e6b4fd0 @flags=1>,

Sinatra with install_rack: false and not middleware configured:

I, [2024-06-20T16:05:50.324558 #86401]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Rack was successfully installed with the following options {:allowed_request_headers=>[], :allowed_response_headers=>[], :application=>nil, :record_frontend_span=>false, :untraced_endpoints=>[], :url_quantization=>nil, :untraced_requests=>nil, :response_propagators=>[], :use_rack_events=>true, :allowed_rack_request_headers=>{}, :allowed_rack_response_headers=>{}}
I, [2024-06-20T16:05:50.324619 #86401]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Sinatra was successfully installed with the following options {:install_rack=>false}

#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x00000001053de8b0 @code=1, @description="">,
 parent_span_id="c\xA9z\xE9.tm;",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917553631134000,
 end_timestamp=1718917553631189000,
 attributes={"sinatra.template_name"=>"layout"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000122a154c8
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>86401,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\x81S\xEC\v\x95\xD7#\x88",
 trace_id="vVYzf4\xF6\x8D\xCD\x9A\xBA\x105K\xCD\xEC",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x00000001205d36a0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010539cdc0 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x00000001053de8b0 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917553627571000,
 end_timestamp=1718917553635237000,
 attributes={"sinatra.template_name"=>"example_render"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x0000000122a154c8
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>86401,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="c\xA9z\xE9.tm;",
 trace_id="vVYzf4\xF6\x8D\xCD\x9A\xBA\x105K\xCD\xEC",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x00000001205d36a0 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x000000010539cdc0 @hash={}>>

With rack explicitly disabled

W, [2024-06-20T16:09:13.006688 #88639]  WARN -- : Instrumentation OpenTelemetry::Instrumentation::Rack ignored the following unknown configuration options [:enabled]
W, [2024-06-20T16:09:13.006723 #88639]  WARN -- : Instrumentation: OpenTelemetry::Instrumentation::Rack failed to install
I, [2024-06-20T16:09:13.006761 #88639]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Sinatra was successfully installed with the following options {:install_rack=>false}

#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000012031f440 @code=1, @description="">,
 parent_span_id="\x9E-&\xBE$\x00F~",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917786011116000,
 end_timestamp=1718917786011167000,
 attributes={"sinatra.template_name"=>"layout"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x00000001212b52d8
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>88639,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="mA}\xFB\xAF4o\x8D",
 trace_id="\xB8\x83\xF7\xF3\x1Du\n" + "\xD1\x14\x92\xB3f\xEA#\xC5\x9E",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x00000001202d4080 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x0000000104ce9be0 @hash={}>>
#<struct OpenTelemetry::SDK::Trace::SpanData
 name="sinatra.render_template",
 kind=:internal,
 status=#<OpenTelemetry::Trace::Status:0x000000012031f440 @code=1, @description="">,
 parent_span_id="\x00\x00\x00\x00\x00\x00\x00\x00",
 total_recorded_attributes=1,
 total_recorded_events=0,
 total_recorded_links=0,
 start_timestamp=1718917786009000000,
 end_timestamp=1718917786014840000,
 attributes={"sinatra.template_name"=>"example_render"},
 links=nil,
 events=nil,
 resource=
  #<OpenTelemetry::SDK::Resources::Resource:0x00000001212b52d8
   @attributes=
    {"service.name"=>"unknown_service",
     "process.pid"=>88639,
     "process.command"=>"/Users/arielvalentin/.rbenv/versions/3.3.0/bin/rackup",
     "process.runtime.name"=>"ruby",
     "process.runtime.version"=>"3.3.0",
     "process.runtime.description"=>"ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]",
     "telemetry.sdk.name"=>"opentelemetry",
     "telemetry.sdk.language"=>"ruby",
     "telemetry.sdk.version"=>"1.4.1"}>,
 instrumentation_scope=
  #<struct OpenTelemetry::SDK::InstrumentationScope
   name="OpenTelemetry::Instrumentation::Sinatra",
   version="0.23.5">,
 span_id="\x9E-&\xBE$\x00F~",
 trace_id="\xB8\x83\xF7\xF3\x1Du\n" + "\xD1\x14\x92\xB3f\xEA#\xC5\x9E",
 trace_flags=#<OpenTelemetry::Trace::TraceFlags:0x00000001202d4080 @flags=1>,
 tracestate=#<OpenTelemetry::Trace::Tracestate:0x0000000104ce9be0 @hash={}>>

@arielvalentin arielvalentin force-pushed the sinatra-rack-optional branch from 1ccab8b to 2b8f182 Compare June 16, 2024 17:38
This allows for scenarios when there are multiple Rack Applications mounted in the same builder and you want to avoid installing the Rack Events middleware multiple times in the stack.

A common example is a Rails application that mounts a Sinatra App in the routes file:

```ruby

Rails.application.routes.draw do
  mount Sinatra::Application, at: '/sinatra'
end

```

This results in the Rack middleware being installed multiple times.

Once by the Rails ActionPack instrumentation and then by the Sinatra application leading to multiple Rack spans being created in the same trace.

This change allows you to avoid this by setting the install_rack option to false when using Sinatra instrumentation and allowing users to manually configure the middleware in the stack as they see fit.
@arielvalentin arielvalentin force-pushed the sinatra-rack-optional branch from 2b8f182 to dd3e3b0 Compare June 16, 2024 17:40
@arielvalentin
Copy link
Collaborator Author

Working through some inconsistent and unexpected behavior in tests while trying to make sure the app is installed properly.

Copy link
Contributor

@stevenharman stevenharman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get an example of what using this looks like in the wild? Maybe an example of some real middleware that would cause this case would be helpful?

Also, would it be possible to detect that the Rack instrumentation is already installed, and emit a warning that it's going to be installed again, unless install_rack: false is set?

# Sinatra hook after extension is registered
def self.registered(app)
# Create tracing `render` method
::Sinatra::Base.prepend(RenderPatches)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the use of a module we can prepend rather than module_eval. The later always causes me a little anxiety, and to go re-read the docs; probably trauma from over-use and being overly-clever back in the Ruby meta-programming heydays. 😆

Copy link
Contributor

@kaylareopelle kaylareopelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great as-is. Thanks for taking on this problem. My only suggestion would be to add some sort of documentation for the new config, perhaps in the README?

@arielvalentin
Copy link
Collaborator Author

@stevenharman thank you for the feedback! I will incorporate those changes. I had mentioned that I do not like this solution in during the SIG meeting and it's a bit of a hack to help unblock some teams turn on Sinatra tracing.

Ideally, I would like the registry to handle dependencies and installation order but that is a bit too much for me to handle right now. @mwear suggested something along the lines of using a combination of declarative dependency management in the Base instrumentation DSL and tsort to ensure that instrumentations were installed in the appropriate order but I am moving ahead with this hack for now 😞

@kaylareopelle that was a bit irresponsible of me. I do need to think about current users and future self.

@arielvalentin
Copy link
Collaborator Author

@kaylareopelle I have added docs

@stevenharman I added a diagnostic message and example output. Please let me know if that is what you are looking for.

Copy link
Contributor

@stevenharman stevenharman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢

@arielvalentin
Copy link
Collaborator Author

@stevenharman I have a follow up question for you then.

If the Rack is not already installed, should this instrumentation basically fail the installation rather than partially install the template rendering?

Now that I am thinking about it, it will seem so weird to generate incomplete instrumentation spans where rendering a template may be the ingress span.

@stevenharman
Copy link
Contributor

So, the situation would be that someone has, for some reason explicitly set install_rack: false, and then not manually installed it either?

I cannot think of a reason that would be desirable, but perhaps it is? That said, I'd also prefer keep things simple (at least conceptually, if not also technically) until there's an actual use case/need for the carrying cost of the added complexity. i.e., maybe it should error because at least then folks won't be accidentally half-installing the instrumentation and then confused why spans are missing?

@arielvalentin
Copy link
Collaborator Author

So, the situation would be that someone has, for some reason explicitly set install_rack: false, and then not manually installed it either?

Yes.

I cannot think of a reason that would be desirable, but perhaps it is? That said, I'd also prefer keep things simple (at least conceptually, if not also technically) until there's an actual use case/need for the carrying cost of the added complexity. i.e., maybe it should error because at least then folks won't be accidentally half-installing the instrumentation and then confused why spans are missing?

Yes. I think it would be strange for Sinatra think it is installed, though its dependency was not. We will still have the scenario where the middleware is missing but at least this takes one use case out of the equation.

Copy link
Contributor

@kaylareopelle kaylareopelle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small typos, looks great otherwise. Thanks for adding the documentation!

arielvalentin and others added 2 commits June 21, 2024 12:02
…tra/instrumentation.rb

Co-authored-by: Kayla Reopelle (she/her) <[email protected]>
@arielvalentin
Copy link
Collaborator Author

@stevenharman On second thought, I am going to leave this as is. If someone intentionally disables the Rack dependency, they may install it at any time much later in the process and everything will still work as expected.

@arielvalentin arielvalentin merged commit 08fad6e into open-telemetry:main Jun 21, 2024
54 checks passed
@arielvalentin arielvalentin deleted the sinatra-rack-optional branch June 21, 2024 23:29
@github-actions github-actions bot mentioned this pull request Jun 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants