opentelemetry-instrumentation-rack 0.25.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 +28 -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 +17 -195
- data/lib/opentelemetry/instrumentation/rack/middlewares/event_handler.rb +0 -268
- data/lib/opentelemetry/instrumentation/rack/middlewares/tracer_middleware.rb +0 -203
@@ -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
|