prometheus_exporter 0.4.9 → 0.5.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -1
  3. data/CHANGELOG +43 -0
  4. data/Gemfile +2 -0
  5. data/Guardfile +2 -0
  6. data/README.md +84 -3
  7. data/Rakefile +2 -0
  8. data/bench/bench.rb +2 -0
  9. data/bin/prometheus_exporter +8 -1
  10. data/examples/custom_collector.rb +2 -0
  11. data/lib/prometheus_exporter/client.rb +19 -7
  12. data/lib/prometheus_exporter/instrumentation/active_record.rb +87 -0
  13. data/lib/prometheus_exporter/instrumentation/delayed_job.rb +9 -3
  14. data/lib/prometheus_exporter/instrumentation/method_profiler.rb +2 -0
  15. data/lib/prometheus_exporter/instrumentation/process.rb +1 -12
  16. data/lib/prometheus_exporter/instrumentation/puma.rb +4 -2
  17. data/lib/prometheus_exporter/instrumentation/shoryuken.rb +31 -0
  18. data/lib/prometheus_exporter/instrumentation/sidekiq.rb +9 -5
  19. data/lib/prometheus_exporter/instrumentation/unicorn.rb +8 -6
  20. data/lib/prometheus_exporter/instrumentation.rb +5 -0
  21. data/lib/prometheus_exporter/metric/base.rb +8 -0
  22. data/lib/prometheus_exporter/metric/counter.rb +9 -1
  23. data/lib/prometheus_exporter/metric/gauge.rb +9 -1
  24. data/lib/prometheus_exporter/metric/histogram.rb +14 -0
  25. data/lib/prometheus_exporter/metric/summary.rb +15 -1
  26. data/lib/prometheus_exporter/metric.rb +2 -0
  27. data/lib/prometheus_exporter/server/active_record_collector.rb +56 -0
  28. data/lib/prometheus_exporter/server/collector.rb +9 -2
  29. data/lib/prometheus_exporter/server/collector_base.rb +2 -0
  30. data/lib/prometheus_exporter/server/delayed_job_collector.rb +14 -1
  31. data/lib/prometheus_exporter/server/hutch_collector.rb +2 -0
  32. data/lib/prometheus_exporter/server/process_collector.rb +1 -0
  33. data/lib/prometheus_exporter/server/puma_collector.rb +11 -1
  34. data/lib/prometheus_exporter/server/runner.rb +8 -30
  35. data/lib/prometheus_exporter/server/shoryuken_collector.rb +59 -0
  36. data/lib/prometheus_exporter/server/sidekiq_collector.rb +2 -0
  37. data/lib/prometheus_exporter/server/type_collector.rb +2 -0
  38. data/lib/prometheus_exporter/server/unicorn_collector.rb +8 -1
  39. data/lib/prometheus_exporter/server/web_server.rb +2 -1
  40. data/lib/prometheus_exporter/server.rb +4 -0
  41. data/lib/prometheus_exporter/version.rb +3 -1
  42. data/lib/prometheus_exporter.rb +14 -0
  43. data/prometheus_exporter.gemspec +17 -13
  44. metadata +38 -6
  45. data/.travis +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71b406ba7c421d68e71912c260c0f520e31ee1f77eaf1fa1d5c1c2787f2de7d4
4
- data.tar.gz: abdb9ec293106feb25d997887558229fe293f6530fb7799d03b94c6183f91a29
3
+ metadata.gz: c77ede3528c6008266620498f698c8a0e9e17bfa0c0691fedf5a3c4c3bdb8ebd
4
+ data.tar.gz: 62f8b159f68036f05b785fea4eebef48df66606b083136eda5c37e4d3c294132
5
5
  SHA512:
