epsagon 0.0.21 → 0.0.26
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 +125 -16
- data/lib/epsagon_constants.rb +1 -1
- data/lib/exporter_extension.rb +78 -0
- data/lib/instrumentation/aws_sdk_plugin.rb +116 -5
- data/lib/instrumentation/epsagon_faraday_middleware.rb +5 -5
- data/lib/instrumentation/epsagon_rails_middleware.rb +1 -1
- data/lib/instrumentation/net_http.rb +2 -0
- data/lib/instrumentation/version.rb +0 -0
- data/lib/util.rb +9 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8c6a213fb12b5ded33f1d938b306f56f6de61936197c10be80d53b11258483c1
|
|
4
|
+
data.tar.gz: 34890a4a25d8bf528dc442b975504d1e70b177a09a7868b56538a9b6b112f3d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0ba4c84e5db6f25b6f68c7c31cbd8c694098ac8542c893735955328a18b58f5b49b436dab779dcac6759f246394c8152e0c3ef8791e4169a4abcf2f73e831e0
|
|
7
|
+
data.tar.gz: 95f6e44d44be2917c1a6cb9b224c838a1b46aa0866fe991c5aa8e0ebf6cbd9703f71ede6d871c7eaebbd499d391c19bd8d887ee25a637c581628362e2fa7442d
|
data/lib/epsagon.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
require 'json'
|
|
3
3
|
require 'rubygems'
|
|
4
4
|
require 'net/http'
|
|
5
5
|
require 'bundler/setup'
|
|
6
6
|
require 'opentelemetry/sdk'
|
|
7
7
|
require 'opentelemetry/exporter/otlp'
|
|
8
|
+
require 'opentelemetry/instrumentation/sidekiq'
|
|
8
9
|
|
|
9
10
|
require_relative 'instrumentation/sinatra'
|
|
10
11
|
require_relative 'instrumentation/net_http'
|
|
@@ -13,26 +14,29 @@ require_relative 'instrumentation/aws_sdk'
|
|
|
13
14
|
require_relative 'instrumentation/rails'
|
|
14
15
|
require_relative 'util'
|
|
15
16
|
require_relative 'epsagon_constants'
|
|
17
|
+
require_relative 'exporter_extension'
|
|
16
18
|
|
|
17
19
|
Bundler.require
|
|
18
20
|
|
|
19
21
|
# Epsagon tracing main entry point
|
|
20
22
|
module Epsagon
|
|
21
|
-
|
|
22
23
|
DEFAULT_BACKEND = 'opentelemetry.tc.epsagon.com:443/traces'
|
|
24
|
+
DEFAULT_IGNORE_DOMAINS = ['newrelic.com']
|
|
23
25
|
|
|
24
|
-
@@epsagon_config = {
|
|
25
|
-
metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
|
|
26
|
-
debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
|
|
27
|
-
token: ENV['EPSAGON_TOKEN'],
|
|
28
|
-
app_name: ENV['EPSAGON_APP_NAME'],
|
|
29
|
-
max_attribute_size: ENV['EPSAGON_MAX_ATTRIBUTE_SIZE'] || 5000,
|
|
30
|
-
backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND
|
|
31
|
-
}
|
|
26
|
+
@@epsagon_config = {}
|
|
32
27
|
|
|
33
28
|
module_function
|
|
34
29
|
|
|
35
30
|
def init(**args)
|
|
31
|
+
@@epsagon_config = {
|
|
32
|
+
metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
|
|
33
|
+
debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
|
|
34
|
+
token: ENV['EPSAGON_TOKEN'] || '',
|
|
35
|
+
app_name: ENV['EPSAGON_APP_NAME'] || '',
|
|
36
|
+
max_attribute_size: ENV['EPSAGON_MAX_ATTRIBUTE_SIZE'] || 5000,
|
|
37
|
+
backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND,
|
|
38
|
+
ignore_domains: ENV['EPSAGON_IGNORE_DOMAINS'] || DEFAULT_IGNORE_DOMAINS
|
|
39
|
+
}
|
|
36
40
|
@@epsagon_config.merge!(args)
|
|
37
41
|
OpenTelemetry::SDK.configure
|
|
38
42
|
end
|
|
@@ -45,9 +49,10 @@ module Epsagon
|
|
|
45
49
|
|
|
46
50
|
def epsagon_confs(configurator)
|
|
47
51
|
configurator.resource = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk.merge(
|
|
48
|
-
OpenTelemetry::SDK::Resources::Resource.create({
|
|
52
|
+
OpenTelemetry::SDK::Resources::Resource.create({
|
|
49
53
|
'application' => @@epsagon_config[:app_name],
|
|
50
|
-
'epsagon.version' => EpsagonConstants::VERSION
|
|
54
|
+
'epsagon.version' => EpsagonConstants::VERSION,
|
|
55
|
+
'epsagon.metadata_only' => @@epsagon_config[:metadata_only]
|
|
51
56
|
})
|
|
52
57
|
)
|
|
53
58
|
configurator.use 'EpsagonSinatraInstrumentation', { epsagon: @@epsagon_config }
|
|
@@ -55,8 +60,7 @@ module Epsagon
|
|
|
55
60
|
configurator.use 'EpsagonFaradayInstrumentation', { epsagon: @@epsagon_config }
|
|
56
61
|
configurator.use 'EpsagonAwsSdkInstrumentation', { epsagon: @@epsagon_config }
|
|
57
62
|
configurator.use 'EpsagonRailsInstrumentation', { epsagon: @@epsagon_config }
|
|
58
|
-
configurator.use 'OpenTelemetry::Instrumentation::Sidekiq'
|
|
59
|
-
|
|
63
|
+
configurator.use 'OpenTelemetry::Instrumentation::Sidekiq', { epsagon: @@epsagon_config }
|
|
60
64
|
|
|
61
65
|
if @@epsagon_config[:debug]
|
|
62
66
|
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
|
@@ -97,15 +101,89 @@ module SpanExtension
|
|
|
97
101
|
def initialize(*args)
|
|
98
102
|
super(*args)
|
|
99
103
|
if @attributes
|
|
100
|
-
@attributes = Hash[@attributes.map { |k,v|
|
|
104
|
+
@attributes = Hash[@attributes.select {|k,v| not BLANKS.include? v}.map { |k,v|
|
|
101
105
|
[k, Util.trim_attr(v, Epsagon.get_config[:max_attribute_size])]
|
|
102
106
|
}]
|
|
103
107
|
end
|
|
104
|
-
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
module SidekiqClientMiddlewareExtension
|
|
112
|
+
def call(_worker_class, job, _queue, _redis_pool)
|
|
113
|
+
config = OpenTelemetry::Instrumentation::Sidekiq::Instrumentation.instance.config[:epsagon] || {}
|
|
114
|
+
attributes = {
|
|
115
|
+
'operation' => job['at'] ? 'perform_at' : 'perform_async',
|
|
116
|
+
'messaging.system' => 'sidekiq',
|
|
117
|
+
'messaging.sidekiq.job_class' => job['wrapped']&.to_s || job['class'],
|
|
118
|
+
'messaging.message_id' => job['jid'],
|
|
119
|
+
'messaging.destination' => job['queue'],
|
|
120
|
+
'messaging.destination_kind' => 'queue',
|
|
121
|
+
'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url
|
|
122
|
+
}
|
|
123
|
+
unless config[:metadata_only]
|
|
124
|
+
attributes.merge!({
|
|
125
|
+
'messaging.sidekiq.args' => JSON.dump(job['args'])
|
|
126
|
+
})
|
|
127
|
+
end
|
|
128
|
+
tracer.in_span(
|
|
129
|
+
job['queue'],
|
|
130
|
+
attributes: attributes,
|
|
131
|
+
kind: :producer
|
|
132
|
+
) do |span|
|
|
133
|
+
OpenTelemetry.propagation.text.inject(job)
|
|
134
|
+
span.add_event('created_at', timestamp: job['created_at'])
|
|
135
|
+
Util.untraced {yield}
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
module SidekiqServerMiddlewareExtension
|
|
141
|
+
def call(_worker, msg, _queue)
|
|
142
|
+
inner_exception = nil
|
|
143
|
+
config = OpenTelemetry::Instrumentation::Sidekiq::Instrumentation.instance.config[:epsagon] || {}
|
|
144
|
+
parent_context = OpenTelemetry.propagation.text.extract(msg)
|
|
145
|
+
attributes = {
|
|
146
|
+
'operation' => 'perform',
|
|
147
|
+
'messaging.system' => 'sidekiq',
|
|
148
|
+
'messaging.sidekiq.job_class' => msg['wrapped']&.to_s || msg['class'],
|
|
149
|
+
'messaging.message_id' => msg['jid'],
|
|
150
|
+
'messaging.destination' => msg['queue'],
|
|
151
|
+
'messaging.destination_kind' => 'queue',
|
|
152
|
+
'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url
|
|
153
|
+
}
|
|
154
|
+
runner_attributes = {
|
|
155
|
+
'type' => 'sidekiq_worker',
|
|
156
|
+
'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url,
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
unless config[:metadata_only]
|
|
160
|
+
attributes.merge!({
|
|
161
|
+
'messaging.sidekiq.args' => JSON.dump(msg['args'])
|
|
162
|
+
})
|
|
163
|
+
end
|
|
164
|
+
tracer.in_span(
|
|
165
|
+
msg['queue'],
|
|
166
|
+
attributes: attributes,
|
|
167
|
+
with_parent: parent_context,
|
|
168
|
+
kind: :consumer
|
|
169
|
+
) do |trigger_span|
|
|
170
|
+
trigger_span.add_event('created_at', timestamp: msg['created_at'])
|
|
171
|
+
trigger_span.add_event('enqueued_at', timestamp: msg['enqueued_at'])
|
|
172
|
+
tracer.in_span(msg['wrapped']&.to_s || msg['class'],
|
|
173
|
+
attributes: runner_attributes,
|
|
174
|
+
kind: :consumer
|
|
175
|
+
) do |runner_span|
|
|
176
|
+
yield
|
|
177
|
+
end
|
|
178
|
+
rescue Exception => e
|
|
179
|
+
inner_exception = e
|
|
180
|
+
end
|
|
181
|
+
raise inner_exception if inner_exception
|
|
105
182
|
end
|
|
106
183
|
end
|
|
107
184
|
|
|
108
185
|
# monkey patch to include epsagon confs
|
|
186
|
+
|
|
109
187
|
module OpenTelemetry
|
|
110
188
|
# monkey patch inner SDK module
|
|
111
189
|
module SDK
|
|
@@ -122,4 +200,35 @@ module OpenTelemetry
|
|
|
122
200
|
end
|
|
123
201
|
end
|
|
124
202
|
end
|
|
203
|
+
module Instrumentation
|
|
204
|
+
module Sidekiq
|
|
205
|
+
class Instrumentation
|
|
206
|
+
def add_server_middleware
|
|
207
|
+
::Sidekiq.configure_server do |config|
|
|
208
|
+
config.server_middleware do |chain|
|
|
209
|
+
chain.add Middlewares::Server::TracerMiddleware
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
if defined?(::Sidekiq::Testing) # rubocop:disable Style/GuardClause
|
|
214
|
+
::Sidekiq::Testing.server_middleware do |chain|
|
|
215
|
+
chain.add Middlewares::Server::TracerMiddleware
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
module Middlewares
|
|
221
|
+
module Client
|
|
222
|
+
class TracerMiddleware
|
|
223
|
+
prepend SidekiqClientMiddlewareExtension
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
module Server
|
|
227
|
+
class TracerMiddleware
|
|
228
|
+
prepend SidekiqServerMiddlewareExtension
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
125
234
|
end
|
data/lib/epsagon_constants.rb
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module OpenTelemetry
|
|
4
|
+
module Exporter
|
|
5
|
+
module OTLP
|
|
6
|
+
# An OpenTelemetry trace exporter that sends spans over HTTP as Protobuf encoded OTLP ExportTraceServiceRequests.
|
|
7
|
+
class Exporter # rubocop:disable Metrics/ClassLength
|
|
8
|
+
def send_bytes(bytes, timeout:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
9
|
+
retry_count = 0
|
|
10
|
+
timeout ||= @timeout
|
|
11
|
+
start_time = Time.now
|
|
12
|
+
untraced do # rubocop:disable Metrics/BlockLength
|
|
13
|
+
request = Net::HTTP::Post.new(@path)
|
|
14
|
+
request.body = if @compression == 'gzip'
|
|
15
|
+
request.add_field('Content-Encoding', 'gzip')
|
|
16
|
+
Zlib.gzip(bytes)
|
|
17
|
+
else
|
|
18
|
+
bytes
|
|
19
|
+
end
|
|
20
|
+
request.add_field('Content-Type', 'application/x-protobuf')
|
|
21
|
+
@headers&.each { |key, value| request.add_field(key, value) }
|
|
22
|
+
|
|
23
|
+
remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
|
|
24
|
+
return TIMEOUT if remaining_timeout.zero?
|
|
25
|
+
|
|
26
|
+
@http.open_timeout = remaining_timeout
|
|
27
|
+
@http.read_timeout = remaining_timeout
|
|
28
|
+
@http.write_timeout = remaining_timeout if WRITE_TIMEOUT_SUPPORTED
|
|
29
|
+
@http.start unless @http.started?
|
|
30
|
+
response = measure_request_duration { @http.request(request) }
|
|
31
|
+
|
|
32
|
+
case response
|
|
33
|
+
when Net::HTTPOK
|
|
34
|
+
response.body # Read and discard body
|
|
35
|
+
SUCCESS
|
|
36
|
+
when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests
|
|
37
|
+
response.body # Read and discard body
|
|
38
|
+
redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code)
|
|
39
|
+
FAILURE
|
|
40
|
+
when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway
|
|
41
|
+
response.body # Read and discard body
|
|
42
|
+
redo if backoff?(retry_count: retry_count += 1, reason: response.code)
|
|
43
|
+
FAILURE
|
|
44
|
+
when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError
|
|
45
|
+
# TODO: decode the body as a google.rpc.Status Protobuf-encoded message when https://github.com/open-telemetry/opentelemetry-collector/issues/1357 is fixed.
|
|
46
|
+
response.body # Read and discard body
|
|
47
|
+
FAILURE
|
|
48
|
+
when Net::HTTPRedirection
|
|
49
|
+
@http.finish
|
|
50
|
+
handle_redirect(response['location'])
|
|
51
|
+
redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code)
|
|
52
|
+
else
|
|
53
|
+
@http.finish
|
|
54
|
+
FAILURE
|
|
55
|
+
end
|
|
56
|
+
rescue Net::OpenTimeout, Net::ReadTimeout
|
|
57
|
+
puts "Epsagon: timeout while sending trace" if Epsagon.get_config[:debug]
|
|
58
|
+
retry if backoff?(retry_count: retry_count += 1, reason: 'timeout')
|
|
59
|
+
return FAILURE
|
|
60
|
+
ensure
|
|
61
|
+
if Epsagon.get_config[:debug] && response && response.code.to_i >= 400
|
|
62
|
+
puts "Epsagon: Error while sending trace:"
|
|
63
|
+
puts "#{response.code} #{response.class.name} #{response.message}"
|
|
64
|
+
puts "Headers: #{response.to_hash.inspect}"
|
|
65
|
+
puts response.body
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
ensure
|
|
69
|
+
# Reset timeouts to defaults for the next call.
|
|
70
|
+
@http.open_timeout = @timeout
|
|
71
|
+
@http.read_timeout = @timeout
|
|
72
|
+
@http.write_timeout = @timeout if WRITE_TIMEOUT_SUPPORTED
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
@@ -4,6 +4,7 @@ require 'aws-sdk-core'
|
|
|
4
4
|
require 'opentelemetry/common'
|
|
5
5
|
require 'opentelemetry/sdk'
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
def untraced(&block)
|
|
8
9
|
OpenTelemetry::Trace.with_span(OpenTelemetry::Trace::Span.new, &block)
|
|
9
10
|
end
|
|
@@ -17,14 +18,120 @@ end
|
|
|
17
18
|
|
|
18
19
|
# Generates Spans for all uses of AWS SDK
|
|
19
20
|
class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
21
|
+
SPAN_KIND = {
|
|
22
|
+
'ReceiveMessage' => :consumer,
|
|
23
|
+
'SendMessage' => :producer,
|
|
24
|
+
'SendMessageBatch' => :producer,
|
|
25
|
+
'Publish' => :producer,
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
def call(context)
|
|
21
|
-
|
|
29
|
+
span_name = ''
|
|
30
|
+
span_kind = :client
|
|
31
|
+
attributes = {
|
|
32
|
+
'aws.service' => context.client.class.to_s.split('::')[1].downcase,
|
|
33
|
+
'aws.operation' => context.operation.name
|
|
34
|
+
}
|
|
35
|
+
attributes['aws.region'] = context.client.config.region unless attributes['aws.service'] == 's3'
|
|
36
|
+
|
|
37
|
+
span_kind = SPAN_KIND[attributes['aws.operation']] || span_kind
|
|
38
|
+
if attributes['aws.service'] == 's3'
|
|
39
|
+
attributes['aws.s3.bucket'] = context.params[:bucket]
|
|
40
|
+
span_name = attributes['aws.s3.bucket'] if attributes['aws.s3.bucket']
|
|
41
|
+
attributes['aws.s3.key'] = context.params[:key]
|
|
42
|
+
attributes['aws.s3.copy_source'] = context.params[:copy_source]
|
|
43
|
+
elsif attributes['aws.service'] == 'sqs'
|
|
44
|
+
queue_url = context.params[:queue_url]
|
|
45
|
+
queue_name = queue_url ? queue_url[queue_url.rindex('/')+1..-1] : context.params[:queue_name]
|
|
46
|
+
attributes['aws.sqs.max_number_of_messages'] = context.params[:max_number_of_messages]
|
|
47
|
+
attributes['aws.sqs.wait_time_seconds'] = context.params[:wait_time_seconds]
|
|
48
|
+
attributes['aws.sqs.visibility_timeout'] = context.params[:visibility_timeout]
|
|
49
|
+
attributes['aws.sqs.message_id'] = context.params[:message_id]
|
|
50
|
+
if queue_name
|
|
51
|
+
attributes['aws.sqs.queue_name'] = queue_name
|
|
52
|
+
span_name = attributes['aws.sqs.queue_name'] if attributes['aws.sqs.queue_name']
|
|
53
|
+
end
|
|
54
|
+
unless config[:epsagon][:metadata_only]
|
|
55
|
+
if attributes['aws.operation'] == 'SendMessageBatch'
|
|
56
|
+
messages_attributes = context.params[:entries].map do |m|
|
|
57
|
+
record = {
|
|
58
|
+
'message_attributes' => m[:message_attributes].map {|k,v| [k, v.to_h]},
|
|
59
|
+
'message_body' => m[:message_body],
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
attributes['aws.sqs.record'] = JSON.dump(messages_attributes) if messages_attributes
|
|
63
|
+
end
|
|
64
|
+
attributes['aws.sqs.record.message_body'] = context.params[:message_body]
|
|
65
|
+
attributes['aws.sqs.record.message_attributes'] = JSON.dump(context.params[:message_attributes]) if context.params[:message_attributes]
|
|
66
|
+
end
|
|
67
|
+
elsif attributes['aws.service'] == 'sns'
|
|
68
|
+
topic_arn = context.params[:topic_arn]
|
|
69
|
+
topic_name = topic_arn ? topic_arn[topic_arn.rindex(':')+1..-1] : context.params[:name]
|
|
70
|
+
span_name = attributes['aws.sns.topic_name'] = topic_name if topic_name
|
|
71
|
+
unless config[:epsagon][:metadata_only]
|
|
72
|
+
attributes['aws.sns.subject'] = context.params[:subject]
|
|
73
|
+
attributes['aws.sns.message'] = context.params[:message]
|
|
74
|
+
attributes['aws.sns.message_attributes'] = JSON.dump(context.params[:message_attributes]) if context.params[:message_attributes]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
tracer.in_span(span_name, kind: span_kind, attributes: attributes) do |span|
|
|
22
78
|
untraced do
|
|
23
|
-
@handler.call(context).tap do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
79
|
+
@handler.call(context).tap do |result|
|
|
80
|
+
if attributes['aws.service'] == 's3'
|
|
81
|
+
modified = context.http_response.headers[:'last-modified']
|
|
82
|
+
reformatted_modified = modified ?
|
|
83
|
+
Time.strptime(modified, '%a, %d %b %Y %H:%M:%S %Z')
|
|
84
|
+
.strftime('%Y-%m-%dT%H:%M:%SZ') :
|
|
85
|
+
nil
|
|
86
|
+
if context.operation.name == 'GetObject'
|
|
87
|
+
span.set_attribute('aws.s3.content_length', context.http_response.headers[:'content-length']&.to_i)
|
|
88
|
+
end
|
|
89
|
+
span.set_attribute('aws.s3.etag', context.http_response.headers[:etag]&.tr('"',''))
|
|
90
|
+
span.set_attribute('aws.s3.last_modified', reformatted_modified)
|
|
91
|
+
elsif attributes['aws.service'] == 'sqs'
|
|
92
|
+
if context.operation.name == 'SendMessage'
|
|
93
|
+
span.set_attribute('aws.sqs.record.message_id', result.message_id)
|
|
94
|
+
end
|
|
95
|
+
if context.operation.name == 'SendMessageBatch'
|
|
96
|
+
messages_attributes = result.successful.map do |m|
|
|
97
|
+
record = {'message_id' => m.message_id}
|
|
98
|
+
unless config[:epsagon][:metadata_only]
|
|
99
|
+
context.params[:entries].each do |e|
|
|
100
|
+
record.merge!({
|
|
101
|
+
'message_attributes' => e[:message_attributes].map {|k,v| [k, v.to_h]},
|
|
102
|
+
'message_body' => e[:message_body],
|
|
103
|
+
}) if e[:id] == m.id
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
record
|
|
107
|
+
end
|
|
108
|
+
span.set_attribute('aws.sqs.record', JSON.dump(messages_attributes)) if messages_attributes
|
|
109
|
+
end
|
|
110
|
+
if context.operation.name == 'ReceiveMessage'
|
|
111
|
+
messages_attributes = result.messages.map do |m|
|
|
112
|
+
record = {
|
|
113
|
+
'message_id' => m.message_id,
|
|
114
|
+
'attributes' => {
|
|
115
|
+
'sender_id' => m.attributes['SenderId'],
|
|
116
|
+
'sent_timestamp' => m.attributes['SentTimestamp'],
|
|
117
|
+
'aws_trace_header' => m.attributes['AWSTraceHeader'],
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
unless config[:epsagon][:metadata_only]
|
|
121
|
+
record['message_attributes'] = m.message_attributes.map {|k,v| [k, v.to_h]}
|
|
122
|
+
record['message_body'] = m.body
|
|
123
|
+
end
|
|
124
|
+
record
|
|
125
|
+
end
|
|
126
|
+
span.set_attribute('aws.sqs.record', JSON.dump(messages_attributes)) if messages_attributes
|
|
127
|
+
end
|
|
128
|
+
elsif attributes['aws.service'] == 'sns'
|
|
129
|
+
span.set_attribute('aws.sns.message_id', result.message_id) if context.operation.name == 'Publish'
|
|
130
|
+
end
|
|
27
131
|
span.set_attribute('http.status_code', context.http_response.status_code)
|
|
132
|
+
span.status = OpenTelemetry::Trace::Status.http_to_status(
|
|
133
|
+
context.http_response.status_code
|
|
134
|
+
)
|
|
28
135
|
end
|
|
29
136
|
end
|
|
30
137
|
end
|
|
@@ -33,4 +140,8 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
|
33
140
|
def tracer
|
|
34
141
|
EpsagonAwsSdkInstrumentation.instance.tracer()
|
|
35
142
|
end
|
|
143
|
+
|
|
144
|
+
def config
|
|
145
|
+
EpsagonAwsSdkInstrumentation.instance.config
|
|
146
|
+
end
|
|
36
147
|
end
|
|
@@ -37,11 +37,11 @@ class EpsagonFaradayMiddleware < ::Faraday::Middleware
|
|
|
37
37
|
unless config[:epsagon][:metadata_only]
|
|
38
38
|
attributes.merge!(Util.epsagon_query_attributes(env.url.query))
|
|
39
39
|
attributes.merge!({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
'http.request.path_params' => path_params,
|
|
41
|
+
'http.request.headers' => env.request_headers.to_json,
|
|
42
|
+
'http.request.body' => env.body,
|
|
43
|
+
'http.request.headers.User-Agent' => env.request_headers['User-Agent']
|
|
44
|
+
})
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
tracer.in_span(
|
|
@@ -130,7 +130,7 @@ class EpsagonRackMiddleware
|
|
|
130
130
|
def request_span_attributes(env:)
|
|
131
131
|
request = Rack::Request.new(env)
|
|
132
132
|
path, path_params = request.path.split(';')
|
|
133
|
-
request_headers = JSON.generate(Hash[*env.select { |k, _v| k.start_with? 'HTTP_' }
|
|
133
|
+
request_headers = JSON.generate(Hash[*env.select { |k, _v| k.to_s.start_with? 'HTTP_' }
|
|
134
134
|
.collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
|
|
135
135
|
.collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
|
|
136
136
|
.sort
|
|
@@ -17,6 +17,7 @@ module EpsagonNetHTTPExtension
|
|
|
17
17
|
def request(req, body = nil, &block)
|
|
18
18
|
# Do not trace recursive call for starting the connection
|
|
19
19
|
return super(req, body, &block) unless started?
|
|
20
|
+
return super(req, body, &block) if config[:epsagon][:ignore_domains].any? {|d| @address.include? d}
|
|
20
21
|
|
|
21
22
|
attributes = Hash[OpenTelemetry::Common::HTTP::ClientContext.attributes]
|
|
22
23
|
path_with_params, query = req.path.split('?')
|
|
@@ -55,6 +56,7 @@ module EpsagonNetHTTPExtension
|
|
|
55
56
|
|
|
56
57
|
def annotate_span_with_response!(span, response)
|
|
57
58
|
return unless response&.code
|
|
59
|
+
return unless span.respond_to?(:set_attribute)
|
|
58
60
|
|
|
59
61
|
status_code = response.code.to_i
|
|
60
62
|
|
|
File without changes
|
data/lib/util.rb
CHANGED
|
@@ -30,4 +30,13 @@ module Util
|
|
|
30
30
|
value
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
def self.redis_default_url
|
|
35
|
+
@@redis_default_url ||= "#{Redis::Client::DEFAULTS[:scheme]}://#{Redis::Client::DEFAULTS[:host]}:#{Redis::Client::DEFAULTS[:port]}/#{Redis::Client::DEFAULTS[:db]}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.untraced(&block)
|
|
39
|
+
OpenTelemetry::Trace.with_span(OpenTelemetry::Trace::Span.new, &block)
|
|
40
|
+
end
|
|
41
|
+
|
|
33
42
|
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.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Epsagon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: opentelemetry-api
|
|
@@ -92,6 +92,7 @@ extra_rdoc_files: []
|
|
|
92
92
|
files:
|
|
93
93
|
- lib/epsagon.rb
|
|
94
94
|
- lib/epsagon_constants.rb
|
|
95
|
+
- lib/exporter_extension.rb
|
|
95
96
|
- lib/instrumentation/aws_sdk.rb
|
|
96
97
|
- lib/instrumentation/aws_sdk_plugin.rb
|
|
97
98
|
- lib/instrumentation/epsagon_faraday_middleware.rb
|
|
@@ -100,6 +101,7 @@ files:
|
|
|
100
101
|
- lib/instrumentation/net_http.rb
|
|
101
102
|
- lib/instrumentation/rails.rb
|
|
102
103
|
- lib/instrumentation/sinatra.rb
|
|
104
|
+
- lib/instrumentation/version.rb
|
|
103
105
|
- lib/util.rb
|
|
104
106
|
homepage: https://github.com/epsagon/epsagon-ruby
|
|
105
107
|
licenses:
|