elastic-apm 3.11.0 → 3.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ci/.jenkins_codecov.yml +1 -1
- data/.ci/.jenkins_exclude.yml +46 -61
- data/.ci/.jenkins_framework.yml +3 -4
- data/.ci/.jenkins_master_framework.yml +1 -1
- data/.ci/.jenkins_ruby.yml +1 -3
- data/.ci/Jenkinsfile +22 -2
- data/.ci/docker/jruby/11-jdk/Dockerfile +2 -1
- data/.ci/docker/jruby/12-jdk/Dockerfile +2 -1
- data/.ci/docker/jruby/13-jdk/Dockerfile +2 -1
- data/.ci/docker/jruby/7-jdk/Dockerfile +2 -1
- data/.ci/docker/jruby/8-jdk/Dockerfile +2 -1
- data/.github/labeler-config.yml +3 -0
- data/.github/workflows/addToProject.yml +29 -0
- data/.github/workflows/labeler.yml +16 -0
- data/.rubocop.yml +33 -4
- data/CHANGELOG.asciidoc +58 -0
- data/Gemfile +23 -9
- data/Rakefile +10 -10
- data/docs/api.asciidoc +2 -1
- data/docs/configuration.asciidoc +20 -3
- data/docs/supported-technologies.asciidoc +2 -0
- data/lib/elastic_apm.rb +14 -2
- data/lib/elastic_apm/central_config.rb +7 -2
- data/lib/elastic_apm/config.rb +35 -18
- data/lib/elastic_apm/config/log_level_map.rb +47 -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 +1 -1
- data/lib/elastic_apm/metadata/cloud_info.rb +6 -4
- 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/normalizers/rails/active_record.rb +16 -4
- data/lib/elastic_apm/opentracing.rb +2 -1
- data/lib/elastic_apm/span.rb +13 -2
- data/lib/elastic_apm/span/context/db.rb +1 -1
- data/lib/elastic_apm/span/context/http.rb +2 -0
- data/lib/elastic_apm/spies/delayed_job.rb +11 -3
- data/lib/elastic_apm/spies/dynamo_db.rb +8 -1
- data/lib/elastic_apm/spies/elasticsearch.rb +4 -2
- data/lib/elastic_apm/spies/faraday.rb +19 -11
- data/lib/elastic_apm/spies/http.rb +1 -0
- data/lib/elastic_apm/spies/mongo.rb +10 -2
- data/lib/elastic_apm/spies/net_http.rb +4 -1
- 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 +0 -2
- data/lib/elastic_apm/trace_context/traceparent.rb +0 -2
- data/lib/elastic_apm/trace_context/tracestate.rb +7 -5
- data/lib/elastic_apm/transaction.rb +17 -4
- data/lib/elastic_apm/transport/connection.rb +1 -1
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +8 -1
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +32 -10
- data/lib/elastic_apm/transport/serializers.rb +1 -0
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +23 -8
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -3
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
- data/lib/elastic_apm/util/deep_dup.rb +65 -0
- data/lib/elastic_apm/util/precision_validator.rb +1 -1
- data/lib/elastic_apm/version.rb +1 -1
- metadata +7 -2
@@ -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
|
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
|
@@ -31,7 +31,7 @@ module ElasticAPM
|
|
31
31
|
|
32
32
|
def initialize(config)
|
33
33
|
@config = config
|
34
|
-
@client = HTTP.timeout(0.1)
|
34
|
+
@client = HTTP.timeout(connect: 0.1, read: 0.1)
|
35
35
|
end
|
36
36
|
|
37
37
|
attr_reader :config
|
@@ -49,6 +49,7 @@ module ElasticAPM
|
|
49
49
|
:region
|
50
50
|
)
|
51
51
|
|
52
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
52
53
|
def fetch!
|
53
54
|
case config.cloud_provider
|
54
55
|
when "aws"
|
@@ -67,13 +68,14 @@ module ElasticAPM
|
|
67
68
|
|
68
69
|
self
|
69
70
|
end
|
71
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
70
72
|
|
71
73
|
private
|
72
74
|
|
73
75
|
def fetch_aws
|
74
76
|
resp = @client.get(AWS_URI)
|
75
77
|
|
76
|
-
return unless resp.status
|
78
|
+
return unless resp.status == 200
|
77
79
|
return unless (metadata = JSON.parse(resp.body))
|
78
80
|
|
79
81
|
self.provider = "aws"
|
@@ -89,7 +91,7 @@ module ElasticAPM
|
|
89
91
|
def fetch_gcp
|
90
92
|
resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
|
91
93
|
|
92
|
-
return unless resp.status
|
94
|
+
return unless resp.status == 200
|
93
95
|
return unless (metadata = JSON.parse(resp.body))
|
94
96
|
|
95
97
|
zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
|
@@ -109,7 +111,7 @@ module ElasticAPM
|
|
109
111
|
def fetch_azure
|
110
112
|
resp = @client.headers("Metadata" => "true").get(AZURE_URI)
|
111
113
|
|
112
|
-
return unless resp.status
|
114
|
+
return unless resp.status == 200
|
113
115
|
return unless (metadata = JSON.parse(resp.body))
|
114
116
|
|
115
117
|
self.provider = 'azure'
|
@@ -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)
|
@@ -57,10 +57,17 @@ module ElasticAPM
|
|
57
57
|
private
|
58
58
|
|
59
59
|
def subtype_for(payload)
|
60
|
-
cached_adapter_name(
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
return cached_adapter_name(payload[:connection].adapter_name) if payload[:connection]
|
61
|
+
|
62
|
+
if can_attempt_connection_id_lookup?(payload)
|
63
|
+
begin
|
64
|
+
loaded_object = ObjectSpace._id2ref(payload[:connection_id])
|
65
|
+
return cached_adapter_name(loaded_object.adapter_name) if loaded_object.respond_to?(:adapter_name)
|
66
|
+
rescue RangeError # if connection object has somehow been garbage collected
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
cached_adapter_name(::ActiveRecord::Base.connection_config[:adapter])
|
64
71
|
end
|
65
72
|
|
66
73
|
def summarize(sql)
|
@@ -69,11 +76,16 @@ module ElasticAPM
|
|
69
76
|
|
70
77
|
def cached_adapter_name(adapter_name)
|
71
78
|
return UNKNOWN if adapter_name.nil? || adapter_name.empty?
|
79
|
+
|
72
80
|
@adapters[adapter_name] ||
|
73
81
|
(@adapters[adapter_name] = adapter_name.downcase)
|
74
82
|
rescue StandardError
|
75
83
|
nil
|
76
84
|
end
|
85
|
+
|
86
|
+
def can_attempt_connection_id_lookup?(payload)
|
87
|
+
RUBY_ENGINE == "ruby" && payload[:connection_id] && ObjectSpace.respond_to?(:_id2ref)
|
88
|
+
end
|
77
89
|
end
|
78
90
|
end
|
79
91
|
end
|
@@ -123,6 +123,7 @@ module ElasticAPM
|
|
123
123
|
end
|
124
124
|
|
125
125
|
attr_accessor :trace_context
|
126
|
+
|
126
127
|
def_delegators :trace_context, :trace_id, :id, :parent_id
|
127
128
|
|
128
129
|
def self.from_header(header)
|
@@ -345,7 +346,7 @@ module ElasticAPM
|
|
345
346
|
context = context_from_child_of(child_of) ||
|
346
347
|
context_from_references(references) ||
|
347
348
|
context_from_active_scope(ignore_active_scope)
|
348
|
-
return context.child if context
|
349
|
+
return context.child if context.respond_to?(:child)
|
349
350
|
|
350
351
|
context
|
351
352
|
end
|
data/lib/elastic_apm/span.rb
CHANGED
@@ -25,6 +25,17 @@ module ElasticAPM
|
|
25
25
|
extend Forwardable
|
26
26
|
include ChildDurations::Methods
|
27
27
|
|
28
|
+
# @api private
|
29
|
+
class Outcome
|
30
|
+
FAILURE = "failure"
|
31
|
+
SUCCESS = "success"
|
32
|
+
UNKNOWN = "unknown"
|
33
|
+
|
34
|
+
def self.from_http_status(code)
|
35
|
+
code.to_i >= 400 ? FAILURE : SUCCESS
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
28
39
|
DEFAULT_TYPE = 'custom'
|
29
40
|
|
30
41
|
# rubocop:disable Metrics/ParameterLists
|
@@ -38,8 +49,7 @@ module ElasticAPM
|
|
38
49
|
action: nil,
|
39
50
|
context: nil,
|
40
51
|
stacktrace_builder: nil,
|
41
|
-
sync: nil
|
42
|
-
sample_rate: nil
|
52
|
+
sync: nil
|
43
53
|
)
|
44
54
|
@name = name
|
45
55
|
|
@@ -67,6 +77,7 @@ module ElasticAPM
|
|
67
77
|
:action,
|
68
78
|
:name,
|
69
79
|
:original_backtrace,
|
80
|
+
:outcome,
|
70
81
|
:subtype,
|
71
82
|
:trace_context,
|
72
83
|
:type
|
@@ -38,26 +38,34 @@ module ElasticAPM
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def self.invoke_job(job, *args, &block)
|
41
|
-
job_name =
|
41
|
+
job_name = job_name(job)
|
42
42
|
transaction = ElasticAPM.start_transaction(job_name, TYPE)
|
43
43
|
job.invoke_job_without_apm(*args, &block)
|
44
44
|
transaction&.done 'success'
|
45
|
+
transaction&.outcome = Transaction::Outcome::SUCCESS
|
45
46
|
rescue ::Exception => e
|
46
47
|
ElasticAPM.report(e, handled: false)
|
47
48
|
transaction&.done 'error'
|
49
|
+
transaction&.outcome = Transaction::Outcome::FAILURE
|
48
50
|
raise
|
49
51
|
ensure
|
50
52
|
ElasticAPM.end_transaction
|
51
53
|
end
|
52
54
|
|
53
|
-
def self.
|
55
|
+
def self.job_name(job)
|
56
|
+
payload_object = job.payload_object
|
57
|
+
|
54
58
|
if payload_object.is_a?(::Delayed::PerformableMethod)
|
55
59
|
performable_method_name(payload_object)
|
56
|
-
elsif payload_object.
|
60
|
+
elsif payload_object.instance_of?(
|
61
|
+
ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper
|
62
|
+
)
|
57
63
|
payload_object.job_data['job_class']
|
58
64
|
else
|
59
65
|
payload_object.class.name
|
60
66
|
end
|
67
|
+
rescue
|
68
|
+
job.name
|
61
69
|
end
|
62
70
|
|
63
71
|
def self.performable_method_name(payload_object)
|
@@ -25,9 +25,11 @@ module ElasticAPM
|
|
25
25
|
def self.without_net_http
|
26
26
|
return yield unless defined?(NetHTTPSpy)
|
27
27
|
|
28
|
+
# rubocop:disable Style/ExplicitBlockArgument
|
28
29
|
ElasticAPM::Spies::NetHTTPSpy.disable_in do
|
29
30
|
yield
|
30
31
|
end
|
32
|
+
# rubocop:enable Style/ExplicitBlockArgument
|
31
33
|
end
|
32
34
|
|
33
35
|
def install
|
@@ -37,7 +39,12 @@ module ElasticAPM
|
|
37
39
|
alias :"#{operation_name}_without_apm" :"#{operation_name}"
|
38
40
|
|
39
41
|
define_method(operation_name) do |params = {}, options = {}|
|
40
|
-
ElasticAPM.with_span(
|
42
|
+
ElasticAPM.with_span(
|
43
|
+
operation_name,
|
44
|
+
'db',
|
45
|
+
subtype: 'dynamodb',
|
46
|
+
action: operation_name
|
47
|
+
) do
|
41
48
|
ElasticAPM::Spies::DynamoDBSpy.without_net_http do
|
42
49
|
original_method = method("#{operation_name}_without_apm")
|
43
50
|
original_method.call(params, options)
|