6
- metadata.gz: b09926d30a09bd16df3afa1b55dddc37016d7ce93e4c42d54d874e6f04bc0b2f44ea8ee32211e09abe02ca1ee296bd02e0087640832d6f7854efc7ca300c8c32
7
- data.tar.gz: 3c7d52250ce85c35a461959273b98b6d51db63ff33aa926a34b0c82dc447690dad66e557e7f0cde2bf3c0f99eaad615109c0ad8df7d8b81b36d0238a05515cab
6
+ metadata.gz: 2c0699014753102b514f1bd6fa965cd82f6d83c34084daf4a3b07537b9224295a1df02aedce3082588e44f4c001e71ea83178456bd7efd8a8eae836b2fc6e9a7
7
+ data.tar.gz: df738517c22422ba9e4e2e7cfd6ea6bbcf7542175a827b1632e7ff69af4651d4acf45bcec9693c4dd899b9b305b952fac989999ebea23013f3ddad0d3c396e00
data/.travis.yml CHANGED
@@ -2,6 +2,11 @@ sudo: required
2
2
  language: ruby
3
3
  dist: trusty
4
4
  rvm:
5
- - 2.5.0
5
+ - 2.3
6
+ - 2.4
7
+ - 2.5
8
+ - 2.6
6
9
  before_install: gem install bundler -v 1.16.1
7
10
  before_script: sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'
11
+
12
+ script: bundle exec rubocop && bundle exec rake
data/CHANGELOG CHANGED
@@ -1,3 +1,46 @@
1
+ 0.5.0 - 14-02-2019
2
+
3
+ - Breaking change: listen only to localhost by default to prevent unintended insecure configuration
4
+ - FIX: Avoid calling `hostname` aggressively, instead cache it on the exporter instance
5
+
6
+ 0.4.17 - 13-01-2019
7
+
8
+ - FEATURE: add support for `to_h` on all metrics which can be used to query existing key/values
9
+
10
+ 0.4.16 - 04-11-2019
11
+
12
+ - FEATURE: Support #reset! on all metric types to reset a metric to default
13
+
14
+ 0.4.15 - 04-11-2019
15
+
16
+ - FEATURE: Improve delayed job collector, add pending counts
17
+ - FEATURE: New ActiveRecord collector (documented in readme)
18
+ - FEATURE: Allow passing in histogram and summary options
19
+ - FEATURE: Allow custom labels for unicorn collector
20
+
21
+ 0.4.14 - 10-09-2019
22
+
23
+ - FEATURE: allow finding metrics by name RemoteMetric #find_registered_metric
24
+ - FIX: guard socket closing
25
+
26
+ 0.4.13 - 09-07-2019
27
+
28
+ - Fix: Memory leak in unicorn and puma collectors
29
+
30
+ 0.4.12 - 30-05-2019
31
+
32
+ - Fix: unicorn collector reporting incorrect number of unicorn workers
33
+
34
+ 0.4.11 - 15-05-2019
35
+
36
+ - Fix: Handle stopping nil worker_threads in Client
37
+ - Dev: add frozen string literals
38
+
39
+ 0.4.10 - 29-04-2019
40
+
41
+ - Fix: Custom label support for puma collector
42
+ - Fix: Raindrops socket collector not working correctly
43
+
1
44
  0.4.9 - 11-04-2019
2
45
 
3
46
  - Fix: Gem was not working correctly in Ruby 2.4 and below due to a syntax error
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  guard :minitest do
2
4
  # with Minitest::Unit
3
5
  watch(%r{^test/(.*)\/?(.*)_test\.rb$})
