elastic-apm 2.9.1 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/.jenkins_codecov.yml +5 -0
  3. data/.ci/.jenkins_exclude.yml +9 -19
  4. data/.ci/.jenkins_framework.yml +1 -4
  5. data/.ci/.jenkins_master_framework.yml +3 -0
  6. data/.ci/Jenkinsfile +43 -118
  7. data/.ci/downstreamTests.groovy +59 -30
  8. data/.ci/jobs/apm-agent-ruby-downstream.yml +31 -0
  9. data/.ci/jobs/apm-agent-ruby-mbp.yml +34 -0
  10. data/.ci/jobs/defaults.yml +1 -36
  11. data/.pre-commit-config.yaml +22 -0
  12. data/.rspec +0 -1
  13. data/.rubocop.yml +3 -3
  14. data/CHANGELOG.md +12 -0
  15. data/docs/api.asciidoc +2 -2
  16. data/docs/configuration.asciidoc +37 -1
  17. data/docs/metrics.asciidoc +77 -6
  18. data/lib/elastic_apm.rb +2 -2
  19. data/lib/elastic_apm/agent.rb +11 -2
  20. data/lib/elastic_apm/central_config.rb +141 -0
  21. data/lib/elastic_apm/central_config/cache_control.rb +34 -0
  22. data/lib/elastic_apm/config.rb +152 -338
  23. data/lib/elastic_apm/config/bytes.rb +25 -0
  24. data/lib/elastic_apm/config/duration.rb +6 -8
  25. data/lib/elastic_apm/config/options.rb +134 -0
  26. data/lib/elastic_apm/config/regexp_list.rb +13 -0
  27. data/lib/elastic_apm/metadata.rb +2 -1
  28. data/lib/elastic_apm/metrics.rb +2 -1
  29. data/lib/elastic_apm/metrics/vm.rb +57 -0
  30. data/lib/elastic_apm/normalizers/action_view.rb +1 -1
  31. data/lib/elastic_apm/railtie.rb +10 -5
  32. data/lib/elastic_apm/spies/mongo.rb +13 -2
  33. data/lib/elastic_apm/stacktrace_builder.rb +2 -2
  34. data/lib/elastic_apm/transport/connection.rb +2 -0
  35. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +6 -1
  36. data/lib/elastic_apm/version.rb +1 -1
  37. metadata +11 -3
  38. data/lib/elastic_apm/config/size.rb +0 -28
@@ -4,3 +4,37 @@
4
4
  display-name: APM Agent Ruby
5
5
  description: APM Agent Ruby
6
6
  script-path: .ci/Jenkinsfile
7
+ scm:
8
+ - github:
9
+ branch-discovery: no-pr
10
+ discover-pr-forks-strategy: merge-current
11
+ discover-pr-forks-trust: permission
12
+ discover-pr-origin: merge-current
13
+ discover-tags: true
14
+ repo: apm-agent-ruby
15
+ repo-owner: elastic
16
+ credentials-id: 2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken
17
+ ssh-checkout:
18
+ credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba
19
+ build-strategies:
20
+ - tags:
21
+ ignore-tags-older-than: -1
22
+ ignore-tags-newer-than: -1
23
+ - regular-branches: true
24
+ - change-request:
25
+ ignore-target-only-changes: false
26
+ clean:
27
+ after: true
28
+ before: true
29
+ prune: true
30
+ shallow-clone: true
31
+ depth: 3
32
+ do-not-fetch-tags: true
33
+ submodule:
34
+ disable: false
35
+ recursive: true
36
+ parent-credentials: true
37
+ timeout: 100
38
+ timeout: '15'
39
+ use-author: true
40
+ wipe-workspace: 'True'
@@ -12,46 +12,11 @@
12
12
  project-type: multibranch
13
13
  logrotate:
14
14
  daysToKeep: 30
15
- numToKeep: 100
15
+ numToKeep: 300
16
16
  number-to-keep: '100'
