elastic-apm 3.10.1 → 3.13.0
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/.github/labeler-config.yml +3 -0
- data/.github/workflows/labeler.yml +16 -0
- data/.rubocop.yml +33 -4
- data/CHANGELOG.asciidoc +67 -0
- data/Gemfile +5 -6
- data/Rakefile +10 -10
- data/docs/api.asciidoc +2 -1
- data/docs/configuration.asciidoc +4 -3
- data/lib/elastic_apm.rb +14 -2
- data/lib/elastic_apm/central_config.rb +7 -2
- data/lib/elastic_apm/config.rb +51 -19
- data/lib/elastic_apm/config/log_level_map.rb +47 -0
- 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 +2 -0
- data/lib/elastic_apm/context.rb +2 -8
- data/lib/elastic_apm/graphql.rb +2 -0
- data/lib/elastic_apm/grpc.rb +3 -3
- data/lib/elastic_apm/instrumenter.rb +11 -4
- data/lib/elastic_apm/metadata.rb +3 -1
- data/lib/elastic_apm/metadata/cloud_info.rb +130 -0
- data/lib/elastic_apm/metadata/service_info.rb +2 -2
- data/lib/elastic_apm/metadata/system_info/container_info.rb +16 -5
- data/lib/elastic_apm/metrics.rb +1 -0
- data/lib/elastic_apm/metrics/cpu_mem_set.rb +1 -0
- data/lib/elastic_apm/middleware.rb +8 -3
- data/lib/elastic_apm/opentracing.rb +2 -1
- data/lib/elastic_apm/span.rb +14 -0
- data/lib/elastic_apm/span/context/db.rb +1 -1
- data/lib/elastic_apm/spies/delayed_job.rb +8 -2
- data/lib/elastic_apm/spies/dynamo_db.rb +8 -1
- data/lib/elastic_apm/spies/elasticsearch.rb +10 -2
- data/lib/elastic_apm/spies/faraday.rb +5 -2
- data/lib/elastic_apm/spies/http.rb +1 -0
- data/lib/elastic_apm/spies/mongo.rb +6 -2
- data/lib/elastic_apm/spies/net_http.rb +3 -0
- data/lib/elastic_apm/spies/rake.rb +4 -2
- data/lib/elastic_apm/spies/resque.rb +4 -2
- data/lib/elastic_apm/spies/sequel.rb +12 -1
- data/lib/elastic_apm/spies/shoryuken.rb +2 -0
- data/lib/elastic_apm/spies/sidekiq.rb +2 -0
- data/lib/elastic_apm/spies/sneakers.rb +2 -0
- data/lib/elastic_apm/spies/sucker_punch.rb +2 -0
- data/lib/elastic_apm/sql/signature.rb +4 -2
- data/lib/elastic_apm/sql/tokenizer.rb +2 -2
- data/lib/elastic_apm/stacktrace/frame.rb +1 -0
- data/lib/elastic_apm/stacktrace_builder.rb +2 -4
- data/lib/elastic_apm/trace_context.rb +1 -3
- data/lib/elastic_apm/trace_context/traceparent.rb +2 -6
- data/lib/elastic_apm/trace_context/tracestate.rb +114 -9
- data/lib/elastic_apm/transaction.rb +41 -7
- data/lib/elastic_apm/transport/connection.rb +2 -1
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +16 -16
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +35 -12
- data/lib/elastic_apm/transport/serializers.rb +9 -6
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +44 -4
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -3
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +2 -0
- data/lib/elastic_apm/transport/user_agent.rb +3 -3
- data/lib/elastic_apm/transport/worker.rb +1 -0
- data/lib/elastic_apm/util.rb +2 -0
- data/lib/elastic_apm/util/deep_dup.rb +65 -0
- data/lib/elastic_apm/util/precision_validator.rb +46 -0
- data/lib/elastic_apm/version.rb +1 -1
- metadata +10 -3
@@ -0,0 +1,47 @@
|
|
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
|
+
class Config
|
22
|
+
# @api private
|
23
|
+
class LogLevelMap
|
24
|
+
LEVELS = {
|
25
|
+
debug: Logger::DEBUG,
|
26
|
+
info: Logger::INFO,
|
27
|
+
warn: Logger::WARN,
|
28
|
+
error: Logger::ERROR,
|
29
|
+
fatal: Logger::FATAL,
|
30
|
+
trace: Logger::DEBUG,
|
31
|
+
warning: Logger::WARN,
|
32
|
+
critical: Logger::FATAL,
|
33
|
+
off: Logger::FATAL
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
DEFAULT = Logger::INFO
|
37
|
+
|
38
|
+
def call(value)
|
39
|
+
if value.is_a?(Integer)
|
40
|
+
LEVELS.value?(value) ? value : DEFAULT
|
41
|
+
else
|
42
|
+
LEVELS.fetch(value.to_sym, DEFAULT)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
+
require 'elastic_apm/util/precision_validator'
|
21
|
+
|
22
|
+
module ElasticAPM
|
23
|
+
class Config
|
24
|
+
# @api private
|
25
|
+
class RoundFloat
|
26
|
+
def call(value)
|
27
|
+
Util::PrecisionValidator.validate(value, precision: 4, minimum: 0.0001)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/elastic_apm/context.rb
CHANGED
@@ -36,14 +36,9 @@ module ElasticAPM
|
|
36
36
|
Service = Struct.new(:framework)
|
37
37
|
Framework = Struct.new(:name, :version)
|
38
38
|
|
39
|
-
attr_accessor :request
|
40
|
-
|
41
|
-
attr_accessor :user
|
42
|
-
attr_reader :custom
|
43
|
-
attr_reader :labels
|
44
|
-
attr_reader :service
|
39
|
+
attr_accessor :request, :response, :user
|
40
|
+
attr_reader :custom, :labels, :service
|
45
41
|
|
46
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
47
42
|
def empty?
|
48
43
|
return false if labels.any?
|
49
44
|
return false if custom.any?
|
@@ -53,7 +48,6 @@ module ElasticAPM
|
|
53
48
|
|
54
49
|
true
|
55
50
|
end
|
56
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
57
51
|
|
58
52
|
def set_service(framework_name: nil, framework_version: nil)
|
59
53
|
@service = Service.new(
|
data/lib/elastic_apm/graphql.rb
CHANGED
@@ -48,6 +48,7 @@ module ElasticAPM
|
|
48
48
|
# "authorized" => "graphql.authorized",
|
49
49
|
}.freeze
|
50
50
|
|
51
|
+
# rubocop:disable Style/ExplicitBlockArgument
|
51
52
|
def self.trace(key, data)
|
52
53
|
return yield unless KEYS_TO_NAME.key?(key)
|
53
54
|
return yield unless (transaction = ElasticAPM.current_transaction)
|
@@ -69,6 +70,7 @@ module ElasticAPM
|
|
69
70
|
|
70
71
|
results
|
71
72
|
end
|
73
|
+
# rubocop:enable Style/ExplicitBlockArgument
|
72
74
|
|
73
75
|
class << self
|
74
76
|
private
|
data/lib/elastic_apm/grpc.rb
CHANGED
@@ -25,7 +25,7 @@ module ElasticAPM
|
|
25
25
|
TYPE = 'external'
|
26
26
|
SUBTYPE = 'grpc'
|
27
27
|
|
28
|
-
# rubocop:disable Lint/UnusedMethodArgument
|
28
|
+
# rubocop:disable Lint/UnusedMethodArgument, Style/ExplicitBlockArgument
|
29
29
|
def request_response(request:, call:, method:, metadata:)
|
30
30
|
return yield unless (transaction = ElasticAPM.current_transaction)
|
31
31
|
if (trace_context = transaction.trace_context)
|
@@ -40,7 +40,7 @@ module ElasticAPM
|
|
40
40
|
yield
|
41
41
|
end
|
42
42
|
end
|
43
|
-
# rubocop:enable Lint/UnusedMethodArgument
|
43
|
+
# rubocop:enable Lint/UnusedMethodArgument, Style/ExplicitBlockArgument
|
44
44
|
|
45
45
|
private
|
46
46
|
|
@@ -71,7 +71,7 @@ module ElasticAPM
|
|
71
71
|
transaction.done 'success'
|
72
72
|
rescue ::Exception => e
|
73
73
|
ElasticAPM.report(e, handled: false)
|
74
|
-
transaction
|
74
|
+
transaction&.done 'error'
|
75
75
|
raise
|
76
76
|
ensure
|
77
77
|
ElasticAPM.end_transaction
|
@@ -119,7 +119,13 @@ module ElasticAPM
|
|
119
119
|
"Already inside #{transaction.inspect}"
|
120
120
|
end
|
121
121
|
|
122
|
-
|
122
|
+
if trace_context
|
123
|
+
sampled = trace_context.recorded?
|
124
|
+
sample_rate = trace_context.tracestate.sample_rate
|
125
|
+
else
|
126
|
+
sampled = random_sample?(config)
|
127
|
+
sample_rate = config.transaction_sample_rate
|
128
|
+
end
|
123
129
|
|
124
130
|
transaction =
|
125
131
|
Transaction.new(
|
@@ -128,6 +134,7 @@ module ElasticAPM
|
|
128
134
|
context: context,
|
129
135
|
trace_context: trace_context,
|
130
136
|
sampled: sampled,
|
137
|
+
sample_rate: sample_rate,
|
131
138
|
config: config
|
132
139
|
)
|
133
140
|
|
@@ -232,7 +239,7 @@ module ElasticAPM
|
|
232
239
|
def set_label(key, value)
|
233
240
|
return unless current_transaction
|
234
241
|
|
235
|
-
key = key.to_s.gsub(/[
|
242
|
+
key = key.to_s.gsub(/[."*]/, '_').to_sym
|
236
243
|
current_transaction.context.labels[key] = value
|
237
244
|
end
|
238
245
|
|
@@ -259,7 +266,7 @@ module ElasticAPM
|
|
259
266
|
end
|
260
267
|
|
261
268
|
def update_transaction_metrics(transaction)
|
262
|
-
return unless transaction.collect_metrics
|
269
|
+
return unless transaction.collect_metrics?
|
263
270
|
|
264
271
|
tags = {
|
265
272
|
'transaction.name': transaction.name,
|
@@ -298,7 +305,7 @@ module ElasticAPM
|
|
298
305
|
end
|
299
306
|
|
300
307
|
def update_span_metrics(span)
|
301
|
-
return unless span.transaction.
|
308
|
+
return unless span.transaction.collect_metrics?
|
302
309
|
|
303
310
|
tags = {
|
304
311
|
'span.type': span.type,
|
data/lib/elastic_apm/metadata.rb
CHANGED
@@ -25,12 +25,14 @@ module ElasticAPM
|
|
25
25
|
@process = ProcessInfo.new(config)
|
26
26
|
@system = SystemInfo.new(config)
|
27
27
|
@labels = config.global_labels
|
28
|
+
@cloud = CloudInfo.new(config).fetch!
|
28
29
|
end
|
29
30
|
|
30
|
-
attr_reader :service, :process, :system, :labels
|
31
|
+
attr_reader :service, :process, :system, :cloud, :labels
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
require 'elastic_apm/metadata/service_info'
|
35
36
|
require 'elastic_apm/metadata/system_info'
|
36
37
|
require 'elastic_apm/metadata/process_info'
|
38
|
+
require 'elastic_apm/metadata/cloud_info'
|
@@ -0,0 +1,130 @@
|
|
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
|
+
require "http"
|
21
|
+
|
22
|
+
module ElasticAPM
|
23
|
+
class Metadata
|
24
|
+
# @api private
|
25
|
+
class CloudInfo
|
26
|
+
include Logging
|
27
|
+
|
28
|
+
AWS_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document"
|
29
|
+
GCP_URI = "http://metadata.google.internal/computeMetadata/v1/?recursive=true"
|
30
|
+
AZURE_URI = "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15"
|
31
|
+
|
32
|
+
def initialize(config)
|
33
|
+
@config = config
|
34
|
+
@client = HTTP.timeout(connect: 0.1, read: 0.1)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :config
|
38
|
+
|
39
|
+
attr_accessor(
|
40
|
+
:account_id,
|
41
|
+
:account_name,
|
42
|
+
:instance_id,
|
43
|
+
:instance_name,
|
44
|
+
:machine_type,
|
45
|
+
:project_id,
|
46
|
+
:project_name,
|
47
|
+
:availability_zone,
|
48
|
+
:provider,
|
49
|
+
:region
|
50
|
+
)
|
51
|
+
|
52
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
53
|
+
def fetch!
|
54
|
+
case config.cloud_provider
|
55
|
+
when "aws"
|
56
|
+
fetch_aws
|
57
|
+
when "gcp"
|
58
|
+
fetch_gcp
|
59
|
+
when "azure"
|
60
|
+
fetch_azure
|
61
|
+
when "auto"
|
62
|
+
fetch_aws || fetch_gcp || fetch_azure
|
63
|
+
when "none"
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
error("Unknown setting for cloud_provider '#{config.cloud_provider}'")
|
67
|
+
end
|
68
|
+
|
69
|
+
self
|
70
|
+
end
|
71
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def fetch_aws
|
76
|
+
resp = @client.get(AWS_URI)
|
77
|
+
|
78
|
+
return unless resp.status == 200
|
79
|
+
return unless (metadata = JSON.parse(resp.body))
|
80
|
+
|
81
|
+
self.provider = "aws"
|
82
|
+
self.account_id = metadata["accountId"]
|
83
|
+
self.instance_id = metadata["instanceId"]
|
84
|
+
self.availability_zone = metadata["availabilityZone"]
|
85
|
+
self.machine_type = metadata["instanceType"]
|
86
|
+
self.region = metadata["region"]
|
87
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch_gcp
|
92
|
+
resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
|
93
|
+
|
94
|
+
return unless resp.status == 200
|
95
|
+
return unless (metadata = JSON.parse(resp.body))
|
96
|
+
|
97
|
+
zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
|
98
|
+
|
99
|
+
self.provider = "gcp"
|
100
|
+
self.instance_id = metadata["instance"]["id"]
|
101
|
+
self.instance_name = metadata["instance"]["name"]
|
102
|
+
self.project_id = metadata["project"]["numericProjectId"]
|
103
|
+
self.project_name = metadata["project"]["projectId"]
|
104
|
+
self.availability_zone = zone
|
105
|
+
self.region = zone.split("-")[0..-2].join("-")
|
106
|
+
self.machine_type = metadata["instance"]["machineType"]
|
107
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def fetch_azure
|
112
|
+
resp = @client.headers("Metadata" => "true").get(AZURE_URI)
|
113
|
+
|
114
|
+
return unless resp.status == 200
|
115
|
+
return unless (metadata = JSON.parse(resp.body))
|
116
|
+
|
117
|
+
self.provider = 'azure'
|
118
|
+
self.account_id = metadata["subscriptionId"]
|
119
|
+
self.instance_id = metadata["vmId"]
|
120
|
+
self.instance_name = metadata["name"]
|
121
|
+
self.project_name = metadata["resourceGroupName"]
|
122
|
+
self.availability_zone = metadata["zone"]
|
123
|
+
self.machine_type = metadata["vmSize"]
|
124
|
+
self.region = metadata["location"]
|
125
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -52,8 +52,8 @@ module ElasticAPM
|
|
52
52
|
@version = @config.service_version || Util.git_sha
|
53
53
|
end
|
54
54
|
|
55
|
-
attr_reader :name, :node_name, :environment, :agent, :framework,
|
56
|
-
:runtime, :version
|
55
|
+
attr_reader :name, :node_name, :environment, :agent, :framework,
|
56
|
+
:language, :runtime, :version
|
57
57
|
|
58
58
|
private
|
59
59
|
|
@@ -81,15 +81,23 @@ module ElasticAPM
|
|
81
81
|
ENV.fetch('KUBERNETES_POD_UID', kubernetes_pod_uid)
|
82
82
|
end
|
83
83
|
|
84
|
+
# rubocop:disable Style/RegexpLiteral
|
84
85
|
CONTAINER_ID_REGEXES = [
|
85
86
|
%r{^[[:xdigit:]]{64}$},
|
86
|
-
%r{
|
87
|
-
|
87
|
+
%r{
|
88
|
+
^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]
|
89
|
+
{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4,}$
|
90
|
+
}x
|
91
|
+
].freeze
|
88
92
|
KUBEPODS_REGEXES = [
|
89
93
|
%r{(?:^/kubepods[^\s]*/pod([^/]+)$)},
|
90
|
-
%r{
|
91
|
-
|
94
|
+
%r{
|
95
|
+
(?:^/kubepods\.slice/(kubepods-[^/]+\.slice/)?
|
96
|
+
kubepods[^/]*-pod([^/]+)\.slice$)
|
97
|
+
}x
|
98
|
+
].freeze
|
92
99
|
SYSTEMD_SCOPE_SUFFIX = '.scope'
|
100
|
+
# rubocop:enable Style/RegexpLiteral
|
93
101
|
|
94
102
|
# rubocop:disable Metrics/PerceivedComplexity
|
95
103
|
# rubocop:disable Metrics/CyclomaticComplexity
|
@@ -125,7 +133,10 @@ module ElasticAPM
|
|
125
133
|
end
|
126
134
|
|
127
135
|
if (kubepods_match = match_kubepods(directory))
|
128
|
-
pod_id = kubepods_match[1]
|
136
|
+
unless (pod_id = kubepods_match[1])
|
137
|
+
pod_id = kubepods_match[2]
|
138
|
+
pod_id&.tr!('_', '-')
|
139
|
+
end
|
129
140
|
|
130
141
|
self.container_id = container_id
|
131
142
|
self.kubernetes_pod_uid = pod_id
|
data/lib/elastic_apm/metrics.rb
CHANGED
@@ -41,9 +41,14 @@ module ElasticAPM
|
|
41
41
|
ElasticAPM.report(e, context: context, handled: false)
|
42
42
|
raise
|
43
43
|
ensure
|
44
|
-
if
|
45
|
-
|
46
|
-
|
44
|
+
if transaction
|
45
|
+
if resp
|
46
|
+
status, headers, _body = resp
|
47
|
+
transaction.add_response(status, headers: headers.dup)
|
48
|
+
transaction&.outcome = Transaction::Outcome.from_http_status(status)
|
49
|
+
else
|
50
|
+
transaction&.outcome = Transaction::Outcome::FAILURE
|
51
|
+
end
|
47
52
|
end
|
48
53
|
|
49
54
|
ElasticAPM.end_transaction http_result(status)
|