elastic-apm 4.0.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/jobs/apm-agent-ruby-mbp.yml +1 -0
  3. data/.github/dependabot.yml +16 -0
  4. data/CHANGELOG.asciidoc +55 -0
  5. data/Gemfile +2 -0
  6. data/SECURITY.md +7 -0
  7. data/docker-compose.yml +1 -1
  8. data/docs/configuration.asciidoc +25 -1
  9. data/docs/metrics.asciidoc +92 -0
  10. data/lib/elastic_apm/agent.rb +4 -4
  11. data/lib/elastic_apm/central_config.rb +4 -3
  12. data/lib/elastic_apm/config.rb +20 -0
  13. data/lib/elastic_apm/fields.rb +88 -0
  14. data/lib/elastic_apm/instrumenter.rb +8 -2
  15. data/lib/elastic_apm/metadata/cloud_info.rb +32 -5
  16. data/lib/elastic_apm/metadata/system_info.rb +14 -4
  17. data/lib/elastic_apm/metrics/cpu_mem_set.rb +6 -6
  18. data/lib/elastic_apm/metrics/jvm_set.rb +88 -0
  19. data/lib/elastic_apm/metrics.rb +22 -8
  20. data/lib/elastic_apm/span/context/destination.rb +39 -58
  21. data/lib/elastic_apm/span.rb +18 -0
  22. data/lib/elastic_apm/span_helpers.rb +3 -3
  23. data/lib/elastic_apm/spies/azure_storage_table.rb +148 -0
  24. data/lib/elastic_apm/spies/dynamo_db.rb +2 -8
  25. data/lib/elastic_apm/spies/faraday.rb +25 -11
  26. data/lib/elastic_apm/spies/http.rb +3 -4
  27. data/lib/elastic_apm/spies/net_http.rb +21 -14
  28. data/lib/elastic_apm/spies/s3.rb +4 -7
  29. data/lib/elastic_apm/spies/sequel.rb +1 -1
  30. data/lib/elastic_apm/spies/sns.rb +3 -9
  31. data/lib/elastic_apm/spies/sqs.rb +3 -11
  32. data/lib/elastic_apm/spies.rb +20 -0
  33. data/lib/elastic_apm/subscriber.rb +1 -0
  34. data/lib/elastic_apm/transport/connection/http.rb +3 -1
  35. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +3 -2
  36. data/lib/elastic_apm/transport/serializers/span_serializer.rb +4 -8
  37. data/lib/elastic_apm/version.rb +1 -1
  38. data/lib/elastic_apm.rb +6 -2
  39. metadata +12 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1714ce04cac580fae85d1db089aad12a44e6ae719ae4c077c695ed19331d1862
4
- data.tar.gz: 2d2dc08bec25deca316844d1d4f8e878bf552ea473b27447f4476a82cf03872d
3
+ metadata.gz: 9bd0b20acebcfa91bc6e5d4e580c6a1a5f8da35b06152a4fe949c76380667e58
4
+ data.tar.gz: 85935357d8a1831233cb8ab837564b38ff800e29a0cf1891fd4ba6263bb1a0d2
5
5
  SHA512:
6
- metadata.gz: b1fba467b1fffb405b354511485b373d3e25bf141b6ab4404d80f4b9e431b4defc640b2f4c3edbc5e9253914844b09949ad6d617e3a5f0efe0c9f496ea7444ee
7
- data.tar.gz: dd749fc5782ab61ae5fe489fbe1d69dec7a33fac44a0a26fbd1c8b95b03f2f372c1e9350987e266f4098ec18cc3d409712a23ac92edfe0a1f25cef5760f64ebb
6
+ metadata.gz: 6eabaee4c14440607bdc5b5a0e53fd8b3844e0756a16cf6ef0da4b2a5431e7c1b441d0ef6329baf7d89f165f44031aa6a8396541930576acf54b254a787994cc
7
+ data.tar.gz: e601985643738456350d49ba9c261ccc17a0259651830230cf353d20c241403846f73c3ce371ab5b6f1a97b1223ac797ddfc1b764333e51055ff53104becd212
@@ -15,6 +15,7 @@
15
15
  repo: apm-agent-ruby