17
17
  days-to-keep: '30'
18
18
  concurrent: true
19
19
  node: linux
20
- script-path: .ci/Jenkinsfile
21
- scm:
22
- - github:
23
- branch-discovery: all
24
- discover-pr-forks-strategy: merge-current
25
- discover-pr-forks-trust: permission
26
- discover-pr-origin: merge-current
27
- discover-tags: true
28
- repo: apm-agent-ruby
29
- repo-owner: elastic
30
- credentials-id: 2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken
31
- ssh-checkout:
32
- credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba
33
- build-strategies:
34
- - tags:
35
- ignore-tags-older-than: -1
36
- ignore-tags-newer-than: -1
37
- - regular-branches: true
38
- - change-request:
39
- ignore-target-only-changes: false
40
- clean:
41
- after: true
42
- before: true
43
- prune: true
44
- shallow-clone: true
45
- depth: 3
46
- do-not-fetch-tags: true
47
- submodule:
48
- disable: false
49
- recursive: true
50
- parent-credentials: true
51
- timeout: 100
52
- timeout: '15'
53
- use-author: true
54
- wipe-workspace: 'True'
55
20
  periodic-folder-trigger: 1d
56
21
  prune-dead-branches: true
57
22
  publishers:
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: git://github.com/pre-commit/pre-commit-hooks
3
+ rev: v2.2.3
4
+ hooks:
5
+ - id: check-case-conflict
6
+ - id: check-executables-have-shebangs
7
+ - id: check-json
8
+ - id: check-merge-conflict
9
+ - id: check-yaml
10
+ - id: check-xml
11
+ - id: end-of-file-fixer
12
+
13
+ - repo: git@github.com:elastic/apm-pipeline-library
14
+ rev: current
15
+ hooks:
16
+ - id: check-bash-syntax
17
+ - id: check-abstract-classes-and-trait
18
+ - id: check-jsonslurper-class
19
+ - id: check-jenkins-pipelines
20
+ - id: check-unicode-non-breaking-spaces
21
+ - id: remove-unicode-non-breaking-spaces
22
+ - id: check-jjbb
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
2
  --require spec_helper
3
- --format documentation
@@ -66,9 +66,6 @@ Style/DoubleNegation:
66
66
  Style/EmptyMethod:
67
67
  Enabled: false
68
68
 
69
- Rails/Delegate:
70
- Enabled: false
71
-
72
69
  Style/NumericPredicate:
73
70
  Enabled: false
74
71
 
@@ -83,3 +80,6 @@ Naming/MemoizedInstanceVariableName:
83
80
 
84
81
  Style/SpecialGlobalVars:
85
82
  Enabled: false
