epsagon 0.0.26 → 0.0.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/arn_parser.rb +27 -0
- data/lib/epsagon.rb +98 -40
- data/lib/epsagon_constants.rb +1 -1
- data/lib/instrumentation/aws_sdk.rb +1 -0
- data/lib/instrumentation/aws_sdk_plugin.rb +5 -5
- data/lib/instrumentation/epsagon_faraday_middleware.rb +2 -2
- data/lib/instrumentation/epsagon_rails_middleware.rb +47 -1
- data/lib/instrumentation/epsagon_resque_job.rb +113 -0
- data/lib/instrumentation/net_http.rb +2 -2
- data/lib/instrumentation/postgres.rb +294 -0
- data/lib/instrumentation/rails.rb +0 -47
- data/lib/instrumentation/resque.rb +21 -0
- data/lib/util.rb +29 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ebb5eb2134eb62b3b83fa8cbb48b9c535cf528999c55ad1b58ed161b63cd97
|
4
|
+
data.tar.gz: 1e5ac5ea5ef2d6a3ba156b89061cca64bab22b90ce27b37e128a9b62c2633526
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34adf61a4771b2e61f916a79f3e038a1b23235214d13ec9771c33e3a1be34edc843c735f2ca2fd6d1a84fc895e9ffd7f6ec4b7674b1ace141b39f193e0d7b7b0
|
7
|
+
data.tar.gz: 289999c6c4d93c3521598fc0ac30eefd29f83e46f74079d1133b50d4a459c6f8e397f6fa1cf151492308d6089b9bfda9f9ed4279aab29c0e1de787aba26e4361
|
data/lib/arn_parser.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# Credit: https://gist.github.com/RulerOf/b9f5dd00a9911aba8271b57d3d269d7a
|
3
|
+
#
|
4
|
+
class Arn
|
5
|
+
attr_accessor :partition, :service, :region, :account, :resource
|
6
|
+
|
7
|
+
def initialize(partition, service, region, account, resource)
|
8
|
+
@partition = partition
|
9
|
+
@service = service
|
10
|
+
@region = region
|
11
|
+
@account = account
|
12
|
+
@resource = resource
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse(arn)
|
16
|
+
raise TypeError, 'ARN must be supplied as a string' unless arn.is_a?(String)
|
17
|
+
|
18
|
+
arn_components = arn.split(':', 6)
|
19
|
+
raise ArgumentError, 'Could not parse ARN' if arn_components.length < 6
|
20
|
+
|
21
|
+
Arn.new arn_components[1],
|
22
|
+
arn_components[2],
|
23
|
+
arn_components[3],
|
24
|
+
arn_components[4],
|
25
|
+
arn_components[5]
|
26
|
+
end
|
27
|
+
end
|
data/lib/epsagon.rb
CHANGED
@@ -12,77 +12,129 @@ require_relative 'instrumentation/net_http'
|
|
12
12
|
require_relative 'instrumentation/faraday'
|
13
13
|
require_relative 'instrumentation/aws_sdk'
|
14
14
|
require_relative 'instrumentation/rails'
|
15
|
+
require_relative 'instrumentation/postgres'
|
16
|
+
require_relative 'instrumentation/resque'
|
15
17
|
require_relative 'util'
|
16
18
|
require_relative 'epsagon_constants'
|
17
19
|
require_relative 'exporter_extension'
|
20
|
+
require_relative 'arn_parser'
|
18
21
|
|
19
22
|
Bundler.require
|
20
23
|
|
24
|
+
|
21
25
|
# Epsagon tracing main entry point
|
22
26
|
module Epsagon
|
23
27
|
DEFAULT_BACKEND = 'opentelemetry.tc.epsagon.com:443/traces'
|
24
|
-
DEFAULT_IGNORE_DOMAINS = ['newrelic.com']
|
28
|
+
DEFAULT_IGNORE_DOMAINS = ['newrelic.com'].freeze
|
29
|
+
MUTABLE_CONF_KEYS = Set.new([:metadata_only, :max_attribute_size, :ignore_domains, :ignored_keys])
|
25
30
|
|
26
|
-
@@epsagon_config =
|
31
|
+
@@epsagon_config = nil
|
27
32
|
|
28
33
|
module_function
|
29
34
|
|
30
35
|
def init(**args)
|
31
|
-
|
36
|
+
get_config.merge!(args)
|
37
|
+
validate(get_config)
|
38
|
+
OpenTelemetry::SDK.configure
|
39
|
+
@@initialized = true
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate(config)
|
43
|
+
Util.validate_value(config, :metadata_only, 'Must be a boolean') {|v| !!v == v}
|
44
|
+
Util.validate_value(config, :debug, 'Must be a boolean') {|v| !!v == v}
|
45
|
+
Util.validate_value(config, :token, 'Must be a valid Epsagon token') {|v| (v.is_a? String) && (v.size > 10) }
|
46
|
+
Util.validate_value(config, :app_name, 'Must be a String') {|v| (v.is_a? String) && (v.size > 0) }
|
47
|
+
Util.validate_value(config, :max_attribute_size, 'Must be an Integer') {|v| v.is_a? Integer}
|
48
|
+
Util.validate_value(config, :ignore_domains, 'Must be iterable') {|v| v.respond_to?(:each)}
|
49
|
+
Util.validate_value(config, :ignored_keys, 'Must be iterable') {|v| v.respond_to?(:each)}
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_config(**args)
|
53
|
+
unless args.keys.all? {|a| MUTABLE_CONF_KEYS.include?(a)}
|
54
|
+
raise ArgumentError.new("only #{MUTABLE_CONF_KEYS.to_a} are mutable after `Epsagon.init`")
|
55
|
+
end
|
56
|
+
Epsagon.init unless @@initialized
|
57
|
+
new_conf = get_config.merge(args)
|
58
|
+
validate(new_conf)
|
59
|
+
@@epsagon_config = new_conf
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_ignored_key(key)
|
63
|
+
get_config[:ignored_keys].push(key).uniq
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_ignored_key(key)
|
67
|
+
get_config[:ignored_keys].delete(key)
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_config
|
71
|
+
@@epsagon_config ||= {
|
32
72
|
metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
|
33
73
|
debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
|
34
74
|
token: ENV['EPSAGON_TOKEN'] || '',
|
35
75
|
app_name: ENV['EPSAGON_APP_NAME'] || '',
|
36
76
|
max_attribute_size: ENV['EPSAGON_MAX_ATTRIBUTE_SIZE'] || 5000,
|
37
77
|
backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND,
|
38
|
-
ignore_domains: ENV['EPSAGON_IGNORE_DOMAINS'] || DEFAULT_IGNORE_DOMAINS
|
78
|
+
ignore_domains: ENV['EPSAGON_IGNORE_DOMAINS']&.split(',') || DEFAULT_IGNORE_DOMAINS,
|
79
|
+
ignored_keys: ENV['EPSAGON_IGNORED_KEYS']&.split(',') || []
|
39
80
|
}
|
40
|
-
@@epsagon_config.merge!(args)
|
41
|
-
OpenTelemetry::SDK.configure
|
42
81
|
end
|
43
82
|
|
44
|
-
def
|
45
|
-
|
83
|
+
def set_ecs_metadata
|
84
|
+
metadata_uri = ENV['ECS_CONTAINER_METADATA_URI']
|
85
|
+
return {} if metadata_uri.nil?
|
86
|
+
|
87
|
+
response = Net::HTTP.get(URI(metadata_uri))
|
88
|
+
ecs_metadata = JSON.parse(response)
|
89
|
+
arn = Arn.parse(ecs_metadata['Labels']['com.amazonaws.ecs.task-arn'])
|
90
|
+
|
91
|
+
{
|
92
|
+
'aws.account_id' => arn.account,
|
93
|
+
'aws.region' => arn.region,
|
94
|
+
'aws.ecs.cluster' => ecs_metadata['Labels']['com.amazonaws.ecs.cluster'],
|
95
|
+
'aws.ecs.task_arn' => ecs_metadata['Labels']['com.amazonaws.ecs.task-arn'],
|
96
|
+
'aws.ecs.container_name' => ecs_metadata['Labels']['com.amazonaws.ecs.container-name'],
|
97
|
+
'aws.ecs.task.family' => ecs_metadata['Labels']['com.amazonaws.ecs.task-definition-family'],
|
98
|
+
'aws.ecs.task.revision' => ecs_metadata['Labels']['com.amazonaws.ecs.task-definition-version']
|
99
|
+
}
|
46
100
|
end
|
47
101
|
|
48
102
|
# config opentelemetry with epsaon extensions:
|
49
103
|
|
50
104
|
def epsagon_confs(configurator)
|
105
|
+
otel_resource = {
|
106
|
+
'application' => get_config[:app_name],
|
107
|
+
'epsagon.version' => EpsagonConstants::VERSION,
|
108
|
+
'epsagon.metadata_only' => get_config[:metadata_only]
|
109
|
+
}.merge(set_ecs_metadata)
|
110
|
+
|
51
111
|
configurator.resource = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk.merge(
|
52
|
-
OpenTelemetry::SDK::Resources::Resource.create(
|
53
|
-
'application' => @@epsagon_config[:app_name],
|
54
|
-
'epsagon.version' => EpsagonConstants::VERSION,
|
55
|
-
'epsagon.metadata_only' => @@epsagon_config[:metadata_only]
|
56
|
-
})
|
112
|
+
OpenTelemetry::SDK::Resources::Resource.create(otel_resource)
|
57
113
|
)
|
58
|
-
configurator.use 'EpsagonSinatraInstrumentation', { epsagon: @@epsagon_config }
|
59
|
-
configurator.use 'EpsagonNetHTTPInstrumentation', { epsagon: @@epsagon_config }
|
60
|
-
configurator.use 'EpsagonFaradayInstrumentation', { epsagon: @@epsagon_config }
|
61
|
-
configurator.use 'EpsagonAwsSdkInstrumentation', { epsagon: @@epsagon_config }
|
62
|
-
configurator.use 'EpsagonRailsInstrumentation', { epsagon: @@epsagon_config }
|
63
|
-
configurator.use 'OpenTelemetry::Instrumentation::Sidekiq', { epsagon: @@epsagon_config }
|
64
|
-
|
65
|
-
if @@epsagon_config[:debug]
|
66
|
-
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
67
|
-
OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
68
|
-
'x-epsagon-token' => @@epsagon_config[:token]
|
69
|
-
},
|
70
|
-
endpoint: @@epsagon_config[:backend],
|
71
|
-
insecure: @@epsagon_config[:insecure] || false)
|
72
|
-
)
|
73
114
|
|
115
|
+
configurator.use 'EpsagonSinatraInstrumentation', { epsagon: get_config }
|
116
|
+
configurator.use 'EpsagonNetHTTPInstrumentation', { epsagon: get_config }
|
117
|
+
configurator.use 'EpsagonFaradayInstrumentation', { epsagon: get_config }
|
118
|
+
configurator.use 'EpsagonAwsSdkInstrumentation', { epsagon: get_config }
|
119
|
+
configurator.use 'EpsagonRailsInstrumentation', { epsagon: get_config }
|
120
|
+
configurator.use 'OpenTelemetry::Instrumentation::Sidekiq', { epsagon: get_config }
|
121
|
+
configurator.use 'EpsagonPostgresInstrumentation', { epsagon: get_config }
|
122
|
+
configurator.use 'EpsagonResqueInstrumentation', { epsagon: get_config }
|
123
|
+
|
124
|
+
|
125
|
+
if get_config[:debug]
|
74
126
|
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
75
127
|
OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
|
76
128
|
)
|
77
|
-
else
|
78
|
-
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
79
|
-
exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
80
|
-
'x-epsagon-token' => @@epsagon_config[:token]
|
81
|
-
},
|
82
|
-
endpoint: @@epsagon_config[:backend],
|
83
|
-
insecure: @@epsagon_config[:insecure] || false)
|
84
|
-
)
|
85
129
|
end
|
130
|
+
|
131
|
+
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
132
|
+
exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
133
|
+
'x-epsagon-token' => get_config[:token]
|
134
|
+
},
|
135
|
+
endpoint: get_config[:backend],
|
136
|
+
insecure: get_config[:insecure] || false)
|
137
|
+
)
|
86
138
|
end
|
87
139
|
end
|
88
140
|
|
@@ -91,10 +143,15 @@ module SpanExtension
|
|
91
143
|
|
92
144
|
BLANKS = [nil, [], '']
|
93
145
|
|
146
|
+
def set_mapping_attribute(key, value)
|
147
|
+
value = Util.prepare_attr(key, value, Epsagon.get_config[:max_attribute_size], Epsagon.get_config[:ignored_keys])
|
148
|
+
set_attribute(key, value) if value
|
149
|
+
end
|
150
|
+
|
94
151
|
def set_attribute(key, value)
|
95
152
|
unless BLANKS.include?(value)
|
96
|
-
value = Util.
|
97
|
-
super(key, value)
|
153
|
+
value = Util.prepare_attr(key, value, Epsagon.get_config[:max_attribute_size], Epsagon.get_config[:ignored_keys])
|
154
|
+
super(key, value) if value
|
98
155
|
end
|
99
156
|
end
|
100
157
|
|
@@ -102,8 +159,9 @@ module SpanExtension
|
|
102
159
|
super(*args)
|
103
160
|
if @attributes
|
104
161
|
@attributes = Hash[@attributes.select {|k,v| not BLANKS.include? v}.map { |k,v|
|
105
|
-
|
106
|
-
|
162
|
+
v = Util.prepare_attr(k, v, Epsagon.get_config[:max_attribute_size], Epsagon.get_config[:ignored_keys])
|
163
|
+
[k, v] if v
|
164
|
+
}.compact]
|
107
165
|
end
|
108
166
|
end
|
109
167
|
end
|
data/lib/epsagon_constants.rb
CHANGED
@@ -59,10 +59,10 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
59
59
|
'message_body' => m[:message_body],
|
60
60
|
}
|
61
61
|
end
|
62
|
-
attributes['aws.sqs.record'] =
|
62
|
+
attributes['aws.sqs.record'] = messages_attributes if messages_attributes
|
63
63
|
end
|
64
64
|
attributes['aws.sqs.record.message_body'] = context.params[:message_body]
|
65
|
-
attributes['aws.sqs.record.message_attributes'] =
|
65
|
+
attributes['aws.sqs.record.message_attributes'] = context.params[:message_attributes] if context.params[:message_attributes]
|
66
66
|
end
|
67
67
|
elsif attributes['aws.service'] == 'sns'
|
68
68
|
topic_arn = context.params[:topic_arn]
|
@@ -71,7 +71,7 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
71
71
|
unless config[:epsagon][:metadata_only]
|
72
72
|
attributes['aws.sns.subject'] = context.params[:subject]
|
73
73
|
attributes['aws.sns.message'] = context.params[:message]
|
74
|
-
attributes['aws.sns.message_attributes'] =
|
74
|
+
attributes['aws.sns.message_attributes'] = context.params[:message_attributes] if context.params[:message_attributes]
|
75
75
|
end
|
76
76
|
end
|
77
77
|
tracer.in_span(span_name, kind: span_kind, attributes: attributes) do |span|
|
@@ -105,7 +105,7 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
105
105
|
end
|
106
106
|
record
|
107
107
|
end
|
108
|
-
span.
|
108
|
+
span.set_mapping_attribute('aws.sqs.record', messages_attributes) if messages_attributes
|
109
109
|
end
|
110
110
|
if context.operation.name == 'ReceiveMessage'
|
111
111
|
messages_attributes = result.messages.map do |m|
|
@@ -123,7 +123,7 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
|
|
123
123
|
end
|
124
124
|
record
|
125
125
|
end
|
126
|
-
span.
|
126
|
+
span.set_mapping_attribute('aws.sqs.record', messages_attributes) if messages_attributes
|
127
127
|
end
|
128
128
|
elsif attributes['aws.service'] == 'sns'
|
129
129
|
span.set_attribute('aws.sns.message_id', result.message_id) if context.operation.name == 'Publish'
|
@@ -38,7 +38,7 @@ class EpsagonFaradayMiddleware < ::Faraday::Middleware
|
|
38
38
|
attributes.merge!(Util.epsagon_query_attributes(env.url.query))
|
39
39
|
attributes.merge!({
|
40
40
|
'http.request.path_params' => path_params,
|
41
|
-
'http.request.headers' => env.request_headers
|
41
|
+
'http.request.headers' => Hash[env.request_headers],
|
42
42
|
'http.request.body' => env.body,
|
43
43
|
'http.request.headers.User-Agent' => env.request_headers['User-Agent']
|
44
44
|
})
|
@@ -67,7 +67,7 @@ class EpsagonFaradayMiddleware < ::Faraday::Middleware
|
|
67
67
|
span.set_attribute('http.status_code', response.status)
|
68
68
|
|
69
69
|
unless config[:epsagon][:metadata_only]
|
70
|
-
span.set_attribute('http.response.headers', response.headers
|
70
|
+
span.set_attribute('http.response.headers', Hash[env.response.headers])
|
71
71
|
span.set_attribute('http.response.body', response.body)
|
72
72
|
end
|
73
73
|
span.status = OpenTelemetry::Trace::Status.http_to_status(
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'opentelemetry/trace/status'
|
4
|
+
require 'action_controller/railtie'
|
4
5
|
|
5
6
|
module QueueTime
|
6
7
|
REQUEST_START = 'HTTP_X_REQUEST_START'
|
@@ -189,7 +190,7 @@ class EpsagonRackMiddleware
|
|
189
190
|
def set_attributes_after_request(http_span, _framework_span, status, headers, response)
|
190
191
|
unless config[:epsagon][:metadata_only]
|
191
192
|
http_span.set_attribute('http.response.headers', JSON.generate(headers))
|
192
|
-
http_span.set_attribute('http.response.body', response.join)
|
193
|
+
http_span.set_attribute('http.response.body', response.join) if response.respond_to?(:join)
|
193
194
|
end
|
194
195
|
|
195
196
|
http_span.set_attribute('http.status_code', status)
|
@@ -284,3 +285,48 @@ class EpsagonRailtie < ::Rails::Railtie
|
|
284
285
|
)
|
285
286
|
end
|
286
287
|
end
|
288
|
+
|
289
|
+
module RackExtension
|
290
|
+
module_function
|
291
|
+
|
292
|
+
CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
|
293
|
+
|
294
|
+
private_constant :CURRENT_SPAN_KEY
|
295
|
+
|
296
|
+
# Returns the current span from the current or provided context
|
297
|
+
#
|
298
|
+
# @param [optional Context] context The context to lookup the current
|
299
|
+
# {Span} from. Defaults to Context.current
|
300
|
+
def current_span(context = nil)
|
301
|
+
context ||= OpenTelemetry::Context.current
|
302
|
+
context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
|
303
|
+
end
|
304
|
+
|
305
|
+
# Returns a context containing the span, derived from the optional parent
|
306
|
+
# context, or the current context if one was not provided.
|
307
|
+
#
|
308
|
+
# @param [optional Context] context The context to use as the parent for
|
309
|
+
# the returned context
|
310
|
+
def context_with_span(span, parent_context: OpenTelemetry::Context.current)
|
311
|
+
parent_context.set_value(CURRENT_SPAN_KEY, span)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Activates/deactivates the Span within the current Context, which makes the "current span"
|
315
|
+
# available implicitly.
|
316
|
+
#
|
317
|
+
# On exit, the Span that was active before calling this method will be reactivated.
|
318
|
+
#
|
319
|
+
# @param [Span] span the span to activate
|
320
|
+
# @yield [span, context] yields span and a context containing the span to the block.
|
321
|
+
def with_span(span)
|
322
|
+
OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
module MetalPatch
|
327
|
+
def dispatch(name, request, response)
|
328
|
+
rack_span = RackExtension.current_span
|
329
|
+
# rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
|
330
|
+
super(name, request, response)
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module EpsagonResqueModule
|
4
|
+
def self.prepended(base)
|
5
|
+
class << base
|
6
|
+
prepend ClassMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Module to prepend to Resque singleton class
|
11
|
+
module ClassMethods
|
12
|
+
def push(queue, item)
|
13
|
+
epsagon_conf = config[:epsagon] || {}
|
14
|
+
# Check if the job is being wrapped by ActiveJob
|
15
|
+
# before retrieving the job class name
|
16
|
+
job_class = if item[:class] == 'ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper' && item[:args][0]&.is_a?(Hash)
|
17
|
+
item[:args][0]['job_class']
|
18
|
+
else
|
19
|
+
item[:class]
|
20
|
+
end
|
21
|
+
attributes = {
|
22
|
+
'operation' => 'enqueue',
|
23
|
+
'messaging.system' => 'resque',
|
24
|
+
'messaging.resque.job_class' => job_class,
|
25
|
+
'messaging.destination' => queue.to_s,
|
26
|
+
'messaging.destination_kind' => 'queue',
|
27
|
+
'messaging.resque.redis_url' => Resque.redis.connection[:id]
|
28
|
+
}
|
29
|
+
unless epsagon_conf[:metadata_only]
|
30
|
+
attributes.merge!({
|
31
|
+
'messaging.resque.args' => item
|
32
|
+
})
|
33
|
+
end
|
34
|
+
|
35
|
+
tracer.in_span(queue.to_s, attributes: attributes, kind: :producer) do
|
36
|
+
OpenTelemetry.propagation.text.inject(item)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def tracer
|
42
|
+
EpsagonResqueInstrumentation.instance.tracer
|
43
|
+
end
|
44
|
+
|
45
|
+
def config
|
46
|
+
EpsagonResqueInstrumentation.instance.config
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module EpsagonResqueJob
|
52
|
+
def perform # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
53
|
+
inner_exception = nil
|
54
|
+
epsagon_conf = config[:epsagon] || {}
|
55
|
+
job_args = args || []
|
56
|
+
|
57
|
+
# Check if the job is being wrapped by ActiveJob
|
58
|
+
# before retrieving the job class name
|
59
|
+
job_class = if payload_class_name == 'ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper' && job_args[0]&.is_a?(Hash)
|
60
|
+
job_args[0]['job_class']
|
61
|
+
else
|
62
|
+
payload_class_name
|
63
|
+
end
|
64
|
+
|
65
|
+
attributes = {
|
66
|
+
'operation' => 'perform',
|
67
|
+
'messaging.system' => 'resque',
|
68
|
+
'messaging.resque.job_class' => job_class,
|
69
|
+
'messaging.destination' => queue.to_s,
|
70
|
+
'messaging.destination_kind' => 'queue',
|
71
|
+
'messaging.resque.redis_url' => Resque.redis.connection[:id]
|
72
|
+
}
|
73
|
+
runner_attributes = {
|
74
|
+
'type' => 'resque_worker',
|
75
|
+
'messaging.resque.redis_url' => Resque.redis.connection[:id],
|
76
|
+
|
77
|
+
}
|
78
|
+
|
79
|
+
extracted_context = OpenTelemetry.propagation.text.extract(@payload)
|
80
|
+
|
81
|
+
unless epsagon_conf[:metadata_only]
|
82
|
+
attributes.merge!({
|
83
|
+
'messaging.resque.args' => args
|
84
|
+
})
|
85
|
+
end
|
86
|
+
tracer.in_span(
|
87
|
+
queue.to_s,
|
88
|
+
attributes: attributes,
|
89
|
+
with_parent: extracted_context,
|
90
|
+
kind: :consumer
|
91
|
+
) do |trigger_span|
|
92
|
+
tracer.in_span(job_class,
|
93
|
+
attributes: runner_attributes,
|
94
|
+
kind: :consumer
|
95
|
+
) do |runner_span|
|
96
|
+
super
|
97
|
+
end
|
98
|
+
rescue Exception => e
|
99
|
+
inner_exception = e
|
100
|
+
end
|
101
|
+
raise inner_exception if inner_exception
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def tracer
|
107
|
+
EpsagonResqueInstrumentation.instance.tracer
|
108
|
+
end
|
109
|
+
|
110
|
+
def config
|
111
|
+
EpsagonResqueInstrumentation.instance.config
|
112
|
+
end
|
113
|
+
end
|
@@ -34,7 +34,7 @@ module EpsagonNetHTTPExtension
|
|
34
34
|
attributes.merge!({
|
35
35
|
'http.request.path_params' => path_params,
|
36
36
|
'http.request.body' => body,
|
37
|
-
'http.request.headers' => headers
|
37
|
+
'http.request.headers' => Hash[headers],
|
38
38
|
'http.request.headers.User-Agent' => headers['user-agent']
|
39
39
|
})
|
40
40
|
attributes.merge!(Util.epsagon_query_attributes(query))
|
@@ -62,7 +62,7 @@ module EpsagonNetHTTPExtension
|
|
62
62
|
|
63
63
|
span.set_attribute('http.status_code', status_code)
|
64
64
|
unless config[:epsagon][:metadata_only]
|
65
|
-
span.set_attribute('http.response.headers', Hash[response.each_header.to_a]
|
65
|
+
span.set_attribute('http.response.headers', Hash[response.each_header.to_a])
|
66
66
|
span.set_attribute('http.response.body', response.body)
|
67
67
|
end
|
68
68
|
span.status = OpenTelemetry::Trace::Status.http_to_status(
|
@@ -0,0 +1,294 @@
|
|
1
|
+
require 'pg_query'
|
2
|
+
|
3
|
+
module PostgresExtension
|
4
|
+
# A list of SQL commands, from: https://www.postgresql.org/docs/current/sql-commands.html
|
5
|
+
# Commands are truncated to their first word, and all duplicates
|
6
|
+
# are removed, This favors brevity and low-cardinality over descriptiveness.
|
7
|
+
SQL_COMMANDS = %w[
|
8
|
+
ABORT
|
9
|
+
ALTER
|
10
|
+
ANALYZE
|
11
|
+
BEGIN
|
12
|
+
CALL
|
13
|
+
CHECKPOINT
|
14
|
+
CLOSE
|
15
|
+
CLUSTER
|
16
|
+
COMMENT
|
17
|
+
COMMIT
|
18
|
+
COPY
|
19
|
+
CREATE
|
20
|
+
DEALLOCATE
|
21
|
+
DECLARE
|
22
|
+
DELETE
|
23
|
+
DISCARD
|
24
|
+
DO
|
25
|
+
DROP
|
26
|
+
END
|
27
|
+
EXECUTE
|
28
|
+
EXPLAIN
|
29
|
+
FETCH
|
30
|
+
GRANT
|
31
|
+
IMPORT
|
32
|
+
INSERT
|
33
|
+
LISTEN
|
34
|
+
LOAD
|
35
|
+
LOCK
|
36
|
+
MOVE
|
37
|
+
NOTIFY
|
38
|
+
PREPARE
|
39
|
+
PREPARE
|
40
|
+
REASSIGN
|
41
|
+
REFRESH
|
42
|
+
REINDEX
|
43
|
+
RELEASE
|
44
|
+
RESET
|
45
|
+
REVOKE
|
46
|
+
ROLLBACK
|
47
|
+
SAVEPOINT
|
48
|
+
SECURITY
|
49
|
+
SELECT
|
50
|
+
SELECT
|
51
|
+
SET
|
52
|
+
SHOW
|
53
|
+
START
|
54
|
+
TRUNCATE
|
55
|
+
UNLISTEN
|
56
|
+
UPDATE
|
57
|
+
VACUUM
|
58
|
+
VALUES
|
59
|
+
].freeze
|
60
|
+
|
61
|
+
# From: https://github.com/newrelic/newrelic-ruby-agent/blob/9787095d4b5b2d8fcaf2fdbd964ed07c731a8b6b/lib/new_relic/agent/database/obfuscation_helpers.rb#L9-L34
|
62
|
+
COMPONENTS_REGEX_MAP = {
|
63
|
+
single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
|
64
|
+
dollar_quotes: /(\$(?!\d)[^$]*?\$).*?(?:\1|$)/,
|
65
|
+
uuids: /\{?(?:[0-9a-fA-F]\-*){32}\}?/,
|
66
|
+
numeric_literals: /-?\b(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
|
67
|
+
boolean_literals: /\b(?:true|false|null)\b/i,
|
68
|
+
comments: /(?:#|--).*?(?=\r|\n|$)/i,
|
69
|
+
multi_line_comments: %r{\/\*(?:[^\/]|\/[^*])*?(?:\*\/|\/\*.*)}
|
70
|
+
}.freeze
|
71
|
+
|
72
|
+
POSTGRES_COMPONENTS = %i[
|
73
|
+
single_quotes
|
74
|
+
dollar_quotes
|
75
|
+
uuids
|
76
|
+
numeric_literals
|
77
|
+
boolean_literals
|
78
|
+
comments
|
79
|
+
multi_line_comments
|
80
|
+
].freeze
|
81
|
+
|
82
|
+
UNMATCHED_PAIRS_REGEX = %r{'|\/\*|\*\/|\$(?!\?)}.freeze
|
83
|
+
|
84
|
+
# These are all alike in that they will have a SQL statement as the first parameter.
|
85
|
+
# That statement may possibly be parameterized, but we can still use it - the
|
86
|
+
# obfuscation code will just transform $1 -> $? in that case (which is fine enough).
|
87
|
+
EXEC_ISH_METHODS = %i[
|
88
|
+
exec
|
89
|
+
query
|
90
|
+
sync_exec
|
91
|
+
async_exec
|
92
|
+
exec_params
|
93
|
+
async_exec_params
|
94
|
+
sync_exec_params
|
95
|
+
].freeze
|
96
|
+
|
97
|
+
# The following methods all take a statement name as the first
|
98
|
+
# parameter, and a SQL statement as the second - and possibly
|
99
|
+
# further parameters after that. We can trace them all alike.
|
100
|
+
PREPARE_ISH_METHODS = %i[
|
101
|
+
prepare
|
102
|
+
async_prepare
|
103
|
+
sync_prepare
|
104
|
+
].freeze
|
105
|
+
|
106
|
+
# The following methods take a prepared statement name as their first
|
107
|
+
# parameter - everything after that is either potentially quite sensitive
|
108
|
+
# (an array of bind params) or not useful to us. We trace them all alike.
|
109
|
+
EXEC_PREPARED_ISH_METHODS = %i[
|
110
|
+
exec_prepared
|
111
|
+
async_exec_prepared
|
112
|
+
sync_exec_prepared
|
113
|
+
].freeze
|
114
|
+
|
115
|
+
EXEC_ISH_METHODS.each do |method|
|
116
|
+
define_method method do |*args|
|
117
|
+
span_name, attrs = span_attrs(:query, *args)
|
118
|
+
tracer.in_span(span_name, attributes: attrs, kind: :client) do
|
119
|
+
super(*args)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
PREPARE_ISH_METHODS.each do |method|
|
125
|
+
define_method method do |*args|
|
126
|
+
span_name, attrs = span_attrs(:prepare, *args)
|
127
|
+
tracer.in_span(span_name, attributes: attrs, kind: :client) do
|
128
|
+
super(*args)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
EXEC_PREPARED_ISH_METHODS.each do |method|
|
134
|
+
define_method method do |*args|
|
135
|
+
span_name, attrs = span_attrs(:execute, *args)
|
136
|
+
tracer.in_span(span_name, attributes: attrs, kind: :client) do
|
137
|
+
super(*args)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def config
|
143
|
+
EpsagonPostgresInstrumentation.instance.config
|
144
|
+
end
|
145
|
+
|
146
|
+
def tracer
|
147
|
+
EpsagonPostgresInstrumentation.instance.tracer
|
148
|
+
end
|
149
|
+
|
150
|
+
def lru_cache
|
151
|
+
# When SQL is being sanitized, we know that this cache will
|
152
|
+
# never be more than 50 entries * 2000 characters (so, presumably
|
153
|
+
# 100k bytes - or 97k). When not sanitizing SQL, then this cache
|
154
|
+
# could grow much larger - but the small cache size should otherwise
|
155
|
+
# help contain memory growth. The intended use here is to cache
|
156
|
+
# prepared SQL statements, so that we can attach a reasonable
|
157
|
+
# `db.sql.statement` value to spans when those prepared statements
|
158
|
+
# are executed later on.
|
159
|
+
@lru_cache ||= LruCache.new(50)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Rubocop is complaining about 19.31/18 for Metrics/AbcSize.
|
163
|
+
# But, getting that metric in line would force us over the
|
164
|
+
# module size limit! We can't win here unless we want to start
|
165
|
+
# abstracting things into a million pieces.
|
166
|
+
def span_attrs(kind, *args) # rubocop:disable Metrics/AbcSize
|
167
|
+
if kind == :query
|
168
|
+
operation = extract_operation(args[0])
|
169
|
+
sql = args[0]
|
170
|
+
else
|
171
|
+
statement_name = args[0]
|
172
|
+
|
173
|
+
if kind == :prepare
|
174
|
+
sql = args[1]
|
175
|
+
lru_cache[statement_name] = sql
|
176
|
+
operation = 'PREPARE'
|
177
|
+
else
|
178
|
+
sql = lru_cache[statement_name]
|
179
|
+
operation = 'EXECUTE'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
attrs = { 'db.operation' => validated_operation(operation), 'db.postgresql.prepared_statement_name' => statement_name }
|
184
|
+
attrs['db.statement'] = sql if config[:epsagon][:metadata_only] == false
|
185
|
+
attrs['db.sql.table'] = table_name(sql)
|
186
|
+
attrs.reject! { |_, v| v.nil? }
|
187
|
+
|
188
|
+
[database_name, client_attributes.merge(attrs)]
|
189
|
+
end
|
190
|
+
|
191
|
+
def table_name(sql)
|
192
|
+
return '' if sql.nil?
|
193
|
+
|
194
|
+
parsed_query = PgQuery.parse(sql)
|
195
|
+
if parsed_query.tables.length == 0
|
196
|
+
''
|
197
|
+
else
|
198
|
+
parsed_query.tables[0]
|
199
|
+
end
|
200
|
+
rescue PgQuery::ParseError
|
201
|
+
''
|
202
|
+
end
|
203
|
+
|
204
|
+
def validated_operation(operation)
|
205
|
+
operation if PostgresExtension::SQL_COMMANDS.include?(operation)
|
206
|
+
end
|
207
|
+
|
208
|
+
def extract_operation(sql)
|
209
|
+
# From: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/9244a08a8d014afe26b82b91cf86e407c2599d73/plugins/node/opentelemetry-instrumentation-pg/src/utils.ts#L35
|
210
|
+
sql.to_s.split[0].to_s.upcase
|
211
|
+
end
|
212
|
+
|
213
|
+
def generated_postgres_regex
|
214
|
+
@generated_postgres_regex ||= Regexp.union(PostgresExtension::POSTGRES_COMPONENTS.map { |component| PostgresExtension::COMPONENTS_REGEX_MAP[component] })
|
215
|
+
end
|
216
|
+
|
217
|
+
def database_name
|
218
|
+
conninfo_hash[:dbname]&.to_s
|
219
|
+
end
|
220
|
+
|
221
|
+
def client_attributes
|
222
|
+
attributes = {
|
223
|
+
'db.system' => 'postgresql',
|
224
|
+
'db.user' => conninfo_hash[:user]&.to_s,
|
225
|
+
'db.name' => database_name,
|
226
|
+
'net.peer.name' => conninfo_hash[:host]&.to_s
|
227
|
+
}
|
228
|
+
# attributes['peer.service'] = config[:peer_service] # if config[:peer_service]
|
229
|
+
|
230
|
+
attributes.merge(transport_attrs).reject { |_, v| v.nil? }
|
231
|
+
end
|
232
|
+
|
233
|
+
def transport_attrs
|
234
|
+
if conninfo_hash[:host]&.start_with?('/')
|
235
|
+
{ 'net.transport' => 'Unix' }
|
236
|
+
else
|
237
|
+
{
|
238
|
+
'net.transport' => 'IP.TCP',
|
239
|
+
'net.peer.ip' => conninfo_hash[:hostaddr]&.to_s,
|
240
|
+
'net.peer.port' => conninfo_hash[:port]&.to_s
|
241
|
+
}
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Copyright The OpenTelemetry Authors
|
247
|
+
#
|
248
|
+
# SPDX-License-Identifier: Apache-2.0
|
249
|
+
# A simple LRU cache for the postgres instrumentation.
|
250
|
+
class LruCache
|
251
|
+
# Rather than take a dependency on another gem, we implement a very, very basic
|
252
|
+
# LRU cache here. We can take advantage of the fact that Ruby hashes are ordered
|
253
|
+
# to always keep the recently-accessed keys at the top.
|
254
|
+
def initialize(size)
|
255
|
+
raise ArgumentError, 'Invalid size' if size < 1
|
256
|
+
|
257
|
+
@limit = size
|
258
|
+
@store = {}
|
259
|
+
end
|
260
|
+
|
261
|
+
def [](key)
|
262
|
+
# We need to check for the key explicitly, because `nil` is a valid hash value.
|
263
|
+
return unless @store.key?(key)
|
264
|
+
|
265
|
+
# Since the cache contains the item, we delete and re-insert into the hash.
|
266
|
+
# This guarantees that hash keys are ordered by access recency.
|
267
|
+
value = @store.delete(key)
|
268
|
+
@store[key] = value
|
269
|
+
|
270
|
+
value
|
271
|
+
end
|
272
|
+
|
273
|
+
def []=(key, value)
|
274
|
+
# We remove the value if it's already present, so that the hash keys remain ordered
|
275
|
+
# by access recency.
|
276
|
+
@store.delete(key)
|
277
|
+
@store[key] = value
|
278
|
+
@store.shift if @store.length > @limit
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
#
|
283
|
+
# EpsagonPostgresInstrumentation
|
284
|
+
# Installs the Instrumentation on the PG::Connection class
|
285
|
+
#
|
286
|
+
class EpsagonPostgresInstrumentation < OpenTelemetry::Instrumentation::Base
|
287
|
+
install do |_config|
|
288
|
+
::PG::Connection.prepend(PostgresExtension)
|
289
|
+
end
|
290
|
+
|
291
|
+
present do
|
292
|
+
defined?(::PG)
|
293
|
+
end
|
294
|
+
end
|
@@ -1,58 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'opentelemetry'
|
4
|
-
require 'rails'
|
5
|
-
require 'action_controller/railtie'
|
6
4
|
|
7
5
|
require_relative '../util'
|
8
6
|
require_relative '../epsagon_constants'
|
9
7
|
|
10
8
|
|
11
|
-
module RackExtension
|
12
|
-
module_function
|
13
|
-
|
14
|
-
CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
|
15
|
-
|
16
|
-
private_constant :CURRENT_SPAN_KEY
|
17
|
-
|
18
|
-
# Returns the current span from the current or provided context
|
19
|
-
#
|
20
|
-
# @param [optional Context] context The context to lookup the current
|
21
|
-
# {Span} from. Defaults to Context.current
|
22
|
-
def current_span(context = nil)
|
23
|
-
context ||= OpenTelemetry::Context.current
|
24
|
-
context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
|
25
|
-
end
|
26
|
-
|
27
|
-
# Returns a context containing the span, derived from the optional parent
|
28
|
-
# context, or the current context if one was not provided.
|
29
|
-
#
|
30
|
-
# @param [optional Context] context The context to use as the parent for
|
31
|
-
# the returned context
|
32
|
-
def context_with_span(span, parent_context: OpenTelemetry::Context.current)
|
33
|
-
parent_context.set_value(CURRENT_SPAN_KEY, span)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Activates/deactivates the Span within the current Context, which makes the "current span"
|
37
|
-
# available implicitly.
|
38
|
-
#
|
39
|
-
# On exit, the Span that was active before calling this method will be reactivated.
|
40
|
-
#
|
41
|
-
# @param [Span] span the span to activate
|
42
|
-
# @yield [span, context] yields span and a context containing the span to the block.
|
43
|
-
def with_span(span)
|
44
|
-
OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
module MetalPatch
|
49
|
-
def dispatch(name, request, response)
|
50
|
-
rack_span = RackExtension.current_span
|
51
|
-
# rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
|
52
|
-
super(name, request, response)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
9
|
class EpsagonRailsInstrumentation < OpenTelemetry::Instrumentation::Base
|
57
10
|
install do |_config|
|
58
11
|
require_relative 'epsagon_rails_middleware'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class EpsagonResqueInstrumentation < OpenTelemetry::Instrumentation::Base
|
2
|
+
install do |_config|
|
3
|
+
require_dependencies
|
4
|
+
patch
|
5
|
+
end
|
6
|
+
|
7
|
+
present do
|
8
|
+
defined?(::Resque)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def patch
|
14
|
+
::Resque.prepend(EpsagonResqueModule)
|
15
|
+
::Resque::Job.prepend(EpsagonResqueJob)
|
16
|
+
end
|
17
|
+
|
18
|
+
def require_dependencies
|
19
|
+
require_relative 'epsagon_resque_job'
|
20
|
+
end
|
21
|
+
end
|
data/lib/util.rb
CHANGED
@@ -4,6 +4,10 @@ require 'cgi'
|
|
4
4
|
|
5
5
|
# Utilities for epsagon opentelemetry solution
|
6
6
|
module Util
|
7
|
+
def self.validate_value(h, k, message, &block)
|
8
|
+
raise ArgumentError.new( "#{k} #{message}. Got #{h[k].class}: #{h[k]}" ) unless yield(h[k])
|
9
|
+
end
|
10
|
+
|
7
11
|
def self.epsagon_query_attributes(query_string)
|
8
12
|
if query_string&.include? '='
|
9
13
|
{ 'http.request.query_params' => CGI.parse(query_string).to_json }
|
@@ -12,6 +16,30 @@ module Util
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
19
|
+
|
20
|
+
def self.remove_key_recursive(h, key)
|
21
|
+
dot_idx = key.index('.')
|
22
|
+
if not dot_idx.nil?
|
23
|
+
next_hash = h[key[0..dot_idx - 1]]
|
24
|
+
self.remove_key_recursive(next_hash, key[dot_idx + 1..-1]) if next_hash
|
25
|
+
else
|
26
|
+
h.delete(key)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.prepare_attr(key, value, max_size, excluded_keys)
|
31
|
+
return nil if excluded_keys.include? key
|
32
|
+
return self.trim_attr(value, max_size) unless value.instance_of? Hash
|
33
|
+
value = value.dup
|
34
|
+
excluded_keys.each do |ekey|
|
35
|
+
if ekey.start_with? (key + '.')
|
36
|
+
rest_of_key = ekey[key.size + 1..-1]
|
37
|
+
self.remove_key_recursive(value, rest_of_key)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return self.trim_attr(JSON.dump(value), max_size)
|
41
|
+
end
|
42
|
+
|
15
43
|
def self.trim_attr(value, max_size)
|
16
44
|
if value.instance_of? Array then
|
17
45
|
current_size = 2
|
@@ -25,7 +53,7 @@ module Util
|
|
25
53
|
end
|
26
54
|
return value
|
27
55
|
elsif value.instance_of? String then
|
28
|
-
value[0, max_size]
|
56
|
+
(value.frozen? ? value.dup : value).force_encoding('utf-8')[0, max_size]
|
29
57
|
else
|
30
58
|
value
|
31
59
|
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.30
|
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-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: opentelemetry-api
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.11.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pg_query
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
83
97
|
description: 'Epsagon provides tracing to Ruby applications for the collection of
|
84
98
|
distributed tracing and performance metrics to simplify complex architectures, eliminate
|
85
99
|
manual work, visualize and correlate data to identify and fix problems fast.
|
@@ -90,6 +104,7 @@ executables: []
|
|
90
104
|
extensions: []
|
91
105
|
extra_rdoc_files: []
|
92
106
|
files:
|
107
|
+
- lib/arn_parser.rb
|
93
108
|
- lib/epsagon.rb
|
94
109
|
- lib/epsagon_constants.rb
|
95
110
|
- lib/exporter_extension.rb
|
@@ -97,9 +112,12 @@ files:
|
|
97
112
|
- lib/instrumentation/aws_sdk_plugin.rb
|
98
113
|
- lib/instrumentation/epsagon_faraday_middleware.rb
|
99
114
|
- lib/instrumentation/epsagon_rails_middleware.rb
|
115
|
+
- lib/instrumentation/epsagon_resque_job.rb
|
100
116
|
- lib/instrumentation/faraday.rb
|
101
117
|
- lib/instrumentation/net_http.rb
|
118
|
+
- lib/instrumentation/postgres.rb
|
102
119
|
- lib/instrumentation/rails.rb
|
120
|
+
- lib/instrumentation/resque.rb
|
103
121
|
- lib/instrumentation/sinatra.rb
|
104
122
|
- lib/instrumentation/version.rb
|
105
123
|
- lib/util.rb
|