16
16
  repo-owner: elastic
17
17
  credentials-id: 2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken
18
+ head-filter-regex: '^(master|PR-.*|[3-4]\.x)$'
18
19
  ssh-checkout:
19
20
  credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba
20
21
  build-strategies:
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
8
+ ignore:
9
+ - dependency-name: sucker_punch
10
+ versions:
11
+ - "> 2.0"
12
+ - dependency-name: i18n
13
+ versions:
14
+ - 1.8.10
15
+ - 1.8.8
16
+ - 1.8.9
data/CHANGELOG.asciidoc CHANGED
@@ -35,6 +35,61 @@ endif::[]
35
35
  [[release-notes-4.x]]
36
36
  === Ruby Agent version 4.x
37
37
 
38
+ [[release-notes-4.4.0]]
39
+ ==== 4.4.0
40
+
41
+ [float]
42
+ ===== Added
43
+ - Optional span to be ended instead of current span {pull}1039[#1039]
44
+ - Config option `log_ecs_formatting` {pull}1053[#1053]
45
+
46
+ [float]
47
+ ===== Fixed
48
+ - Fixed detecting Linux on Alpine for CPU/MEM metrics {pull}1057[#1057]
49
+
50
+ [[release-notes-4.3.0]]
51
+ ==== 4.3.0
52
+
53
+ [float]
54
+ ===== Added
55
+
56
+ - Add JVM memory metrics {pull}1040[#1040]
57
+
58
+ [[release-notes-4.2.0]]
59
+ ==== 4.2.0
60
+
61
+ [float]
62
+ ===== Added
63
+
64
+ - Add support for AWS Storage Table/CosmosDB {pull}999[#999]
65
+
66
+ [float]
67
+ ===== Fixed
68
+
69
+ - Align HTTP span types/subtypes with spec {pull}1014[#1014]
70
+ - Passing a full URL as a path to `Net::HTTP` {pull}1029[#1029]
71
+ - Fix growing number of open file descriptors {pull}1033[#1033]
72
+
73
+ [[release-notes-4.1.0]]
74
+ ==== 4.1.0
75
+
76
+ [float]
77
+ ===== Added
78
+
79
+ - Azure App Services instance metadata {pull}1007[#1007]
80
+
81
+ [float]
82
+ ===== Changed
83
+
84
+ - `hostname` is now reported split by `configured_hostname` and `detected_hostname` {pull}1009[#1009]
85
+
86
+ [float]
87
+ ===== Fixed
88
+ - `service_node_name` is now correctly reported as `service.node.configured_name` {pull}1009[#1009]
89
+ - Fix JSON parsing when using yajl-ruby {pull}1012[#1012]
90
+ - Fix SpanHelpers when methods take blocks {pull}1013[#1013]
91
+ - Fix missing `environment` param when fetching from Central Config {pull}1014[#1014]
92
+
38
93
  [[release-notes-4.0.0]]
39
94
  ==== 4.0.0
40
95
 
data/Gemfile CHANGED
@@ -36,6 +36,8 @@ gem 'aws-sdk-dynamodb', require: nil
36
36
  gem 'aws-sdk-s3', require: nil
37
37
  gem 'aws-sdk-sqs', require: nil
38
38
  gem 'aws-sdk-sns', require: nil
39
+ gem 'azure-storage-table', require: nil if RUBY_VERSION < '3.0'
40
+ gem 'ecs-logging', require: 'ecs_logging/logger'
39
41
  gem 'elasticsearch', require: nil
40
42
  gem 'fakeredis', require: nil
41
43
  gem 'faraday', require: nil
data/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Security Policy
2
+
3
+ Thanks for your interest in the security of our products.
4
+ Our security policy can be found at [https://www.elastic.co/community/security](https://www.elastic.co/community/security).
5
+
6
+ ## Reporting a Vulnerability
7
+ Please send security vulnerability reports to security@elastic.co.
data/docker-compose.yml CHANGED
@@ -11,7 +11,7 @@ services:
11
11
  build:
12
12
  context: .
13
13
  args:
14
- BUNDLER_VERSION: '2.0.2'
14
+ BUNDLER_VERSION: '2.2.21'
15
15
  image: '$IMAGE_NAME'
16
16
  environment:
17
17
  HOME: '/tmp'
@@ -502,7 +502,31 @@ Use this option to ignore certain URL patterns such as healthchecks or admin sec
502
502
  | `ELASTIC_APM_INSTRUMENTED_RAKE_TASKS` | `instrumented_rake_tasks` | `[]` | `['my_task']`
503
503
  |============
504
504
 
505
- Elastic APM can instrument your Rake tasks. Theis is an opt-in field, as they are used are for a multitude of things.
505
+ Elastic APM can instrument your Rake tasks. Theis is an opt-in field, as they are used are for a multitude of things.
506
+
507
+ [float]
508
+ [[config-log-ecs-formatting]]
509
+ ==== `log_ecs_formatting`
510
+
511
+ [options="header"]
512
+ |============
513
+ | Environment | `Config` key | Default
514
+ | `ELASTIC_APM_LOG_ECS_FORMATTING` | `log_ecs_formatting` | `off`
515
+ |============
516
+
517
+ This is an experimental option that configures the agent to use the logger from the `ecs-logging` gem. The two
518
+ valid options are `off` and `override`.
519
+
520
+ Setting this option to `override` will set the agent logger to a `EcsLogging::Logger` instance and all logged output
521
+ will be in ECS-compatible json.
522
+
523
+ The `ecs-logging` gem must be installed before the agent is started. If `log_ecs_formatting` is set to `override`,
524
+ the agent will attempt to require the gem and if it cannot be loaded, it will fall back to using the standard Ruby
525
+ `::Logger` and log the load error.
526
+
527
+ Note that if you're using Rails, the agent will ignore this option and use the Rails logger. If you want to use a
528
+ `EcsLogging::Logger` when using Rails, set the agent's logger config option explicitly to a `EcsLogging::Logger`
529
+ instance.
506
530
 
507
531
  [float]
508
532
  [[config-log-level]]
@@ -141,3 +141,95 @@ The total time spent in garbage collection.
141
141
 
142
142
  **NB:** You need to enable Ruby's GC Profiler for this to get reported.
143
143
  You can do this at any time when your application boots by calling `GC::Profiler.enable`.
144
+
145
+ [float]
146
+ [[metrics-jvm-metrics]]
147
+ === JVM Metrics
148
+
149
+ The following metrics are available when using JRuby. They use the ruby java API to gather metrics via MXBean.
150
+
151
+ [float]
152
+ [[metric-jvm.memory.heap.used]]
153
+ ==== `jvm.memory.heap.used`
154
+
155
+ * *Type:* Long
156
+ * *Format:* Bytes
157
+
158
+ The amount of used heap memory in bytes.
159
+
160
+ [float]
161
+ [[metric-jvm.memory.heap.committed]]
162
+ ==== `jvm.memory.heap.committed`
163
+
164
+ * *Type:* Long
165
+ * *Format:* Bytes
166
+
167
+ The amount of heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
168
+ guaranteed for the Java virtual machine to use.
169
+
170
+ [float]
171
+ [[metric-jvm.memory.heap.max]]
172
+ ==== `jvm.memory.heap.max`
173
+
174
+ * *Type:* Long
175
+ * *Format:* Bytes
176
+
177
+ The amount of heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
178
+ guaranteed for the Java virtual machine to use.
179
+
180
+ [float]
181
+ [[metric-jvm.memory.non_heap.used]]
182
+ ==== `jvm.memory.non_heap.used`
183
+
184
+ * *Type:* Long
185
+ * *Format:* Bytes
186
+
187
+ The amount of used non-heap memory in bytes.
188
+
189
+ [float]
190
+ [[metric-jvm.memory.non_heap.committed]]
191
+ ==== `jvm.memory.non_heap.committed`
192
+
193
+ * *Type:* Long
194
+ * *Format:* Bytes
195
+
196
+ The amount of non-heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
197
+ guaranteed for the Java virtual machine to use.
198
+
199
+ [float]
200
+ [[metric-jvm.memory.non_heap.max]]
201
+ ==== `jvm.memory.non_heap.max`
202
+
203
+ * *Type:* Long
204
+ * *Format:* Bytes
205
+
206
+ The maximum amount of non-heap memory in bytes that can be used for memory management. If the maximum memory size is
207
+ undefined, the value is -1.
208
+
209
+ [float]
210
+ [[metric-jvm.memory.heap.pool.used]]
211
+ ==== `jvm.memory.heap.pool.used`
212
+
213
+ * *Type:* Long
214
+ * *Format:* Bytes
215
+
216
+ The amount of used memory in bytes of the memory pool.
217
+
218
+ [float]
219
+ [[metric-jvm.memory.heap.pool.committed]]
220
+ ==== `jvm.memory.heap.pool.committed`
221
+
222
+ * *Type:* Long
223
+ * *Format:* Bytes
224
+
225
+ The amount of memory in bytes that is committed for the memory pool. This amount of memory is guaranteed for this
226
+ specific pool.
227
+
228
+ [float]
229
+ [[metric-jvm.memory.heap.pool.max]]
230
+ ==== `jvm.memory.heap.pool.max`
231
+
232
+ * *Type:* Long
233
+ * *Format:* Bytes
234
+
235
+ The maximum amount of memory in bytes that can be used for the memory pool.
@@ -211,8 +211,8 @@ module ElasticAPM
211
211
  end
212
212
  # rubocop:enable Metrics/ParameterLists
213
213
 
214
- def end_span
215
- instrumenter.end_span
214
+ def end_span(span = nil)
215
+ instrumenter.end_span(span)
216
216
  end
217
217
 
218
218
  def set_label(key, value)
@@ -280,8 +280,8 @@ module ElasticAPM
280
280
  def detect_forking!
281
281
  return if @pid == Process.pid
282
282
 
283
- config.logger.debug "Forked process detected,
284
- restarting threads in process [PID:#{Process.pid}]"
283
+ config.logger.debug(
284
+ "Forked process detected, restarting threads in process [PID:#{Process.pid}]")
285
285
 
286
286
  central_config.handle_forking!
287
287
  transport.handle_forking!
@@ -119,7 +119,7 @@ module ElasticAPM
119
119
  end
120
120
 
121
121
  if resp.status == 304
122
- info 'Received 304 Not Modified'
122
+ debug 'Received 304 Not Modified'
123
123
  else
124
124
  if resp.body && !resp.body.empty?
125
125
  update = JSON.parse(resp.body.to_s)
@@ -164,11 +164,12 @@ module ElasticAPM
164
164
  @server_url ||=
165
165
  config.server_url +
166
166
  '/config/v1/agents' \
167
- "?service.name=#{CGI.escape(config.service_name)}"
167
+ "?service.name=#{CGI.escape(config.service_name)}" \
168
+ "&service.environment=#{CGI.escape(config.environment || '')}"
168
169
  end
169
170
 
170
171
  def headers
171
- { 'Etag': @etag }
172
+ { 'If-None-Match': @etag }
172
173
  end
173
174
 
174
175
  def schedule_next_fetch(resp = nil)
@@ -70,6 +70,7 @@ module ElasticAPM
70
70
  option :ignore_url_patterns, type: :list, default: [], converter: RegexpList.new
71
71
  option :instrument, type: :bool, default: true
72
72
  option :instrumented_rake_tasks, type: :list, default: []
73
+ option :log_ecs_formatting, type: :string, default: 'off'
73
74
  option :log_level, type: :int, default: Logger::INFO, converter: LogLevelMap.new
74
75
  option :log_path, type: :string
75
76
  option :metrics_interval, type: :int, default: '30s', converter: Duration.new
@@ -133,6 +134,7 @@ module ElasticAPM
133
134
  def available_instrumentations
134
135
  %w[
135
136
  action_dispatch
137
+ azure_storage_table
136
138
  delayed_job
137
139
  dynamo_db
138
140
  elasticsearch
@@ -245,11 +247,29 @@ module ElasticAPM
245
247
  end
246
248
 
247
249
  def build_logger
250
+ if self.log_ecs_formatting == 'override'
251
+ begin
252
+ return build_ecs_logger
253
+ rescue LoadError
254
+ logger.info "Attempted to use EcsLogging::Logger but the gem couldn't be " \
255
+ "loaded so a ::Logger was created instead. Check if you have the `ecs-logging` " \
256
+ "gem installed and attempt to start the agent again."
257
+ end
258
+ end
259
+
248
260
  Logger.new(log_path == '-' ? $stdout : log_path).tap do |logger|
249
261
  logger.level = log_level
250
262
  end
251
263
  end
252
264
 
265
+ def build_ecs_logger
266
+ require 'ecs_logging/logger'
267
+
268
+ ::EcsLogging::Logger.new(log_path == '-' ? $stdout : log_path).tap do |logger|
269
+ logger.level = log_level
270
+ end
271
+ end
272
+
253
273
  def app_type?(app)
254
274
  if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
255
275
  return :rails
@@ -0,0 +1,88 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # frozen_string_literal: true
19
+
20
+ module ElasticAPM
21
+ # An interface for creating simple, value holding objects that correspond to
22
+ # object fields in the API.
23
+ #
24
+ # Example:
25
+ # class MyThing
26
+ # include Fields
27
+ # field :name
28
+ # field :address, optional: true
29
+ # end
30
+ #
31
+ # MyThing.new(name: 'AJ').to_h
32
+ # # => { name: 'AJ' }
33
+ # MyThing.new().empty?
34
+ # # => true
35
+ module Fields
36
+ module InstanceMethods
37
+ def initialize(**attrs)
38
+ attrs.each do |key, value|
39
+ self.send(:"#{key}=", value)
40
+ end
41
+
42
+ super()
43
+ end
44
+
45
+ def empty?
46
+ self.class.fields.each do |key|
47
+ next if send(key)
48
+ next if optionals.include?(key)
49
+
50
+ return true
51
+ end
52
+
53
+ false
54
+ end
55
+
56
+ def to_h
57
+ self.class.fields.each_with_object({}) do |key, fields|
58
+ fields[key] = send(key)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def optionals
65
+ self.class.optionals
66
+ end
67
+ end
68
+
69
+ module ClassMethods
70
+ def field(key, optional: false)
71
+ attr_accessor(key)
72
+
73
+ fields.push(key)
74
+ optionals.push(key) if optional
75
+ end
76
+
77
+ attr_reader :fields, :optionals
78
+ end
79
+
80
+ def self.included(cls)
81
+ cls.extend(ClassMethods)
82
+ cls.include(InstanceMethods)
83
+
84
+ cls.instance_variable_set(:@fields, [])
85
+ cls.instance_variable_set(:@optionals, [])
86
+ end
87
+ end
88
+ end
@@ -222,8 +222,14 @@ module ElasticAPM
222
222
  # rubocop:enable Metrics/CyclomaticComplexity
223
223
  # rubocop:enable Metrics/PerceivedComplexity
224
224
 
225
- def end_span
226
- return unless (span = current_spans.pop)
225
+ def end_span(span = nil)
226
+ if span
227
+ current_spans.delete(span)
228
+ else
229
+ span = current_spans.pop
230
+ end
231
+
232
+ return unless span
227
233
 
228
234
  span.done
229
235
 
@@ -57,9 +57,9 @@ module ElasticAPM
57
57
  when "gcp"
58
58
  fetch_gcp
59
59
  when "azure"
60
- fetch_azure
60
+ fetch_azure || read_azure_app_services
61
61
  when "auto"
62
- fetch_aws || fetch_gcp || fetch_azure
62
+ fetch_aws || fetch_gcp || fetch_azure || read_azure_app_services
63
63
  when "none"
64
64
  nil
65
65
  else
@@ -76,7 +76,7 @@ module ElasticAPM
76
76
  resp = @client.get(AWS_URI)
77
77
 
78
78
  return unless resp.status == 200
79
- return unless (metadata = JSON.parse(resp.body))
79
+ return unless (metadata = JSON.parse(resp.body.to_s))
80
80
 
81
81
  self.provider = "aws"
82
82
  self.account_id = metadata["accountId"]
@@ -92,7 +92,7 @@ module ElasticAPM
92
92
  resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
93
93
 
94
94
  return unless resp.status == 200
95
- return unless (metadata = JSON.parse(resp.body))
95
+ return unless (metadata = JSON.parse(resp.body.to_s))
96
96
 
97
97
  zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
98
98
 
@@ -112,7 +112,7 @@ module ElasticAPM
112
112
  resp = @client.headers("Metadata" => "true").get(AZURE_URI)
113
113
 
114
114
  return unless resp.status == 200
115
- return unless (metadata = JSON.parse(resp.body))
115
+ return unless (metadata = JSON.parse(resp.body.to_s))
116
116
 
117
117
  self.provider = 'azure'
118
118
  self.account_id = metadata["subscriptionId"]
@@ -125,6 +125,33 @@ module ElasticAPM
125
125
  rescue HTTP::TimeoutError, HTTP::ConnectionError
126
126
  nil
127
127
  end
128
+
129
+ def read_azure_app_services
130
+ owner_name, instance_id, site_name, resource_group =
131
+ ENV.values_at(
132
+ 'WEBSITE_OWNER_NAME',
133
+ 'WEBSITE_INSTANCE_ID',
134
+ 'WEBSITE_SITE_NAME',
135
+ 'WEBSITE_RESOURCE_GROUP'
136
+ )
137
+
138
+ return unless owner_name && instance_id && site_name && resource_group
139
+
140
+ self.provider = 'azure'
141
+ self.instance_id = instance_id
142
+ self.instance_name = site_name
143
+ self.project_name = resource_group
144
+ self.account_id, self.region = parse_azure_app_services_owner_name(owner_name)
145
+ end
146
+
147
+ private
148
+
149
+ def parse_azure_app_services_owner_name(owner_name)
150
+ id, rest = owner_name.split('+')
151
+ *_, region = rest.split('-')
152
+ region.gsub!(/webspace.*$/, '')
153
+ [id, region]
154
+ end
128
155
  end
129
156
  end
130
157
  end
@@ -24,7 +24,8 @@ module ElasticAPM
24
24
  def initialize(config)
25
25
  @config = config
26
26
 
27
- @hostname = @config.hostname || self.class.system_hostname
27
+ @configured_hostname = @config.hostname
28
+ @detected_hostname = detect_hostname
28
29
  @architecture = gem_platform.cpu
29
30
  @platform = gem_platform.os
30
31
 
@@ -33,14 +34,23 @@ module ElasticAPM
33
34
  @kubernetes = container_info.kubernetes
34
35
  end
35
36
 
36
- attr_reader :hostname, :architecture, :platform, :container, :kubernetes
37
+ attr_reader(
38
+ :detected_hostname,
39
+ :configured_hostname,
40
+ :architecture,
41
+ :platform,
42
+ :container,
43
+ :kubernetes
44
+ )
37
45
 
38
46
  def gem_platform
39
47
  @gem_platform ||= Gem::Platform.local
40
48
  end
41
49
 
42
- def self.system_hostname
43
- @system_hostname ||= `hostname`.chomp
50
+ private
51
+
52
+ def detect_hostname
53
+ `hostname`.chomp
44
54
  end
45
55
  end
46
56
  end
@@ -62,8 +62,8 @@ module ElasticAPM
62
62
  def initialize(config)
63
63
  super
64
64
 
65
- @sampler = sampler_for_platform(Metrics.platform)
66
- read! # set @previous on boot
65
+ @sampler = sampler_for_os(Metrics.os)
66
+ read! # set initial values to calculate deltas from
67
67
  end
68
68
 
69
69
  attr_reader :config
@@ -75,11 +75,11 @@ module ElasticAPM
75
75
 
76
76
  private
77
77
 
78
- def sampler_for_platform(platform)
79
- case platform
80
- when :linux then Linux.new
78
+ def sampler_for_os(os)
79
+ case os
80
+ when /^linux/ then Linux.new
81
81
  else
82
- warn "Disabling system metrics, unsupported platform '#{platform}'"
82
+ warn "Disabling system metrics, unsupported host OS '#{os}'"
83
83
  disable!
84
84
  nil
85
85
  end
@@ -0,0 +1,88 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # frozen_string_literal: true
19
+
20
+ require 'java'
21
+
22
+ module ElasticAPM
23
+ module Metrics
24
+ # @api private
25
+ class JVMSet < Set
26
+ include Logging
27
+
28
+ MAX_TRIES = 3
29
+
30
+ def initialize(*args)
31
+ super
32
+
33
+ @error_count = 0
34
+ end
35
+
36
+ def collect
37
+ read!
38
+ super
39
+ end
40
+
41
+ def read!
42
+ return if disabled?
43
+
44
+ heap = platform_bean.get_heap_memory_usage
45
+ non_heap = platform_bean.get_non_heap_memory_usage
46
+
47
+ gauge(:"jvm.memory.heap.used").value = heap.get_used
48
+ gauge(:"jvm.memory.heap.committed").value = heap.get_committed
49
+ gauge(:"jvm.memory.heap.max").value = heap.get_max
50
+
51
+ gauge(:"jvm.memory.non_heap.used").value = non_heap.get_used
52
+ gauge(:"jvm.memory.non_heap.committed").value = non_heap.get_committed
53
+ gauge(:"jvm.memory.non_heap.max").value = non_heap.get_max
54
+
55
+ pool_beans.each do |bean|
56
+ next unless bean.type.name == "HEAP"
57
+
58
+ tags = { name: bean.get_name }
59
+
60
+ gauge(:"jvm.memory.heap.pool.used", tags: tags).value = bean.get_usage.get_used
61
+ gauge(:"jvm.memory.heap.pool.committed", tags: tags).value = bean.get_usage.get_committed
62
+ gauge(:"jvm.memory.heap.pool.max", tags: tags).value = bean.get_usage.get_max
63
+ end
64
+ rescue Exception => e
65
+ error("JVM metrics encountered error: %s", e)
66
+ debug("Backtrace:") { e.backtrace.join("\n") }
67
+
68
+ @error_count += 1
69
+ if @error_count >= MAX_TRIES
70
+ disable!
71
+ error("Disabling JVM metrics after #{MAX_TRIES} errors", e)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def platform_bean
78
+ @platform_bean ||= java.lang.management.ManagementFactory.getPlatformMXBean(
79
+ java.lang.management.MemoryMXBean.java_class
80
+ )
81
+ end
82
+
83
+ def pool_beans
84
+ @pool_beans ||= java.lang.management.ManagementFactory.getMemoryPoolMXBeans()
85
+ end
86
+ end
87
+ end
88
+ end