83
+
84
+ Style/MissingRespondToMissing:
85
+ Enabled: false
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 2.10.0
8
+
9
+ ### Added
10
+
11
+ - Add Ruby specific metrics ([#437](https://github.com/elastic/apm-agent-ruby/pull/437))
12
+ - Support for APM Agent Configuration via Kibana ([#464](https://github.com/elastic/apm-agent-ruby/pull/464))
13
+ - Change span name format and add command to context's db.statement for `MongoSpy` ([#488](https://github.com/elastic/apm-agent-ruby/pull/490))
14
+
15
+ ### Changed
16
+
17
+ - `ElasticAPM.report` and `ElasticAPM.report_message` return the string ID of the generated `Error` objects ([#507](https://github.com/elastic/apm-agent-ruby/pull/507))
18
+
7
19
  ## 2.9.1 (2019-06-28)
8
20
 
9
21
  ### Fixed
@@ -212,7 +212,7 @@ Arguments:
212
212
  * `handled`: Whether the error was _handled_ eg. wasn't rescued and was represented
213
213
  to the user. Default: `true`.
214
214
 
215
- Returns `[ElasticAPM::Error]`.
215
+ Returns `[String]` ID of the generated `[ElasticAPM::Error]` object.
216
216
 
217
217
  [float]
218
218
  [[api-agent-report-message]]
@@ -231,7 +231,7 @@ Arguments:
231
231
 
232
232
  * `message`: A custom error string. **Required**.
233
233
 
234
- Returns `[ElasticAPM::Error]`.
234
+ Returns `[String]` ID of the generated `[ElasticAPM::Error]` object.
235
235
 
236
236
  [float]
237
237
  [[api-context]]
@@ -62,7 +62,7 @@ Some options can be set with `ENV` variables and all of them may be set in
62
62
  your source code.
63
63
 
64
64
  When setting values for lists using `ENV` variables, strings are split by comma
65
- eg `ELASTIC_APM_ENABLED_ENVIRONMENTS=development,production`.
65
+ eg `ELASTIC_APM_CUSTOM_KEY_FILTERS="a,b" # => [/a/, /b/]`.
66
66
 
67
67
  [float]
68
68
  [[config-config-file]]
@@ -213,6 +213,21 @@ Whether or not to attach the request headers to transactions and errors.
213
213
 
214
214
  Whether or not to attach `ENV` from Rack to transactions and errors.
215
215
 
216
+ [float]
217
+ [[config-central-config]]
218
+ ==== `central_config`
219
+ |============
220
+ | Environment | `Config` key | Default
221
+ | `ELASTIC_APM_CENTRAL_CONFIG` | `central_config` | `true`
222
+ |============
223
+
224
+ Enable {kibana-ref}/agent-configuration.html[APM Agent Configuration via Kibana].
225
+ If set to `true`, the client will poll the APM Server regularly for new agent configuration.
226
+
227
+ Usually APM Server determines how often to poll, but if not the default interval is 5 minutes.
228
+
229
+ NOTE: This feature requires APM Server v7.3 or later and that the APM Server is configured with `kibana.enabled: true`.
230
+
216
231
  [float]
217
232
  [[config-custom-key-filters]]
218
233
  ==== `custom_key_filters`
@@ -244,6 +259,10 @@ Add default tags to add to every transaction.
244
259
 
245
260
  WARNING: Be aware that tags are indexed in Elasticsearch. Using too many unique keys will result in *https://www.elastic.co/blog/found-crash-elasticsearch#mapping-explosion[Mapping explosion]*.
246
261
 
262
+ NOTE: `global_labels` are supported as of APM server version 7.2. `default_tags` will eventually be
263
+ deprecated so please transition to using `global_labels` instead. In the meantime, any `default_tags`
264
+ that are set will override `global_labels`.
265
+
247
266
  [float]
248
267
  [[config-disable-send]]
249
268
  ==== `disable_send`
@@ -337,6 +356,23 @@ Version number of the used framework.
337
356
  For Ruby on Rails and Sinatra, this defaults to the used version of the framework,
338
357
  otherwise, the default is `nil`.
339
358
 
359
+ [float]
360
+ [[config-global-labels]]
361
+ ==== `global_labels`
362
+
363
+ [options="header"]
364
+ |============
365
+ | Environment | `Config` key | Default | Example
366
+ | `ELASTIC_APM_GLOBAL_LABELS` | `global_labels` | `nil` | `dept=engineering,rack=number8`
367
+ |============
368
+
369
+ Labels added to all events, with the format key=value[,key=value[,...]].
370
+
371
+ NOTE: This option requires APM Server 7.2 or greater, and will have no effect when using older
372
+ server versions. `default_tags` will eventually be deprecated but in the meantime, their value
373
+ will override any `global_labels`. Please transition to using `global_labels` instead of
374
+ `default_tags` in light of this deprecation.
375
+
340
376
  [float]
341
377
  [[config-hostname]]
342
378
  ==== `hostname`
@@ -12,11 +12,15 @@ You can adjust the interval by setting <<config-metrics-interval,`metrics_interv
12
12
 
13
13
  The metrics will be stored in the `apm-*` index and have the `processor.event` property set to `metric`.
14
14
 
15
+ [float]
16
+ [[metrics-system]]
17
+ === System metrics
18
+
15
19
  **Note:** Metrics from the Ruby agent are Linux only for now.
16
20
 
17
21
  [float]
18
22
  [[metric-system.cpu.total.norm.pct]]
19
- === `system.cpu.total.norm.pct`
23
+ ==== `system.cpu.total.norm.pct`
20
24
 
21
25
  * *Type:* Float
22
26
  * *Format:* Percent
@@ -26,7 +30,7 @@ normalised by the number of cores.
26
30
 
27
31
  [float]
28
32
  [[metric-system.memory.total]]
29
- === `system.memory.total`
33
+ ==== `system.memory.total`
30
34
 
31
35
  * *Type:* Long
32
36
  * *Format:* Bytes
@@ -35,7 +39,7 @@ The total memory of the system in bytes.
35
39
 
36
40
  [float]
37
41
  [[metric-system.memory.actual.free]]
38
- === `system.memory.actual.free`
42
+ ==== `system.memory.actual.free`
39
43
 
40
44
  * *Type:* Long
41
45
  * *Format:* Bytes
@@ -44,7 +48,7 @@ Free memory of the system in bytes.
44
48
 
45
49
  [float]
46
50
  [[metric-system.process.cpu.total.norm.pct]]
47
- === `system.process.cpu.total.norm.pct`
51
+ ==== `system.process.cpu.total.norm.pct`
48
52
 
49
53
  * *Type:* Float
50
54
  * *Format:* Percent
@@ -54,7 +58,7 @@ This value is normalized by the number of CPU cores and it ranges from 0 to 100%
54
58
 
55
59
  [float]
56
60
  [[metric-system.process.memory.size]]
57
- === `system.process.memory.size`
61
+ ==== `system.process.memory.size`
58
62
 
59
63
  * *Type:* Long
60
64
  * *Format:* Bytes
@@ -63,10 +67,77 @@ The total virtual memory the process has.
63
67
 
64
68
  [float]
65
69
  [[metric-system.process.memory.rss.bytes]]
66
- === `system.process.memory.rss.bytes`
70
+ ==== `system.process.memory.rss.bytes`
67
71
 
68
72
  * *Type:* Long
69
73
  * *Format:* Bytes
70
74
 
71
75
  The Resident Set Size,
72
76
  the amount of memory the process occupies in main memory (RAM).
77
+
78
+ [float]
79
+ [[metrics-ruby]]
80
+ === Ruby Metrics
81
+
82
+ [float]
83
+ [[metric-ruby.gc.counts]]
84
+ ==== `ruby.gc.count`
85
+
86
+ * *Type:* Integer
87
+ * *Format:* Count
88
+
89
+ The number of Garbage Collection runs since the process started.
90
+
91
+ [float]
92
+ [[metric-ruby.threads]]
93
+ ==== `ruby.threads`
94
+
95
+ * *Type:* Integer
96
+ * *Format:* Count
97
+
98
+ The number of threads belonging to the current process.
99
+
100
+ [float]
101
+ [[metric-ruby.heap.slots.live]]
102
+ ==== `ruby.heap.slots.live`
103
+
104
+ * *Type:* Integer
105
+ * *Format:* Slots
106
+
107
+ Current amount of heap slots that are live.
108
+
109
+ **NB:** Not currently supported on JRuby.
110
+
111
+ [float]
112
+ [[metric-ruby.heap.slots.free]]
113
+ ==== `ruby.heap.slots.free`
114
+
115
+ * *Type:* Integer
116
+ * *Format:* Slots
117
+
118
+ Current amount of heap slots that are free.
119
+
120
+ **NB:** Not currently supported on JRuby.
121
+
122
+ [float]
123
+ [[metrics-ruby.heap.allocations.total]]
124
+ ==== `ruby.heap.allocations.total`
125
+
126
+ * *Type:* Integer
127
+ * *Format:* Objects
128
+
129
+ Current amount of allocated objects on the heap.
130
+
131
+ **NB:** Not currently supported on JRuby.
132
+
133
+ [float]
134
+ [[metrics-ruby.gc.time]]
135
+ ==== `ruby.gc.time`
136
+
137
+ * *Type:* Float
138
+ * *Format:* Seconds
139
+
140
+ The total time spent in garbage collection.
141
+
142
+ **NB:** You need to enable Ruby's GC Profiler for this to get reported.
143
+ You can do this at any time when your application boots by calling `GC::Profiler.enable`.
@@ -305,7 +305,7 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
305
305
  # @param exception [Exception] The exception
306
306
  # @param context [Context] An optional [Context]
307
307
  # @param handled [Boolean] Whether the exception was rescued
308
- # @return [Error] The generated [Error]
308
+ # @return [String] ID of the generated [Error]
309
309
  def report(exception, context: nil, handled: true)
310
310
  agent&.report(exception, context: context, handled: handled)
311
311
  end
@@ -314,7 +314,7 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
314
314
  #
315
315
  # @param message [String] The message
316
316
  # @param context [Context] An optional [Context]
317
- # @return [Error] The generated [Error]
317
+ # @return [String] ID of the generated [Error]
318
318
  def report_message(message, context: nil, **attrs)
319
319
  agent&.report_message(
320
320
  message,
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'elastic_apm/error'
4
+
3
5
  require 'elastic_apm/context_builder'
4
6
  require 'elastic_apm/error_builder'
5
7
  require 'elastic_apm/stacktrace_builder'
6
- require 'elastic_apm/error'
8
+
9
+ require 'elastic_apm/central_config'
7
10
  require 'elastic_apm/transport/base'
8
- require 'elastic_apm/spies'
9
11
  require 'elastic_apm/metrics'
10
12
 
13
+ require 'elastic_apm/spies'
14
+
11
15
  module ElasticAPM
12
16
  # rubocop:disable Metrics/ClassLength
13
17
  # @api private
@@ -57,6 +61,7 @@ module ElasticAPM
57
61
  !!@instance
58
62
  end
59
63
 
64
+ # rubocop:disable Metrics/MethodLength
60
65
  def initialize(config)
61
66
  @config = config
62
67
 
@@ -64,6 +69,7 @@ module ElasticAPM
64
69
  @context_builder = ContextBuilder.new(config)
65
70
  @error_builder = ErrorBuilder.new(self)
66
71
 
72
+ @central_config = CentralConfig.new(config)
67
73
  @transport = Transport::Base.new(config)
68
74
  @instrumenter = Instrumenter.new(
69
75
  config,
@@ -71,6 +77,7 @@ module ElasticAPM
71
77
  ) { |event| enqueue event }
72
78
  @metrics = Metrics.new(config) { |event| enqueue event }
73
79
  end
80
+ # rubocop:enable Metrics/MethodLength
74
81
 
75
82
  attr_reader :config, :transport, :instrumenter,
76
83
  :stacktrace_builder, :context_builder, :error_builder, :metrics
@@ -189,6 +196,7 @@ module ElasticAPM
189
196
  handled: handled
190
197
  )
191
198
  enqueue error
199
+ error.id
192
200
  end
193
201
 
194
202
  def report_message(message, context: nil, backtrace: nil, **attrs)
@@ -199,6 +207,7 @@ module ElasticAPM
199
207
  **attrs
200
208
  )
201
209
  enqueue error
210
+ error.id
202
211
  end
203
212
 
204
213
  # filters
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_apm/central_config/cache_control'
4
+
5
+ module ElasticAPM
6
+ # @api private
7
+ class CentralConfig
8
+ include Logging
9
+
10
+ # @api private
11
+ class ResponseError < InternalError
12
+ def initialize(response)
13
+ @response = response
14
+ end
15
+
16
+ attr_reader :response
17
+ end
18
+ class ClientError < ResponseError; end
19
+ class ServerError < ResponseError; end
20
+
21
+ DEFAULT_MAX_AGE = 300
22
+
23
+ def initialize(config)
24
+ @config = config
25
+ @modified_options = {}
26
+ @service_info = {
27
+ 'service.name': config.service_name,
28
+ 'service.environment': config.environment
29
+ }.to_json
30
+ end
31
+
32
+ attr_reader :config
33
+ attr_reader :scheduled_task, :promise # for specs
34
+
35
+ def start
36
+ return unless config.central_config?
37
+
38
+ fetch_and_apply_config
39
+ end
40
+
41
+ def fetch_and_apply_config
42
+ @promise =
43
+ Concurrent::Promise
44
+ .execute(&method(:fetch_config))
45
+ .on_success(&method(:handle_success))
46
+ .rescue(&method(:handle_error))
47
+ end
48
+
49
+ def stop
50
+ @scheduled_task&.cancel
51
+ end
52
+
53
+ # rubocop:disable Metrics/MethodLength
54
+ def fetch_config
55
+ resp = perform_request
56
+
57
+ case resp.status
58
+ when 200..299
59
+ resp
60
+ when 300..399
61
+ resp
62
+ when 400..499
63
+ raise ClientError, resp
64
+ when 500..599
65
+ raise ServerError, resp
66
+ end
67
+ end
68
+ # rubocop:enable Metrics/MethodLength
69
+
70
+ def assign(update)
71
+ # For each updated option, store the original value,
72
+ # unless already stored
73
+ update.each_key do |key|
74
+ @modified_options[key] ||= config.get(key.to_sym)&.value
75
+ end
76
+
77
+ # If the new update doesn't set a previously modified option,
78
+ # revert it to the original
79
+ @modified_options.each_key do |key|
80
+ next if update.key?(key)
81
+ update[key] = @modified_options.delete(key)
82
+ end
83
+
84
+ config.assign(update)
85
+ end
86
+
87
+ private
88
+
89
+ # rubocop:disable Metrics/MethodLength
90
+ def handle_success(resp)
91
+ if resp.status == 304
92
+ info 'Received 304 Not Modified'
93
+ else
94
+ update = JSON.parse(resp.body.to_s)
95
+ assign(update)
96
+
97
+ info 'Updated config from Kibana'
98
+ end
99
+
100
+ schedule_next_fetch(resp)
101
+
102
+ true
103
+ rescue Exception => e
104
+ error 'Failed to apply remote config, %s', e.inspect
105
+ debug { e.backtrace.join('\n') }
106
+ end
107
+ # rubocop:enable Metrics/MethodLength
108
+
109
+ def handle_error(error)
110
+ error(
111
+ 'Failed fetching config: %s, trying again in %d seconds',
112
+ error.response.body, DEFAULT_MAX_AGE
113
+ )
114
+
115
+ assign({})
116
+
117
+ schedule_next_fetch(error.response)
118
+ end
119
+
120
+ def perform_request
121
+ Http.post(
122
+ config.server_url + '/agent/v1/config/',
123
+ body: @service_info,
124
+ headers: { etag: 1, content_type: 'application/json' }
125
+ )
126
+ end
127
+
128
+ def schedule_next_fetch(resp)
129
+ seconds =
130
+ if (cache_header = resp.headers['Cache-Control'])
131
+ CacheControl.new(cache_header).max_age
132
+ else
133
+ DEFAULT_MAX_AGE
134
+ end
135
+
136
+ @scheduled_task =
137
+ Concurrent::ScheduledTask
138
+ .execute(seconds, &method(:fetch_and_apply_config))
139
+ end
140
+ end
141
+ end