elastic-apm 3.1.0 → 3.2.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 +47 -0
- data/.ci/.jenkins_framework.yml +4 -0
- data/.ci/.jenkins_master_framework.yml +1 -0
- data/.ci/.jenkins_ruby.yml +1 -0
- data/.ci/downstreamTests.groovy +1 -1
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/CHANGELOG.asciidoc +24 -0
- data/Dockerfile +43 -0
- data/Gemfile +34 -15
- data/README.md +30 -1
- data/bin/dev +54 -0
- data/bin/run-tests +27 -0
- data/docker-compose.yml +32 -0
- data/docs/api.asciidoc +13 -2
- data/docs/configuration.asciidoc +30 -0
- data/docs/getting-started-rack.asciidoc +24 -0
- data/docs/release-notes.asciidoc +1 -1
- data/lib/elastic_apm.rb +12 -1
- data/lib/elastic_apm/agent.rb +15 -3
- data/lib/elastic_apm/central_config.rb +39 -19
- data/lib/elastic_apm/child_durations.rb +42 -0
- data/lib/elastic_apm/config.rb +27 -11
- data/lib/elastic_apm/context/request/socket.rb +1 -1
- data/lib/elastic_apm/context_builder.rb +1 -1
- data/lib/elastic_apm/error.rb +10 -0
- data/lib/elastic_apm/error/exception.rb +7 -0
- data/lib/elastic_apm/grape.rb +48 -0
- data/lib/elastic_apm/instrumenter.rb +77 -4
- data/lib/elastic_apm/logging.rb +0 -2
- data/lib/elastic_apm/metrics.rb +39 -26
- data/lib/elastic_apm/metrics/breakdown_set.rb +14 -0
- data/lib/elastic_apm/metrics/{cpu_mem.rb → cpu_mem_set.rb} +62 -54
- data/lib/elastic_apm/metrics/metric.rb +117 -0
- data/lib/elastic_apm/metrics/set.rb +106 -0
- data/lib/elastic_apm/metrics/span_scoped_set.rb +39 -0
- data/lib/elastic_apm/metrics/transaction_set.rb +11 -0
- data/lib/elastic_apm/metrics/vm_set.rb +44 -0
- data/lib/elastic_apm/metricset.rb +31 -4
- data/lib/elastic_apm/normalizers.rb +6 -0
- data/lib/elastic_apm/normalizers/grape.rb +5 -0
- data/lib/elastic_apm/normalizers/grape/endpoint_run.rb +47 -0
- data/lib/elastic_apm/normalizers/rails/active_record.rb +16 -5
- data/lib/elastic_apm/opentracing.rb +4 -4
- data/lib/elastic_apm/rails.rb +12 -2
- data/lib/elastic_apm/railtie.rb +1 -5
- data/lib/elastic_apm/sinatra.rb +1 -1
- data/lib/elastic_apm/span.rb +15 -10
- data/lib/elastic_apm/spies.rb +0 -1
- data/lib/elastic_apm/sql_summarizer.rb +8 -6
- data/lib/elastic_apm/subscriber.rb +4 -1
- data/lib/elastic_apm/transaction.rb +6 -6
- data/lib/elastic_apm/transport/base.rb +7 -0
- data/lib/elastic_apm/transport/connection.rb +11 -69
- data/lib/elastic_apm/transport/connection/http.rb +43 -35
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +0 -3
- data/lib/elastic_apm/transport/headers.rb +62 -0
- data/lib/elastic_apm/transport/serializers.rb +0 -2
- data/lib/elastic_apm/transport/serializers/metricset_serializer.rb +19 -6
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -3
- data/lib/elastic_apm/transport/user_agent.rb +31 -0
- data/lib/elastic_apm/transport/worker.rb +1 -2
- data/lib/elastic_apm/version.rb +1 -1
- metadata +20 -6
- data/lib/elastic_apm/metrics/vm.rb +0 -60
- data/lib/elastic_apm/util/prefixed_logger.rb +0 -18
data/docs/api.asciidoc
CHANGED
@@ -193,9 +193,9 @@ Returns the built context.
|
|
193
193
|
|
194
194
|
[float]
|
195
195
|
[[rails-start]]
|
196
|
-
===
|
196
|
+
=== Rails
|
197
197
|
|
198
|
-
Start the agent and hook into Rails
|
198
|
+
Start the agent and hook into Rails manually. This is useful if you skip requiring
|
199
199
|
the gem and using the `Railtie`.
|
200
200
|
|
201
201
|
[source,ruby]
|
@@ -214,6 +214,17 @@ Start the agent and hook into Sinatra.
|
|
214
214
|
ElasticAPM::Sinatra.start(MySinatraApp, server_url: 'http://localhost:8200')
|
215
215
|
----
|
216
216
|
|
217
|
+
[float]
|
218
|
+
[[grape-start]]
|
219
|
+
=== Grape
|
220
|
+
|
221
|
+
Start the agent and hook into Grape.
|
222
|
+
|
223
|
+
[source,ruby]
|
224
|
+
----
|
225
|
+
ElasticAPM::Grape.start(MyGrapeApp, server_url: 'http://localhost:8200')
|
226
|
+
----
|
227
|
+
|
217
228
|
[float]
|
218
229
|
=== Errors
|
219
230
|
|
data/docs/configuration.asciidoc
CHANGED
@@ -66,6 +66,23 @@ ElasticAPM::Sinatra.start(
|
|
66
66
|
|
67
67
|
See <<getting-started-rack>>.
|
68
68
|
|
69
|
+
[float]
|
70
|
+
=== Grape and Rack
|
71
|
+
|
72
|
+
When using APM with Grape and Rack (without Rails), you can configure it when starting
|
73
|
+
the agent:
|
74
|
+
|
75
|
+
[source,ruby]
|
76
|
+
----
|
77
|
+
# config.ru or similar
|
78
|
+
ElasticAPM::Grape.start(
|
79
|
+
MyApp,
|
80
|
+
service_name: 'SomeOtherName'
|
81
|
+
)
|
82
|
+
----
|
83
|
+
|
84
|
+
See <<getting-started-rack>>.
|
85
|
+
|
69
86
|
[float]
|
70
87
|
=== Options
|
71
88
|
|
@@ -184,6 +201,19 @@ APM Server has its own limit of 30 seconds before it will close requests.
|
|
184
201
|
|
185
202
|
It has to be provided in *<<config-format-duration, duration format>>*.
|
186
203
|
|
204
|
+
[float]
|
205
|
+
[[config-breakdown-metrics]]
|
206
|
+
==== `breakdown-metrics`
|
207
|
+
|============
|
208
|
+
| Environment | `Config` key | Default
|
209
|
+
| `ELASTIC_APM_BREAKDOWN_METRICS` | `breakdown_metrics` | `true`
|
210
|
+
|============
|
211
|
+
|
212
|
+
Enable/disable the tracking and collection of breakdown metrics.
|
213
|
+
By setting this to `False`, tracking this metric is completely disabled, which can reduce the overhead of the agent.
|
214
|
+
|
215
|
+
NOTE: This feature requires APM Server and Kibana >= 7.3.
|
216
|
+
|
187
217
|
[float]
|
188
218
|
[[config-capture-body]]
|
189
219
|
==== `capture_body`
|
@@ -72,3 +72,27 @@ run MySinatraApp
|
|
72
72
|
at_exit { ElasticAPM.stop }
|
73
73
|
----
|
74
74
|
|
75
|
+
[float]
|
76
|
+
[[getting-started-grape]]
|
77
|
+
==== Grape example
|
78
|
+
|
79
|
+
[source,ruby]
|
80
|
+
----
|
81
|
+
# Example config.ru
|
82
|
+
|
83
|
+
require 'grape'
|
84
|
+
|
85
|
+
module Twitter
|
86
|
+
class API < Grape::API
|
87
|
+
use ElasticAPM::Middleware
|
88
|
+
|
89
|
+
# ...
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Start the agent and hook in your app
|
94
|
+
ElasticAPM::Grape.start(Twitter::API, config)
|
95
|
+
|
96
|
+
run Twitter::API
|
97
|
+
|
98
|
+
----
|
data/docs/release-notes.asciidoc
CHANGED
data/lib/elastic_apm.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'erb'
|
4
|
+
require 'http'
|
5
|
+
require 'json'
|
6
|
+
require 'yaml'
|
7
|
+
require 'zlib'
|
8
|
+
require 'logger'
|
9
|
+
require 'concurrent'
|
10
|
+
require 'forwardable'
|
11
|
+
require 'securerandom'
|
12
|
+
|
3
13
|
require 'elastic_apm/version'
|
4
14
|
require 'elastic_apm/internal_error'
|
5
15
|
require 'elastic_apm/logging'
|
@@ -13,8 +23,9 @@ require 'elastic_apm/util'
|
|
13
23
|
|
14
24
|
require 'elastic_apm/middleware'
|
15
25
|
|
16
|
-
require 'elastic_apm/
|
26
|
+
require 'elastic_apm/rails' if defined?(::Rails::Railtie)
|
17
27
|
require 'elastic_apm/sinatra' if defined?(::Sinatra)
|
28
|
+
require 'elastic_apm/grape' if defined?(::Grape)
|
18
29
|
|
19
30
|
# ElasticAPM
|
20
31
|
module ElasticAPM # rubocop:disable Metrics/ModuleLength
|
data/lib/elastic_apm/agent.rb
CHANGED
@@ -62,6 +62,7 @@ module ElasticAPM
|
|
62
62
|
!!@instance
|
63
63
|
end
|
64
64
|
|
65
|
+
# rubocop:disable Metrics/MethodLength
|
65
66
|
def initialize(config)
|
66
67
|
@stacktrace_builder = StacktraceBuilder.new(config)
|
67
68
|
@context_builder = ContextBuilder.new(config)
|
@@ -69,12 +70,14 @@ module ElasticAPM
|
|
69
70
|
|
70
71
|
@central_config = CentralConfig.new(config)
|
71
72
|
@transport = Transport::Base.new(config)
|
73
|
+
@metrics = Metrics.new(config) { |event| enqueue event }
|
72
74
|
@instrumenter = Instrumenter.new(
|
73
75
|
config,
|
76
|
+
metrics: metrics,
|
74
77
|
stacktrace_builder: stacktrace_builder
|
75
78
|
) { |event| enqueue event }
|
76
|
-
@metrics = Metrics.new(config) { |event| enqueue event }
|
77
79
|
end
|
80
|
+
# rubocop:enable Metrics/MethodLength
|
78
81
|
|
79
82
|
attr_reader(
|
80
83
|
:central_config,
|
@@ -91,8 +94,11 @@ module ElasticAPM
|
|
91
94
|
|
92
95
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
93
96
|
def start
|
94
|
-
unless config.disable_start_message
|
95
|
-
info
|
97
|
+
unless config.disable_start_message?
|
98
|
+
config.logger.info format(
|
99
|
+
'[%s] Starting agent, reporting to %s',
|
100
|
+
VERSION, config.server_url
|
101
|
+
)
|
96
102
|
end
|
97
103
|
|
98
104
|
central_config.start
|
@@ -231,6 +237,12 @@ module ElasticAPM
|
|
231
237
|
def add_filter(key, callback)
|
232
238
|
transport.add_filter(key, callback)
|
233
239
|
end
|
240
|
+
|
241
|
+
# misc
|
242
|
+
|
243
|
+
def inspect
|
244
|
+
super.split.first + '>'
|
245
|
+
end
|
234
246
|
end
|
235
247
|
# rubocop:enable Metrics/ClassLength
|
236
248
|
end
|
@@ -4,7 +4,7 @@ require 'elastic_apm/central_config/cache_control'
|
|
4
4
|
|
5
5
|
module ElasticAPM
|
6
6
|
# @api private
|
7
|
-
class CentralConfig
|
7
|
+
class CentralConfig # rubocop:disable Metrics/ClassLength
|
8
8
|
include Logging
|
9
9
|
|
10
10
|
# @api private
|
@@ -23,10 +23,8 @@ module ElasticAPM
|
|
23
23
|
def initialize(config)
|
24
24
|
@config = config
|
25
25
|
@modified_options = {}
|
26
|
-
@
|
27
|
-
|
28
|
-
'service.environment': config.environment
|
29
|
-
}.to_json
|
26
|
+
@http = Transport::Connection::Http.new(config)
|
27
|
+
@etag = 1
|
30
28
|
end
|
31
29
|
|
32
30
|
attr_reader :config
|
@@ -35,9 +33,17 @@ module ElasticAPM
|
|
35
33
|
def start
|
36
34
|
return unless config.central_config?
|
37
35
|
|
36
|
+
debug 'Starting CentralConfig'
|
37
|
+
|
38
38
|
fetch_and_apply_config
|
39
39
|
end
|
40
40
|
|
41
|
+
def stop
|
42
|
+
debug 'Stopping CentralConfig'
|
43
|
+
|
44
|
+
@scheduled_task&.cancel
|
45
|
+
end
|
46
|
+
|
41
47
|
def fetch_and_apply_config
|
42
48
|
@promise =
|
43
49
|
Concurrent::Promise
|
@@ -46,10 +52,6 @@ module ElasticAPM
|
|
46
52
|
.rescue(&method(:handle_error))
|
47
53
|
end
|
48
54
|
|
49
|
-
def stop
|
50
|
-
@scheduled_task&.cancel
|
51
|
-
end
|
52
|
-
|
53
55
|
# rubocop:disable Metrics/MethodLength
|
54
56
|
def fetch_config
|
55
57
|
resp = perform_request
|
@@ -90,15 +92,25 @@ module ElasticAPM
|
|
90
92
|
@config = config.dup.tap { |new_config| new_config.assign(new_options) }
|
91
93
|
end
|
92
94
|
|
93
|
-
# rubocop:disable Metrics/MethodLength
|
95
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
96
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
94
97
|
def handle_success(resp)
|
98
|
+
if (etag = resp.headers['Etag'])
|
99
|
+
@etag = etag
|
100
|
+
end
|
101
|
+
|
95
102
|
if resp.status == 304
|
96
103
|
info 'Received 304 Not Modified'
|
97
104
|
else
|
98
|
-
|
99
|
-
|
105
|
+
if resp.body && !resp.body.empty?
|
106
|
+
update = JSON.parse(resp.body.to_s)
|
107
|
+
assign(update)
|
108
|
+
end
|
100
109
|
|
101
|
-
|
110
|
+
if @modified_options.any?
|
111
|
+
info 'Updated config from Kibana'
|
112
|
+
debug 'Modified: %s', @modified_options.inspect
|
113
|
+
end
|
102
114
|
end
|
103
115
|
|
104
116
|
schedule_next_fetch(resp)
|
@@ -108,7 +120,8 @@ module ElasticAPM
|
|
108
120
|
error 'Failed to apply remote config, %s', e.inspect
|
109
121
|
debug { e.backtrace.join('\n') }
|
110
122
|
end
|
111
|
-
# rubocop:enable Metrics/
|
123
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
124
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
112
125
|
|
113
126
|
def handle_error(error)
|
114
127
|
debug(
|
@@ -122,11 +135,18 @@ module ElasticAPM
|
|
122
135
|
end
|
123
136
|
|
124
137
|
def perform_request
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
138
|
+
@http.get(server_url, headers: headers)
|
139
|
+
end
|
140
|
+
|
141
|
+
def server_url
|
142
|
+
@server_url ||=
|
143
|
+
config.server_url +
|
144
|
+
'/config/v1/agents' \
|
145
|
+
"?service.name=#{config.service_name}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def headers
|
149
|
+
{ 'Etag': @etag }
|
130
150
|
end
|
131
151
|
|
132
152
|
def schedule_next_fetch(resp)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
module ChildDurations
|
6
|
+
# @api private
|
7
|
+
module Methods
|
8
|
+
def child_durations
|
9
|
+
@child_durations ||= Durations.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def child_started
|
13
|
+
child_durations.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def child_stopped
|
17
|
+
child_durations.stop
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
class Durations
|
23
|
+
def initialize
|
24
|
+
@nesting_level = 0
|
25
|
+
@start = nil
|
26
|
+
@duration = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :duration
|
30
|
+
|
31
|
+
def start
|
32
|
+
@nesting_level += 1
|
33
|
+
@start = Util.micros if @nesting_level == 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop
|
37
|
+
@nesting_level -= 1
|
38
|
+
@duration = (Util.micros - @start) if @nesting_level == 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'logger'
|
4
|
-
require 'yaml'
|
5
|
-
require 'erb'
|
6
|
-
|
7
|
-
require 'elastic_apm/util/prefixed_logger'
|
8
|
-
|
9
3
|
require 'elastic_apm/config/options'
|
10
4
|
require 'elastic_apm/config/duration'
|
11
5
|
require 'elastic_apm/config/bytes'
|
@@ -28,6 +22,7 @@ module ElasticAPM
|
|
28
22
|
option :api_buffer_size, type: :int, default: 256
|
29
23
|
option :api_request_size, type: :bytes, default: '750kb', converter: Bytes.new
|
30
24
|
option :api_request_time, type: :float, default: '10s', converter: Duration.new
|
25
|
+
option :breakdown_metrics, type: :bool, default: true
|
31
26
|
option :capture_body, type: :string, default: 'off'
|
32
27
|
option :capture_headers, type: :bool, default: true
|
33
28
|
option :capture_env, type: :bool, default: true
|
@@ -79,8 +74,6 @@ module ElasticAPM
|
|
79
74
|
def initialize(options = {})
|
80
75
|
@options = load_schema
|
81
76
|
|
82
|
-
custom_logger = options.delete(:logger)
|
83
|
-
|
84
77
|
assign(options)
|
85
78
|
|
86
79
|
# Pick out config_file specifically as we need it now to load it,
|
@@ -95,10 +88,10 @@ module ElasticAPM
|
|
95
88
|
|
96
89
|
yield self if block_given?
|
97
90
|
|
98
|
-
|
91
|
+
self.logger ||= build_logger
|
99
92
|
|
100
|
-
@__view_paths
|
101
|
-
@__root_path
|
93
|
+
@__view_paths ||= []
|
94
|
+
@__root_path ||= Dir.pwd
|
102
95
|
end
|
103
96
|
# rubocop:enable Metrics/MethodLength
|
104
97
|
|
@@ -173,6 +166,29 @@ module ElasticAPM
|
|
173
166
|
@span_frames_min_duration_us ||= span_frames_min_duration * 1_000_000
|
174
167
|
end
|
175
168
|
|
169
|
+
# rubocop:disable Metrics/MethodLength
|
170
|
+
def ssl_context
|
171
|
+
return unless use_ssl?
|
172
|
+
|
173
|
+
@ssl_context ||=
|
174
|
+
OpenSSL::SSL::SSLContext.new.tap do |context|
|
175
|
+
if server_ca_cert
|
176
|
+
context.ca_file = server_ca_cert
|
177
|
+
else
|
178
|
+
context.cert_store =
|
179
|
+
OpenSSL::X509::Store.new.tap(&:set_default_paths)
|
180
|
+
end
|
181
|
+
|
182
|
+
context.verify_mode =
|
183
|
+
if verify_server_cert
|
184
|
+
OpenSSL::SSL::VERIFY_PEER
|
185
|
+
else
|
186
|
+
OpenSSL::SSL::VERIFY_NONE
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
# rubocop:enable Metrics/MethodLength
|
191
|
+
|
176
192
|
def inspect
|
177
193
|
super.split.first + '>'
|
178
194
|
end
|
data/lib/elastic_apm/error.rb
CHANGED
@@ -18,5 +18,15 @@ module ElasticAPM
|
|
18
18
|
attr_accessor :id, :culprit, :exception, :log, :transaction_id,
|
19
19
|
:transaction, :context, :parent_id, :trace_id
|
20
20
|
attr_reader :timestamp
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"<ElasticAPM::Error id:#{id}" \
|
24
|
+
" culprit:#{culprit}" \
|
25
|
+
" timestamp:#{timestamp}" \
|
26
|
+
" transaction_id:#{transaction_id}" \
|
27
|
+
" trace_id:#{trace_id}" \
|
28
|
+
" exception:#{exception.inspect}" \
|
29
|
+
">"
|
30
|
+
end
|
21
31
|
end
|
22
32
|
end
|