elastic-apm 3.7.0 → 3.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ci/Jenkinsfile +139 -96
- data/.ci/packer_cache.sh +12 -10
- data/.rspec +0 -1
- data/CHANGELOG.asciidoc +80 -0
- data/Gemfile +4 -3
- data/bin/run-tests +4 -1
- data/docker-compose.yml +2 -0
- data/docs/configuration.asciidoc +38 -0
- data/docs/debugging.asciidoc +14 -0
- data/docs/supported-technologies.asciidoc +2 -1
- data/lib/elastic_apm/config.rb +36 -13
- data/lib/elastic_apm/config/options.rb +2 -1
- data/lib/elastic_apm/config/round_float.rb +31 -0
- data/lib/elastic_apm/config/wildcard_pattern_list.rb +13 -1
- data/lib/elastic_apm/context_builder.rb +1 -1
- data/lib/elastic_apm/grpc.rb +2 -2
- data/lib/elastic_apm/instrumenter.rb +10 -3
- data/lib/elastic_apm/metadata.rb +3 -1
- data/lib/elastic_apm/metadata/cloud_info.rb +128 -0
- data/lib/elastic_apm/metadata/service_info.rb +5 -2
- data/lib/elastic_apm/metadata/system_info.rb +5 -3
- data/lib/elastic_apm/metadata/system_info/container_info.rb +28 -4
- data/lib/elastic_apm/middleware.rb +8 -2
- data/lib/elastic_apm/opentracing.rb +47 -23
- data/lib/elastic_apm/span.rb +7 -3
- data/lib/elastic_apm/spies.rb +16 -14
- data/lib/elastic_apm/spies/delayed_job.rb +4 -2
- data/lib/elastic_apm/spies/dynamo_db.rb +58 -0
- data/lib/elastic_apm/spies/elasticsearch.rb +26 -2
- data/lib/elastic_apm/spies/mongo.rb +1 -1
- data/lib/elastic_apm/spies/net_http.rb +6 -2
- data/lib/elastic_apm/spies/sequel.rb +1 -1
- data/lib/elastic_apm/trace_context.rb +1 -1
- data/lib/elastic_apm/trace_context/traceparent.rb +2 -4
- data/lib/elastic_apm/trace_context/tracestate.rb +112 -9
- data/lib/elastic_apm/transaction.rb +26 -5
- data/lib/elastic_apm/transport/connection.rb +1 -0
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +70 -0
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +14 -56
- data/lib/elastic_apm/transport/serializers.rb +8 -6
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +56 -23
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -1
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
- data/lib/elastic_apm/transport/user_agent.rb +3 -3
- data/lib/elastic_apm/transport/worker.rb +5 -0
- data/lib/elastic_apm/util.rb +2 -0
- data/lib/elastic_apm/util/precision_validator.rb +46 -0
- data/lib/elastic_apm/version.rb +1 -1
- metadata +12 -8
- data/.ci/downstreamTests.groovy +0 -192
@@ -30,14 +30,17 @@ module ElasticAPM
|
|
30
30
|
|
31
31
|
attr_reader :name, :version
|
32
32
|
end
|
33
|
+
|
33
34
|
class Agent < Versioned; end
|
34
35
|
class Framework < Versioned; end
|
35
36
|
class Language < Versioned; end
|
36
37
|
class Runtime < Versioned; end
|
38
|
+
|
37
39
|
def initialize(config)
|
38
40
|
@config = config
|
39
41
|
|
40
42
|
@name = @config.service_name
|
43
|
+
@node_name = @config.service_node_name
|
41
44
|
@environment = @config.environment
|
42
45
|
@agent = Agent.new(name: 'ruby', version: VERSION)
|
43
46
|
@framework = Framework.new(
|
@@ -49,8 +52,8 @@ module ElasticAPM
|
|
49
52
|
@version = @config.service_version || Util.git_sha
|
50
53
|
end
|
51
54
|
|
52
|
-
attr_reader :name, :environment, :agent, :framework, :language,
|
53
|
-
:version
|
55
|
+
attr_reader :name, :node_name, :environment, :agent, :framework, :language,
|
56
|
+
:runtime, :version
|
54
57
|
|
55
58
|
private
|
56
59
|
|
@@ -24,7 +24,7 @@ module ElasticAPM
|
|
24
24
|
def initialize(config)
|
25
25
|
@config = config
|
26
26
|
|
27
|
-
@hostname = @config.hostname ||
|
27
|
+
@hostname = @config.hostname || self.class.system_hostname
|
28
28
|
@architecture = gem_platform.cpu
|
29
29
|
@platform = gem_platform.os
|
30
30
|
|
@@ -35,11 +35,13 @@ module ElasticAPM
|
|
35
35
|
|
36
36
|
attr_reader :hostname, :architecture, :platform, :container, :kubernetes
|
37
37
|
|
38
|
-
private
|
39
|
-
|
40
38
|
def gem_platform
|
41
39
|
@gem_platform ||= Gem::Platform.local
|
42
40
|
end
|
41
|
+
|
42
|
+
def self.system_hostname
|
43
|
+
@system_hostname ||= `hostname`.chomp
|
44
|
+
end
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
@@ -81,8 +81,14 @@ module ElasticAPM
|
|
81
81
|
ENV.fetch('KUBERNETES_POD_UID', kubernetes_pod_uid)
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
|
84
|
+
CONTAINER_ID_REGEXES = [
|
85
|
+
%r{^[[:xdigit:]]{64}$},
|
86
|
+
%r{^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4,}$}
|
87
|
+
]
|
88
|
+
KUBEPODS_REGEXES = [
|
89
|
+
%r{(?:^/kubepods[^\s]*/pod([^/]+)$)},
|
90
|
+
%r{(?:^/kubepods\.slice/kubepods-[^/]+\.slice/kubepods-[^/]+-pod([^/]+)\.slice$)}
|
91
|
+
]
|
86
92
|
SYSTEMD_SCOPE_SUFFIX = '.scope'
|
87
93
|
|
88
94
|
# rubocop:disable Metrics/PerceivedComplexity
|
@@ -118,18 +124,36 @@ module ElasticAPM
|
|
118
124
|
end
|
119
125
|
end
|
120
126
|
|
121
|
-
if (kubepods_match =
|
127
|
+
if (kubepods_match = match_kubepods(directory))
|
122
128
|
pod_id = kubepods_match[1] || kubepods_match[2]
|
123
129
|
|
124
130
|
self.container_id = container_id
|
125
131
|
self.kubernetes_pod_uid = pod_id
|
126
|
-
elsif
|
132
|
+
elsif match_container(container_id)
|
127
133
|
self.container_id = container_id
|
128
134
|
end
|
129
135
|
end
|
130
136
|
end
|
131
137
|
# rubocop:enable Metrics/PerceivedComplexity
|
132
138
|
# rubocop:enable Metrics/CyclomaticComplexity
|
139
|
+
|
140
|
+
def match_kubepods(directory)
|
141
|
+
KUBEPODS_REGEXES.each do |r|
|
142
|
+
next unless (match = r.match(directory))
|
143
|
+
return match
|
144
|
+
end
|
145
|
+
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def match_container(container_id)
|
150
|
+
CONTAINER_ID_REGEXES.each do |r|
|
151
|
+
next unless (match = r.match(container_id))
|
152
|
+
return match
|
153
|
+
end
|
154
|
+
|
155
|
+
nil
|
156
|
+
end
|
133
157
|
end
|
134
158
|
end
|
135
159
|
end
|
@@ -59,9 +59,15 @@ module ElasticAPM
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def path_ignored?(env)
|
62
|
-
config.ignore_url_patterns.any? do |r|
|
63
|
-
env['PATH_INFO']
|
62
|
+
return true if config.ignore_url_patterns.any? do |r|
|
63
|
+
r.match(env['PATH_INFO'])
|
64
64
|
end
|
65
|
+
|
66
|
+
return true if config.transaction_ignore_urls.any? do |r|
|
67
|
+
r.match(env['PATH_INFO'])
|
68
|
+
end
|
69
|
+
|
70
|
+
false
|
65
71
|
end
|
66
72
|
|
67
73
|
def start_transaction(env)
|
@@ -39,7 +39,7 @@ module ElasticAPM
|
|
39
39
|
@span_context
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def set_tag(key, val)
|
43
43
|
if elastic_span.is_a?(Transaction)
|
44
44
|
case key.to_s
|
45
45
|
when 'type'
|
@@ -54,6 +54,8 @@ module ElasticAPM
|
|
54
54
|
else
|
55
55
|
elastic_span.context.labels[key] = val
|
56
56
|
end
|
57
|
+
|
58
|
+
self
|
57
59
|
end
|
58
60
|
|
59
61
|
def set_baggage_item(_key, _value)
|
@@ -78,18 +80,12 @@ module ElasticAPM
|
|
78
80
|
ElasticAPM.report_message message
|
79
81
|
end
|
80
82
|
end
|
81
|
-
|
82
83
|
# rubocop:enable Lint/UnusedMethodArgument
|
83
|
-
def finish(clock_end: Util.monotonic_micros, end_time: nil)
|
84
|
-
return unless (agent = ElasticAPM.agent)
|
85
84
|
|
86
|
-
|
87
|
-
|
88
|
-
'`Time` is deprecated. Use `clock_end:` and monotonic time instead.'
|
89
|
-
clock_end = end_time
|
90
|
-
end
|
85
|
+
def finish(end_time: Time.now)
|
86
|
+
return unless (agent = ElasticAPM.agent)
|
91
87
|
|
92
|
-
elastic_span.done clock_end:
|
88
|
+
elastic_span.done clock_end: Util.micros(end_time)
|
93
89
|
|
94
90
|
case elastic_span
|
95
91
|
when ElasticAPM::Transaction
|
@@ -114,6 +110,8 @@ module ElasticAPM
|
|
114
110
|
|
115
111
|
# @api private
|
116
112
|
class SpanContext
|
113
|
+
extend Forwardable
|
114
|
+
|
117
115
|
def initialize(trace_context:, baggage: nil)
|
118
116
|
if baggage
|
119
117
|
ElasticAPM.agent.config.logger.warn(
|
@@ -125,10 +123,27 @@ module ElasticAPM
|
|
125
123
|
end
|
126
124
|
|
127
125
|
attr_accessor :trace_context
|
126
|
+
def_delegators :trace_context, :trace_id, :id, :parent_id
|
127
|
+
|
128
|
+
def self.from_header(header)
|
129
|
+
return unless header
|
130
|
+
|
131
|
+
trace_context = ElasticAPM::TraceContext.parse(header)
|
132
|
+
return unless trace_context
|
133
|
+
|
134
|
+
trace_context.traceparent.id = trace_context.parent_id
|
135
|
+
trace_context.traceparent.parent_id = nil
|
136
|
+
|
137
|
+
from_trace_context(trace_context)
|
138
|
+
end
|
128
139
|
|
129
140
|
def self.from_trace_context(trace_context)
|
130
141
|
new(trace_context: trace_context)
|
131
142
|
end
|
143
|
+
|
144
|
+
def child
|
145
|
+
self.class.from_trace_context(trace_context.child)
|
146
|
+
end
|
132
147
|
end
|
133
148
|
|
134
149
|
# @api private
|
@@ -210,7 +225,7 @@ module ElasticAPM
|
|
210
225
|
child_of: nil,
|
211
226
|
references: nil,
|
212
227
|
start_time: Time.now,
|
213
|
-
|
228
|
+
tags: {},
|
214
229
|
ignore_active_scope: false,
|
215
230
|
finish_on_close: true,
|
216
231
|
**
|
@@ -220,14 +235,14 @@ module ElasticAPM
|
|
220
235
|
child_of: child_of,
|
221
236
|
references: references,
|
222
237
|
start_time: start_time,
|
223
|
-
|
238
|
+
tags: tags,
|
224
239
|
ignore_active_scope: ignore_active_scope
|
225
240
|
)
|
226
241
|
scope = scope_manager.activate(span, finish_on_close: finish_on_close)
|
227
242
|
|
228
243
|
if block_given?
|
229
244
|
begin
|
230
|
-
yield scope
|
245
|
+
return yield scope
|
231
246
|
ensure
|
232
247
|
scope.close
|
233
248
|
end
|
@@ -243,7 +258,7 @@ module ElasticAPM
|
|
243
258
|
child_of: nil,
|
244
259
|
references: nil,
|
245
260
|
start_time: Time.now,
|
246
|
-
|
261
|
+
tags: {},
|
247
262
|
ignore_active_scope: false,
|
248
263
|
**
|
249
264
|
)
|
@@ -280,7 +295,7 @@ module ElasticAPM
|
|
280
295
|
span_context ||=
|
281
296
|
SpanContext.from_trace_context(elastic_span.trace_context)
|
282
297
|
|
283
|
-
|
298
|
+
tags.each do |key, value|
|
284
299
|
elastic_span.context.labels[key] = value
|
285
300
|
end
|
286
301
|
|
@@ -293,7 +308,7 @@ module ElasticAPM
|
|
293
308
|
|
294
309
|
def inject(span_context, format, carrier)
|
295
310
|
case format
|
296
|
-
when ::OpenTracing::FORMAT_RACK
|
311
|
+
when ::OpenTracing::FORMAT_RACK, ::OpenTracing::FORMAT_TEXT_MAP
|
297
312
|
carrier['elastic-apm-traceparent'] =
|
298
313
|
span_context.traceparent.to_header
|
299
314
|
else
|
@@ -304,10 +319,16 @@ module ElasticAPM
|
|
304
319
|
def extract(format, carrier)
|
305
320
|
case format
|
306
321
|
when ::OpenTracing::FORMAT_RACK
|
307
|
-
|
308
|
-
|
322
|
+
SpanContext.from_header(
|
323
|
+
carrier['HTTP_ELASTIC_APM_TRACEPARENT']
|
324
|
+
)
|
325
|
+
when ::OpenTracing::FORMAT_TEXT_MAP
|
326
|
+
SpanContext.from_header(
|
327
|
+
carrier['elastic-apm-traceparent']
|
328
|
+
)
|
309
329
|
else
|
310
|
-
warn 'Only extraction from HTTP headers via Rack
|
330
|
+
warn 'Only extraction from HTTP headers via Rack or in ' \
|
331
|
+
'text map format are available'
|
311
332
|
nil
|
312
333
|
end
|
313
334
|
rescue ElasticAPM::TraceContext::InvalidTraceparentHeader
|
@@ -321,9 +342,12 @@ module ElasticAPM
|
|
321
342
|
references:,
|
322
343
|
ignore_active_scope:
|
323
344
|
)
|
324
|
-
context_from_child_of(child_of) ||
|
325
|
-
|
326
|
-
|
345
|
+
context = context_from_child_of(child_of) ||
|
346
|
+
context_from_references(references) ||
|
347
|
+
context_from_active_scope(ignore_active_scope)
|
348
|
+
return context.child if context&.respond_to?(:child)
|
349
|
+
|
350
|
+
context
|
327
351
|
end
|
328
352
|
|
329
353
|
def context_from_child_of(child_of)
|
@@ -344,7 +368,7 @@ module ElasticAPM
|
|
344
368
|
def context_from_active_scope(ignore_active_scope)
|
345
369
|
if ignore_active_scope
|
346
370
|
ElasticAPM.agent&.config&.logger&.warn(
|
347
|
-
'ignore_active_scope might lead to
|
371
|
+
'ignore_active_scope might lead to unexpected results'
|
348
372
|
)
|
349
373
|
return
|
350
374
|
end
|
data/lib/elastic_apm/span.rb
CHANGED
@@ -38,7 +38,8 @@ module ElasticAPM
|
|
38
38
|
action: nil,
|
39
39
|
context: nil,
|
40
40
|
stacktrace_builder: nil,
|
41
|
-
sync: nil
|
41
|
+
sync: nil,
|
42
|
+
sample_rate: nil
|
42
43
|
)
|
43
44
|
@name = name
|
44
45
|
|
@@ -53,6 +54,7 @@ module ElasticAPM
|
|
53
54
|
@transaction = transaction
|
54
55
|
@parent = parent
|
55
56
|
@trace_context = trace_context || parent.trace_context.child
|
57
|
+
@sample_rate = transaction.sample_rate
|
56
58
|
|
57
59
|
@context = context || Span::Context.new(sync: sync)
|
58
60
|
@stacktrace_builder = stacktrace_builder
|
@@ -73,6 +75,7 @@ module ElasticAPM
|
|
73
75
|
:context,
|
74
76
|
:duration,
|
75
77
|
:parent,
|
78
|
+
:sample_rate,
|
76
79
|
:self_time,
|
77
80
|
:stacktrace,
|
78
81
|
:timestamp,
|
@@ -97,11 +100,12 @@ module ElasticAPM
|
|
97
100
|
|
98
101
|
def done(clock_end: Util.monotonic_micros)
|
99
102
|
stop clock_end
|
103
|
+
self
|
104
|
+
end
|
100
105
|
|
106
|
+
def prepare_for_serialization!
|
101
107
|
build_stacktrace! if should_build_stacktrace?
|
102
108
|
self.original_backtrace = nil # release original
|
103
|
-
|
104
|
-
self
|
105
109
|
end
|
106
110
|
|
107
111
|
def stopped?
|
data/lib/elastic_apm/spies.rb
CHANGED
@@ -80,23 +80,25 @@ module ElasticAPM
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
unless ENV['ELASTIC_APM_SKIP_REQUIRE_PATCH'] == '1'
|
84
|
+
# @api private
|
85
|
+
module Kernel
|
86
|
+
private
|
86
87
|
|
87
|
-
|
88
|
+
alias require_without_apm require
|
88
89
|
|
89
|
-
|
90
|
-
|
90
|
+
def require(path)
|
91
|
+
res = require_without_apm(path)
|
91
92
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
93
|
+
begin
|
94
|
+
ElasticAPM::Spies.hook_into(path)
|
95
|
+
rescue ::Exception => e
|
96
|
+
puts "Failed hooking into '#{path}'. Please report this at " \
|
97
|
+
'github.com/elastic/apm-agent-ruby'
|
98
|
+
puts e.backtrace.join("\n")
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
+
res
|
102
|
+
end
|
101
103
|
end
|
102
104
|
end
|
@@ -41,10 +41,10 @@ module ElasticAPM
|
|
41
41
|
job_name = name_from_payload(job.payload_object)
|
42
42
|
transaction = ElasticAPM.start_transaction(job_name, TYPE)
|
43
43
|
job.invoke_job_without_apm(*args, &block)
|
44
|
-
transaction
|
44
|
+
transaction&.done 'success'
|
45
45
|
rescue ::Exception => e
|
46
46
|
ElasticAPM.report(e, handled: false)
|
47
|
-
transaction
|
47
|
+
transaction&.done 'error'
|
48
48
|
raise
|
49
49
|
ensure
|
50
50
|
ElasticAPM.end_transaction
|
@@ -53,6 +53,8 @@ module ElasticAPM
|
|
53
53
|
def self.name_from_payload(payload_object)
|
54
54
|
if payload_object.is_a?(::Delayed::PerformableMethod)
|
55
55
|
performable_method_name(payload_object)
|
56
|
+
elsif payload_object.class.name == 'ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper'
|
57
|
+
payload_object.job_data['job_class']
|
56
58
|
else
|
57
59
|
payload_object.class.name
|
58
60
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
module ElasticAPM
|
21
|
+
# @api private
|
22
|
+
module Spies
|
23
|
+
# @api private
|
24
|
+
class DynamoDBSpy
|
25
|
+
def self.without_net_http
|
26
|
+
return yield unless defined?(NetHTTPSpy)
|
27
|
+
|
28
|
+
ElasticAPM::Spies::NetHTTPSpy.disable_in do
|
29
|
+
yield
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def install
|
34
|
+
::Aws::DynamoDB::Client.class_eval do
|
35
|
+
# Alias all available operations
|
36
|
+
api.operation_names.each do |operation_name|
|
37
|
+
alias :"#{operation_name}_without_apm" :"#{operation_name}"
|
38
|
+
|
39
|
+
define_method(operation_name) do |params = {}, options = {}|
|
40
|
+
ElasticAPM.with_span(operation_name, 'db', subtype: 'dynamodb', action: operation_name) do
|
41
|
+
ElasticAPM::Spies::DynamoDBSpy.without_net_http do
|
42
|
+
original_method = method("#{operation_name}_without_apm")
|
43
|
+
original_method.call(params, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
register(
|
53
|
+
'Aws::DynamoDB::Client',
|
54
|
+
'aws-sdk-dynamodb',
|
55
|
+
DynamoDBSpy.new
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|