elastic-apm 2.8.1 → 2.11.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/.ci/.jenkins_codecov.yml +5 -0
- data/.ci/.jenkins_exclude.yml +63 -0
- data/.ci/.jenkins_framework.yml +9 -0
- data/.ci/.jenkins_master_framework.yml +3 -0
- data/.ci/.jenkins_ruby.yml +11 -0
- data/.ci/Jenkinsfile +268 -0
- data/.ci/bin/check_paths_for_matches.py +80 -0
- data/.ci/downstreamTests.groovy +188 -0
- data/.ci/jobs/apm-agent-ruby-downstream.yml +37 -0
- data/.ci/jobs/apm-agent-ruby-linting-mbp.yml +38 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +41 -0
- data/.ci/jobs/apm-agent-ruby.yml +4 -0
- data/.ci/jobs/defaults.yml +24 -0
- data/.ci/linting.groovy +32 -0
- data/.ci/prepare-git-context.sh +23 -0
- data/.pre-commit-config.yaml +22 -0
- data/.rspec +0 -1
- data/.rubocop.yml +3 -3
- data/CHANGELOG.md +59 -2
- data/docs/api.asciidoc +24 -7
- data/docs/configuration.asciidoc +43 -4
- data/docs/index.asciidoc +2 -0
- data/docs/log-correlation.asciidoc +96 -0
- data/docs/metrics.asciidoc +77 -6
- data/lib/elastic_apm.rb +37 -5
- data/lib/elastic_apm/agent.rb +29 -4
- data/lib/elastic_apm/central_config.rb +141 -0
- data/lib/elastic_apm/central_config/cache_control.rb +34 -0
- data/lib/elastic_apm/config.rb +165 -340
- data/lib/elastic_apm/config/bytes.rb +25 -0
- data/lib/elastic_apm/config/duration.rb +6 -8
- data/lib/elastic_apm/config/options.rb +134 -0
- data/lib/elastic_apm/config/regexp_list.rb +13 -0
- data/lib/elastic_apm/context_builder.rb +2 -0
- data/lib/elastic_apm/error/exception.rb +3 -1
- data/lib/elastic_apm/error_builder.rb +6 -3
- data/lib/elastic_apm/instrumenter.rb +6 -0
- data/lib/elastic_apm/metadata.rb +2 -1
- data/lib/elastic_apm/metrics.rb +2 -1
- data/lib/elastic_apm/metrics/vm.rb +60 -0
- data/lib/elastic_apm/normalizers/action_controller.rb +5 -2
- data/lib/elastic_apm/normalizers/action_mailer.rb +5 -2
- data/lib/elastic_apm/normalizers/action_view.rb +14 -9
- data/lib/elastic_apm/normalizers/active_record.rb +5 -2
- data/lib/elastic_apm/rails.rb +59 -0
- data/lib/elastic_apm/railtie.rb +11 -48
- data/lib/elastic_apm/span.rb +29 -7
- data/lib/elastic_apm/spies/faraday.rb +9 -2
- data/lib/elastic_apm/spies/http.rb +9 -2
- data/lib/elastic_apm/spies/mongo.rb +18 -3
- data/lib/elastic_apm/spies/net_http.rb +8 -2
- data/lib/elastic_apm/stacktrace/frame.rb +3 -1
- data/lib/elastic_apm/stacktrace_builder.rb +2 -2
- data/lib/elastic_apm/subscriber.rb +11 -3
- data/lib/elastic_apm/transport/connection.rb +17 -3
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +8 -1
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +3 -1
- data/lib/elastic_apm/transport/serializers/error_serializer.rb +12 -2
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +6 -1
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +11 -3
- data/lib/elastic_apm/version.rb +1 -1
- metadata +26 -4
- data/Jenkinsfile +0 -280
- data/lib/elastic_apm/config/size.rb +0 -28
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Config
|
5
|
+
# @api private
|
6
|
+
class Bytes
|
7
|
+
MULTIPLIERS = {
|
8
|
+
'kb' => 1024,
|
9
|
+
'mb' => 1024 * 1_000,
|
10
|
+
'gb' => 1024 * 100_000
|
11
|
+
}.freeze
|
12
|
+
REGEX = /^(\d+)(b|kb|mb|gb)?$/i.freeze
|
13
|
+
|
14
|
+
def initialize(default_unit: 'kb')
|
15
|
+
@default_unit = default_unit
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(value)
|
19
|
+
_, amount, unit = REGEX.match(String(value)).to_a
|
20
|
+
unit ||= @default_unit
|
21
|
+
MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -7,18 +7,16 @@ module ElasticAPM
|
|
7
7
|
MULTIPLIERS = { 'ms' => 0.001, 'm' => 60 }.freeze
|
8
8
|
REGEX = /^(-)?(\d+)(m|ms|s)?$/i.freeze
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(default_unit: 's')
|
11
|
+
@default_unit = default_unit
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
_, negative, amount, unit = REGEX.match(str).to_a
|
18
|
-
unit ||= default_unit
|
14
|
+
def call(str)
|
15
|
+
_, negative, amount, unit = REGEX.match(String(str)).to_a
|
16
|
+
unit ||= @default_unit
|
19
17
|
seconds = MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i
|
20
18
|
seconds = 0 - seconds if negative
|
21
|
-
|
19
|
+
seconds
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Config
|
5
|
+
# @api private
|
6
|
+
module Options
|
7
|
+
# @api private
|
8
|
+
class Option
|
9
|
+
def initialize(
|
10
|
+
key,
|
11
|
+
value: nil,
|
12
|
+
type: :string,
|
13
|
+
default: nil,
|
14
|
+
converter: nil
|
15
|
+
)
|
16
|
+
@key = key
|
17
|
+
@type = type
|
18
|
+
@default = default
|
19
|
+
@converter = converter
|
20
|
+
|
21
|
+
set(value || default)
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :key, :value, :default, :type
|
25
|
+
|
26
|
+
def set(value)
|
27
|
+
@value = normalize(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def env_key
|
31
|
+
"ELASTIC_APM_#{key.upcase}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
37
|
+
def normalize(val)
|
38
|
+
return unless val
|
39
|
+
|
40
|
+
if @converter
|
41
|
+
return @converter.call(val)
|
42
|
+
end
|
43
|
+
|
44
|
+
case type
|
45
|
+
when :string then val.to_s
|
46
|
+
when :int then val.to_i
|
47
|
+
when :float then val.to_f
|
48
|
+
when :bool then normalize_bool(val)
|
49
|
+
when :list then normalize_list(val)
|
50
|
+
when :dict then normalize_dict(val)
|
51
|
+
else
|
52
|
+
# raise "Unknown options type '#{type.inspect}'"
|
53
|
+
val
|
54
|
+
end
|
55
|
+
end
|
56
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
57
|
+
|
58
|
+
def normalize_bool(val)
|
59
|
+
return val unless val.is_a?(String)
|
60
|
+
!%w[0 false].include?(val.strip.downcase)
|
61
|
+
end
|
62
|
+
|
63
|
+
def normalize_list(val)
|
64
|
+
return Array(val) unless val.is_a?(String)
|
65
|
+
val.split(/[ ,]/)
|
66
|
+
end
|
67
|
+
|
68
|
+
def normalize_dict(val)
|
69
|
+
return val unless val.is_a?(String)
|
70
|
+
Hash[val.split(/[&,]/).map { |kv| kv.split('=') }]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
module ClassMethods
|
76
|
+
def schema
|
77
|
+
@schema ||= {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def option(*args)
|
81
|
+
key = args.shift
|
82
|
+
schema[key] = *args
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
module InstanceMethods
|
88
|
+
def load_schema
|
89
|
+
Hash[self.class.schema.map do |key, args|
|
90
|
+
[key, Option.new(key, *args)]
|
91
|
+
end]
|
92
|
+
end
|
93
|
+
|
94
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
95
|
+
def method_missing(name, *value)
|
96
|
+
name_str = name.to_s
|
97
|
+
|
98
|
+
if name_str.end_with?('=')
|
99
|
+
key = name_str[0...-1].to_sym
|
100
|
+
set(key, value.first)
|
101
|
+
|
102
|
+
elsif name_str.end_with?('?')
|
103
|
+
key = name_str[0...-1].to_sym
|
104
|
+
options.key?(key) ? options[key].value : super
|
105
|
+
|
106
|
+
elsif options.key?(name)
|
107
|
+
options.fetch(name).value
|
108
|
+
|
109
|
+
else
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
114
|
+
|
115
|
+
def [](key)
|
116
|
+
options[key]
|
117
|
+
end
|
118
|
+
alias :get :[]
|
119
|
+
|
120
|
+
def set(key, value)
|
121
|
+
options.fetch(key.to_sym).set(value)
|
122
|
+
rescue KeyError
|
123
|
+
warn format("Unknown option '%s'", key)
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.extended(kls)
|
129
|
+
kls.instance_eval { extend ClassMethods }
|
130
|
+
kls.class_eval { include InstanceMethods }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -11,6 +11,7 @@ module ElasticAPM
|
|
11
11
|
"#{exception.class}: #{exception.message}"
|
12
12
|
@type = exception.class.to_s
|
13
13
|
@module = format_module exception
|
14
|
+
@cause = exception.cause && Exception.new(exception.cause)
|
14
15
|
|
15
16
|
attrs.each do |key, val|
|
16
17
|
send(:"#{key}=", val)
|
@@ -24,7 +25,8 @@ module ElasticAPM
|
|
24
25
|
:message,
|
25
26
|
:module,
|
26
27
|
:stacktrace,
|
27
|
-
:type
|
28
|
+
:type,
|
29
|
+
:cause
|
28
30
|
)
|
29
31
|
|
30
32
|
private
|
@@ -52,12 +52,15 @@ module ElasticAPM
|
|
52
52
|
error.culprit = stacktrace.frames.first.function
|
53
53
|
end
|
54
54
|
|
55
|
-
# rubocop:disable Metrics/AbcSize
|
55
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
56
56
|
def add_current_transaction_fields(error, transaction)
|
57
57
|
return unless transaction
|
58
58
|
|
59
59
|
error.transaction_id = transaction.id
|
60
|
-
error.transaction = {
|
60
|
+
error.transaction = {
|
61
|
+
sampled: transaction.sampled?,
|
62
|
+
type: transaction.type
|
63
|
+
}
|
61
64
|
error.trace_id = transaction.trace_id
|
62
65
|
error.parent_id = ElasticAPM.current_span&.id || transaction.id
|
63
66
|
|
@@ -66,6 +69,6 @@ module ElasticAPM
|
|
66
69
|
Util.reverse_merge!(error.context.tags, transaction.context.tags)
|
67
70
|
Util.reverse_merge!(error.context.custom, transaction.context.custom)
|
68
71
|
end
|
69
|
-
# rubocop:enable Metrics/AbcSize
|
72
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
70
73
|
end
|
71
74
|
end
|
@@ -134,9 +134,12 @@ module ElasticAPM
|
|
134
134
|
|
135
135
|
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
136
136
|
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
137
|
+
# rubocop:disable Metrics/ParameterLists
|
137
138
|
def start_span(
|
138
139
|
name,
|
139
140
|
type = nil,
|
141
|
+
subtype: nil,
|
142
|
+
action: nil,
|
140
143
|
backtrace: nil,
|
141
144
|
context: nil,
|
142
145
|
trace_context: nil
|
@@ -155,6 +158,8 @@ module ElasticAPM
|
|
155
158
|
|
156
159
|
span = Span.new(
|
157
160
|
name: name,
|
161
|
+
subtype: subtype,
|
162
|
+
action: action,
|
158
163
|
transaction_id: transaction.id,
|
159
164
|
trace_context: trace_context || parent.trace_context.child,
|
160
165
|
type: type,
|
@@ -170,6 +175,7 @@ module ElasticAPM
|
|
170
175
|
|
171
176
|
span.start
|
172
177
|
end
|
178
|
+
# rubocop:enable Metrics/ParameterLists
|
173
179
|
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
|
174
180
|
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
175
181
|
|
data/lib/elastic_apm/metadata.rb
CHANGED
@@ -7,9 +7,10 @@ module ElasticAPM
|
|
7
7
|
@service = ServiceInfo.new(config)
|
8
8
|
@process = ProcessInfo.new(config)
|
9
9
|
@system = SystemInfo.new(config)
|
10
|
+
@labels = config.global_labels
|
10
11
|
end
|
11
12
|
|
12
|
-
attr_reader :service, :process, :system
|
13
|
+
attr_reader :service, :process, :system, :labels
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
data/lib/elastic_apm/metrics.rb
CHANGED
@@ -24,7 +24,7 @@ module ElasticAPM
|
|
24
24
|
def initialize(config, tags: nil, &block)
|
25
25
|
@config = config
|
26
26
|
@tags = tags
|
27
|
-
@samplers = [CpuMem].map do |kls|
|
27
|
+
@samplers = [CpuMem, VM].map do |kls|
|
28
28
|
debug "Adding metrics collector '#{kls}'"
|
29
29
|
kls.new(config)
|
30
30
|
end
|
@@ -95,3 +95,4 @@ module ElasticAPM
|
|
95
95
|
end
|
96
96
|
|
97
97
|
require 'elastic_apm/metrics/cpu_mem'
|
98
|
+
require 'elastic_apm/metrics/vm'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
module Metrics
|
5
|
+
# @api private
|
6
|
+
class VM
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
@total_time = 0
|
12
|
+
@disabled = false
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :config
|
16
|
+
attr_writer :disabled
|
17
|
+
|
18
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
19
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
20
|
+
def collect
|
21
|
+
return if disabled?
|
22
|
+
|
23
|
+
stat = GC.stat
|
24
|
+
thread_count = Thread.list.count
|
25
|
+
|
26
|
+
sample = {
|
27
|
+
'ruby.gc.count': stat[:count],
|
28
|
+
'ruby.threads': thread_count
|
29
|
+
}
|
30
|
+
|
31
|
+
(live_slots = stat[:heap_live_slots]) &&
|
32
|
+
sample[:'ruby.heap.slots.live'] = live_slots
|
33
|
+
(heap_slots = stat[:heap_free_slots]) &&
|
34
|
+
sample[:'ruby.heap.slots.free'] = heap_slots
|
35
|
+
(allocated = stat[:total_allocated_objects]) &&
|
36
|
+
sample[:'ruby.heap.allocations.total'] = allocated
|
37
|
+
|
38
|
+
return sample unless GC::Profiler.enabled?
|
39
|
+
|
40
|
+
@total_time += GC::Profiler.total_time
|
41
|
+
GC::Profiler.clear
|
42
|
+
sample[:'ruby.gc.time'] = @total_time
|
43
|
+
|
44
|
+
sample
|
45
|
+
rescue TypeError => e
|
46
|
+
error 'VM metrics encountered error: %s', e
|
47
|
+
debug('Backtrace:') { e.backtrace.join("\n") }
|
48
|
+
|
49
|
+
@disabled = true
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
53
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
54
|
+
|
55
|
+
def disabled?
|
56
|
+
@disabled
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -6,11 +6,14 @@ module ElasticAPM
|
|
6
6
|
# @api private
|
7
7
|
class ProcessActionNormalizer < Normalizer
|
8
8
|
register 'process_action.action_controller'
|
9
|
-
|
9
|
+
|
10
|
+
TYPE = 'app'
|
11
|
+
SUBTYPE = 'controller'
|
12
|
+
ACTION = 'action'
|
10
13
|
|
11
14
|
def normalize(transaction, _name, payload)
|
12
15
|
transaction.name = endpoint(payload)
|
13
|
-
[transaction.name, TYPE, nil]
|
16
|
+
[transaction.name, TYPE, SUBTYPE, ACTION, nil]
|
14
17
|
end
|
15
18
|
|
16
19
|
private
|
@@ -6,10 +6,13 @@ module ElasticAPM
|
|
6
6
|
# @api private
|
7
7
|
class ProcessActionNormalizer < Normalizer
|
8
8
|
register 'process.action_mailer'
|
9
|
-
|
9
|
+
|
10
|
+
TYPE = 'app'
|
11
|
+
SUBTYPE = 'mailer'
|
12
|
+
ACTION = 'action'
|
10
13
|
|
11
14
|
def normalize(_transaction, _name, payload)
|
12
|
-
[endpoint(payload), TYPE, nil]
|
15
|
+
[endpoint(payload), TYPE, SUBTYPE, ACTION, nil]
|
13
16
|
end
|
14
17
|
|
15
18
|
private
|
@@ -7,8 +7,8 @@ module ElasticAPM
|
|
7
7
|
class RenderNormalizer < Normalizer
|
8
8
|
private
|
9
9
|
|
10
|
-
def normalize_render(payload, type)
|
11
|
-
[path_for(payload[:identifier]), type, nil]
|
10
|
+
def normalize_render(payload, type, subtype, action)
|
11
|
+
[path_for(payload[:identifier]), type, subtype, action, nil]
|
12
12
|
end
|
13
13
|
|
14
14
|
def path_for(path)
|
@@ -19,7 +19,7 @@ module ElasticAPM
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def view_path(path)
|
22
|
-
root = @config.
|
22
|
+
root = @config.__view_paths.find { |vp| path.start_with?(vp) }
|
23
23
|
return unless root
|
24
24
|
|
25
25
|
strip_root(root, path)
|
@@ -41,30 +41,35 @@ module ElasticAPM
|
|
41
41
|
# @api private
|
42
42
|
class RenderTemplateNormalizer < RenderNormalizer
|
43
43
|
register 'render_template.action_view'
|
44
|
-
TYPE = 'template
|
44
|
+
TYPE = 'template'
|
45
|
+
SUBTYPE = 'view'
|
45
46
|
|
46
47
|
def normalize(_transaction, _name, payload)
|
47
|
-
normalize_render(payload, TYPE)
|
48
|
+
normalize_render(payload, TYPE, SUBTYPE, nil)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
52
|
# @api private
|
52
53
|
class RenderPartialNormalizer < RenderNormalizer
|
53
54
|
register 'render_partial.action_view'
|
54
|
-
TYPE = 'template
|
55
|
+
TYPE = 'template'
|
56
|
+
SUBTYPE = 'view'
|
57
|
+
ACTION = 'partial'
|
55
58
|
|
56
59
|
def normalize(_transaction, _name, payload)
|
57
|
-
normalize_render(payload, TYPE)
|
60
|
+
normalize_render(payload, TYPE, SUBTYPE, ACTION)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
61
64
|
# @api private
|
62
65
|
class RenderCollectionNormalizer < RenderNormalizer
|
63
66
|
register 'render_collection.action_view'
|
64
|
-
TYPE = 'template
|
67
|
+
TYPE = 'template'
|
68
|
+
SUBTYPE = 'view'
|
69
|
+
ACTION = 'collection'
|
65
70
|
|
66
71
|
def normalize(_transaction, _name, payload)
|
67
|
-
normalize_render(payload, TYPE)
|
72
|
+
normalize_render(payload, TYPE, SUBTYPE, ACTION)
|
68
73
|
end
|
69
74
|
end
|
70
75
|
end
|