elastic-apm 2.9.1 → 2.10.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.
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