data/README.md CHANGED
@@ -13,10 +13,12 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
13
13
  * [Rails integration](#rails-integration)
14
14
  * [Per-process stats](#per-process-stats)
15
15
  * [Sidekiq metrics](#sidekiq-metrics)
16
+ * [Shoryuken metrics](#shoryuken-metrics)
17
+ * [ActiveRecord Connection Pool Metrics](#activerecord-connection-pool-metrics)
16
18
  * [Delayed Job plugin](#delayed-job-plugin)
17
19
  * [Hutch metrics](#hutch-message-processing-tracer)
18
20
  * [Puma metrics](#puma-metrics)
19
- * [Unicorn metrics](#unicorn-metrics)
21
+ * [Unicorn metrics](#unicorn-process-metrics)
20
22
  * [Custom type collectors](#custom-type-collectors)
21
23
  * [Multi process mode with custom collector](#multi-process-mode-with-custom-collector)
22
24
  * [GraphQL support](#graphql-support)
@@ -61,8 +63,9 @@ require 'prometheus_exporter/server'
61
63
  require 'prometheus_exporter/client'
62
64
  require 'prometheus_exporter/instrumentation'
63
65
 
66
+ # bind is the address, on which the webserver will listen
64
67
  # port is the port that will provide the /metrics route
65
- server = PrometheusExporter::Server::WebServer.new port: 12345
68
+ server = PrometheusExporter::Server::WebServer.new bind: 'localhost', port: 12345
66
69
  server.start
67
70
 
68
71
  # wire up a default local client
@@ -114,7 +117,7 @@ In some cases (for example, unicorn or puma clusters) you may want to aggregate
114
117
 
115
118
  Simplest way to achieve this is to use the built-in collector.
116
119
 
117
- First, run an exporter on your desired port (we use the default port of 9394):
120
+ First, run an exporter on your desired port (we use the default bind to localhost and port of 9394):
118
121
 
119
122
  ```
120
123
  $ prometheus_exporter
@@ -148,6 +151,17 @@ awesome 10
148
151
 
149
152
  ```
150
153
 
154
+ Custom quantiles for summaries and buckets for histograms can also be passed in.
155
+
156
+ ```ruby
157
+ require 'prometheus_exporter/client'
158
+
159
+ client = PrometheusExporter::Client.default
160
+ histogram = client.register(:histogram, "api_time", "time to call api", buckets: [0.1, 0.5, 1])
161
+
162
+ histogram.observe(0.2, api: 'twitter')
163
+ ```
164
+
151
165
  ### Rails integration
152
166
 
153
167
  You can easily integrate into any Rack application.
@@ -175,6 +189,60 @@ Ensure you run the exporter in a monitored background process:
175
189
  $ bundle exec prometheus_exporter
176
190
  ```
177
191
 
192
+ #### Activerecord Connection Pool Metrics
193
+
194
+ This collects activerecord connection pool metrics.
195
+
196
+ It supports injection of custom labels and the connection config options (`username`, `database`, `host`, `port`) as labels.
197
+
198
+ For Puma single mode
199
+ ```ruby
200
+ #in puma.rb
201
+ require 'prometheus_exporter/instrumentation'
202
+ PrometheusExporter::Instrumentation::ActiveRecord.start(
203
+ custom_labels: { type: "puma_single_mode" }, #optional params
204
+ config_labels: [:database, :host] #optional params
205
+ )
206
+ ```
207
+
208
+ For Puma cluster mode
209
+
210
+ ```ruby
211
+ # in puma.rb
212
+ on_worker_boot do
213
+ require 'prometheus_exporter/instrumentation'
214
+ PrometheusExporter::Instrumentation::ActiveRecord.start(
215
+ custom_labels: { type: "puma_worker" }, #optional params
216
+ config_labels: [:database, :host] #optional params
217
+ )
218
+ end
219
+ ```
220
+
221
+ For Unicorn / Passenger
222
+
223
+ ```ruby
224
+ after_fork do |_server, _worker|
225
+ require 'prometheus_exporter/instrumentation'
226
+ PrometheusExporter::Instrumentation::ActiveRecord.start(
227
+ custom_labels: { type: "unicorn_worker" }, #optional params
228
+ config_labels: [:database, :host] #optional params
229
+ )
230
+ end
231
+ ```
232
+
233
+ For Sidekiq
234
+ ```ruby
235
+ Sidekiq.configure_server do |config|
236
+ config.on :startup do
237
+ require 'prometheus_exporter/instrumentation'
238
+ PrometheusExporter::Instrumentation::ActiveRecord.start(
239
+ custom_labels: { type: "sidekiq" }, #optional params
240
+ config_labels: [:database, :host] #optional params
241
+ )
242
+ end
243
+ end
244
+ ```
245
+
178
246
  #### Per-process stats
179
247
 
180
248
  You may also be interested in per-process stats. This collects memory and GC stats:
@@ -231,6 +299,19 @@ Sometimes the Sidekiq server shuts down before it can send metrics, that were ge
231
299
  end
232
300
  ```
233
301
 
302
+ #### Shoryuken metrics
303
+
304
+ For Shoryuken metrics (how many jobs ran? how many failed? how long did they take? how many were restarted?)
305
+
306
+ ```ruby
307
+ Shoryuken.configure_server do |config|
308
+ config.server_middleware do |chain|
309
+ require 'prometheus_exporter/instrumentation'
310
+ chain.add PrometheusExporter::Instrumentation::Shoryuken
311
+ end
312
+ end
313
+ ```
314
+
234
315
  #### Delayed Job plugin
235
316
 
236
317
  In an initializer:
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
3
5
 
data/bench/bench.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/prometheus_exporter'
2
4
  require_relative '../lib/prometheus_exporter/client'
3
5
  require_relative '../lib/prometheus_exporter/server'
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'optparse'
4
5
 
@@ -18,6 +19,12 @@ def run
18
19
  "Port exporter should listen on (default: #{PrometheusExporter::DEFAULT_PORT})") do |o|
19
20
  options[:port] = o.to_i
20
21
  end
22
+ opt.on('-b',
23
+ '--bind STRING',
24
+ String,
25
+ "IP address exporter should listen on (default: #{PrometheusExporter::DEFAULT_BIND_ADDRESS})") do |o|
26
+ options[:bind] = o.to_s
27
+ end
21
28
  opt.on('-t',
22
29
  '--timeout INTEGER',
23
30
  Integer,
@@ -80,7 +87,7 @@ def run
80
87
 
81
88
  runner = PrometheusExporter::Server::Runner.new(options)
82
89
 
83
- puts "#{Time.now} Starting prometheus exporter on port #{runner.port}"
90
+ puts "#{Time.now} Starting prometheus exporter on #{runner.bind}:#{runner.port}"
84
91
  runner.start
85
92
  sleep
86
93
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class MyCustomCollector < PrometheusExporter::Server::Collector
2
4
  def initialize
3
5
  @gauge1 = PrometheusExporter::Metric::Gauge.new("thing1", "I am thing 1")
@@ -4,14 +4,16 @@ require 'socket'
4
4
  require 'thread'
5
5
 
6
6
  module PrometheusExporter
7
-
8
7
  class Client
9
8
  class RemoteMetric
10
- def initialize(name:, help:, type:, client:)
9
+ attr_reader :name, :type, :help
10
+
11
+ def initialize(name:, help:, type:, client:, opts: nil)
11
12
  @name = name
12
13
  @help = help
13
14
  @client = client
14
15
  @type = type
16
+ @opts = opts
15
17
  end
16
18
 
17
19
  def standard_values(value, keys, prometheus_exporter_action = nil)
@@ -23,6 +25,7 @@ module PrometheusExporter
23
25
  value: value
24
26
  }
25
27
  values[:prometheus_exporter_action] = prometheus_exporter_action if prometheus_exporter_action
28
+ values[:opts] = @opts if @opts
26
29
  values
27
30
  end
28
31
 
@@ -37,7 +40,6 @@ module PrometheusExporter
37
40
  def decrement(keys = nil, value = 1)
38
41
  @client.send_json(standard_values(value, keys, :decrement))
39
42
  end
40
-
41
43
  end
42
44
 
43
45
  def self.default
@@ -81,12 +83,22 @@ module PrometheusExporter
81
83
  @custom_labels = custom_labels
82
84
  end
83
85
 
84
- def register(type, name, help)
85
- metric = RemoteMetric.new(type: type, name: name, help: help, client: self)
86
+ def register(type, name, help, opts = nil)
87
+ metric = RemoteMetric.new(type: type, name: name, help: help, client: self, opts: opts)
86
88
  @metrics << metric
87
89
  metric
88
90
  end
89
91
 
92
+ def find_registered_metric(name, type: nil, help: nil)
93
+ @metrics.find do |metric|
94
+ type_match = type ? metric.type == type : true
95
+ help_match = help ? metric.help == help : true
96
+ name_match = metric.name == name
97
+
98
+ type_match && help_match && name_match
99
+ end
100
+ end
101
+
90
102
  def send_json(obj)
91
103
  payload = @custom_labels.nil? ? obj : obj.merge(custom_labels: @custom_labels)
92
104
  send(@json_serializer.dump(payload))
@@ -124,12 +136,12 @@ module PrometheusExporter
124
136
  @mutex.synchronize do
125
137
  wait_for_empty_queue_with_timeout(wait_timeout_seconds)
126
138
  @worker_thread&.kill
127
- while @worker_thread.alive?
139
+ while @worker_thread&.alive?
128
140
  sleep 0.001
129
141
  end
130
142
  @worker_thread = nil
131
- end
132
143
  close_socket!
144
+ end
133
145
  end
134
146
 
135
147
  private
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ # collects stats from currently running process
4
+ module PrometheusExporter::Instrumentation
5
+ class ActiveRecord
6
+ ALLOWED_CONFIG_LABELS = %i(database username host port)
7
+
8
+ def self.start(client: nil, frequency: 30, custom_labels: {}, config_labels: [])
9
+
10
+ # Not all rails versions support coonection pool stats
11
+ unless ::ActiveRecord::Base.connection_pool.respond_to?(:stat)
12
+ STDERR.puts("ActiveRecord connection pool stats not supported in your rails version")
13
+ return
14
+ end
15
+
16
+ config_labels.map!(&:to_sym)
17
+ validate_config_labels(config_labels)
18
+
19
+ active_record_collector = new(custom_labels, config_labels)
20
+
21
+ client ||= PrometheusExporter::Client.default
22
+
23
+ stop if @thread
24
+
25
+ @thread = Thread.new do
26
+ while true
27
+ begin
28
+ metrics = active_record_collector.collect
29
+ metrics.each { |metric| client.send_json metric }
30
+ rescue => e
31
+ STDERR.puts("Prometheus Exporter Failed To Collect Process Stats #{e}")
32
+ ensure
33
+ sleep frequency
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.validate_config_labels(config_labels)
40
+ return if config_labels.size == 0
41
+ raise "Invalid Config Labels, available options #{ALLOWED_CONFIG_LABELS}" if (config_labels - ALLOWED_CONFIG_LABELS).size > 0
42
+ end
43
+
44
+ def self.stop
45
+ if t = @thread
46
+ t.kill
47
+ @thread = nil
48
+ end
49
+ end
50
+
51
+ def initialize(metric_labels, config_labels)
52
+ @metric_labels = metric_labels
53
+ @config_labels = config_labels
54
+ end
55
+
56
+ def collect
57
+ metrics = []
58
+ collect_active_record_pool_stats(metrics)
59
+ metrics
60
+ end
61
+
62
+ def pid
63
+ @pid = ::Process.pid
64
+ end
65
+
66
+ def collect_active_record_pool_stats(metrics)
67
+ ObjectSpace.each_object(::ActiveRecord::ConnectionAdapters::ConnectionPool) do |pool|
68
+ next if pool.connections.nil?
69
+
70
+ labels_from_config = pool.spec.config
71
+ .select { |k, v| @config_labels.include? k }
72
+ .map { |k, v| [k.to_s.prepend("dbconfig_"), v] }
73
+
74
+ labels = @metric_labels.merge(pool_name: pool.spec.name).merge(Hash[labels_from_config])
75
+
76
+ metric = {
77
+ pid: pid,
78
+ type: "active_record",
79
+ hostname: ::PrometheusExporter.hostname,
80
+ metric_labels: labels
81
+ }
82
+ metric.merge!(pool.stat)
83
+ metrics << metric
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PrometheusExporter::Instrumentation
2
4
  class DelayedJob
3
5
  JOB_CLASS_REGEXP = %r{job_class: (\w+:{0,2})+}.freeze
@@ -11,7 +13,9 @@ module PrometheusExporter::Instrumentation
11
13
  callbacks do |lifecycle|
12
14
  lifecycle.around(:invoke_job) do |job, *args, &block|
13
15
  max_attempts = Delayed::Worker.max_attempts
14
- instrumenter.call(job, max_attempts, *args, &block)
16
+ enqueued_count = Delayed::Job.count
17
+ pending_count = Delayed::Job.where(attempts: 0, locked_at: nil).count
18
+ instrumenter.call(job, max_attempts, enqueued_count, pending_count, *args, &block)
15
19
  end
16
20
  end
17
21
  end
@@ -24,7 +28,7 @@ module PrometheusExporter::Instrumentation
24
28
  @client = client || PrometheusExporter::Client.default
25
29
  end
26
30
 
27
- def call(job, max_attempts, *args, &block)
31
+ def call(job, max_attempts, enqueued_count, pending_count, *args, &block)
28
32
  success = false
29
33
  start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
30
34
  attempts = job.attempts + 1 # Increment because we're adding the current attempt
@@ -40,7 +44,9 @@ module PrometheusExporter::Instrumentation
40
44
  success: success,
41
45
  duration: duration,
42
46
  attempts: attempts,
43
- max_attempts: max_attempts
47
+ max_attempts: max_attempts,
48
+ enqueued: enqueued_count,
49
+ pending: pending_count
44
50
  )
45
51
  end
46
52
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # see https://samsaffron.com/archive/2017/10/18/fastest-way-to-profile-a-method-in-ruby
2
4
  module PrometheusExporter::Instrumentation; end
3
5
 
@@ -42,24 +42,13 @@ module PrometheusExporter::Instrumentation
42
42
 
43
43
  def initialize(metric_labels)
44
44
  @metric_labels = metric_labels
45
- @hostname = nil
46
- end
47
-
48
- def hostname
49
- @hostname ||=
50
- begin
51
- `hostname`.strip
52
- rescue => e
53
- STDERR.puts "Unable to lookup hostname #{e}"
54
- "unknown-host"
55
- end
56
45
  end
57
46
 
58
47
  def collect
59
48
  metric = {}
60
49
  metric[:type] = "process"
61
50
  metric[:metric_labels] = @metric_labels
62
- metric[:hostname] = hostname
51
+ metric[:hostname] = ::PrometheusExporter.hostname
63
52
  collect_gc_stats(metric)
64
53
  collect_v8_stats(metric)
65
54
  collect_process_stats(metric)
@@ -1,4 +1,6 @@
1
- require 'json'
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
2
4
 
3
5
  # collects stats from puma
4
6
  module PrometheusExporter::Instrumentation
@@ -30,7 +32,7 @@ module PrometheusExporter::Instrumentation
30
32
  def collect_puma_stats(metric)
31
33
  stats = JSON.parse(::Puma.stats)
32
34
 
33
- if stats.key? 'workers'
35
+ if stats.key?("workers")
34
36
  metric[:phase] = stats["phase"]
35
37
  metric[:workers_total] = stats["workers"]
36
38
  metric[:booted_workers_total] = stats["booted_workers"]
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Instrumentation
4
+ class Shoryuken
5
+
6
+ def initialize(client: nil)
7
+ @client = client || PrometheusExporter::Client.default
8
+ end
9
+
10
+ def call(worker, queue, msg, body)
11
+ success = false
12
+ start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
13
+ result = yield
14
+ success = true
15
+ result
16
+ rescue ::Shoryuken::Shutdown => e
17
+ shutdown = true
18
+ raise e
19
+ ensure
20
+ duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
21
+ @client.send_json(
22
+ type: "shoryuken",
23
+ queue: queue,
24
+ name: worker.class.name,
25
+ success: success,
26
+ shutdown: shutdown,
27
+ duration: duration
28
+ )
29
+ end
30
+ end
31
+ end
@@ -4,11 +4,15 @@ module PrometheusExporter::Instrumentation
4
4
  class Sidekiq
5
5
  def self.death_handler
6
6
  -> (job, ex) do
7
- PrometheusExporter::Client.default.send_json(
8
- type: "sidekiq",
9
- name: job["class"],
10
- dead: true,
11
- )
7
+ job_is_fire_and_forget = job["retry"] == false
8
+
9
+ unless job_is_fire_and_forget
10
+ PrometheusExporter::Client.default.send_json(
11
+ type: "sidekiq",
12
+ name: job["class"],
13
+ dead: true,
14
+ )
15
+ end
12
16
  end
13
17
  end
14
18
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'raindrops'
3
5
  rescue LoadError
@@ -7,7 +9,7 @@ end
7
9
  module PrometheusExporter::Instrumentation
8
10
  # collects stats from unicorn
9
11
  class Unicorn
10
- def self.start(pid_file:, listener_address:, client:, frequency: 30)
12
+ def self.start(pid_file:, listener_address:, client: nil, frequency: 30)
11
13
  unicorn_collector = new(pid_file: pid_file, listener_address: listener_address)
12
14
  client ||= PrometheusExporter::Client.default
13
15
  Thread.new do
@@ -49,21 +51,21 @@ module PrometheusExporter::Instrumentation
49
51
 
50
52
  def worker_process_count
51
53
  return nil unless File.exist?(@pid_file)
52
- pid = File.read(@pid_file)
54
+ pid = File.read(@pid_file).to_i
53
55
 
54
- return nil unless pid && pid.to_i > 0
56
+ return nil if pid < 1
55
57
 
56
58
  # find all processes whose parent is the unicorn master
57
59
  # but we're actually only interested in the number of processes (= lines of output)
58
- result = `ps --no-header -o pid --ppid #{pid}`
60
+ result = `pgrep -P #{pid} -f unicorn -a`
59
61
  result.lines.count
60
62
  end
61
63
 
62
64
  def listener_address_stats
63
65
  if @tcp
64
- Raindrops::Linux.tcp_listener_stats(@listener_address)[@listener_address]
66
+ Raindrops::Linux.tcp_listener_stats([@listener_address])[@listener_address]
65
67
  else
66
- Raindrops::Linux.unix_listener_stats(@listener_address)[@listener_address]
68
+ Raindrops::Linux.unix_listener_stats([@listener_address])[@listener_address]
67
69
  end
68
70
  end
69
71
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "client"
1
4
  require_relative "instrumentation/process"
2
5
  require_relative "instrumentation/method_profiler"
3
6
  require_relative "instrumentation/sidekiq"
@@ -5,3 +8,5 @@ require_relative "instrumentation/delayed_job"
5
8
  require_relative "instrumentation/puma"
6
9
  require_relative "instrumentation/hutch"
7
10
  require_relative "instrumentation/unicorn"
11
+ require_relative "instrumentation/active_record"
12
+ require_relative "instrumentation/shoryuken"
@@ -34,6 +34,14 @@ module PrometheusExporter::Metric
34
34
  raise "Not implemented"
35
35
  end
36
36
 
37
+ def reset!
38
+ raise "Not implemented"
39
+ end
40
+
41
+ def to_h
42
+ raise "Not implemented"
43
+ end
44
+
37
45
  def from_json(json)
38
46
  json = JSON.parse(json) if String === json
39
47
  @name = json["name"]
@@ -6,19 +6,27 @@ module PrometheusExporter::Metric
6
6
 
7
7
  def initialize(name, help)
8
8
  super
9
- @data = {}
9
+ reset!
10
10
  end
11
11
 
12
12
  def type
13
13
  "counter"
14
14
  end
15
15
 
16
+ def reset!
17
+ @data = {}
18
+ end
19
+
16
20
  def metric_text
17
21
  @data.map do |labels, value|
18
22
  "#{prefix(@name)}#{labels_text(labels)} #{value}"
19
23
  end.join("\n")
20
24
  end
21
25
 
26
+ def to_h
27
+ @data.dup
28
+ end
29
+
22
30
  def observe(increment = 1, labels = {})
23
31
  @data[labels] ||= 0
24
32
  @data[labels] += increment