elastic-apm 2.9.1 → 2.10.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 +9 -19
- data/.ci/.jenkins_framework.yml +1 -4
- data/.ci/.jenkins_master_framework.yml +3 -0
- data/.ci/Jenkinsfile +43 -118
- data/.ci/downstreamTests.groovy +59 -30
- data/.ci/jobs/apm-agent-ruby-downstream.yml +31 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +34 -0
- data/.ci/jobs/defaults.yml +1 -36
- data/.pre-commit-config.yaml +22 -0
- data/.rspec +0 -1
- data/.rubocop.yml +3 -3
- data/CHANGELOG.md +12 -0
- data/docs/api.asciidoc +2 -2
- data/docs/configuration.asciidoc +37 -1
- data/docs/metrics.asciidoc +77 -6
- data/lib/elastic_apm.rb +2 -2
- data/lib/elastic_apm/agent.rb +11 -2
- 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 +152 -338
- 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/metadata.rb +2 -1
- data/lib/elastic_apm/metrics.rb +2 -1
- data/lib/elastic_apm/metrics/vm.rb +57 -0
- data/lib/elastic_apm/normalizers/action_view.rb +1 -1
- data/lib/elastic_apm/railtie.rb +10 -5
- data/lib/elastic_apm/spies/mongo.rb +13 -2
- data/lib/elastic_apm/stacktrace_builder.rb +2 -2
- data/lib/elastic_apm/transport/connection.rb +2 -0
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +6 -1
- data/lib/elastic_apm/version.rb +1 -1
- metadata +11 -3
- 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
|
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,57 @@
|
|
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
|
+
@total_time = 0
|
11
|
+
@disabled = false
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :config
|
15
|
+
attr_writer :disabled
|
16
|
+
|
17
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
18
|
+
def collect
|
19
|
+
return if disabled?
|
20
|
+
|
21
|
+
stat = GC.stat
|
22
|
+
thread_count = Thread.list.count
|
23
|
+
|
24
|
+
sample = {
|
25
|
+
'ruby.gc.count': stat[:count],
|
26
|
+
'ruby.threads': thread_count
|
27
|
+
}
|
28
|
+
|
29
|
+
(live_slots = stat[:heap_live_slots]) &&
|
30
|
+
sample[:'ruby.heap.slots.live'] = live_slots
|
31
|
+
(heap_slots = stat[:heap_free_slots]) &&
|
32
|
+
sample[:'ruby.heap.slots.free'] = heap_slots
|
33
|
+
(allocated = stat[:total_allocated_objects]) &&
|
34
|
+
sample[:'ruby.heap.allocations.total'] = allocated
|
35
|
+
|
36
|
+
return sample unless GC::Profiler.enabled?
|
37
|
+
|
38
|
+
@total_time += GC::Profiler.total_time
|
39
|
+
GC::Profiler.clear
|
40
|
+
sample[:'ruby.gc.time'] = @total_time
|
41
|
+
|
42
|
+
sample
|
43
|
+
rescue TypeError => e
|
44
|
+
error 'VM metrics encountered error: %s', e
|
45
|
+
debug('Backtrace:') { e.backtrace.join("\n") }
|
46
|
+
|
47
|
+
@disabled = true
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
51
|
+
|
52
|
+
def disabled?
|
53
|
+
@disabled
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/elastic_apm/railtie.rb
CHANGED
@@ -7,10 +7,15 @@ module ElasticAPM
|
|
7
7
|
class Railtie < Rails::Railtie
|
8
8
|
config.elastic_apm = ActiveSupport::OrderedOptions.new
|
9
9
|
|
10
|
-
Config
|
10
|
+
Config.schema.each do |key, args|
|
11
|
+
next unless args.length > 1
|
12
|
+
config.elastic_apm[key] = args.last[:default]
|
13
|
+
end
|
11
14
|
|
12
15
|
initializer 'elastic_apm.initialize' do |app|
|
13
|
-
config = Config.new(app.config.elastic_apm
|
16
|
+
config = Config.new(app.config.elastic_apm).tap do |c|
|
17
|
+
c.app = app
|
18
|
+
|
14
19
|
# Prepend Rails.root to log_path if present
|
15
20
|
if c.log_path && !c.log_path.start_with?('/')
|
16
21
|
c.log_path = Rails.root.join(c.log_path)
|
@@ -35,7 +40,7 @@ module ElasticAPM
|
|
35
40
|
def start(config)
|
36
41
|
if (reason = should_skip?(config))
|
37
42
|
unless config.disable_start_message?
|
38
|
-
config.
|
43
|
+
config.logger.info "Skipping because: #{reason}. " \
|
39
44
|
"Start manually with `ElasticAPM.start'"
|
40
45
|
end
|
41
46
|
return
|
@@ -45,8 +50,8 @@ module ElasticAPM
|
|
45
50
|
attach_subscriber(agent)
|
46
51
|
end
|
47
52
|
rescue StandardError => e
|
48
|
-
config.
|
49
|
-
config.
|
53
|
+
config.logger.error format('Failed to start: %s', e.message)
|
54
|
+
config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
|
50
55
|
end
|
51
56
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
52
57
|
|
@@ -34,18 +34,29 @@ module ElasticAPM
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
+
# rubocop:disable Metrics/MethodLength
|
37
38
|
def push_event(event)
|
38
39
|
return unless ElasticAPM.current_transaction
|
40
|
+
# Some MongoDB commands are not on collections but rather are db
|
41
|
+
# admin commands. For these commands, the value at the `command_name`
|
42
|
+
# key is the integer 1.
|
43
|
+
unless event.command[event.command_name] == 1
|
44
|
+
collection = event.command[event.command_name]
|
45
|
+
end
|
46
|
+
name = [event.database_name,
|
47
|
+
collection,
|
48
|
+
event.command_name].compact.join('.')
|
39
49
|
|
40
50
|
span =
|
41
51
|
ElasticAPM.start_span(
|
42
|
-
|
52
|
+
name,
|
43
53
|
TYPE,
|
44
54
|
context: build_context(event)
|
45
55
|
)
|
46
56
|
|
47
57
|
@events[event.operation_id] = span
|
48
58
|
end
|
59
|
+
# rubocop:enable Metrics/MethodLength
|
49
60
|
|
50
61
|
def pop_event(event)
|
51
62
|
return unless (curr = ElasticAPM.current_span)
|
@@ -58,7 +69,7 @@ module ElasticAPM
|
|
58
69
|
Span::Context.new(
|
59
70
|
db: {
|
60
71
|
instance: event.database_name,
|
61
|
-
statement:
|
72
|
+
statement: event.command.to_s,
|
62
73
|
type: 'mongodb',
|
63
74
|
user: nil
|
64
75
|
}
|
@@ -67,8 +67,8 @@ module ElasticAPM
|
|
67
67
|
def library_frame?(config, abs_path)
|
68
68
|
return false unless abs_path
|
69
69
|
|
70
|
-
if abs_path.start_with?(config.
|
71
|
-
return true if abs_path.start_with?(config.
|
70
|
+
if abs_path.start_with?(config.__root_path)
|
71
|
+
return true if abs_path.start_with?(config.__root_path + '/vendor')
|
72
72
|
return false
|
73
73
|
end
|
74
74
|
|
@@ -7,6 +7,7 @@ require 'elastic_apm/transport/connection/http'
|
|
7
7
|
|
8
8
|
module ElasticAPM
|
9
9
|
module Transport
|
10
|
+
# rubocop:disable Metrics/ClassLength
|
10
11
|
# @api private
|
11
12
|
class Connection
|
12
13
|
include Logging
|
@@ -150,5 +151,6 @@ module ElasticAPM
|
|
150
151
|
end
|
151
152
|
end
|
152
153
|
end
|
154
|
+
# rubocop:enable Metrics/ClassLength
|
153
155
|
end
|
154
156
|
end
|
@@ -10,7 +10,8 @@ module ElasticAPM
|
|
10
10
|
metadata: {
|
11
11
|
service: build_service(metadata.service),
|
12
12
|
process: build_process(metadata.process),
|
13
|
-
system: build_system(metadata.system)
|
13
|
+
system: build_system(metadata.system),
|
14
|
+
labels: build_labels(metadata.labels)
|
14
15
|
}
|
15
16
|
}
|
16
17
|
end
|
@@ -59,6 +60,10 @@ module ElasticAPM
|
|
59
60
|
kubernetes: keyword_object(system.kubernetes)
|
60
61
|
}
|
61
62
|
end
|
63
|
+
|
64
|
+
def build_labels(labels)
|
65
|
+
keyword_object(labels)
|
66
|
+
end
|
62
67
|
end
|
63
68
|
end
|
64
69
|
end
|
data/lib/elastic_apm/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic-apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Malmberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -45,8 +45,10 @@ executables: []
|
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
+
- ".ci/.jenkins_codecov.yml"
|
48
49
|
- ".ci/.jenkins_exclude.yml"
|
49
50
|
- ".ci/.jenkins_framework.yml"
|
51
|
+
- ".ci/.jenkins_master_framework.yml"
|
50
52
|
- ".ci/.jenkins_ruby.yml"
|
51
53
|
- ".ci/Jenkinsfile"
|
52
54
|
- ".ci/bin/check_paths_for_matches.py"
|
@@ -57,6 +59,7 @@ files:
|
|
57
59
|
- ".ci/jobs/defaults.yml"
|
58
60
|
- ".gitignore"
|
59
61
|
- ".hound.yml"
|
62
|
+
- ".pre-commit-config.yaml"
|
60
63
|
- ".rspec"
|
61
64
|
- ".rubocop.yml"
|
62
65
|
- CHANGELOG.md
|
@@ -95,9 +98,13 @@ files:
|
|
95
98
|
- lib/elastic-apm.rb
|
96
99
|
- lib/elastic_apm.rb
|
97
100
|
- lib/elastic_apm/agent.rb
|
101
|
+
- lib/elastic_apm/central_config.rb
|
102
|
+
- lib/elastic_apm/central_config/cache_control.rb
|
98
103
|
- lib/elastic_apm/config.rb
|
104
|
+
- lib/elastic_apm/config/bytes.rb
|
99
105
|
- lib/elastic_apm/config/duration.rb
|
100
|
-
- lib/elastic_apm/config/
|
106
|
+
- lib/elastic_apm/config/options.rb
|
107
|
+
- lib/elastic_apm/config/regexp_list.rb
|
101
108
|
- lib/elastic_apm/context.rb
|
102
109
|
- lib/elastic_apm/context/request.rb
|
103
110
|
- lib/elastic_apm/context/request/socket.rb
|
@@ -120,6 +127,7 @@ files:
|
|
120
127
|
- lib/elastic_apm/metadata/system_info/container_info.rb
|
121
128
|
- lib/elastic_apm/metrics.rb
|
122
129
|
- lib/elastic_apm/metrics/cpu_mem.rb
|
130
|
+
- lib/elastic_apm/metrics/vm.rb
|
123
131
|
- lib/elastic_apm/metricset.rb
|
124
132
|
- lib/elastic_apm/middleware.rb
|
125
133
|
- lib/elastic_apm/naively_hashable.rb
|