epsagon 0.0.9 → 0.0.14
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/lib/epsagon.rb +16 -11
- data/lib/instrumentation/aws_sdk.rb +1 -26
- data/lib/instrumentation/aws_sdk_plugin.rb +36 -0
- data/lib/instrumentation/epsagon_faraday_middleware.rb +1 -0
- data/lib/instrumentation/epsagon_rails_middleware.rb +286 -0
- data/lib/instrumentation/faraday.rb +1 -0
- data/lib/instrumentation/net_http.rb +1 -1
- data/lib/instrumentation/rails.rb +63 -0
- data/lib/instrumentation/sinatra.rb +8 -4
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71cba45cc332a05fb153ec269dd3370a5a6e925f53db822e283247b4dca3d298
|
4
|
+
data.tar.gz: 1a083edb95e3648a3bb3ac8943c1bb1e3cae0ac60aca1795bf9e496851d83dfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84c185df8c80ba4bf24905e7e84dabe300bdf40d3b17a761cd024cdabf89a1ccabf3b2e2c9e20bfd8495973700cf0b46b4c19e133f8dcc8310cebde1b550adc6
|
7
|
+
data.tar.gz: 64d07d50a14609618ed1f836e05b1d05a89de2b5c360608e8622ed64a10df5062fd29cc24ea3beb6fecb1f95401727e89599d689afbb4189e6985a2248719330
|
data/lib/epsagon.rb
CHANGED
@@ -9,19 +9,23 @@ require 'opentelemetry/exporter/otlp'
|
|
9
9
|
require_relative 'instrumentation/sinatra'
|
10
10
|
require_relative 'instrumentation/net_http'
|
11
11
|
require_relative 'instrumentation/faraday'
|
12
|
+
require_relative 'instrumentation/aws_sdk'
|
13
|
+
require_relative 'instrumentation/rails'
|
12
14
|
require_relative 'util'
|
13
15
|
|
14
16
|
Bundler.require
|
15
17
|
|
16
18
|
# Epsagon tracing main entry point
|
17
19
|
module Epsagon
|
20
|
+
DEFAULT_BACKEND = 'opentelemetry.tc.epsagon.com:443/traces'
|
21
|
+
|
18
22
|
@@epsagon_config = {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
|
24
|
+
debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
|
25
|
+
token: ENV['EPSAGON_TOKEN'],
|
26
|
+
app_name: ENV['EPSAGON_APP_NAME'],
|
27
|
+
backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND
|
28
|
+
}
|
25
29
|
|
26
30
|
module_function
|
27
31
|
|
@@ -39,14 +43,16 @@ module Epsagon
|
|
39
43
|
configurator.use 'EpsagonSinatraInstrumentation', { epsagon: @@epsagon_config }
|
40
44
|
configurator.use 'EpsagonNetHTTPInstrumentation', { epsagon: @@epsagon_config }
|
41
45
|
configurator.use 'EpsagonFaradayInstrumentation', { epsagon: @@epsagon_config }
|
46
|
+
configurator.use 'EpsagonAwsSdkInstrumentation', { epsagon: @@epsagon_config }
|
47
|
+
configurator.use 'EpsagonRailsInstrumentation', { epsagon: @@epsagon_config }
|
42
48
|
|
43
49
|
if @@epsagon_config[:debug]
|
44
50
|
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
45
51
|
OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
'x-epsagon-token' => @@epsagon_config[:token]
|
53
|
+
},
|
54
|
+
endpoint: @@epsagon_config[:backend],
|
55
|
+
insecure: @@epsagon_config[:insecure] || false)
|
50
56
|
)
|
51
57
|
|
52
58
|
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
@@ -62,7 +68,6 @@ module Epsagon
|
|
62
68
|
)
|
63
69
|
end
|
64
70
|
end
|
65
|
-
|
66
71
|
end
|
67
72
|
|
68
73
|
# monkey patch to include epsagon confs
|
@@ -2,32 +2,6 @@
|
|
2
2
|
|
3
3
|
require_relative '../util'
|
4
4
|
|
5
|
-
# AWS SDK plugin for epsagon instrumentation
|
6
|
-
class EpsagonAwsPlugin < Seahorse::Client::Plugin
|
7
|
-
def add_handlers(handlers, _)
|
8
|
-
handlers.add(EpsagonAwsHandler, step: :validate)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Generates Spans for all uses of AWS SDK
|
13
|
-
class EpsagonAwsHandler < Seahorse::Client::Handler
|
14
|
-
def call(context)
|
15
|
-
tracer.in_span('') do |span|
|
16
|
-
@handler.call(context).tap do
|
17
|
-
span.set_attribute('aws.operation', context[:command])
|
18
|
-
span.set_attribute('aws.status_code', context[:status_code])
|
19
|
-
span.set_attribute('aws.service', context[:service_name])
|
20
|
-
span.set_attribute('aws.account_id', context[:account_id])
|
21
|
-
span.set_attribute('aws.status_code', context[:status_code])
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def tracer
|
27
|
-
EpsagonAwsSdkInstrumentation.instance.tracer
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
5
|
# AWS SDK epsagon instrumentation
|
32
6
|
class EpsagonAwsSdkInstrumentation < OpenTelemetry::Instrumentation::Base
|
33
7
|
VERSION = '0.0.0'
|
@@ -143,6 +117,7 @@ class EpsagonAwsSdkInstrumentation < OpenTelemetry::Instrumentation::Base
|
|
143
117
|
].freeze
|
144
118
|
|
145
119
|
install do |_|
|
120
|
+
require_relative 'aws_sdk_plugin'
|
146
121
|
::Seahorse::Client::Base.add_plugin(EpsagonAwsPlugin)
|
147
122
|
loaded_constants.each { |klass| klass.add_plugin(EpsagonAwsPlugin) }
|
148
123
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'aws-sdk-core'
|
4
|
+
require 'opentelemetry/common'
|
5
|
+
require 'opentelemetry/sdk'
|
6
|
+
|
7
|
+
def untraced(&block)
|
8
|
+
OpenTelemetry::Trace.with_span(OpenTelemetry::Trace::Span.new, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
# AWS SDK plugin for epsagon instrumentation
|
12
|
+
class EpsagonAwsPlugin < Seahorse::Client::Plugin
|
13
|
+
def add_handlers(handlers, _)
|
14
|
+
handlers.add(EpsagonAwsHandler, step: :validate)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates Spans for all uses of AWS SDK
|
19
|
+
class EpsagonAwsHandler < Seahorse::Client::Handler
|
20
|
+
def call(context)
|
21
|
+
tracer.in_span('', kind: :client) do |span|
|
22
|
+
untraced do
|
23
|
+
@handler.call(context).tap do
|
24
|
+
span.set_attribute('aws.service', context.client.class.to_s.split('::')[1].downcase)
|
25
|
+
span.set_attribute('aws.operation', context.operation.name)
|
26
|
+
span.set_attribute('aws.region', context.client.config.region)
|
27
|
+
span.set_attribute('http.status_code', context.http_response.status_code)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def tracer
|
34
|
+
EpsagonAwsSdkInstrumentation.instance.tracer
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opentelemetry/trace/status'
|
4
|
+
|
5
|
+
module QueueTime
|
6
|
+
REQUEST_START = 'HTTP_X_REQUEST_START'
|
7
|
+
QUEUE_START = 'HTTP_X_QUEUE_START'
|
8
|
+
MINIMUM_ACCEPTABLE_TIME_VALUE = 1_000_000_000
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def get_request_start(env, now = nil) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
13
|
+
header = env[REQUEST_START] || env[QUEUE_START]
|
14
|
+
return unless header
|
15
|
+
|
16
|
+
# nginx header is seconds in the format "t=1512379167.574"
|
17
|
+
# apache header is microseconds in the format "t=1570633834463123"
|
18
|
+
# heroku header is milliseconds in the format "1570634024294"
|
19
|
+
time_string = header.to_s.delete('^0-9')
|
20
|
+
return if time_string.nil?
|
21
|
+
|
22
|
+
# Return nil if the time is clearly invalid
|
23
|
+
time_value = "#{time_string[0, 10]}.#{time_string[10, 6]}".to_f
|
24
|
+
return if time_value.zero? || time_value < MINIMUM_ACCEPTABLE_TIME_VALUE
|
25
|
+
|
26
|
+
# return the request_start only if it's lesser than
|
27
|
+
# current time, to avoid significant clock skew
|
28
|
+
request_start = Time.at(time_value)
|
29
|
+
now ||= Time.now.utc
|
30
|
+
request_start.utc > now ? nil : request_start
|
31
|
+
rescue StandardError => e
|
32
|
+
# in case of an Exception we don't create a
|
33
|
+
# `request.queuing` span
|
34
|
+
OpenTelemetry.logger.debug("[rack] unable to parse request queue headers: #{e}")
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class EpsagonRackMiddleware
|
40
|
+
class << self
|
41
|
+
def allowed_rack_request_headers
|
42
|
+
@allowed_rack_request_headers ||= Array(config[:allowed_request_headers]).each_with_object({}) do |header, memo|
|
43
|
+
memo["HTTP_#{header.to_s.upcase.gsub(/[-\s]/, '_')}"] = build_attribute_name('http.request.headers.', header)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def allowed_response_headers
|
48
|
+
@allowed_response_headers ||= Array(config[:allowed_response_headers]).each_with_object({}) do |header, memo|
|
49
|
+
memo[header] = build_attribute_name('http.response.headers.', header)
|
50
|
+
memo[header.to_s.upcase] = build_attribute_name('http.response.headers.', header)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_attribute_name(prefix, suffix)
|
55
|
+
prefix + suffix.to_s.downcase.gsub(/[-\s]/, '_')
|
56
|
+
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
EpsagonRailsInstrumentation.instance.config
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def clear_cached_config
|
65
|
+
@allowed_rack_request_headers = nil
|
66
|
+
@allowed_response_headers = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
EMPTY_HASH = {}.freeze
|
71
|
+
|
72
|
+
def initialize(app)
|
73
|
+
@app = app
|
74
|
+
end
|
75
|
+
|
76
|
+
def call(env)
|
77
|
+
original_env = env.dup
|
78
|
+
extracted_context = OpenTelemetry.propagation.http.extract(env)
|
79
|
+
frontend_context = create_frontend_span(env, extracted_context)
|
80
|
+
|
81
|
+
# restore extracted context in this process:
|
82
|
+
OpenTelemetry::Context.with_current(frontend_context || extracted_context) do
|
83
|
+
request_span_name = create_request_span_name(env['REQUEST_URI'] || original_env['PATH_INFO'])
|
84
|
+
tracer.in_span(env['HTTP_HOST'] || 'unknown',
|
85
|
+
attributes: request_span_attributes(env: env),
|
86
|
+
kind: :server) do |http_span|
|
87
|
+
RackExtension.with_span(http_span) do
|
88
|
+
tracer.in_span(
|
89
|
+
env['HTTP_HOST'],
|
90
|
+
kind: :server,
|
91
|
+
attributes: {type: 'rails'}
|
92
|
+
) do |framework_span|
|
93
|
+
@app.call(env).tap do |status, headers, response|
|
94
|
+
set_attributes_after_request(http_span, framework_span, status, headers, response)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
ensure
|
101
|
+
finish_span(frontend_context)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# return Context with the frontend span as the current span
|
107
|
+
def create_frontend_span(env, extracted_context)
|
108
|
+
request_start_time = QueueTime.get_request_start(env)
|
109
|
+
|
110
|
+
return unless config[:record_frontend_span] && !request_start_time.nil?
|
111
|
+
|
112
|
+
span = tracer.start_span('http_server.proxy',
|
113
|
+
with_parent: extracted_context,
|
114
|
+
attributes: {
|
115
|
+
'start_time' => request_start_time.to_f
|
116
|
+
},
|
117
|
+
kind: :server)
|
118
|
+
|
119
|
+
OpenTelemetry::Trace.context_with_span(span, parent_context: extracted_context)
|
120
|
+
end
|
121
|
+
|
122
|
+
def finish_span(context)
|
123
|
+
OpenTelemetry::Trace.current_span(context).finish if context
|
124
|
+
end
|
125
|
+
|
126
|
+
def tracer
|
127
|
+
EpsagonRailsInstrumentation.instance.tracer
|
128
|
+
end
|
129
|
+
|
130
|
+
def request_span_attributes(env:)
|
131
|
+
request = Rack::Request.new(env)
|
132
|
+
path, path_params = request.path.split(';')
|
133
|
+
request_headers = JSON.generate(Hash[*env.select { |k, _v| k.start_with? 'HTTP_' }
|
134
|
+
.collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
|
135
|
+
.collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
|
136
|
+
.sort
|
137
|
+
.flatten])
|
138
|
+
|
139
|
+
attributes = {
|
140
|
+
'operation' => env['REQUEST_METHOD'],
|
141
|
+
'type' => 'http',
|
142
|
+
'http.scheme' => env['rack.url_scheme'],
|
143
|
+
'http.request.path' => path,
|
144
|
+
'http.request.headers' => request_headers
|
145
|
+
}
|
146
|
+
|
147
|
+
unless config[:epsagon][:metadata_only]
|
148
|
+
request.body.rewind
|
149
|
+
request_body = request.body.read
|
150
|
+
request.body.rewind
|
151
|
+
|
152
|
+
attributes.merge!(Util.epsagon_query_attributes(request.query_string))
|
153
|
+
|
154
|
+
attributes.merge!({
|
155
|
+
'http.request.body' => request_body,
|
156
|
+
'http.request.path_params' => path_params,
|
157
|
+
'http.request.headers.User-Agent' => env['HTTP_USER_AGENT']
|
158
|
+
})
|
159
|
+
end
|
160
|
+
|
161
|
+
attributes
|
162
|
+
end
|
163
|
+
|
164
|
+
# e.g., "/webshop/articles/4?s=1":
|
165
|
+
def fullpath(env)
|
166
|
+
query_string = env['QUERY_STRING']
|
167
|
+
path = env['SCRIPT_NAME'] + env['PATH_INFO']
|
168
|
+
|
169
|
+
query_string.empty? ? path : "#{path}?#{query_string}"
|
170
|
+
end
|
171
|
+
|
172
|
+
# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-http.md#name
|
173
|
+
#
|
174
|
+
# recommendation: span.name(s) should be low-cardinality (e.g.,
|
175
|
+
# strip off query param value, keep param name)
|
176
|
+
#
|
177
|
+
# see http://github.com/open-telemetry/opentelemetry-specification/pull/416/files
|
178
|
+
def create_request_span_name(request_uri_or_path_info)
|
179
|
+
# NOTE: dd-trace-rb has implemented 'quantization' (which lowers url cardinality)
|
180
|
+
# see Datadog::Quantization::HTTP.url
|
181
|
+
|
182
|
+
if (implementation = config[:url_quantization])
|
183
|
+
implementation.call(request_uri_or_path_info)
|
184
|
+
else
|
185
|
+
request_uri_or_path_info
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def set_attributes_after_request(http_span, _framework_span, status, headers, response)
|
190
|
+
unless config[:epsagon][:metadata_only]
|
191
|
+
http_span.set_attribute('http.response.headers', JSON.generate(headers))
|
192
|
+
http_span.set_attribute('http.response.body', response.join)
|
193
|
+
end
|
194
|
+
|
195
|
+
http_span.set_attribute('http.status_code', status)
|
196
|
+
http_span.status = OpenTelemetry::Trace::Status.http_to_status(status)
|
197
|
+
end
|
198
|
+
|
199
|
+
def allowed_request_headers(env)
|
200
|
+
return EMPTY_HASH if self.class.allowed_rack_request_headers.empty?
|
201
|
+
|
202
|
+
{}.tap do |result|
|
203
|
+
self.class.allowed_rack_request_headers.each do |key, value|
|
204
|
+
result[value] = env[key] if env.key?(key)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def allowed_response_headers(headers)
|
210
|
+
return EMPTY_HASH if headers.nil?
|
211
|
+
return EMPTY_HASH if self.class.allowed_response_headers.empty?
|
212
|
+
|
213
|
+
{}.tap do |result|
|
214
|
+
self.class.allowed_response_headers.each do |key, value|
|
215
|
+
if headers.key?(key)
|
216
|
+
result[value] = headers[key]
|
217
|
+
else
|
218
|
+
# do case-insensitive match:
|
219
|
+
headers.each do |k, v|
|
220
|
+
if k.upcase == key
|
221
|
+
result[value] = v
|
222
|
+
break
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def config
|
231
|
+
EpsagonRailsInstrumentation.instance.config
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# class EpsagonRackInstrumentation < OpenTelemetry::Instrumentation::Base
|
236
|
+
# install do |config|
|
237
|
+
# require_dependencies
|
238
|
+
|
239
|
+
# retain_middleware_names if config[:retain_middleware_names]
|
240
|
+
# end
|
241
|
+
|
242
|
+
# present do
|
243
|
+
# defined?(::Rack)
|
244
|
+
# end
|
245
|
+
|
246
|
+
# private
|
247
|
+
|
248
|
+
# def require_dependencies
|
249
|
+
# require_relative 'middlewares/tracer_middleware'
|
250
|
+
# end
|
251
|
+
|
252
|
+
# MissingApplicationError = Class.new(StandardError)
|
253
|
+
|
254
|
+
# # intercept all middleware-compatible calls, retain class name
|
255
|
+
# def retain_middleware_names
|
256
|
+
# next_middleware = config[:application]
|
257
|
+
# raise MissingApplicationError unless next_middleware
|
258
|
+
|
259
|
+
# while next_middleware
|
260
|
+
# if next_middleware.respond_to?(:call)
|
261
|
+
# next_middleware.singleton_class.class_eval do
|
262
|
+
# alias_method :__call, :call
|
263
|
+
|
264
|
+
# def call(env)
|
265
|
+
# env['RESPONSE_MIDDLEWARE'] = self.class.to_s
|
266
|
+
# __call(env)
|
267
|
+
# end
|
268
|
+
# end
|
269
|
+
# end
|
270
|
+
|
271
|
+
# next_middleware = next_middleware.instance_variable_defined?('@app') &&
|
272
|
+
# next_middleware.instance_variable_get('@app')
|
273
|
+
# end
|
274
|
+
# end
|
275
|
+
# end
|
276
|
+
|
277
|
+
class EpsagonRailtie < ::Rails::Railtie
|
278
|
+
config.before_initialize do |app|
|
279
|
+
# EpsagonRackInstrumentation.instance.install({})
|
280
|
+
|
281
|
+
app.middleware.insert_after(
|
282
|
+
ActionDispatch::RequestId,
|
283
|
+
EpsagonRackMiddleware
|
284
|
+
)
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opentelemetry'
|
4
|
+
require 'rails'
|
5
|
+
require 'action_controller/railtie'
|
6
|
+
|
7
|
+
require_relative '../util'
|
8
|
+
|
9
|
+
module RackExtension
|
10
|
+
module_function
|
11
|
+
|
12
|
+
CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
|
13
|
+
|
14
|
+
private_constant :CURRENT_SPAN_KEY
|
15
|
+
|
16
|
+
# Returns the current span from the current or provided context
|
17
|
+
#
|
18
|
+
# @param [optional Context] context The context to lookup the current
|
19
|
+
# {Span} from. Defaults to Context.current
|
20
|
+
def current_span(context = nil)
|
21
|
+
context ||= OpenTelemetry::Context.current
|
22
|
+
context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a context containing the span, derived from the optional parent
|
26
|
+
# context, or the current context if one was not provided.
|
27
|
+
#
|
28
|
+
# @param [optional Context] context The context to use as the parent for
|
29
|
+
# the returned context
|
30
|
+
def context_with_span(span, parent_context: OpenTelemetry::Context.current)
|
31
|
+
parent_context.set_value(CURRENT_SPAN_KEY, span)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Activates/deactivates the Span within the current Context, which makes the "current span"
|
35
|
+
# available implicitly.
|
36
|
+
#
|
37
|
+
# On exit, the Span that was active before calling this method will be reactivated.
|
38
|
+
#
|
39
|
+
# @param [Span] span the span to activate
|
40
|
+
# @yield [span, context] yields span and a context containing the span to the block.
|
41
|
+
def with_span(span)
|
42
|
+
OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module MetalPatch
|
47
|
+
def dispatch(name, request, response)
|
48
|
+
rack_span = RackExtension.current_span
|
49
|
+
# rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
|
50
|
+
super(name, request, response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class EpsagonRailsInstrumentation < OpenTelemetry::Instrumentation::Base
|
55
|
+
install do |_config|
|
56
|
+
require_relative 'epsagon_rails_middleware'
|
57
|
+
::ActionController::Metal.prepend(MetalPatch)
|
58
|
+
end
|
59
|
+
|
60
|
+
present do
|
61
|
+
defined?(::Rails)
|
62
|
+
end
|
63
|
+
end
|
@@ -26,7 +26,7 @@ class EpsagonTracerMiddleware
|
|
26
26
|
attributes = {
|
27
27
|
'operation' => env['REQUEST_METHOD'],
|
28
28
|
'type' => 'http',
|
29
|
-
'http.scheme' => env['
|
29
|
+
'http.scheme' => env['rack.url_scheme'],
|
30
30
|
'http.request.path' => path,
|
31
31
|
'http.request.headers' => request_headers
|
32
32
|
}
|
@@ -48,10 +48,14 @@ class EpsagonTracerMiddleware
|
|
48
48
|
tracer.in_span(
|
49
49
|
env['HTTP_HOST'],
|
50
50
|
attributes: attributes,
|
51
|
-
kind: :
|
51
|
+
kind: :server,
|
52
52
|
with_parent: parent_context(env)
|
53
53
|
) do |http_span|
|
54
|
-
tracer.in_span(
|
54
|
+
tracer.in_span(
|
55
|
+
env['HTTP_HOST'],
|
56
|
+
kind: :server,
|
57
|
+
attributes: { type: 'sinatra' }
|
58
|
+
) do |framework_span|
|
55
59
|
app.call(env).tap { |resp| trace_response(http_span, framework_span, env, resp) }
|
56
60
|
end
|
57
61
|
end
|
@@ -78,7 +82,7 @@ class EpsagonTracerMiddleware
|
|
78
82
|
end
|
79
83
|
|
80
84
|
http_span.set_attribute('http.status_code', status)
|
81
|
-
|
85
|
+
http_span.set_attribute('http.route', env['sinatra.route'].split.last) if env['sinatra.route']
|
82
86
|
http_span.status = OpenTelemetry::Trace::Status.http_to_status(status)
|
83
87
|
end
|
84
88
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epsagon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Epsagon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-api
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.11.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: opentelemetry-
|
28
|
+
name: opentelemetry-exporter-otlp
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.11.
|
33
|
+
version: 0.11.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.11.
|
40
|
+
version: 0.11.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: opentelemetry-instrumentation-sinatra
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.11.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: opentelemetry-
|
56
|
+
name: opentelemetry-sdk
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.11.
|
61
|
+
version: 0.11.1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.11.
|
68
|
+
version: 0.11.1
|
69
69
|
description: 'Epsagon provides tracing to Ruby applications for the collection of
|
70
70
|
distributed tracing and performance metrics to simplify complex architectures, eliminate
|
71
71
|
manual work, visualize and correlate data to identify and fix problems fast.
|
@@ -78,9 +78,12 @@ extra_rdoc_files: []
|
|
78
78
|
files:
|
79
79
|
- lib/epsagon.rb
|
80
80
|
- lib/instrumentation/aws_sdk.rb
|
81
|
+
- lib/instrumentation/aws_sdk_plugin.rb
|
81
82
|
- lib/instrumentation/epsagon_faraday_middleware.rb
|
83
|
+
- lib/instrumentation/epsagon_rails_middleware.rb
|
82
84
|
- lib/instrumentation/faraday.rb
|
83
85
|
- lib/instrumentation/net_http.rb
|
86
|
+
- lib/instrumentation/rails.rb
|
84
87
|
- lib/instrumentation/sinatra.rb
|
85
88
|
- lib/util.rb
|
86
89
|
homepage: https://github.com/epsagon/epsagon-ruby
|