elastic-apm 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of elastic-apm might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile +17 -12
- data/bench/app.rb +1 -2
- data/bench/benchmark.rb +1 -1
- data/bench/stackprof.rb +1 -1
- data/docs/api.asciidoc +115 -76
- data/docs/configuration.asciidoc +232 -167
- data/docs/context.asciidoc +7 -3
- data/docs/custom-instrumentation.asciidoc +17 -28
- data/docs/index.asciidoc +13 -7
- data/docs/supported-technologies.asciidoc +65 -0
- data/elastic-apm.gemspec +3 -2
- data/lib/elastic_apm.rb +272 -121
- data/lib/elastic_apm/agent.rb +56 -107
- data/lib/elastic_apm/config.rb +130 -106
- data/lib/elastic_apm/config/duration.rb +25 -0
- data/lib/elastic_apm/config/size.rb +28 -0
- data/lib/elastic_apm/context_builder.rb +1 -0
- data/lib/elastic_apm/deprecations.rb +19 -0
- data/lib/elastic_apm/error.rb +5 -2
- data/lib/elastic_apm/error/exception.rb +1 -1
- data/lib/elastic_apm/error_builder.rb +5 -0
- data/lib/elastic_apm/instrumenter.rb +121 -53
- data/lib/elastic_apm/internal_error.rb +1 -0
- data/lib/elastic_apm/{log.rb → logging.rb} +16 -11
- data/lib/elastic_apm/metadata.rb +20 -0
- data/lib/elastic_apm/metadata/process_info.rb +26 -0
- data/lib/elastic_apm/metadata/service_info.rb +56 -0
- data/lib/elastic_apm/metadata/system_info.rb +30 -0
- data/lib/elastic_apm/middleware.rb +31 -15
- data/lib/elastic_apm/normalizers/action_controller.rb +1 -1
- data/lib/elastic_apm/normalizers/action_mailer.rb +1 -1
- data/lib/elastic_apm/normalizers/action_view.rb +3 -3
- data/lib/elastic_apm/normalizers/active_record.rb +2 -1
- data/lib/elastic_apm/railtie.rb +1 -1
- data/lib/elastic_apm/span.rb +59 -29
- data/lib/elastic_apm/span/context.rb +30 -4
- data/lib/elastic_apm/span_helpers.rb +1 -1
- data/lib/elastic_apm/spies/delayed_job.rb +7 -7
- data/lib/elastic_apm/spies/elasticsearch.rb +4 -4
- data/lib/elastic_apm/spies/http.rb +38 -0
- data/lib/elastic_apm/spies/mongo.rb +22 -11
- data/lib/elastic_apm/spies/net_http.rb +7 -4
- data/lib/elastic_apm/spies/rake.rb +5 -6
- data/lib/elastic_apm/spies/redis.rb +1 -1
- data/lib/elastic_apm/spies/sequel.rb +9 -7
- data/lib/elastic_apm/spies/sidekiq.rb +5 -5
- data/lib/elastic_apm/spies/tilt.rb +2 -2
- data/lib/elastic_apm/sql_summarizer.rb +3 -3
- data/lib/elastic_apm/stacktrace_builder.rb +6 -6
- data/lib/elastic_apm/subscriber.rb +3 -3
- data/lib/elastic_apm/traceparent.rb +62 -0
- data/lib/elastic_apm/transaction.rb +62 -93
- data/lib/elastic_apm/transport/base.rb +98 -0
- data/lib/elastic_apm/transport/connection.rb +175 -0
- data/lib/elastic_apm/transport/filters.rb +45 -0
- data/lib/elastic_apm/transport/filters/request_body_filter.rb +31 -0
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +59 -0
- data/lib/elastic_apm/transport/serializers.rb +58 -0
- data/lib/elastic_apm/transport/serializers/error_serializer.rb +59 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +30 -0
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +33 -0
- data/lib/elastic_apm/transport/worker.rb +73 -0
- data/lib/elastic_apm/util.rb +11 -8
- data/lib/elastic_apm/version.rb +1 -1
- metadata +40 -21
- data/.travis.yml +0 -5
- data/docs/troubleshooting.asciidoc +0 -28
- data/lib/elastic_apm/filters.rb +0 -46
- data/lib/elastic_apm/filters/request_body_filter.rb +0 -33
- data/lib/elastic_apm/filters/secrets_filter.rb +0 -59
- data/lib/elastic_apm/http.rb +0 -139
- data/lib/elastic_apm/process_info.rb +0 -24
- data/lib/elastic_apm/serializers.rb +0 -28
- data/lib/elastic_apm/serializers/errors.rb +0 -61
- data/lib/elastic_apm/serializers/transactions.rb +0 -51
- data/lib/elastic_apm/service_info.rb +0 -54
- data/lib/elastic_apm/system_info.rb +0 -28
- data/lib/elastic_apm/util/dig.rb +0 -31
- data/lib/elastic_apm/util/inspector.rb +0 -61
- data/lib/elastic_apm/worker.rb +0 -106
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Config
|
5
|
+
# @api private
|
6
|
+
class Duration
|
7
|
+
MULTIPLIERS = { 'ms' => 0.001, 'm' => 60 }.freeze
|
8
|
+
REGEX = /^(-)?(\d+)(m|ms|s)?$/i.freeze
|
9
|
+
|
10
|
+
def initialize(seconds)
|
11
|
+
@seconds = seconds
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :seconds
|
15
|
+
|
16
|
+
def self.parse(str, default_unit:)
|
17
|
+
_, negative, amount, unit = REGEX.match(str).to_a
|
18
|
+
unit ||= default_unit
|
19
|
+
seconds = MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i
|
20
|
+
seconds = 0 - seconds if negative
|
21
|
+
new(seconds)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class Config
|
5
|
+
# @api private
|
6
|
+
class Size
|
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(bytes)
|
15
|
+
@bytes = bytes
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :bytes
|
19
|
+
|
20
|
+
def self.parse(str, default_unit:)
|
21
|
+
_, amount, unit = REGEX.match(str).to_a
|
22
|
+
unit ||= default_unit
|
23
|
+
bytes = MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i
|
24
|
+
new(bytes)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
module Deprecations
|
6
|
+
def deprecate(name, replacement = nil)
|
7
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
8
|
+
alias :"#{name}__deprecated" :"#{name}"
|
9
|
+
|
10
|
+
def #{name}(*args, &block)
|
11
|
+
warn "[ElasticAPM] [DEPRECATED] `#{name}' is being removed. " \
|
12
|
+
"#{replacement && "See `#{replacement}'."}" \
|
13
|
+
"\nCalled from \#{caller.first}"
|
14
|
+
#{name}__deprecated(*args, &block)
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/elastic_apm/error.rb
CHANGED
@@ -9,16 +9,19 @@ module ElasticAPM
|
|
9
9
|
# @api private
|
10
10
|
class Error
|
11
11
|
def initialize(culprit: nil)
|
12
|
-
@id = SecureRandom.
|
12
|
+
@id = SecureRandom.hex(16)
|
13
|
+
@trace_id = nil
|
13
14
|
@culprit = culprit
|
14
15
|
|
15
16
|
@timestamp = Util.micros
|
16
17
|
@context = Context.new
|
17
18
|
|
18
19
|
@transaction_id = nil
|
20
|
+
@parent_id = nil
|
19
21
|
end
|
20
22
|
|
21
|
-
attr_accessor :id, :culprit, :exception, :log, :transaction_id, :context
|
23
|
+
attr_accessor :id, :culprit, :exception, :log, :transaction_id, :context,
|
24
|
+
:parent_id, :trace_id
|
22
25
|
attr_reader :timestamp
|
23
26
|
end
|
24
27
|
end
|
@@ -7,6 +7,7 @@ module ElasticAPM
|
|
7
7
|
@agent = agent
|
8
8
|
end
|
9
9
|
|
10
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
10
11
|
def build_exception(exception, handled: true)
|
11
12
|
error = Error.new
|
12
13
|
error.exception = Error::Exception.new(exception, handled: handled)
|
@@ -19,10 +20,14 @@ module ElasticAPM
|
|
19
20
|
|
20
21
|
if (transaction = ElasticAPM.current_transaction)
|
21
22
|
error.context = transaction.context.dup
|
23
|
+
error.trace_id = transaction.trace_id
|
24
|
+
error.transaction_id = transaction.id
|
25
|
+
error.parent_id = ElasticAPM.current_span&.id || transaction.id
|
22
26
|
end
|
23
27
|
|
24
28
|
error
|
25
29
|
end
|
30
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
26
31
|
|
27
32
|
def build_log(message, backtrace: nil, **attrs)
|
28
33
|
error = Error.new
|
@@ -4,41 +4,57 @@ require 'elastic_apm/span'
|
|
4
4
|
require 'elastic_apm/transaction'
|
5
5
|
|
6
6
|
module ElasticAPM
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
7
8
|
# @api private
|
8
9
|
class Instrumenter
|
9
|
-
include
|
10
|
+
include Logging
|
10
11
|
|
11
|
-
|
12
|
+
TRANSACTION_KEY = :__elastic_transaction_key
|
13
|
+
SPAN_KEY = :__elastic_span_key
|
12
14
|
|
13
15
|
# @api private
|
14
|
-
class
|
16
|
+
class Current
|
15
17
|
def initialize
|
16
|
-
self.
|
18
|
+
self.transaction = nil
|
19
|
+
self.span = nil
|
17
20
|
end
|
18
21
|
|
19
|
-
def
|
20
|
-
Thread.current[
|
22
|
+
def transaction
|
23
|
+
Thread.current[TRANSACTION_KEY]
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
Thread.current[
|
26
|
+
def transaction=(transaction)
|
27
|
+
Thread.current[TRANSACTION_KEY] = transaction
|
28
|
+
end
|
29
|
+
|
30
|
+
def span
|
31
|
+
Thread.current[SPAN_KEY]
|
32
|
+
end
|
33
|
+
|
34
|
+
def span=(span)
|
35
|
+
Thread.current[SPAN_KEY] = span
|
25
36
|
end
|
26
37
|
end
|
27
38
|
|
28
|
-
def initialize(
|
29
|
-
@
|
30
|
-
@
|
39
|
+
def initialize(config, &enqueue)
|
40
|
+
@config = config
|
41
|
+
@enqueue = enqueue
|
31
42
|
|
32
|
-
@
|
43
|
+
@current = Current.new
|
33
44
|
end
|
34
45
|
|
35
|
-
attr_reader :
|
46
|
+
attr_reader :config, :enqueue
|
36
47
|
|
37
48
|
def start
|
49
|
+
debug 'Starting instrumenter'
|
38
50
|
end
|
39
51
|
|
40
52
|
def stop
|
41
|
-
|
53
|
+
debug 'Stopping instrumenter'
|
54
|
+
|
55
|
+
self.current_transaction = nil
|
56
|
+
self.current_span = nil
|
57
|
+
|
42
58
|
@subscriber.unregister! if @subscriber
|
43
59
|
end
|
44
60
|
|
@@ -47,70 +63,118 @@ module ElasticAPM
|
|
47
63
|
@subscriber.register!
|
48
64
|
end
|
49
65
|
|
66
|
+
# transactions
|
67
|
+
|
50
68
|
def current_transaction
|
51
|
-
@
|
69
|
+
@current.transaction
|
52
70
|
end
|
53
71
|
|
54
72
|
def current_transaction=(transaction)
|
55
|
-
@
|
73
|
+
@current.transaction = transaction
|
56
74
|
end
|
57
75
|
|
58
76
|
# rubocop:disable Metrics/MethodLength
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
77
|
+
def start_transaction(
|
78
|
+
name = nil,
|
79
|
+
type = nil,
|
80
|
+
context: nil,
|
81
|
+
traceparent: nil
|
82
|
+
)
|
83
|
+
return nil unless config.instrument?
|
65
84
|
|
66
85
|
if (transaction = current_transaction)
|
67
|
-
|
68
|
-
|
86
|
+
raise ExistingTransactionError,
|
87
|
+
"Transactions may not be nested.\nAlready inside #{transaction}"
|
69
88
|
end
|
70
89
|
|
71
|
-
sampled =
|
90
|
+
sampled = traceparent ? traceparent.recorded? : random_sample?
|
72
91
|
|
73
92
|
transaction =
|
74
|
-
Transaction.new
|
93
|
+
Transaction.new(
|
94
|
+
name,
|
95
|
+
type,
|
96
|
+
context: context,
|
97
|
+
traceparent: traceparent,
|
98
|
+
sampled: sampled
|
99
|
+
)
|
100
|
+
|
101
|
+
transaction.start
|
75
102
|
|
76
103
|
self.current_transaction = transaction
|
77
|
-
|
104
|
+
end
|
105
|
+
# rubocop:enable Metrics/MethodLength
|
78
106
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
107
|
+
def end_transaction(result = nil)
|
108
|
+
return nil unless (transaction = current_transaction)
|
109
|
+
|
110
|
+
self.current_transaction = nil
|
111
|
+
|
112
|
+
transaction.done result
|
113
|
+
|
114
|
+
enqueue.call transaction
|
85
115
|
|
86
116
|
transaction
|
87
117
|
end
|
88
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
89
|
-
# rubocop:enable Metrics/MethodLength
|
90
118
|
|
91
|
-
|
92
|
-
|
119
|
+
# spans
|
120
|
+
|
121
|
+
def current_span
|
122
|
+
@current.span
|
93
123
|
end
|
94
124
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
125
|
+
def current_span=(span)
|
126
|
+
@current.span = span
|
127
|
+
end
|
128
|
+
|
129
|
+
# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
130
|
+
def start_span(name, type = nil, backtrace: nil, context: nil)
|
131
|
+
return unless (transaction = current_transaction)
|
132
|
+
return unless transaction.sampled?
|
133
|
+
|
134
|
+
transaction.inc_started_spans!
|
135
|
+
|
136
|
+
if transaction.max_spans_reached?(config)
|
137
|
+
transaction.inc_dropped_spans!
|
99
138
|
return
|
100
139
|
end
|
101
140
|
|
102
|
-
|
141
|
+
span = Span.new(
|
103
142
|
name,
|
104
143
|
type,
|
105
|
-
|
106
|
-
|
107
|
-
|
144
|
+
transaction: transaction,
|
145
|
+
parent: current_span || transaction,
|
146
|
+
context: context
|
108
147
|
)
|
148
|
+
|
149
|
+
if backtrace && span_frames_min_duration?
|
150
|
+
span.original_backtrace = backtrace
|
151
|
+
end
|
152
|
+
|
153
|
+
self.current_span = span
|
154
|
+
|
155
|
+
span.start
|
156
|
+
end
|
157
|
+
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
158
|
+
|
159
|
+
def end_span
|
160
|
+
return unless (span = current_span)
|
161
|
+
|
162
|
+
span.done
|
163
|
+
|
164
|
+
self.current_span =
|
165
|
+
span.parent&.is_a?(Span) && span.parent || nil
|
166
|
+
|
167
|
+
enqueue.call span
|
168
|
+
|
169
|
+
span
|
109
170
|
end
|
110
|
-
|
171
|
+
|
172
|
+
# metadata
|
111
173
|
|
112
174
|
def set_tag(key, value)
|
113
175
|
return unless current_transaction
|
176
|
+
|
177
|
+
key = key.to_s.gsub(/[\."\*]/, '_').to_sym
|
114
178
|
current_transaction.context.tags[key] = value.to_s
|
115
179
|
end
|
116
180
|
|
@@ -124,17 +188,21 @@ module ElasticAPM
|
|
124
188
|
current_transaction.context.user = Context::User.new(config, user)
|
125
189
|
end
|
126
190
|
|
127
|
-
def submit_transaction(transaction)
|
128
|
-
agent.enqueue_transaction transaction
|
129
|
-
|
130
|
-
return unless config.debug_transactions
|
131
|
-
debug('Submitted transaction:') { Util.inspect_transaction transaction }
|
132
|
-
end
|
133
|
-
|
134
191
|
def inspect
|
135
192
|
'<ElasticAPM::Instrumenter ' \
|
136
193
|
"current_transaction=#{current_transaction.inspect}" \
|
137
194
|
'>'
|
138
195
|
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def random_sample?
|
200
|
+
rand <= config.transaction_sample_rate
|
201
|
+
end
|
202
|
+
|
203
|
+
def span_frames_min_duration?
|
204
|
+
config.span_frames_min_duration != 0
|
205
|
+
end
|
139
206
|
end
|
207
|
+
# rubocop:enable Metrics/ClassLength
|
140
208
|
end
|
@@ -1,9 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'logger'
|
4
|
+
|
3
5
|
module ElasticAPM
|
4
6
|
# @api private
|
5
|
-
module
|
6
|
-
PREFIX = '[ElasticAPM] '
|
7
|
+
module Logging
|
8
|
+
PREFIX = '[ElasticAPM] '
|
9
|
+
|
10
|
+
LEVELS = {
|
11
|
+
debug: Logger::DEBUG,
|
12
|
+
info: Logger::INFO,
|
13
|
+
warn: Logger::WARN,
|
14
|
+
error: Logger::ERROR,
|
15
|
+
fatal: Logger::FATAL
|
16
|
+
}.freeze
|
7
17
|
|
8
18
|
def debug(msg, *args, &block)
|
9
19
|
log(:debug, msg, *args, &block)
|
@@ -25,26 +35,21 @@ module ElasticAPM
|
|
25
35
|
log(:fatal, msg, *args, &block)
|
26
36
|
end
|
27
37
|
|
38
|
+
private
|
39
|
+
|
28
40
|
def log(lvl, msg, *args)
|
29
|
-
return unless logger
|
41
|
+
return unless (logger = @config&.logger)
|
42
|
+
return unless LEVELS[lvl] >= (@config&.log_level || 0)
|
30
43
|
|
31
44
|
formatted_msg = prepend_prefix(format(msg.to_s, *args))
|
32
45
|
|
33
46
|
return logger.send(lvl, formatted_msg) unless block_given?
|
34
47
|
|
35
|
-
# TODO: dont evaluate block if level is higher
|
36
48
|
logger.send(lvl, "#{formatted_msg}\n#{yield}")
|
37
49
|
end
|
38
50
|
|
39
|
-
private
|
40
|
-
|
41
51
|
def prepend_prefix(str)
|
42
52
|
"#{PREFIX}#{str}"
|
43
53
|
end
|
44
|
-
|
45
|
-
def logger
|
46
|
-
return false unless (config = instance_variable_get(:@config))
|
47
|
-
config.logger
|
48
|
-
end
|
49
54
|
end
|
50
55
|
end
|