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
@@ -9,10 +9,13 @@ module ElasticAPM
|
|
9
9
|
class SqlNormalizer < Normalizer
|
10
10
|
register 'sql.active_record'
|
11
11
|
|
12
|
+
TYPE = 'db'
|
13
|
+
ACTION = 'sql'
|
14
|
+
|
12
15
|
def initialize(*args)
|
13
16
|
super
|
14
17
|
|
15
|
-
@
|
18
|
+
@subtype = lookup_adapter || 'unknown'
|
16
19
|
@summarizer = SqlSummarizer.new
|
17
20
|
end
|
18
21
|
|
@@ -22,7 +25,7 @@ module ElasticAPM
|
|
22
25
|
name = summarize(payload[:sql]) || payload[:name]
|
23
26
|
context =
|
24
27
|
Span::Context.new(db: { statement: payload[:sql], type: 'sql' })
|
25
|
-
[name, @
|
28
|
+
[name, TYPE, @subtype, ACTION, context]
|
26
29
|
end
|
27
30
|
|
28
31
|
private
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_apm/subscriber'
|
4
|
+
|
5
|
+
module ElasticAPM
|
6
|
+
# Module for explicitly starting the ElasticAPM agent and hooking into Rails.
|
7
|
+
# It is recommended to use the Railtie instead.
|
8
|
+
module Rails
|
9
|
+
extend self
|
10
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
11
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
12
|
+
# Start the ElasticAPM agent and hook into Rails.
|
13
|
+
# Note that the agent won't be started if the Rails console is being used.
|
14
|
+
#
|
15
|
+
# @param config [Config, Hash] An instance of Config or a Hash config.
|
16
|
+
# @return [true, nil] true if the agent was started, nil otherwise.
|
17
|
+
def start(config)
|
18
|
+
config = Config.new(config) unless config.is_a?(Config)
|
19
|
+
if (reason = should_skip?(config))
|
20
|
+
unless config.disable_start_message?
|
21
|
+
config.logger.info "Skipping because: #{reason}. " \
|
22
|
+
"Start manually with `ElasticAPM.start'"
|
23
|
+
end
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
ElasticAPM.start(config).tap do |agent|
|
28
|
+
attach_subscriber(agent)
|
29
|
+
end
|
30
|
+
|
31
|
+
if ElasticAPM.running? &&
|
32
|
+
!ElasticAPM.agent.config.disabled_spies.include?('action_dispatch')
|
33
|
+
require 'elastic_apm/spies/action_dispatch'
|
34
|
+
end
|
35
|
+
ElasticAPM.running?
|
36
|
+
rescue StandardError => e
|
37
|
+
config.logger.error format('Failed to start: %s', e.message)
|
38
|
+
config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
|
39
|
+
end
|
40
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
41
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def should_skip?(_config)
|
46
|
+
if ::Rails.const_defined? 'Rails::Console'
|
47
|
+
return 'Rails console'
|
48
|
+
end
|
49
|
+
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def attach_subscriber(agent)
|
54
|
+
return unless agent
|
55
|
+
|
56
|
+
agent.instrumenter.subscriber = ElasticAPM::Subscriber.new(agent)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/elastic_apm/railtie.rb
CHANGED
@@ -1,67 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'elastic_apm/
|
3
|
+
require 'elastic_apm/rails'
|
4
4
|
|
5
5
|
module ElasticAPM
|
6
6
|
# @api private
|
7
|
-
class Railtie < Rails::Railtie
|
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
|
-
c.log_path = Rails.root.join(c.log_path)
|
21
|
+
c.log_path = ::Rails.root.join(c.log_path)
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
20
|
-
if start(config)
|
25
|
+
if Rails.start(config)
|
21
26
|
app.middleware.insert 0, Middleware
|
22
27
|
end
|
23
28
|
end
|
24
|
-
|
25
|
-
config.after_initialize do
|
26
|
-
if ElasticAPM.running? &&
|
27
|
-
!ElasticAPM.agent.config.disabled_spies.include?('action_dispatch')
|
28
|
-
require 'elastic_apm/spies/action_dispatch'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
35
|
-
def start(config)
|
36
|
-
if (reason = should_skip?(config))
|
37
|
-
unless config.disable_start_message?
|
38
|
-
config.alert_logger.info "Skipping because: #{reason}. " \
|
39
|
-
"Start manually with `ElasticAPM.start'"
|
40
|
-
end
|
41
|
-
return
|
42
|
-
end
|
43
|
-
|
44
|
-
ElasticAPM.start(config).tap do |agent|
|
45
|
-
attach_subscriber(agent)
|
46
|
-
end
|
47
|
-
rescue StandardError => e
|
48
|
-
config.alert_logger.error format('Failed to start: %s', e.message)
|
49
|
-
config.alert_logger.debug "Backtrace:\n" + e.backtrace.join("\n")
|
50
|
-
end
|
51
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
52
|
-
|
53
|
-
def should_skip?(_config)
|
54
|
-
if Rails.const_defined? 'Rails::Console'
|
55
|
-
return 'Rails console'
|
56
|
-
end
|
57
|
-
|
58
|
-
nil
|
59
|
-
end
|
60
|
-
|
61
|
-
def attach_subscriber(agent)
|
62
|
-
return unless agent
|
63
|
-
|
64
|
-
agent.instrumenter.subscriber = ElasticAPM::Subscriber.new(agent)
|
65
|
-
end
|
66
29
|
end
|
67
30
|
end
|
data/lib/elastic_apm/span.rb
CHANGED
@@ -14,17 +14,26 @@ module ElasticAPM
|
|
14
14
|
|
15
15
|
DEFAULT_TYPE = 'custom'
|
16
16
|
|
17
|
-
# rubocop:disable Metrics/ParameterLists
|
17
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
18
18
|
def initialize(
|
19
19
|
name:,
|
20
20
|
transaction_id:,
|
21
21
|
trace_context:,
|
22
22
|
type: nil,
|
23
|
+
subtype: nil,
|
24
|
+
action: nil,
|
23
25
|
context: nil,
|
24
26
|
stacktrace_builder: nil
|
25
27
|
)
|
26
28
|
@name = name
|
27
|
-
|
29
|
+
|
30
|
+
if subtype.nil? && type&.include?('.')
|
31
|
+
@type, @subtype, @action = type.split('.')
|
32
|
+
else
|
33
|
+
@type = type || DEFAULT_TYPE
|
34
|
+
@subtype = subtype
|
35
|
+
@action = action
|
36
|
+
end
|
28
37
|
|
29
38
|
@transaction_id = transaction_id
|
30
39
|
@trace_context = trace_context
|
@@ -32,10 +41,23 @@ module ElasticAPM
|
|
32
41
|
@context = context || Span::Context.new
|
33
42
|
@stacktrace_builder = stacktrace_builder
|
34
43
|
end
|
35
|
-
# rubocop:enable Metrics/ParameterLists
|
36
|
-
|
37
|
-
attr_accessor
|
38
|
-
|
44
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
|
45
|
+
|
46
|
+
attr_accessor(
|
47
|
+
:action,
|
48
|
+
:name,
|
49
|
+
:original_backtrace,
|
50
|
+
:subtype,
|
51
|
+
:trace_context,
|
52
|
+
:type
|
53
|
+
)
|
54
|
+
attr_reader(
|
55
|
+
:context,
|
56
|
+
:duration,
|
57
|
+
:stacktrace,
|
58
|
+
:timestamp,
|
59
|
+
:transaction_id
|
60
|
+
)
|
39
61
|
|
40
62
|
# life cycle
|
41
63
|
|
@@ -53,6 +75,7 @@ module ElasticAPM
|
|
53
75
|
stop end_time
|
54
76
|
|
55
77
|
build_stacktrace! if should_build_stacktrace?
|
78
|
+
self.original_backtrace = nil # release original
|
56
79
|
|
57
80
|
self
|
58
81
|
end
|
@@ -82,7 +105,6 @@ module ElasticAPM
|
|
82
105
|
|
83
106
|
def build_stacktrace!
|
84
107
|
@stacktrace = @stacktrace_builder.build(original_backtrace, type: :span)
|
85
|
-
self.original_backtrace = nil # release original
|
86
108
|
end
|
87
109
|
|
88
110
|
def should_build_stacktrace?
|
@@ -5,6 +5,9 @@ module ElasticAPM
|
|
5
5
|
module Spies
|
6
6
|
# @api private
|
7
7
|
class FaradaySpy
|
8
|
+
TYPE = 'ext'
|
9
|
+
SUBTYPE = 'faraday'
|
10
|
+
|
8
11
|
def self.without_net_http
|
9
12
|
return yield unless defined?(NetHTTPSpy)
|
10
13
|
|
@@ -37,9 +40,13 @@ module ElasticAPM
|
|
37
40
|
end
|
38
41
|
|
39
42
|
name = "#{method.upcase} #{host}"
|
40
|
-
type = "ext.faraday.#{method}"
|
41
43
|
|
42
|
-
ElasticAPM.with_span
|
44
|
+
ElasticAPM.with_span(
|
45
|
+
name,
|
46
|
+
TYPE,
|
47
|
+
subtype: SUBTYPE,
|
48
|
+
action: method.to_s
|
49
|
+
) do |span|
|
43
50
|
ElasticAPM::Spies::FaradaySpy.without_net_http do
|
44
51
|
trace_context = span&.trace_context || transaction.trace_context
|
45
52
|
|
@@ -5,6 +5,9 @@ module ElasticAPM
|
|
5
5
|
module Spies
|
6
6
|
# @api private
|
7
7
|
class HTTPSpy
|
8
|
+
TYPE = 'ext'
|
9
|
+
SUBTYPE = 'http_rb'
|
10
|
+
|
8
11
|
# rubocop:disable Metrics/MethodLength
|
9
12
|
def install
|
10
13
|
::HTTP::Client.class_eval do
|
@@ -19,9 +22,13 @@ module ElasticAPM
|
|
19
22
|
host = req.uri.host
|
20
23
|
|
21
24
|
name = "#{method} #{host}"
|
22
|
-
type = "ext.http_rb.#{method}"
|
23
25
|
|
24
|
-
ElasticAPM.with_span
|
26
|
+
ElasticAPM.with_span(
|
27
|
+
name,
|
28
|
+
TYPE,
|
29
|
+
subtype: SUBTYPE,
|
30
|
+
action: method
|
31
|
+
) do |span|
|
25
32
|
trace_context = span&.trace_context || transaction.trace_context
|
26
33
|
req['Elastic-Apm-Traceparent'] = trace_context.to_header
|
27
34
|
perform_without_apm(req, options)
|
@@ -14,7 +14,9 @@ module ElasticAPM
|
|
14
14
|
|
15
15
|
# @api private
|
16
16
|
class Subscriber
|
17
|
-
TYPE = 'db
|
17
|
+
TYPE = 'db'
|
18
|
+
SUBTYPE = 'mongodb'
|
19
|
+
ACTION = 'query'
|
18
20
|
|
19
21
|
def initialize
|
20
22
|
@events = {}
|
@@ -34,18 +36,31 @@ module ElasticAPM
|
|
34
36
|
|
35
37
|
private
|
36
38
|
|
39
|
+
# rubocop:disable Metrics/MethodLength
|
37
40
|
def push_event(event)
|
38
41
|
return unless ElasticAPM.current_transaction
|
42
|
+
# Some MongoDB commands are not on collections but rather are db
|
43
|
+
# admin commands. For these commands, the value at the `command_name`
|
44
|
+
# key is the integer 1.
|
45
|
+
unless event.command[event.command_name] == 1
|
46
|
+
collection = event.command[event.command_name]
|
47
|
+
end
|
48
|
+
name = [event.database_name,
|
49
|
+
collection,
|
50
|
+
event.command_name].compact.join('.')
|
39
51
|
|
40
52
|
span =
|
41
53
|
ElasticAPM.start_span(
|
42
|
-
|
54
|
+
name,
|
43
55
|
TYPE,
|
56
|
+
subtype: SUBTYPE,
|
57
|
+
action: ACTION,
|
44
58
|
context: build_context(event)
|
45
59
|
)
|
46
60
|
|
47
61
|
@events[event.operation_id] = span
|
48
62
|
end
|
63
|
+
# rubocop:enable Metrics/MethodLength
|
49
64
|
|
50
65
|
def pop_event(event)
|
51
66
|
return unless (curr = ElasticAPM.current_span)
|
@@ -58,7 +73,7 @@ module ElasticAPM
|
|
58
73
|
Span::Context.new(
|
59
74
|
db: {
|
60
75
|
instance: event.database_name,
|
61
|
-
statement:
|
76
|
+
statement: event.command.to_s,
|
62
77
|
type: 'mongodb',
|
63
78
|
user: nil
|
64
79
|
}
|
@@ -6,6 +6,8 @@ module ElasticAPM
|
|
6
6
|
# @api private
|
7
7
|
class NetHTTPSpy
|
8
8
|
KEY = :__elastic_apm_net_http_disabled
|
9
|
+
TYPE = 'ext'
|
10
|
+
SUBTYPE = 'net_http'
|
9
11
|
|
10
12
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
11
13
|
class << self
|
@@ -46,9 +48,13 @@ module ElasticAPM
|
|
46
48
|
host ||= address
|
47
49
|
|
48
50
|
name = "#{method} #{host}"
|
49
|
-
type = "ext.net_http.#{method}"
|
50
51
|
|
51
|
-
ElasticAPM.with_span
|
52
|
+
ElasticAPM.with_span(
|
53
|
+
name,
|
54
|
+
TYPE,
|
55
|
+
subtype: SUBTYPE,
|
56
|
+
action: method.to_s
|
57
|
+
) do |span|
|
52
58
|
trace_context = span&.trace_context || transaction.trace_context
|
53
59
|
req['Elastic-Apm-Traceparent'] = trace_context.to_header
|
54
60
|
request_without_apm(req, body, &block)
|
@@ -32,6 +32,8 @@ module ElasticAPM
|
|
32
32
|
to = lineno + padding - 1
|
33
33
|
file_lines = read_lines(abs_path, from..to)
|
34
34
|
|
35
|
+
return unless file_lines
|
36
|
+
|
35
37
|
self.context_line = file_lines[padding]
|
36
38
|
self.pre_context = file_lines.first(padding)
|
37
39
|
self.post_context = file_lines.last(padding)
|
@@ -43,7 +45,7 @@ module ElasticAPM
|
|
43
45
|
def read_lines(path, range)
|
44
46
|
File.readlines(path)[range]
|
45
47
|
rescue Errno::ENOENT
|
46
|
-
|
48
|
+
nil
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -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
|
|
@@ -29,8 +29,8 @@ module ElasticAPM
|
|
29
29
|
|
30
30
|
Notification = Struct.new(:id, :span)
|
31
31
|
|
32
|
+
# rubocop:disable Metrics/MethodLength
|
32
33
|
def start(name, id, payload)
|
33
|
-
# debug "AS::Notification#start:#{name}:#{id}"
|
34
34
|
return unless (transaction = @agent.current_transaction)
|
35
35
|
|
36
36
|
normalized = @normalizers.normalize(transaction, name, payload)
|
@@ -39,12 +39,20 @@ module ElasticAPM
|
|
39
39
|
if normalized == :skip
|
40
40
|
nil
|
41
41
|
else
|
42
|
-
name, type, context = normalized
|
43
|
-
|
42
|
+
name, type, subtype, action, context = normalized
|
43
|
+
|
44
|
+
@agent.start_span(
|
45
|
+
name,
|
46
|
+
type,
|
47
|
+
subtype: subtype,
|
48
|
+
action: action,
|
49
|
+
context: context
|
50
|
+
)
|
44
51
|
end
|
45
52
|
|
46
53
|
transaction.notifications << Notification.new(id, span)
|
47
54
|
end
|
55
|
+
# rubocop:enable Metrics/MethodLength
|
48
56
|
|
49
57
|
def finish(_name, id, _payload)
|
50
58
|
# debug "AS::Notification#finish:#{name}:#{id}"
|
@@ -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
|
@@ -130,13 +131,26 @@ module ElasticAPM
|
|
130
131
|
].join(' ')
|
131
132
|
end
|
132
133
|
|
133
|
-
def build_ssl_context
|
134
|
-
return unless @config.use_ssl?
|
134
|
+
def build_ssl_context # rubocop:disable Metrics/MethodLength
|
135
|
+
return unless @config.use_ssl?
|
135
136
|
|
136
137
|
OpenSSL::SSL::SSLContext.new.tap do |context|
|
137
|
-
|
138
|
+
if @config.server_ca_cert
|
139
|
+
context.ca_file = @config.server_ca_cert
|
140
|
+
else
|
141
|
+
context.cert_store =
|
142
|
+
OpenSSL::X509::Store.new.tap(&:set_default_paths)
|
143
|
+
end
|
144
|
+
|
145
|
+
context.verify_mode =
|
146
|
+
if @config.verify_server_cert
|
147
|
+
OpenSSL::SSL::VERIFY_PEER
|
148
|
+
else
|
149
|
+
OpenSSL::SSL::VERIFY_NONE
|
150
|
+
end
|
138
151
|
end
|
139
152
|
end
|
140
153
|
end
|
154
|
+
# rubocop:enable Metrics/ClassLength
|
141
155
|
end
|
142
156
|
end
|