opentelemetry-instrumentation-rack 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -18
- data/README.md +17 -1
- data/lib/opentelemetry/instrumentation/rack/instrumentation.rb +49 -7
- data/lib/opentelemetry/instrumentation/rack/middlewares/dup/event_handler.rb +281 -0
- data/lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb +214 -0
- data/lib/opentelemetry/instrumentation/rack/middlewares/old/event_handler.rb +270 -0
- data/lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb +205 -0
- data/lib/opentelemetry/instrumentation/rack/middlewares/stable/event_handler.rb +269 -0
- data/lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb +206 -0
- data/lib/opentelemetry/instrumentation/rack/version.rb +1 -1
- metadata +10 -188
- data/lib/opentelemetry/instrumentation/rack/middlewares/event_handler.rb +0 -268
- data/lib/opentelemetry/instrumentation/rack/middlewares/tracer_middleware.rb +0 -203
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opentelemetry-instrumentation-rack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.27.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OpenTelemetry Authors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-api
|
@@ -38,188 +38,6 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.23.0
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: appraisal
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '2.5'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '2.5'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '2.4'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '2.4'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: minitest
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '5.0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '5.0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: opentelemetry-sdk
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '1.1'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '1.1'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: opentelemetry-sdk-experimental
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0.1'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0.1'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: opentelemetry-test-helpers
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "~>"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0.3'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "~>"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0.3'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rake
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '13.0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '13.0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: rspec-mocks
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - ">="
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: '0'
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - ">="
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: '0'
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: rubocop
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: 1.69.1
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: 1.69.1
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: rubocop-performance
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - "~>"
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 1.23.0
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - "~>"
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 1.23.0
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: simplecov
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - "~>"
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: 0.17.1
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - "~>"
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: 0.17.1
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: webmock
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - "~>"
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: 3.24.0
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - "~>"
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: 3.24.0
|
209
|
-
- !ruby/object:Gem::Dependency
|
210
|
-
name: yard
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
212
|
-
requirements:
|
213
|
-
- - "~>"
|
214
|
-
- !ruby/object:Gem::Version
|
215
|
-
version: '0.9'
|
216
|
-
type: :development
|
217
|
-
prerelease: false
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
219
|
-
requirements:
|
220
|
-
- - "~>"
|
221
|
-
- !ruby/object:Gem::Version
|
222
|
-
version: '0.9'
|
223
41
|
description: Rack instrumentation for the OpenTelemetry framework
|
224
42
|
email:
|
225
43
|
- cncf-opentelemetry-contributors@lists.cncf.io
|
@@ -235,8 +53,12 @@ files:
|
|
235
53
|
- lib/opentelemetry/instrumentation.rb
|
236
54
|
- lib/opentelemetry/instrumentation/rack.rb
|
237
55
|
- lib/opentelemetry/instrumentation/rack/instrumentation.rb
|
238
|
-
- lib/opentelemetry/instrumentation/rack/middlewares/event_handler.rb
|
239
|
-
- lib/opentelemetry/instrumentation/rack/middlewares/tracer_middleware.rb
|
56
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/dup/event_handler.rb
|
57
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/dup/tracer_middleware.rb
|
58
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/old/event_handler.rb
|
59
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/old/tracer_middleware.rb
|
60
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/stable/event_handler.rb
|
61
|
+
- lib/opentelemetry/instrumentation/rack/middlewares/stable/tracer_middleware.rb
|
240
62
|
- lib/opentelemetry/instrumentation/rack/util.rb
|
241
63
|
- lib/opentelemetry/instrumentation/rack/util/queue_time.rb
|
242
64
|
- lib/opentelemetry/instrumentation/rack/version.rb
|
@@ -244,10 +66,10 @@ homepage: https://github.com/open-telemetry/opentelemetry-ruby-contrib
|
|
244
66
|
licenses:
|
245
67
|
- Apache-2.0
|
246
68
|
metadata:
|
247
|
-
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-rack/0.
|
69
|
+
changelog_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-rack/0.27.0/file/CHANGELOG.md
|
248
70
|
source_code_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/rack
|
249
71
|
bug_tracker_uri: https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues
|
250
|
-
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-rack/0.
|
72
|
+
documentation_uri: https://rubydoc.info/gems/opentelemetry-instrumentation-rack/0.27.0
|
251
73
|
post_install_message:
|
252
74
|
rdoc_options: []
|
253
75
|
require_paths:
|
@@ -1,268 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright The OpenTelemetry Authors
|
4
|
-
#
|
5
|
-
# SPDX-License-Identifier: Apache-2.0
|
6
|
-
|
7
|
-
require_relative '../util'
|
8
|
-
require 'opentelemetry/trace/status'
|
9
|
-
|
10
|
-
module OpenTelemetry
|
11
|
-
module Instrumentation
|
12
|
-
module Rack
|
13
|
-
module Middlewares
|
14
|
-
# OTel Rack Event Handler
|
15
|
-
#
|
16
|
-
# This seeds the root context for this service with the server span as the `current_span`
|
17
|
-
# allowing for callers later in the stack to reference it using {OpenTelemetry::Trace.current_span}
|
18
|
-
#
|
19
|
-
# It also registers the server span in a context dedicated to this instrumentation that users may look up
|
20
|
-
# using {OpenTelemetry::Instrumentation::Rack.current_span}, which makes it possible for users to mutate the span,
|
21
|
-
# e.g. add events or update the span name like in the {ActionPack} instrumentation.
|
22
|
-
#
|
23
|
-
# @example Rack App Using BodyProxy
|
24
|
-
# GLOBAL_LOGGER = Logger.new($stderr)
|
25
|
-
# APP_TRACER = OpenTelemetry.tracer_provider.tracer('my-app', '1.0.0')
|
26
|
-
#
|
27
|
-
# Rack::Builder.new do
|
28
|
-
# use Rack::Events, [OpenTelemetry::Instrumentation::Rack::Middlewares::EventHandler.new]
|
29
|
-
# run lambda { |_arg|
|
30
|
-
# APP_TRACER.in_span('hello-world') do |_span|
|
31
|
-
# body = Rack::BodyProxy.new(['hello world!']) do
|
32
|
-
# rack_span = OpenTelemetry::Instrumentation::Rack.current_span
|
33
|
-
# GLOBAL_LOGGER.info("otel.trace_id=#{rack_span.context.hex_trace_id} otel.span_id=#{rack_span.context.hex_span_id}")
|
34
|
-
# end
|
35
|
-
# [200, { 'Content-Type' => 'text/plain' }, body]
|
36
|
-
# end
|
37
|
-
# }
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# @see Rack::Events
|
41
|
-
# @see OpenTelemetry::Instrumentation::Rack.current_span
|
42
|
-
class EventHandler
|
43
|
-
include ::Rack::Events::Abstract
|
44
|
-
|
45
|
-
OTEL_TOKEN_AND_SPAN = 'otel.rack.token_and_span'
|
46
|
-
|
47
|
-
# Creates a server span for this current request using the incoming parent context
|
48
|
-
# and registers them as the {current_span}
|
49
|
-
#
|
50
|
-
# @param [Rack::Request] The current HTTP request
|
51
|
-
# @param [Rack::Response] This is nil in practice
|
52
|
-
# @return [void]
|
53
|
-
def on_start(request, _)
|
54
|
-
parent_context = if untraced_request?(request.env)
|
55
|
-
extract_remote_context(request, OpenTelemetry::Common::Utilities.untraced)
|
56
|
-
else
|
57
|
-
extract_remote_context(request)
|
58
|
-
end
|
59
|
-
|
60
|
-
span = create_span(parent_context, request)
|
61
|
-
span_ctx = OpenTelemetry::Trace.context_with_span(span, parent_context: parent_context)
|
62
|
-
rack_ctx = OpenTelemetry::Instrumentation::Rack.context_with_span(span, parent_context: span_ctx)
|
63
|
-
request.env[OTEL_TOKEN_AND_SPAN] = [OpenTelemetry::Context.attach(rack_ctx), span]
|
64
|
-
rescue StandardError => e
|
65
|
-
OpenTelemetry.handle_error(exception: e)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Optionally adds debugging response headers injected from {response_propagators}
|
69
|
-
#
|
70
|
-
# @param [Rack::Request] The current HTTP request
|
71
|
-
# @param [Rack::Response] This current HTTP response
|
72
|
-
# @return [void]
|
73
|
-
def on_commit(request, response)
|
74
|
-
span = OpenTelemetry::Instrumentation::Rack.current_span
|
75
|
-
return unless span.recording?
|
76
|
-
|
77
|
-
response_propagators&.each do |propagator|
|
78
|
-
propagator.inject(response.headers)
|
79
|
-
rescue StandardError => e
|
80
|
-
OpenTelemetry.handle_error(message: 'Unable to inject response propagation headers', exception: e)
|
81
|
-
end
|
82
|
-
rescue StandardError => e
|
83
|
-
OpenTelemetry.handle_error(exception: e)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Records Unexpected Exceptions on the Rack span and set the Span Status to Error
|
87
|
-
#
|
88
|
-
# @note does nothing if the span is a non-recording span
|
89
|
-
# @param [Rack::Request] The current HTTP request
|
90
|
-
# @param [Rack::Response] The current HTTP response
|
91
|
-
# @param [Exception] An unxpected error raised by the application
|
92
|
-
def on_error(request, _, error)
|
93
|
-
span = OpenTelemetry::Instrumentation::Rack.current_span
|
94
|
-
return unless span.recording?
|
95
|
-
|
96
|
-
span.record_exception(error)
|
97
|
-
span.status = OpenTelemetry::Trace::Status.error(error.class.name)
|
98
|
-
rescue StandardError => e
|
99
|
-
OpenTelemetry.handle_error(exception: e)
|
100
|
-
end
|
101
|
-
|
102
|
-
# Finishes the span making it eligible to be exported and cleans up existing contexts
|
103
|
-
#
|
104
|
-
# @note does nothing if the span is a non-recording span
|
105
|
-
# @param [Rack::Request] The current HTTP request
|
106
|
-
# @param [Rack::Response] The current HTTP response
|
107
|
-
def on_finish(request, response)
|
108
|
-
span = OpenTelemetry::Instrumentation::Rack.current_span
|
109
|
-
return unless span.recording?
|
110
|
-
|
111
|
-
add_response_attributes(span, response) if response
|
112
|
-
rescue StandardError => e
|
113
|
-
OpenTelemetry.handle_error(exception: e)
|
114
|
-
ensure
|
115
|
-
detach_context(request)
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
EMPTY_HASH = {}.freeze
|
121
|
-
def extract_request_headers(env)
|
122
|
-
return EMPTY_HASH if allowed_request_headers.empty?
|
123
|
-
|
124
|
-
allowed_request_headers.each_with_object({}) do |(key, value), result|
|
125
|
-
result[value] = env[key] if env.key?(key)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def extract_response_attributes(response)
|
130
|
-
attributes = { 'http.status_code' => response.status.to_i }
|
131
|
-
attributes.merge!(extract_response_headers(response.headers))
|
132
|
-
attributes
|
133
|
-
end
|
134
|
-
|
135
|
-
def extract_response_headers(headers)
|
136
|
-
return EMPTY_HASH if allowed_response_headers.empty?
|
137
|
-
|
138
|
-
allowed_response_headers.each_with_object({}) do |(key, value), result|
|
139
|
-
if headers.key?(key)
|
140
|
-
result[value] = headers[key]
|
141
|
-
else
|
142
|
-
# do case-insensitive match:
|
143
|
-
headers.each do |k, v|
|
144
|
-
if k.upcase == key
|
145
|
-
result[value] = v
|
146
|
-
break
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def untraced_request?(env)
|
154
|
-
return true if untraced_endpoints.include?(env['PATH_INFO'])
|
155
|
-
return true if untraced_requests&.call(env)
|
156
|
-
|
157
|
-
false
|
158
|
-
end
|
159
|
-
|
160
|
-
# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#name
|
161
|
-
#
|
162
|
-
# recommendation: span.name(s) should be low-cardinality (e.g.,
|
163
|
-
# strip off query param value, keep param name)
|
164
|
-
#
|
165
|
-
# see http://github.com/open-telemetry/opentelemetry-specification/pull/416/files
|
166
|
-
def create_request_span_name(request)
|
167
|
-
# NOTE: dd-trace-rb has implemented 'quantization' (which lowers url cardinality)
|
168
|
-
# see Datadog::Quantization::HTTP.url
|
169
|
-
|
170
|
-
if (implementation = url_quantization)
|
171
|
-
request_uri_or_path_info = request.env['REQUEST_URI'] || request.path_info
|
172
|
-
implementation.call(request_uri_or_path_info, request.env)
|
173
|
-
else
|
174
|
-
"HTTP #{request.request_method}"
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def extract_remote_context(request, context = Context.current)
|
179
|
-
OpenTelemetry.propagation.extract(
|
180
|
-
request.env,
|
181
|
-
context: context,
|
182
|
-
getter: OpenTelemetry::Common::Propagation.rack_env_getter
|
183
|
-
)
|
184
|
-
end
|
185
|
-
|
186
|
-
def request_span_attributes(env)
|
187
|
-
attributes = {
|
188
|
-
'http.method' => env['REQUEST_METHOD'],
|
189
|
-
'http.host' => env['HTTP_HOST'] || 'unknown',
|
190
|
-
'http.scheme' => env['rack.url_scheme'],
|
191
|
-
'http.target' => env['QUERY_STRING'].empty? ? env['PATH_INFO'] : "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
|
192
|
-
}
|
193
|
-
|
194
|
-
attributes['http.user_agent'] = env['HTTP_USER_AGENT'] if env['HTTP_USER_AGENT']
|
195
|
-
attributes.merge!(extract_request_headers(env))
|
196
|
-
attributes
|
197
|
-
end
|
198
|
-
|
199
|
-
def detach_context(request)
|
200
|
-
return nil unless request.env[OTEL_TOKEN_AND_SPAN]
|
201
|
-
|
202
|
-
token, span = request.env[OTEL_TOKEN_AND_SPAN]
|
203
|
-
span.finish
|
204
|
-
OpenTelemetry::Context.detach(token)
|
205
|
-
rescue StandardError => e
|
206
|
-
OpenTelemetry.handle_error(exception: e)
|
207
|
-
end
|
208
|
-
|
209
|
-
def add_response_attributes(span, response)
|
210
|
-
span.status = OpenTelemetry::Trace::Status.error if response.server_error?
|
211
|
-
attributes = extract_response_attributes(response)
|
212
|
-
span.add_attributes(attributes)
|
213
|
-
rescue StandardError => e
|
214
|
-
OpenTelemetry.handle_error(exception: e)
|
215
|
-
end
|
216
|
-
|
217
|
-
def record_frontend_span?
|
218
|
-
config[:record_frontend_span] == true
|
219
|
-
end
|
220
|
-
|
221
|
-
def untraced_endpoints
|
222
|
-
config[:untraced_endpoints]
|
223
|
-
end
|
224
|
-
|
225
|
-
def untraced_requests
|
226
|
-
config[:untraced_requests]
|
227
|
-
end
|
228
|
-
|
229
|
-
def url_quantization
|
230
|
-
config[:url_quantization]
|
231
|
-
end
|
232
|
-
|
233
|
-
def response_propagators
|
234
|
-
config[:response_propagators]
|
235
|
-
end
|
236
|
-
|
237
|
-
def allowed_request_headers
|
238
|
-
config[:allowed_rack_request_headers]
|
239
|
-
end
|
240
|
-
|
241
|
-
def allowed_response_headers
|
242
|
-
config[:allowed_rack_response_headers]
|
243
|
-
end
|
244
|
-
|
245
|
-
def tracer
|
246
|
-
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.tracer
|
247
|
-
end
|
248
|
-
|
249
|
-
def config
|
250
|
-
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.config
|
251
|
-
end
|
252
|
-
|
253
|
-
def create_span(parent_context, request)
|
254
|
-
span = tracer.start_span(
|
255
|
-
create_request_span_name(request),
|
256
|
-
with_parent: parent_context,
|
257
|
-
kind: :server,
|
258
|
-
attributes: request_span_attributes(request.env)
|
259
|
-
)
|
260
|
-
request_start_time = OpenTelemetry::Instrumentation::Rack::Util::QueueTime.get_request_start(request.env)
|
261
|
-
span.add_event('http.proxy.request.started', timestamp: request_start_time) unless request_start_time.nil?
|
262
|
-
span
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
@@ -1,203 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copyright The OpenTelemetry Authors
|
4
|
-
#
|
5
|
-
# SPDX-License-Identifier: Apache-2.0
|
6
|
-
|
7
|
-
require 'opentelemetry/trace/status'
|
8
|
-
|
9
|
-
module OpenTelemetry
|
10
|
-
module Instrumentation
|
11
|
-
module Rack
|
12
|
-
module Middlewares
|
13
|
-
# TracerMiddleware propagates context and instruments Rack requests
|
14
|
-
# by way of its middleware system
|
15
|
-
class TracerMiddleware
|
16
|
-
class << self
|
17
|
-
def allowed_rack_request_headers
|
18
|
-
@allowed_rack_request_headers ||= Array(config[:allowed_request_headers]).each_with_object({}) do |header, memo|
|
19
|
-
key = header.to_s.upcase.gsub(/[-\s]/, '_')
|
20
|
-
case key
|
21
|
-
when 'CONTENT_TYPE', 'CONTENT_LENGTH'
|
22
|
-
memo[key] = build_attribute_name('http.request.header.', header)
|
23
|
-
else
|
24
|
-
memo["HTTP_#{key}"] = build_attribute_name('http.request.header.', header)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def allowed_response_headers
|
30
|
-
@allowed_response_headers ||= Array(config[:allowed_response_headers]).each_with_object({}) do |header, memo|
|
31
|
-
memo[header] = build_attribute_name('http.response.header.', header)
|
32
|
-
memo[header.to_s.upcase] = build_attribute_name('http.response.header.', header)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def build_attribute_name(prefix, suffix)
|
37
|
-
prefix + suffix.to_s.downcase.gsub(/[-\s]/, '_')
|
38
|
-
end
|
39
|
-
|
40
|
-
def config
|
41
|
-
Rack::Instrumentation.instance.config
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def clear_cached_config
|
47
|
-
@allowed_rack_request_headers = nil
|
48
|
-
@allowed_response_headers = nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
EMPTY_HASH = {}.freeze
|
53
|
-
|
54
|
-
def initialize(app)
|
55
|
-
@app = app
|
56
|
-
@untraced_endpoints = config[:untraced_endpoints]
|
57
|
-
end
|
58
|
-
|
59
|
-
def call(env)
|
60
|
-
if untraced_request?(env)
|
61
|
-
OpenTelemetry::Common::Utilities.untraced do
|
62
|
-
return @app.call(env)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
original_env = env.dup
|
67
|
-
extracted_context = OpenTelemetry.propagation.extract(
|
68
|
-
env,
|
69
|
-
getter: OpenTelemetry::Common::Propagation.rack_env_getter
|
70
|
-
)
|
71
|
-
frontend_context = create_frontend_span(env, extracted_context)
|
72
|
-
|
73
|
-
# restore extracted context in this process:
|
74
|
-
OpenTelemetry::Context.with_current(frontend_context || extracted_context) do
|
75
|
-
request_span_name = create_request_span_name(env['REQUEST_URI'] || original_env['PATH_INFO'], env)
|
76
|
-
request_span_kind = frontend_context.nil? ? :server : :internal
|
77
|
-
tracer.in_span(request_span_name,
|
78
|
-
attributes: request_span_attributes(env: env),
|
79
|
-
kind: request_span_kind) do |request_span|
|
80
|
-
request_start_time = OpenTelemetry::Instrumentation::Rack::Util::QueueTime.get_request_start(env)
|
81
|
-
request_span.add_event('http.proxy.request.started', timestamp: request_start_time) unless request_start_time.nil?
|
82
|
-
OpenTelemetry::Instrumentation::Rack.with_span(request_span) do
|
83
|
-
@app.call(env).tap do |status, headers, response|
|
84
|
-
set_attributes_after_request(request_span, status, headers, response)
|
85
|
-
config[:response_propagators].each { |propagator| propagator.inject(headers) }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
ensure
|
91
|
-
finish_span(frontend_context)
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
|
96
|
-
def untraced_request?(env)
|
97
|
-
return true if @untraced_endpoints.include?(env['PATH_INFO'])
|
98
|
-
return true if config[:untraced_requests]&.call(env)
|
99
|
-
|
100
|
-
false
|
101
|
-
end
|
102
|
-
|
103
|
-
# return Context with the frontend span as the current span
|
104
|
-
def create_frontend_span(env, extracted_context)
|
105
|
-
request_start_time = OpenTelemetry::Instrumentation::Rack::Util::QueueTime.get_request_start(env)
|
106
|
-
|
107
|
-
return unless config[:record_frontend_span] && !request_start_time.nil?
|
108
|
-
|
109
|
-
span = tracer.start_span('http_server.proxy',
|
110
|
-
with_parent: extracted_context,
|
111
|
-
start_timestamp: request_start_time,
|
112
|
-
kind: :server)
|
113
|
-
|
114
|
-
OpenTelemetry::Trace.context_with_span(span, parent_context: extracted_context)
|
115
|
-
end
|
116
|
-
|
117
|
-
def finish_span(context)
|
118
|
-
OpenTelemetry::Trace.current_span(context).finish if context
|
119
|
-
end
|
120
|
-
|
121
|
-
def tracer
|
122
|
-
OpenTelemetry::Instrumentation::Rack::Instrumentation.instance.tracer
|
123
|
-
end
|
124
|
-
|
125
|
-
def request_span_attributes(env:)
|
126
|
-
attributes = {
|
127
|
-
'http.method' => env['REQUEST_METHOD'],
|
128
|
-
'http.host' => env['HTTP_HOST'] || 'unknown',
|
129
|
-
'http.scheme' => env['rack.url_scheme'],
|
130
|
-
'http.target' => env['QUERY_STRING'].empty? ? env['PATH_INFO'] : "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
|
131
|
-
}
|
132
|
-
|
133
|
-
attributes['http.user_agent'] = env['HTTP_USER_AGENT'] if env['HTTP_USER_AGENT']
|
134
|
-
attributes.merge!(allowed_request_headers(env))
|
135
|
-
end
|
136
|
-
|
137
|
-
# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#name
|
138
|
-
#
|
139
|
-
# recommendation: span.name(s) should be low-cardinality (e.g.,
|
140
|
-
# strip off query param value, keep param name)
|
141
|
-
#
|
142
|
-
# see http://github.com/open-telemetry/opentelemetry-specification/pull/416/files
|
143
|
-
def create_request_span_name(request_uri_or_path_info, env)
|
144
|
-
# NOTE: dd-trace-rb has implemented 'quantization' (which lowers url cardinality)
|
145
|
-
# see Datadog::Quantization::HTTP.url
|
146
|
-
|
147
|
-
if (implementation = config[:url_quantization])
|
148
|
-
implementation.call(request_uri_or_path_info, env)
|
149
|
-
else
|
150
|
-
"HTTP #{env['REQUEST_METHOD']}"
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def set_attributes_after_request(span, status, headers, _response)
|
155
|
-
span.status = OpenTelemetry::Trace::Status.error if (500..599).cover?(status.to_i)
|
156
|
-
span.set_attribute('http.status_code', status)
|
157
|
-
|
158
|
-
# NOTE: if data is available, it would be good to do this:
|
159
|
-
# set_attribute('http.route', ...
|
160
|
-
# e.g., "/users/:userID?
|
161
|
-
|
162
|
-
allowed_response_headers(headers).each { |k, v| span.set_attribute(k, v) }
|
163
|
-
end
|
164
|
-
|
165
|
-
def allowed_request_headers(env)
|
166
|
-
return EMPTY_HASH if self.class.allowed_rack_request_headers.empty?
|
167
|
-
|
168
|
-
{}.tap do |result|
|
169
|
-
self.class.allowed_rack_request_headers.each do |key, value|
|
170
|
-
result[value] = env[key] if env.key?(key)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def allowed_response_headers(headers)
|
176
|
-
return EMPTY_HASH if headers.nil?
|
177
|
-
return EMPTY_HASH if self.class.allowed_response_headers.empty?
|
178
|
-
|
179
|
-
{}.tap do |result|
|
180
|
-
self.class.allowed_response_headers.each do |key, value|
|
181
|
-
if headers.key?(key)
|
182
|
-
result[value] = headers[key]
|
183
|
-
else
|
184
|
-
# do case-insensitive match:
|
185
|
-
headers.each do |k, v|
|
186
|
-
if k.upcase == key
|
187
|
-
result[value] = v
|
188
|
-
break
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def config
|
197
|
-
Rack::Instrumentation.instance.config
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|