elastic-apm 3.10.1 → 3.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|