elastic-apm 3.3.0 → 3.4.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_exclude.yml +4 -4
- data/.ci/.jenkins_ruby.yml +1 -1
- data/.ci/Jenkinsfile +5 -3
- data/.ci/jobs/apm-agent-ruby-downstream.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-linting-mbp.yml +1 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +1 -0
- data/.ci/prepare-git-context.sh +5 -2
- data/.github/ISSUE_TEMPLATE/Bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +6 -0
- data/CHANGELOG.asciidoc +25 -1
- data/Gemfile +6 -2
- data/bench/sql.rb +49 -0
- data/bin/build_docs +1 -1
- data/codecov.yml +32 -0
- data/docs/api.asciidoc +37 -0
- data/docs/configuration.asciidoc +18 -1
- data/docs/supported-technologies.asciidoc +20 -1
- data/lib/elastic_apm.rb +29 -5
- data/lib/elastic_apm/agent.rb +6 -2
- data/lib/elastic_apm/child_durations.rb +9 -4
- data/lib/elastic_apm/config.rb +8 -1
- data/lib/elastic_apm/config/options.rb +3 -4
- data/lib/elastic_apm/context/response.rb +10 -2
- data/lib/elastic_apm/instrumenter.rb +20 -11
- data/lib/elastic_apm/normalizers/rails/active_record.rb +12 -5
- data/lib/elastic_apm/rails.rb +1 -10
- data/lib/elastic_apm/railtie.rb +1 -1
- data/lib/elastic_apm/span.rb +3 -2
- data/lib/elastic_apm/span/context.rb +26 -44
- data/lib/elastic_apm/span/context/db.rb +19 -0
- data/lib/elastic_apm/span/context/destination.rb +44 -0
- data/lib/elastic_apm/span/context/http.rb +26 -0
- data/lib/elastic_apm/spies/elasticsearch.rb +18 -5
- data/lib/elastic_apm/spies/faraday.rb +36 -18
- data/lib/elastic_apm/spies/http.rb +16 -2
- data/lib/elastic_apm/spies/mongo.rb +5 -0
- data/lib/elastic_apm/spies/net_http.rb +27 -7
- data/lib/elastic_apm/spies/sequel.rb +25 -15
- data/lib/elastic_apm/spies/shoryuken.rb +48 -0
- data/lib/elastic_apm/spies/sneakers.rb +57 -0
- data/lib/elastic_apm/sql.rb +19 -0
- data/lib/elastic_apm/sql/signature.rb +152 -0
- data/lib/elastic_apm/sql/tokenizer.rb +247 -0
- data/lib/elastic_apm/sql/tokens.rb +46 -0
- data/lib/elastic_apm/sql_summarizer.rb +1 -2
- data/lib/elastic_apm/transaction.rb +11 -11
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +2 -2
- data/lib/elastic_apm/transport/headers.rb +4 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +24 -7
- data/lib/elastic_apm/version.rb +1 -1
- metadata +16 -3
- data/.github/workflows/main.yml +0 -14
data/docs/configuration.asciidoc
CHANGED
@@ -145,7 +145,7 @@ One example to generate a secure secret token is:
|
|
145
145
|
ruby -r securerandom -e 'print SecureRandom.uuid'
|
146
146
|
----
|
147
147
|
|
148
|
-
WARNING: Secret tokens only provide any real security if your APM server
|
148
|
+
WARNING: Secret tokens only provide any real security if your APM server uses TLS.
|
149
149
|
|
150
150
|
[float]
|
151
151
|
[[config-active]]
|
@@ -726,6 +726,23 @@ between `0.0` and `1.0`.
|
|
726
726
|
We still record overall time and the result for unsampled transactions, but no
|
727
727
|
context information, tags, or spans.
|
728
728
|
|
729
|
+
[float]
|
730
|
+
[[config-use-experimental-sql-parser]]
|
731
|
+
==== `use_experimental_sql_parser`
|
732
|
+
|============
|
733
|
+
| Environment | `Config` key | Default
|
734
|
+
| `ELASTIC_APM_USE_EXPERIMENTAL_SQL_PARSER` | `use_experimental_sql_parser` | `false`
|
735
|
+
|============
|
736
|
+
|
737
|
+
Use a newer, more precise but still experimental approach to generating summaries of
|
738
|
+
your app's SQL statements.
|
739
|
+
Without this, your SQL statements will still be summarized albeit less accurately.
|
740
|
+
|
741
|
+
The summaries become the spans' names -- what it says in the waterfall view in Kibana.
|
742
|
+
|
743
|
+
NOTE: This should work just fine but is still deamed experimental. That means it is
|
744
|
+
subject to change. Please let us know if you come across any issues.
|
745
|
+
|
729
746
|
[float]
|
730
747
|
[[config-verify-server-cert]]
|
731
748
|
==== `verify_server_cert`
|
@@ -26,7 +26,7 @@ https://www.ruby-lang.org/en/downloads/branches/[Ruby Maintenance Branches].
|
|
26
26
|
We have automatic support for Ruby on Rails and all Rack compatible web
|
27
27
|
frameworks.
|
28
28
|
|
29
|
-
We test against all supported minor versions of Rails and
|
29
|
+
We test against all supported minor versions of Rails, Sinatra, and Grape.
|
30
30
|
|
31
31
|
[float]
|
32
32
|
[[supported-technologies-rails]]
|
@@ -45,6 +45,14 @@ We currently support all versions of Sinatra since 1.0.
|
|
45
45
|
|
46
46
|
See <<getting-started-rack>>.
|
47
47
|
|
48
|
+
[float]
|
49
|
+
[[supported-technologies-grape]]
|
50
|
+
==== Grape
|
51
|
+
|
52
|
+
We currently support all versions of Grape since 1.2.
|
53
|
+
|
54
|
+
See <<getting-started-grape>>.
|
55
|
+
|
48
56
|
[float]
|
49
57
|
[[supported-technologies-databases]]
|
50
58
|
=== Databases
|
@@ -67,3 +75,14 @@ requests using these libraries:
|
|
67
75
|
- `net/http`
|
68
76
|
- Http.rb (v0.6+)
|
69
77
|
- Faraday (v0.2.1+)
|
78
|
+
|
79
|
+
[float]
|
80
|
+
[[supported-technologies-backgroud-processing]]
|
81
|
+
=== Background Processing
|
82
|
+
|
83
|
+
We automatically instrument background processing using:
|
84
|
+
|
85
|
+
- DelayedJob
|
86
|
+
- Sidekiq
|
87
|
+
- Shoryuken
|
88
|
+
- Sneakers (v2.12.0+) (Experimental, see {pull}676[#676])
|
data/lib/elastic_apm.rb
CHANGED
@@ -97,6 +97,8 @@ module ElasticAPM
|
|
97
97
|
# @param type [String] The kind of the transaction, eg `app.request.get` or
|
98
98
|
# `db.mysql2.query`
|
99
99
|
# @param context [Context] An optional [Context]
|
100
|
+
# @param trace_context [TraceContext] An optional [TraceContext] object for
|
101
|
+
# Distributed Tracing.
|
100
102
|
# @return [Transaction]
|
101
103
|
def start_transaction(
|
102
104
|
name = nil,
|
@@ -127,6 +129,8 @@ module ElasticAPM
|
|
127
129
|
# @param type [String] The kind of the transaction, eg `app.request.get` or
|
128
130
|
# `db.mysql2.query`
|
129
131
|
# @param context [Context] An optional [Context]
|
132
|
+
# @param trace_context [TraceContext] An optional [TraceContext] object for
|
133
|
+
# Distributed Tracing.
|
130
134
|
# @yield [Transaction]
|
131
135
|
# @return result of block
|
132
136
|
def with_transaction(
|
@@ -165,6 +169,11 @@ module ElasticAPM
|
|
165
169
|
# @param action [String] The span action type, eq `connect` or `query`
|
166
170
|
# @param context [Span::Context] Context information about the span
|
167
171
|
# @param include_stacktrace [Boolean] Whether or not to capture a stacktrace
|
172
|
+
# @param trace_context [TraceContext] An optional [TraceContext] object for
|
173
|
+
# Distributed Tracing.
|
174
|
+
# @param parent [Transaction,Span] The parent transaction or span.
|
175
|
+
# Relevant when the span is created in another thread.
|
176
|
+
# @param sync [Boolean] Whether the span is created synchronously or not.
|
168
177
|
# @return [Span]
|
169
178
|
def start_span(
|
170
179
|
name,
|
@@ -173,7 +182,9 @@ module ElasticAPM
|
|
173
182
|
action: nil,
|
174
183
|
context: nil,
|
175
184
|
include_stacktrace: true,
|
176
|
-
trace_context: nil
|
185
|
+
trace_context: nil,
|
186
|
+
parent: nil,
|
187
|
+
sync: nil
|
177
188
|
)
|
178
189
|
agent&.start_span(
|
179
190
|
name,
|
@@ -181,7 +192,9 @@ module ElasticAPM
|
|
181
192
|
subtype: subtype,
|
182
193
|
action: action,
|
183
194
|
context: context,
|
184
|
-
trace_context: trace_context
|
195
|
+
trace_context: trace_context,
|
196
|
+
parent: parent,
|
197
|
+
sync: sync
|
185
198
|
).tap do |span|
|
186
199
|
break unless span && include_stacktrace
|
187
200
|
break unless agent.config.span_frames_min_duration?
|
@@ -202,9 +215,16 @@ module ElasticAPM
|
|
202
215
|
# Wrap a block in a Span, ending it after the block
|
203
216
|
#
|
204
217
|
# @param name [String] A description of the span, eq `SELECT FROM "users"`
|
205
|
-
# @param type [String] The kind of span, eq `db
|
218
|
+
# @param type [String] The kind of span, eq `db`
|
219
|
+
# @param subtype [String] The subtype of span eg. `postgresql`.
|
220
|
+
# @param action [String] The action type of span eg. `connect` or `query`.
|
206
221
|
# @param context [Span::Context] Context information about the span
|
207
222
|
# @param include_stacktrace [Boolean] Whether or not to capture a stacktrace
|
223
|
+
# @param trace_context [TraceContext] An optional [TraceContext] object for
|
224
|
+
# Distributed Tracing.
|
225
|
+
# @param parent [Transaction,Span] The parent transaction or span.
|
226
|
+
# Relevant when the span is created in another thread.
|
227
|
+
# @param sync [Boolean] Whether the span is created synchronously or not.
|
208
228
|
# @yield [Span]
|
209
229
|
# @return Result of block
|
210
230
|
def with_span(
|
@@ -214,7 +234,9 @@ module ElasticAPM
|
|
214
234
|
action: nil,
|
215
235
|
context: nil,
|
216
236
|
include_stacktrace: true,
|
217
|
-
trace_context: nil
|
237
|
+
trace_context: nil,
|
238
|
+
parent: nil,
|
239
|
+
sync: nil
|
218
240
|
)
|
219
241
|
unless block_given?
|
220
242
|
raise ArgumentError,
|
@@ -232,7 +254,9 @@ module ElasticAPM
|
|
232
254
|
action: action,
|
233
255
|
context: context,
|
234
256
|
include_stacktrace: include_stacktrace,
|
235
|
-
trace_context: trace_context
|
257
|
+
trace_context: trace_context,
|
258
|
+
parent: parent,
|
259
|
+
sync: sync
|
236
260
|
)
|
237
261
|
yield span
|
238
262
|
ensure
|
data/lib/elastic_apm/agent.rb
CHANGED
@@ -165,7 +165,9 @@ module ElasticAPM
|
|
165
165
|
action: nil,
|
166
166
|
backtrace: nil,
|
167
167
|
context: nil,
|
168
|
-
trace_context: nil
|
168
|
+
trace_context: nil,
|
169
|
+
parent: nil,
|
170
|
+
sync: nil
|
169
171
|
)
|
170
172
|
instrumenter.start_span(
|
171
173
|
name,
|
@@ -174,7 +176,9 @@ module ElasticAPM
|
|
174
176
|
action: action,
|
175
177
|
backtrace: backtrace,
|
176
178
|
context: context,
|
177
|
-
trace_context: trace_context
|
179
|
+
trace_context: trace_context,
|
180
|
+
parent: parent,
|
181
|
+
sync: sync
|
178
182
|
)
|
179
183
|
end
|
180
184
|
# rubocop:enable Metrics/ParameterLists
|
@@ -24,18 +24,23 @@ module ElasticAPM
|
|
24
24
|
@nesting_level = 0
|
25
25
|
@start = nil
|
26
26
|
@duration = 0
|
27
|
+
@mutex = Mutex.new
|
27
28
|
end
|
28
29
|
|
29
30
|
attr_reader :duration
|
30
31
|
|
31
32
|
def start
|
32
|
-
@
|
33
|
-
|
33
|
+
@mutex.synchronize do
|
34
|
+
@nesting_level += 1
|
35
|
+
@start = Util.micros if @nesting_level == 1
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
def stop
|
37
|
-
@
|
38
|
-
|
40
|
+
@mutex.synchronize do
|
41
|
+
@nesting_level -= 1
|
42
|
+
@duration = (Util.micros - @start) if @nesting_level == 0
|
43
|
+
end
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -17,6 +17,7 @@ module ElasticAPM
|
|
17
17
|
option :config_file, type: :string, default: 'config/elastic_apm.yml'
|
18
18
|
option :server_url, type: :url, default: 'http://localhost:8200'
|
19
19
|
option :secret_token, type: :string
|
20
|
+
option :api_key, type: :string
|
20
21
|
|
21
22
|
option :active, type: :bool, default: true
|
22
23
|
option :api_buffer_size, type: :int, default: 256
|
@@ -69,6 +70,9 @@ module ElasticAPM
|
|
69
70
|
option :transaction_max_spans, type: :int, default: 500
|
70
71
|
option :transaction_sample_rate, type: :float, default: 1.0
|
71
72
|
option :verify_server_cert, type: :bool, default: true
|
73
|
+
|
74
|
+
option :use_experimental_sql_parser, type: :bool, default: false
|
75
|
+
|
72
76
|
# rubocop:enable Metrics/LineLength, Layout/ExtraSpacing
|
73
77
|
def initialize(options = {})
|
74
78
|
@options = load_schema
|
@@ -105,6 +109,7 @@ module ElasticAPM
|
|
105
109
|
|
106
110
|
def available_instrumentations
|
107
111
|
%w[
|
112
|
+
action_dispatch
|
108
113
|
delayed_job
|
109
114
|
elasticsearch
|
110
115
|
faraday
|
@@ -112,12 +117,14 @@ module ElasticAPM
|
|
112
117
|
json
|
113
118
|
mongo
|
114
119
|
net_http
|
120
|
+
rake
|
115
121
|
redis
|
116
122
|
sequel
|
123
|
+
shoryuken
|
117
124
|
sidekiq
|
118
125
|
sinatra
|
126
|
+
sneakers
|
119
127
|
tilt
|
120
|
-
rake
|
121
128
|
]
|
122
129
|
end
|
123
130
|
|
@@ -83,9 +83,8 @@ module ElasticAPM
|
|
83
83
|
@schema ||= {}
|
84
84
|
end
|
85
85
|
|
86
|
-
def option(
|
87
|
-
key = args
|
88
|
-
schema[key] = *args
|
86
|
+
def option(key, **args)
|
87
|
+
schema[key] = args
|
89
88
|
end
|
90
89
|
end
|
91
90
|
|
@@ -93,7 +92,7 @@ module ElasticAPM
|
|
93
92
|
module InstanceMethods
|
94
93
|
def load_schema
|
95
94
|
Hash[self.class.schema.map do |key, args|
|
96
|
-
[key, Option.new(key,
|
95
|
+
[key, Option.new(key, **args)]
|
97
96
|
end]
|
98
97
|
end
|
99
98
|
|
@@ -11,12 +11,20 @@ module ElasticAPM
|
|
11
11
|
finished: true
|
12
12
|
)
|
13
13
|
@status_code = status_code
|
14
|
-
@headers = headers
|
15
14
|
@headers_sent = headers_sent
|
16
15
|
@finished = finished
|
16
|
+
|
17
|
+
self.headers = headers
|
17
18
|
end
|
18
19
|
|
19
|
-
attr_accessor :status_code, :
|
20
|
+
attr_accessor :status_code, :headers_sent, :finished
|
21
|
+
attr_reader :headers
|
22
|
+
|
23
|
+
def headers=(headers)
|
24
|
+
@headers = headers.each_with_object({}) do |(k, v), hsh|
|
25
|
+
hsh[k] = v.to_s
|
26
|
+
end
|
27
|
+
end
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
@@ -136,6 +136,7 @@ module ElasticAPM
|
|
136
136
|
end
|
137
137
|
|
138
138
|
# rubocop:disable Metrics/CyclomaticComplexity
|
139
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
139
140
|
# rubocop:disable Metrics/ParameterLists
|
140
141
|
def start_span(
|
141
142
|
name,
|
@@ -144,19 +145,25 @@ module ElasticAPM
|
|
144
145
|
action: nil,
|
145
146
|
backtrace: nil,
|
146
147
|
context: nil,
|
147
|
-
trace_context: nil
|
148
|
+
trace_context: nil,
|
149
|
+
parent: nil,
|
150
|
+
sync: nil
|
148
151
|
)
|
149
|
-
return unless (transaction = current_transaction)
|
150
|
-
return unless transaction.sampled?
|
151
|
-
|
152
|
-
transaction.inc_started_spans!
|
153
152
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
153
|
+
transaction =
|
154
|
+
case parent
|
155
|
+
when Span
|
156
|
+
parent.transaction
|
157
|
+
when Transaction
|
158
|
+
parent
|
159
|
+
else
|
160
|
+
current_transaction
|
161
|
+
end
|
162
|
+
return unless transaction
|
163
|
+
return unless transaction.sampled?
|
164
|
+
return unless transaction.inc_started_spans!
|
158
165
|
|
159
|
-
parent
|
166
|
+
parent ||= (current_span || current_transaction)
|
160
167
|
|
161
168
|
span = Span.new(
|
162
169
|
name: name,
|
@@ -167,7 +174,8 @@ module ElasticAPM
|
|
167
174
|
trace_context: trace_context,
|
168
175
|
type: type,
|
169
176
|
context: context,
|
170
|
-
stacktrace_builder: stacktrace_builder
|
177
|
+
stacktrace_builder: stacktrace_builder,
|
178
|
+
sync: sync
|
171
179
|
)
|
172
180
|
|
173
181
|
if backtrace && transaction.config.span_frames_min_duration?
|
@@ -180,6 +188,7 @@ module ElasticAPM
|
|
180
188
|
end
|
181
189
|
# rubocop:enable Metrics/ParameterLists
|
182
190
|
# rubocop:enable Metrics/CyclomaticComplexity
|
191
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
183
192
|
|
184
193
|
def end_span
|
185
194
|
return unless (span = current_spans.pop)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'elastic_apm/
|
3
|
+
require 'elastic_apm/sql'
|
4
4
|
|
5
5
|
module ElasticAPM
|
6
6
|
module Normalizers
|
@@ -17,7 +17,8 @@ module ElasticAPM
|
|
17
17
|
def initialize(*args)
|
18
18
|
super
|
19
19
|
|
20
|
-
@summarizer =
|
20
|
+
@summarizer = Sql.summarizer
|
21
|
+
|
21
22
|
@adapters = {}
|
22
23
|
end
|
23
24
|
|
@@ -25,14 +26,20 @@ module ElasticAPM
|
|
25
26
|
return :skip if SKIP_NAMES.include?(payload[:name])
|
26
27
|
|
27
28
|
name = summarize(payload[:sql]) || payload[:name]
|
29
|
+
subtype = subtype_for(payload)
|
30
|
+
|
28
31
|
context =
|
29
|
-
Span::Context.new(
|
30
|
-
|
32
|
+
Span::Context.new(
|
33
|
+
db: { statement: payload[:sql], type: 'sql' },
|
34
|
+
destination: { name: subtype, resource: subtype, type: TYPE }
|
35
|
+
)
|
36
|
+
|
37
|
+
[name, TYPE, subtype, ACTION, context]
|
31
38
|
end
|
32
39
|
|
33
40
|
private
|
34
41
|
|
35
|
-
def
|
42
|
+
def subtype_for(payload)
|
36
43
|
cached_adapter_name(
|
37
44
|
payload[:connection]&.adapter_name ||
|
38
45
|
::ActiveRecord::Base.connection_config[:adapter]
|
data/lib/elastic_apm/rails.rb
CHANGED
@@ -9,7 +9,6 @@ module ElasticAPM
|
|
9
9
|
# It is recommended to use the Railtie instead.
|
10
10
|
module Rails
|
11
11
|
extend self
|
12
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
13
12
|
# Start the ElasticAPM agent and hook into Rails.
|
14
13
|
# Note that the agent won't be started if the Rails console is being used.
|
15
14
|
#
|
@@ -31,13 +30,6 @@ module ElasticAPM
|
|
31
30
|
attach_subscriber(agent)
|
32
31
|
end
|
33
32
|
|
34
|
-
if ElasticAPM.running? &&
|
35
|
-
!ElasticAPM.agent.config.disabled_instrumentations.include?(
|
36
|
-
'action_dispatch'
|
37
|
-
)
|
38
|
-
require 'elastic_apm/spies/action_dispatch'
|
39
|
-
end
|
40
|
-
|
41
33
|
ElasticAPM.running?
|
42
34
|
rescue StandardError => e
|
43
35
|
if config.disable_start_message?
|
@@ -48,12 +40,11 @@ module ElasticAPM
|
|
48
40
|
puts "Backtrace:\n" + e.backtrace.join("\n")
|
49
41
|
end
|
50
42
|
end
|
51
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
52
43
|
|
53
44
|
private
|
54
45
|
|
55
46
|
def should_skip?(_config)
|
56
|
-
if ::Rails.const_defined?
|
47
|
+
if ::Rails.const_defined?('Console', false)
|
57
48
|
return 'Rails console'
|
58
49
|
end
|
59
50
|
|
data/lib/elastic_apm/railtie.rb
CHANGED
data/lib/elastic_apm/span.rb
CHANGED
@@ -20,7 +20,8 @@ module ElasticAPM
|
|
20
20
|
subtype: nil,
|
21
21
|
action: nil,
|
22
22
|
context: nil,
|
23
|
-
stacktrace_builder: nil
|
23
|
+
stacktrace_builder: nil,
|
24
|
+
sync: nil
|
24
25
|
)
|
25
26
|
@name = name
|
26
27
|
|
@@ -36,7 +37,7 @@ module ElasticAPM
|
|
36
37
|
@parent = parent
|
37
38
|
@trace_context = trace_context || parent.trace_context.child
|
38
39
|
|
39
|
-
@context = context || Span::Context.new
|
40
|
+
@context = context || Span::Context.new(sync: sync)
|
40
41
|
@stacktrace_builder = stacktrace_builder
|
41
42
|
end
|
42
43
|
# rubocop:enable Metrics/ParameterLists
|