Skip to content

Commit 6dca891

Browse files
committed
Sinatra testing
1 parent 5ad41e3 commit 6dca891

File tree

6 files changed

+504
-7
lines changed

6 files changed

+504
-7
lines changed

instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/dup/event_handler.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def request_span_attributes(env)
191191
attributes = {
192192
'http.method' => env['REQUEST_METHOD'],
193193
'http.host' => env['HTTP_HOST'] || 'unknown',
194+
'server.address' => env['HTTP_HOST'] || 'unknown',
194195
'http.scheme' => env['rack.url_scheme'],
195196
'http.target' => env['QUERY_STRING'].empty? ? env['PATH_INFO'] : "#{env['PATH_INFO']}?#{env['QUERY_STRING']}",
196197
'http.request.method' => env['REQUEST_METHOD'],

instrumentation/rack/lib/opentelemetry/instrumentation/rack/middlewares/stable/event_handler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def extract_remote_context(request, context = Context.current)
185185
def request_span_attributes(env)
186186
attributes = {
187187
'http.request.method' => env['REQUEST_METHOD'],
188-
'http.host' => env['HTTP_HOST'] || 'unknown',
188+
'server.address' => env['HTTP_HOST'] || 'unknown',
189189
'url.scheme' => env['rack.url_scheme'],
190190
'url.path' => env['PATH_INFO']
191191
}

instrumentation/sinatra/Appraisals

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@
44
#
55
# SPDX-License-Identifier: Apache-2.0
66

7-
%w[4.1 3.0 2.1].each do |version|
8-
appraise "sinatra-#{version}" do
9-
gem 'sinatra', "~> #{version}"
7+
# To facilitate HTTP semantic convention stability migration, we are using
8+
# appraisal to test the different semantic convention modes/ Rack middlewares.
9+
# When the migration is complete, we should revert testing with different stability
10+
# modes. For more information see CHANGELOG: HTTP semantic convention stability
11+
semconv_stability = %w[dup stable old]
12+
sinatra_versions = %w[4.1 3.0 2.1]
13+
14+
semconv_stability.each do |mode|
15+
sinatra_versions.each do |version|
16+
appraise "sinatra-#{version}-#{mode}" do
17+
gem 'sinatra', "~> #{version}"
18+
end
1019
end
11-
end
1220

13-
appraise 'sinatra-latest' do
14-
gem 'sinatra'
21+
appraise "sinatra-latest-#{mode}" do
22+
gem 'sinatra'
23+
end
1524
end
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright The OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'test_helper'
8+
9+
describe OpenTelemetry::Instrumentation::Sinatra do
10+
include Rack::Test::Methods
11+
12+
let(:instrumentation) { OpenTelemetry::Instrumentation::Sinatra::Instrumentation.instance }
13+
let(:exporter) { EXPORTER }
14+
let(:config) { {} }
15+
16+
class CustomError < StandardError; end
17+
18+
let(:app_one) do
19+
Class.new(Sinatra::Application) do
20+
set :raise_errors, false
21+
get '/endpoint' do
22+
'1'
23+
end
24+
25+
get '/error' do
26+
raise CustomError, 'custom message'
27+
end
28+
29+
template :foo_template do
30+
'Foo Template'
31+
end
32+
33+
get '/with_template' do
34+
erb :foo_template
35+
end
36+
37+
get '/api/v1/foo/:myname/?' do
38+
'Some name'
39+
end
40+
end
41+
end
42+
43+
let(:app_two) do
44+
Class.new(Sinatra::Application) do
45+
set :raise_errors, false
46+
get '/endpoint' do
47+
'2'
48+
end
49+
end
50+
end
51+
52+
let(:apps) do
53+
{
54+
'/one' => app_one,
55+
'/two' => app_two
56+
}
57+
end
58+
59+
let(:app) do
60+
apps_to_build = apps
61+
62+
Rack::Builder.new do
63+
apps_to_build.each do |root, app|
64+
map root do
65+
run app
66+
end
67+
end
68+
end.to_app
69+
end
70+
71+
before do
72+
skip unless ENV['BUNDLE_GEMFILE'].include?('dup')
73+
ENV['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'http/dup'
74+
75+
Sinatra::Base.reset!
76+
77+
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.instance_variable_set(:@installed, false)
78+
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.config.clear
79+
80+
instrumentation.instance_variable_set(:@installed, false)
81+
instrumentation.config.clear
82+
instrumentation.install(config)
83+
exporter.reset
84+
end
85+
86+
describe 'tracing' do
87+
it 'before request' do
88+
_(exporter.finished_spans.size).must_equal 0
89+
end
90+
91+
it 'after request' do
92+
get '/one/endpoint'
93+
94+
_(exporter.finished_spans.size).must_equal 1
95+
end
96+
97+
it 'traces all apps' do
98+
get '/two/endpoint'
99+
100+
_(exporter.finished_spans.size).must_equal 1
101+
end
102+
103+
it 'records attributes' do
104+
get '/one/endpoint'
105+
106+
_(exporter.finished_spans.first.attributes).must_equal(
107+
'http.host' => 'example.org',
108+
'http.method' => 'GET',
109+
'http.route' => '/endpoint',
110+
'http.scheme' => 'http',
111+
'http.status_code' => 200,
112+
'http.target' => '/endpoint',
113+
'server.address' => 'example.org',
114+
'http.request.method' => 'GET',
115+
'url.scheme' => 'http',
116+
'http.response.status_code' => 200,
117+
'url.path' => '/endpoint'
118+
)
119+
end
120+
121+
it 'traces templates' do
122+
get '/one/with_template'
123+
124+
_(exporter.finished_spans.size).must_equal 3
125+
_(exporter.finished_spans.map(&:name))
126+
.must_equal [
127+
'sinatra.render_template',
128+
'sinatra.render_template',
129+
'GET /with_template'
130+
]
131+
_(exporter.finished_spans[0..1].map(&:attributes)
132+
.map { |h| h['sinatra.template_name'] })
133+
.must_equal %w[layout foo_template]
134+
end
135+
136+
it 'correctly name spans' do
137+
get '/one//api/v1/foo/janedoe/'
138+
139+
_(exporter.finished_spans.size).must_equal 1
140+
_(exporter.finished_spans.first.attributes).must_equal(
141+
'http.host' => 'example.org',
142+
'http.method' => 'GET',
143+
'http.target' => '/api/v1/foo/janedoe/',
144+
'http.scheme' => 'http',
145+
'http.status_code' => 200,
146+
'http.route' => '/api/v1/foo/:myname/?',
147+
'server.address' => 'example.org',
148+
'http.request.method' => 'GET',
149+
'url.path' => '/api/v1/foo/janedoe/',
150+
'url.scheme' => 'http',
151+
'http.response.status_code' => 200,
152+
)
153+
_(exporter.finished_spans.map(&:name))
154+
.must_equal [
155+
'GET /api/v1/foo/:myname/?'
156+
]
157+
end
158+
159+
it 'does not create unhandled exceptions for missing routes' do
160+
get '/one/missing_example/not_present'
161+
162+
_(exporter.finished_spans.first.status.code).must_equal OpenTelemetry::Trace::Status::UNSET
163+
_(exporter.finished_spans.first.attributes).must_equal(
164+
'http.host' => 'example.org',
165+
'http.method' => 'GET',
166+
'http.scheme' => 'http',
167+
'http.status_code' => 404,
168+
'http.target' => '/missing_example/not_present',
169+
'server.address' => 'example.org',
170+
'http.request.method' => 'GET',
171+
'url.scheme' => 'http',
172+
'http.response.status_code' => 404,
173+
'url.path' => '/missing_example/not_present'
174+
)
175+
_(exporter.finished_spans.flat_map(&:events)).must_equal([nil])
176+
end
177+
178+
it 'does correctly name spans and add attributes and exception events when the app raises errors' do
179+
get '/one/error'
180+
181+
_(exporter.finished_spans.first.status.code).must_equal OpenTelemetry::Trace::Status::ERROR
182+
_(exporter.finished_spans.first.name).must_equal('GET /error')
183+
_(exporter.finished_spans.first.attributes).must_equal(
184+
'http.host' => 'example.org',
185+
'http.method' => 'GET',
186+
'http.route' => '/error',
187+
'http.scheme' => 'http',
188+
'http.target' => '/error',
189+
'http.status_code' => 500,
190+
'server.address' => 'example.org',
191+
'http.request.method' => 'GET',
192+
'url.scheme' => 'http',
193+
'url.path' => '/error',
194+
'http.response.status_code' => 500
195+
)
196+
_(exporter.finished_spans.flat_map(&:events).map(&:name)).must_equal(['exception'])
197+
end
198+
199+
it 'adds exception type to events when the app raises errors' do
200+
get '/one/error'
201+
202+
_(exporter.finished_spans.first.events[0].attributes['exception.type']).must_equal('CustomError')
203+
_(exporter.finished_spans.first.events[0].attributes['exception.message']).must_equal('custom message')
204+
end
205+
end
206+
207+
describe 'when install_rack is set to false' do
208+
let(:config) { { install_rack: false } }
209+
210+
describe 'missing rack installation' do
211+
it 'disables tracing' do
212+
get '/one/endpoint'
213+
214+
_(exporter.finished_spans).must_be_empty
215+
end
216+
end
217+
218+
describe 'when rack is manually installed' do
219+
let(:app) do
220+
apps_to_build = apps
221+
Rack::Builder.new do
222+
use(*OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.middleware_args_dup)
223+
224+
apps_to_build.each do |root, app|
225+
map root do
226+
run app
227+
end
228+
end
229+
end.to_app
230+
end
231+
232+
before do
233+
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.install
234+
end
235+
236+
it 'creates a span' do
237+
get '/one/endpoint'
238+
239+
_(exporter.finished_spans.first.attributes).must_equal(
240+
'http.method' => 'GET',
241+
'http.host' => 'example.org',
242+
'http.scheme' => 'http',
243+
'http.target' => '/one/endpoint',
244+
'http.route' => '/endpoint',
245+
'http.status_code' => 200,
246+
'http.request.method' => 'GET',
247+
'server.address' => 'example.org',
248+
'url.scheme' => 'http',
249+
'url.path' => '/one/endpoint',
250+
'http.response.status_code' => 200
251+
)
252+
end
253+
end
254+
end
255+
end

instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_test.rb renamed to instrumentation/sinatra/test/opentelemetry/instrumentation/sinatra_old_http_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class CustomError < StandardError; end
6969
end
7070

7171
before do
72+
skip unless ENV['BUNDLE_GEMFILE'].include?('old')
73+
7274
Sinatra::Base.reset!
7375

7476
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.instance_variable_set(:@installed, false)

0 commit comments

Comments
 (0)