epsagon 0.0.27 → 0.0.31
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/arn_parser.rb +27 -0
- data/lib/epsagon.rb +93 -35
- 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 +48 -22
- 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 +24 -0
- 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: d478fc58c432f30f98f666ae27e27b31bca3fd88254d0ef5b108b6d52a21d715
|
4
|
+
data.tar.gz: d535f30b5a159895c985ef0da98f1e1ea2c433db3f4163805fb5b3d6c7e1fa82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 021e018a393a88cefb02c31ba2ee66fb5ec5f367b468ab1ed2b6537ba7e951ee3ccad40535ceed66b498589fbc1c5190cfac31f69a439a40afbc99547bbe4506
|
7
|
+
data.tar.gz: 0fc691d485e757bc0314acc6ef5901ea39a676da2725bed92700f2260b6f37e48025c5b25c2f9a0a18f46fb35cd175c11bb2843fabeb1dc960b7dd1367f79e2c
|
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,65 +12,117 @@ 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
|
-
|
81
|
+
end
|
41
82
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Util.validate_value(@@epsagon_config, :app_name, 'Must be a String') {|v| v.is_a? String}
|
46
|
-
Util.validate_value(@@epsagon_config, :max_attribute_size, 'Must be an Integer') {|v| v.is_a? Integer}
|
47
|
-
Util.validate_value(@@epsagon_config, :ignore_domains, 'Must be iterable') {|v| v.respond_to?(:each)}
|
83
|
+
def set_ecs_metadata
|
84
|
+
metadata_uri = ENV['ECS_CONTAINER_METADATA_URI']
|
85
|
+
return {} if metadata_uri.nil?
|
48
86
|
|
49
|
-
|
50
|
-
|
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'])
|
51
90
|
|
52
|
-
|
53
|
-
|
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
|
+
}
|
54
100
|
end
|
55
101
|
|
56
102
|
# config opentelemetry with epsaon extensions:
|
57
103
|
|
58
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
|
+
|
59
111
|
configurator.resource = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk.merge(
|
60
|
-
OpenTelemetry::SDK::Resources::Resource.create(
|
61
|
-
'application' => @@epsagon_config[:app_name],
|
62
|
-
'epsagon.version' => EpsagonConstants::VERSION,
|
63
|
-
'epsagon.metadata_only' => @@epsagon_config[:metadata_only]
|
64
|
-
})
|
112
|
+
OpenTelemetry::SDK::Resources::Resource.create(otel_resource)
|
65
113
|
)
|
66
|
-
|
67
|
-
configurator.use '
|
68
|
-
configurator.use '
|
69
|
-
configurator.use '
|
70
|
-
configurator.use '
|
71
|
-
configurator.use '
|
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
|
)
|
@@ -78,10 +130,10 @@ module Epsagon
|
|
78
130
|
|
79
131
|
configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
80
132
|
exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
81
|
-
'x-epsagon-token' =>
|
133
|
+
'x-epsagon-token' => get_config[:token]
|
82
134
|
},
|
83
|
-
endpoint:
|
84
|
-
insecure:
|
135
|
+
endpoint: get_config[:backend],
|
136
|
+
insecure: get_config[:insecure] || false)
|
85
137
|
)
|
86
138
|
end
|
87
139
|
end
|
@@ -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'
|
@@ -75,11 +76,9 @@ class EpsagonRackMiddleware
|
|
75
76
|
|
76
77
|
def call(env)
|
77
78
|
original_env = env.dup
|
78
|
-
extracted_context = OpenTelemetry.propagation.http.extract(env)
|
79
|
-
frontend_context = create_frontend_span(env, extracted_context)
|
80
79
|
|
81
80
|
# restore extracted context in this process:
|
82
|
-
OpenTelemetry::Context.with_current(
|
81
|
+
OpenTelemetry::Context.with_current(OpenTelemetry.propagation.http.extract(env)) do
|
83
82
|
request_span_name = create_request_span_name(env['REQUEST_URI'] || original_env['PATH_INFO'])
|
84
83
|
tracer.in_span(env['HTTP_HOST'] || 'unknown',
|
85
84
|
attributes: request_span_attributes(env: env),
|
@@ -97,28 +96,10 @@ class EpsagonRackMiddleware
|
|
97
96
|
end
|
98
97
|
end
|
99
98
|
end
|
100
|
-
ensure
|
101
|
-
finish_span(frontend_context)
|
102
99
|
end
|
103
100
|
|
104
101
|
private
|
105
102
|
|
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
103
|
def finish_span(context)
|
123
104
|
OpenTelemetry::Trace.current_span(context).finish if context
|
124
105
|
end
|
@@ -189,7 +170,7 @@ class EpsagonRackMiddleware
|
|
189
170
|
def set_attributes_after_request(http_span, _framework_span, status, headers, response)
|
190
171
|
unless config[:epsagon][:metadata_only]
|
191
172
|
http_span.set_attribute('http.response.headers', JSON.generate(headers))
|
192
|
-
http_span.set_attribute('http.response.body', response.join)
|
173
|
+
http_span.set_attribute('http.response.body', response.join) if response.respond_to?(:join)
|
193
174
|
end
|
194
175
|
|
195
176
|
http_span.set_attribute('http.status_code', status)
|
@@ -284,3 +265,48 @@ class EpsagonRailtie < ::Rails::Railtie
|
|
284
265
|
)
|
285
266
|
end
|
286
267
|
end
|
268
|
+
|
269
|
+
module RackExtension
|
270
|
+
module_function
|
271
|
+
|
272
|
+
CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
|
273
|
+
|
274
|
+
private_constant :CURRENT_SPAN_KEY
|
275
|
+
|
276
|
+
# Returns the current span from the current or provided context
|
277
|
+
#
|
278
|
+
# @param [optional Context] context The context to lookup the current
|
279
|
+
# {Span} from. Defaults to Context.current
|
280
|
+
def current_span(context = nil)
|
281
|
+
context ||= OpenTelemetry::Context.current
|
282
|
+
context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns a context containing the span, derived from the optional parent
|
286
|
+
# context, or the current context if one was not provided.
|
287
|
+
#
|
288
|
+
# @param [optional Context] context The context to use as the parent for
|
289
|
+
# the returned context
|
290
|
+
def context_with_span(span, parent_context: OpenTelemetry::Context.current)
|
291
|
+
parent_context.set_value(CURRENT_SPAN_KEY, span)
|
292
|
+
end
|
293
|
+
|
294
|
+
# Activates/deactivates the Span within the current Context, which makes the "current span"
|
295
|
+
# available implicitly.
|
296
|
+
#
|
297
|
+
# On exit, the Span that was active before calling this method will be reactivated.
|
298
|
+
#
|
299
|
+
# @param [Span] span the span to activate
|
300
|
+
# @yield [span, context] yields span and a context containing the span to the block.
|
301
|
+
def with_span(span)
|
302
|
+
OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
module MetalPatch
|
307
|
+
def dispatch(name, request, response)
|
308
|
+
rack_span = RackExtension.current_span
|
309
|
+
# rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
|
310
|
+
super(name, request, response)
|
311
|
+
end
|
312
|
+
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
@@ -16,6 +16,30 @@ module Util
|
|
16
16
|
end
|
17
17
|
end
|
18
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
|
+
|
19
43
|
def self.trim_attr(value, max_size)
|
20
44
|
if value.instance_of? Array then
|
21
45
|
current_size = 2
|
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.31
|
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-31 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
|