prometheus_exporter 0.7.0 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f949348bbafc06b8f7a3dd3927693de8fef54090d85a7889afbfbf7dcb167b6
4
- data.tar.gz: adeca8000ebb59c7225c1453135900aeab5c1760902edd5b4fecf9fa4b879a57
3
+ metadata.gz: 562647915cd81e672056a83fc79a031d2a0ebb2b4a7a5e32f5786ae5c7d480a7
4
+ data.tar.gz: 525e28d0cbe4e853c91bb463970fd231b134dba6b5a190c61a2ff7710074fb7b
5
5
  SHA512:
6
- metadata.gz: 7d3829ad57f03a19f6081c3444f9b13906aedd4fa1c5b99237d454096e0d993c27c2df0993482c421b6034a58a55c9603620bcdf7698248e4ac23a5063d63718
7
- data.tar.gz: fb915716acbbb4f24a152c8ebf9eb35bdc97185a7144eb986e50aafad07a36d5cc83cbbbf7c16e8b399207e9c66d90973d2e6f9ddcdc8a17cab797840d13ed8b
6
+ metadata.gz: 324b3b0acd1c97e8b5535dff8f57500b4dc3f2433456372c609accbc78d1baf839d82a1e38eabb543f043bbc9fefb401ab1460ba252ee8b9330fe0a91acffc1d
7
+ data.tar.gz: b2a0082d4e2431cc1f24f11a6af86d6965e2db7c93df1021ed58b38ff8651c84d1e8939b42a13b1b7cac60c9237435ff838a24918ed0ee18d13c3df2aece76d4
data/Appraisals CHANGED
@@ -6,5 +6,5 @@ appraise "ar-60" do
6
6
  end
7
7
 
8
8
  appraise "ar-61" do
9
- gem "activerecord", "~> 6.1.0.rc2"
9
+ gem "activerecord", "~> 6.1.1"
10
10
  end
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ 0.8.0 - 05-07-2021
2
+
3
+ - FIX: handle ThreadError more gracefully in cases where process shuts down
4
+ - FEATURE: add job_name and queue_name labels to delayed job metrics
5
+ - FEATURE: always scope puma metrics on hostname in collector
6
+ - FEATURE: add customizable labels option to puma collector
7
+ - FEATURE: support for Rescue
8
+ - DEV: Remove support for EOL ruby 2.5
9
+ - FIX: Add source location to MethodProfiler patches
10
+ - FEATURE: Improve Active Record instrumentation
11
+ - FEATURE: Support HTTP_X_AMZN_TRACE_ID when supplied
12
+
1
13
  0.7.0 - 29-12-2020
2
14
 
3
15
  - Dev: Removed support from EOL rubies, only 2.5, 2.6, 2.7 and 3.0 are supported now.
data/README.md CHANGED
@@ -19,6 +19,7 @@ To learn more see [Instrumenting Rails with Prometheus](https://samsaffron.com/a
19
19
  * [Hutch metrics](#hutch-message-processing-tracer)
20
20
  * [Puma metrics](#puma-metrics)
21
21
  * [Unicorn metrics](#unicorn-process-metrics)
22
+ * [Resque metrics](#resque-metrics)
22
23
  * [Custom type collectors](#custom-type-collectors)
23
24
  * [Multi process mode with custom collector](#multi-process-mode-with-custom-collector)
24
25
  * [GraphQL support](#graphql-support)
@@ -478,6 +479,8 @@ end
478
479
  | Summary | `delayed_job_duration_seconds_summary` | Summary of the time it takes jobs to execute | `status` |
479
480
  | Summary | `delayed_job_attempts_summary` | Summary of the amount of attempts it takes delayed jobs to succeed | - |
480
481
 
482
+ All metrics have labels for `job_name` and `queue_name`.
483
+
481
484
  #### Hutch Message Processing Tracer
482
485
 
483
486
  Capture [Hutch](https://github.com/gocardless/hutch) metrics (how many jobs ran? how many failed? how long did they take?)
@@ -505,7 +508,7 @@ Request Queueing is defined as the time it takes for a request to reach your app
505
508
 
506
509
  As this metric starts before `prometheus_exporter` can handle the request, you must add a specific HTTP header as early in your infrastructure as possible (we recommend your load balancer or reverse proxy).
507
510
 
508
- Configure your HTTP server / load balancer to add a header `X-Request-Start: t=<MSEC>` when passing the request upstream. For more information, please consult your software manual.
511
+ The Amazon Application Load Balancer [request tracing header](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html) is natively supported. If you are using another upstream entrypoint, you may configure your HTTP server / load balancer to add a header `X-Request-Start: t=<MSEC>` when passing the request upstream. For more information, please consult your software manual.
509
512
 
510
513
  Hint: we aim to be API-compatible with the big APM solutions, so if you've got requests queueing time configured for them, it should be expected to also work with `prometheus_exporter`.
511
514
 
@@ -535,7 +538,29 @@ end
535
538
  | Gauge | `puma_thread_pool_capacity_total` | Number of puma threads available at current scale |
536
539
  | Gauge | `puma_max_threads_total` | Number of puma threads at available at max scale |
537
540
 
538
- All metrics may have a `phase` label.
541
+ All metrics may have a `phase` label and all custom labels provided with the `labels` option.
542
+
543
+ ### Resque metrics
544
+
545
+ The resque metrics are using the `Resque.info` method, which queries Redis internally. To start monitoring your resque
546
+ installation, you'll need to start the instrumentation:
547
+
548
+ ```ruby
549
+ # e.g. config/initializers/resque.rb
550
+ require 'prometheus_exporter/instrumentation'
551
+ PrometheusExporter::Instrumentation::Resque.start
552
+ ```
553
+
554
+ #### Metrics collected by Resque Instrumentation
555
+
556
+ | Type | Name | Description |
557
+ | --- | --- | --- |
558
+ | Gauge | `processed_jobs_total` | Total number of processed Resque jobs |
559
+ | Gauge | `failed_jobs_total` | Total number of failed Resque jobs |
560
+ | Gauge | `pending_jobs_total` | Total number of pending Resque jobs |
561
+ | Gauge | `queues_total` | Total number of Resque queues |
562
+ | Gauge | `workers_total` | Total number of Resque workers running |
563
+ | Gauge | `working_total` | Total number of Resque workers working |
539
564
 
540
565
  ### Unicorn process metrics
541
566
 
@@ -184,6 +184,9 @@ module PrometheusExporter
184
184
  end
185
185
  end
186
186
  end
187
+ rescue ThreadError => e
188
+ raise unless e.message =~ /can't alloc thread/
189
+ STDERR.puts "Prometheus Exporter, failed to send message ThreadError #{e}"
187
190
  end
188
191
 
189
192
  def close_socket!
@@ -11,3 +11,4 @@ require_relative "instrumentation/hutch"
11
11
  require_relative "instrumentation/unicorn"
12
12
  require_relative "instrumentation/active_record"
13
13
  require_relative "instrumentation/shoryuken"
14
+ require_relative "instrumentation/resque"
@@ -81,15 +81,13 @@ module PrometheusExporter::Instrumentation
81
81
  private
82
82
 
83
83
  def labels(pool)
84
- if pool.respond_to?(:spec) # ActiveRecord <= 6.0
84
+ if ::ActiveRecord.version < Gem::Version.new("6.1.0.rc1")
85
85
  @metric_labels.merge(pool_name: pool.spec.name).merge(pool.spec.config
86
86
  .select { |k, v| @config_labels.include? k }
87
87
  .map { |k, v| [k.to_s.dup.prepend("dbconfig_"), v] }.to_h)
88
- elsif pool.respond_to?(:db_config) # ActiveRecord >= 6.1.rc1
88
+ else
89
89
  @metric_labels.merge(pool_name: pool.db_config.name).merge(
90
90
  @config_labels.each_with_object({}) { |l, acc| acc["dbconfig_#{l}"] = pool.db_config.public_send(l) })
91
- else
92
- raise "Unsupported connection pool"
93
91
  end
94
92
  end
95
93
  end
@@ -13,8 +13,8 @@ module PrometheusExporter::Instrumentation
13
13
  callbacks do |lifecycle|
14
14
  lifecycle.around(:invoke_job) do |job, *args, &block|
15
15
  max_attempts = Delayed::Worker.max_attempts
16
- enqueued_count = Delayed::Job.count
17
- pending_count = Delayed::Job.where(attempts: 0, locked_at: nil).count
16
+ enqueued_count = Delayed::Job.where(queue: job.queue).count
17
+ pending_count = Delayed::Job.where(attempts: 0, locked_at: nil, queue: job.queue).count
18
18
  instrumenter.call(job, max_attempts, enqueued_count, pending_count, *args, &block)
19
19
  end
20
20
  end
@@ -41,6 +41,7 @@ module PrometheusExporter::Instrumentation
41
41
  @client.send_json(
42
42
  type: "delayed_job",
43
43
  name: job.handler.to_s.match(JOB_CLASS_REGEXP).to_a[1].to_s,
44
+ queue_name: job.queue,
44
45
  success: success,
45
46
  duration: duration,
46
47
  attempts: attempts,
@@ -5,6 +5,7 @@ module PrometheusExporter::Instrumentation; end
5
5
 
6
6
  class PrometheusExporter::Instrumentation::MethodProfiler
7
7
  def self.patch(klass, methods, name)
8
+ patch_source_line = __LINE__ + 3
8
9
  patches = methods.map do |method_name|
9
10
  <<~RUBY
10
11
  unless defined?(#{method_name}__mp_unpatched)
@@ -26,7 +27,7 @@ class PrometheusExporter::Instrumentation::MethodProfiler
26
27
  RUBY
27
28
  end.join("\n")
28
29
 
29
- klass.class_eval patches
30
+ klass.class_eval patches, __FILE__, patch_source_line
30
31
  end
31
32
 
32
33
  def self.transfer
@@ -5,8 +5,8 @@ require "json"
5
5
  # collects stats from puma
6
6
  module PrometheusExporter::Instrumentation
7
7
  class Puma
8
- def self.start(client: nil, frequency: 30)
9
- puma_collector = new
8
+ def self.start(client: nil, frequency: 30, labels: {})
9
+ puma_collector = new(labels)
10
10
  client ||= PrometheusExporter::Client.default
11
11
  Thread.new do
12
12
  while true
@@ -22,13 +22,25 @@ module PrometheusExporter::Instrumentation
22
22
  end
23
23
  end
24
24
 
25
+ def initialize(metric_labels = {})
26
+ @metric_labels = metric_labels
27
+ end
28
+
25
29
  def collect
26
- metric = {}
27
- metric[:type] = "puma"
30
+ metric = {
31
+ pid: pid,
32
+ type: "puma",
33
+ hostname: ::PrometheusExporter.hostname,
34
+ metric_labels: @metric_labels
35
+ }
28
36
  collect_puma_stats(metric)
29
37
  metric
30
38
  end
31
39
 
40
+ def pid
41
+ @pid = ::Process.pid
42
+ end
43
+
32
44
  def collect_puma_stats(metric)
33
45
  stats = JSON.parse(::Puma.stats)
34
46
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # collects stats from resque
4
+ module PrometheusExporter::Instrumentation
5
+ class Resque
6
+ def self.start(client: nil, frequency: 30)
7
+ resque_collector = new
8
+ client ||= PrometheusExporter::Client.default
9
+ Thread.new do
10
+ while true
11
+ begin
12
+ client.send_json(resque_collector.collect)
13
+ rescue => e
14
+ STDERR.puts("Prometheus Exporter Failed To Collect Resque Stats #{e}")
15
+ ensure
16
+ sleep frequency
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ def collect
23
+ metric = {}
24
+ metric[:type] = "resque"
25
+ collect_resque_stats(metric)
26
+ metric
27
+ end
28
+
29
+ def collect_resque_stats(metric)
30
+ info = ::Resque.info
31
+
32
+ metric[:processed_jobs_total] = info[:processed]
33
+ metric[:failed_jobs_total] = info[:failed]
34
+ metric[:pending_jobs_total] = info[:pending]
35
+ metric[:queues_total] = info[:queues]
36
+ metric[:worker_total] = info[:workers]
37
+ metric[:working_total] = info[:working]
38
+ end
39
+ end
40
+ end
@@ -90,19 +90,24 @@ class PrometheusExporter::Middleware
90
90
  Process.clock_gettime(Process::CLOCK_REALTIME)
91
91
  end
92
92
 
93
- # get the content of the x-queue-start or x-request-start header
93
+ # determine queue start from well-known trace headers
94
94
  def queue_start(env)
95
+
96
+ # get the content of the x-queue-start or x-request-start header
95
97
  value = env['HTTP_X_REQUEST_START'] || env['HTTP_X_QUEUE_START']
96
98
  unless value.nil? || value == ''
97
- convert_header_to_ms(value.to_s)
99
+ # nginx returns time as milliseconds with 3 decimal places
100
+ # apache returns time as microseconds without decimal places
101
+ # this method takes care to convert both into a proper second + fractions timestamp
102
+ value = value.to_s.gsub(/t=|\./, '')
103
+ return "#{value[0, 10]}.#{value[10, 13]}".to_f
98
104
  end
99
- end
100
105
 
101
- # nginx returns time as milliseconds with 3 decimal places
102
- # apache returns time as microseconds without decimal places
103
- # this method takes care to convert both into a proper second + fractions timestamp
104
- def convert_header_to_ms(str)
105
- str = str.gsub(/t=|\./, '')
106
- "#{str[0, 10]}.#{str[10, 13]}".to_f
106
+ # get the content of the x-amzn-trace-id header
107
+ # see also: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html
108
+ value = env['HTTP_X_AMZN_TRACE_ID']
109
+ value&.split('Root=')&.last&.split('-')&.fetch(1)&.to_i(16)
110
+
107
111
  end
112
+
108
113
  end
@@ -16,3 +16,4 @@ require_relative "server/hutch_collector"
16
16
  require_relative "server/unicorn_collector"
17
17
  require_relative "server/active_record_collector"
18
18
  require_relative "server/shoryuken_collector"
19
+ require_relative "server/resque_collector"
@@ -47,7 +47,8 @@ module PrometheusExporter::Server
47
47
  obj["created_at"] = now
48
48
 
49
49
  @active_record_metrics.delete_if do |current|
50
- (obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"]) ||
50
+ (obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"] &&
51
+ obj["metric_labels"]["pool_name"] == current["metric_labels"]["pool_name"]) ||
51
52
  (current["created_at"] + MAX_ACTIVERECORD_METRIC_AGE < now)
52
53
  end
53
54
 
@@ -20,6 +20,7 @@ module PrometheusExporter::Server
20
20
  register_collector(UnicornCollector.new)
21
21
  register_collector(ActiveRecordCollector.new)
22
22
  register_collector(ShoryukenCollector.new)
23
+ register_collector(ResqueCollector.new)
23
24
  end
24
25
 
25
26
  def register_collector(collector)
@@ -19,21 +19,22 @@ module PrometheusExporter::Server
19
19
  end
20
20
 
21
21
  def collect(obj)
22
- default_labels = { job_name: obj['name'] }
22
+ default_labels = { job_name: obj['name'], queue_name: obj['queue_name'] }
23
23
  custom_labels = obj['custom_labels']
24
+
24
25
  labels = custom_labels.nil? ? default_labels : default_labels.merge(custom_labels)
25
26
 
26
27
  ensure_delayed_job_metrics
27
28
  @delayed_job_duration_seconds.observe(obj["duration"], labels)
28
29
  @delayed_jobs_total.observe(1, labels)
29
30
  @delayed_failed_jobs_total.observe(1, labels) if !obj["success"]
30
- @delayed_jobs_max_attempts_reached_total.observe(1) if obj["attempts"] >= obj["max_attempts"]
31
- @delayed_job_duration_seconds_summary.observe(obj["duration"])
32
- @delayed_job_duration_seconds_summary.observe(obj["duration"], status: "success") if obj["success"]
33
- @delayed_job_duration_seconds_summary.observe(obj["duration"], status: "failed") if !obj["success"]
34
- @delayed_job_attempts_summary.observe(obj["attempts"]) if obj["success"]
35
- @delayed_jobs_enqueued.observe(obj["enqueued"])
36
- @delayed_jobs_pending.observe(obj["pending"])
31
+ @delayed_jobs_max_attempts_reached_total.observe(1, labels) if obj["attempts"] >= obj["max_attempts"]
32
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], labels)
33
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], labels.merge(status: "success")) if obj["success"]
34
+ @delayed_job_duration_seconds_summary.observe(obj["duration"], labels.merge(status: "failed")) if !obj["success"]
35
+ @delayed_job_attempts_summary.observe(obj["attempts"], labels) if obj["success"]
36
+ @delayed_jobs_enqueued.observe(obj["enqueued"], labels)
37
+ @delayed_jobs_pending.observe(obj["pending"], labels)
37
38
  end
38
39
 
39
40
  def metrics
@@ -34,6 +34,9 @@ module PrometheusExporter::Server
34
34
  if m["custom_labels"]
35
35
  labels.merge!(m["custom_labels"])
36
36
  end
37
+ if m["metric_labels"]
38
+ labels.merge!(m["metric_labels"])
39
+ end
37
40
 
38
41
  PUMA_GAUGES.map do |k, help|
39
42
  k = k.to_s
@@ -51,7 +54,12 @@ module PrometheusExporter::Server
51
54
  now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
52
55
 
53
56
  obj["created_at"] = now
54
- @puma_metrics.delete_if { |m| m["created_at"] + MAX_PUMA_METRIC_AGE < now }
57
+
58
+ @puma_metrics.delete_if do |current|
59
+ (obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"]) ||
60
+ (current["created_at"] + MAX_PUMA_METRIC_AGE < now)
61
+ end
62
+
55
63
  @puma_metrics << obj
56
64
  end
57
65
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PrometheusExporter::Server
4
+ class ResqueCollector < TypeCollector
5
+ MAX_RESQUE_METRIC_AGE = 30
6
+ RESQUE_GAUGES = {
7
+ processed_jobs_total: "Total number of processed Resque jobs.",
8
+ failed_jobs_total: "Total number of failed Resque jobs.",
9
+ pending_jobs_total: "Total number of pending Resque jobs.",
10
+ queues_total: "Total number of Resque queues.",
11
+ workers_total: "Total number of Resque workers running.",
12
+ working_total: "Total number of Resque workers working."
13
+ }
14
+
15
+ def initialize
16
+ @resque_metrics = []
17
+ @gauges = {}
18
+ end
19
+
20
+ def type
21
+ "resque"
22
+ end
23
+
24
+ def metrics
25
+ return [] if resque_metrics.length == 0
26
+
27
+ resque_metrics.map do |metric|
28
+ labels = metric.fetch("custom_labels", {})
29
+
30
+ RESQUE_GAUGES.map do |name, help|
31
+ name = name.to_s
32
+ if value = metric[name]
33
+ gauge = gauges[name] ||= PrometheusExporter::Metric::Gauge.new("resque_#{name}", help)
34
+ gauge.observe(value, labels)
35
+ end
36
+ end
37
+ end
38
+
39
+ gauges.values
40
+ end
41
+
42
+ def collect(object)
43
+ now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
44
+
45
+ object["created_at"] = now
46
+ resque_metrics.delete_if { |metric| metric["created_at"] + MAX_RESQUE_METRIC_AGE < now }
47
+ resque_metrics << object
48
+ end
49
+
50
+ private
51
+
52
+ attr_reader :resque_metrics, :gauges
53
+ end
54
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrometheusExporter
4
- VERSION = '0.7.0'
4
+ VERSION = '0.8.0'
5
5
  end
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_dependency "webrick"
28
28
 
29
29
  spec.add_development_dependency "rubocop", ">= 0.69"
30
- spec.add_development_dependency "bundler", ">= 2.2.2"
30
+ spec.add_development_dependency "bundler", ">= 2.1.4"
31
31
  spec.add_development_dependency "rake", "~> 13.0"
32
32
  spec.add_development_dependency "minitest", "~> 5.0"
33
33
  spec.add_development_dependency "guard", "~> 2.0"
@@ -42,5 +42,5 @@ Gem::Specification.new do |spec|
42
42
  if !RUBY_ENGINE == 'jruby'
43
43
  spec.add_development_dependency "raindrops", "~> 0.19"
44
44
  end
45
- spec.required_ruby_version = '>= 2.5.0'
45
+ spec.required_ruby_version = '>= 2.6.0'
46
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus_exporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-28 00:00:00.000000000 Z
11
+ date: 2021-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webrick
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 2.2.2
47
+ version: 2.1.4
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 2.2.2
54
+ version: 2.1.4
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -240,6 +240,7 @@ files:
240
240
  - lib/prometheus_exporter/instrumentation/method_profiler.rb
241
241
  - lib/prometheus_exporter/instrumentation/process.rb
242
242
  - lib/prometheus_exporter/instrumentation/puma.rb
243
+ - lib/prometheus_exporter/instrumentation/resque.rb
243
244
  - lib/prometheus_exporter/instrumentation/shoryuken.rb
244
245
  - lib/prometheus_exporter/instrumentation/sidekiq.rb
245
246
  - lib/prometheus_exporter/instrumentation/sidekiq_queue.rb
@@ -259,6 +260,7 @@ files:
259
260
  - lib/prometheus_exporter/server/hutch_collector.rb
260
261
  - lib/prometheus_exporter/server/process_collector.rb
261
262
  - lib/prometheus_exporter/server/puma_collector.rb
263
+ - lib/prometheus_exporter/server/resque_collector.rb
262
264
  - lib/prometheus_exporter/server/runner.rb
263
265
  - lib/prometheus_exporter/server/shoryuken_collector.rb
264
266
  - lib/prometheus_exporter/server/sidekiq_collector.rb
@@ -282,14 +284,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
282
284
  requirements:
283
285
  - - ">="
284
286
  - !ruby/object:Gem::Version
285
- version: 2.5.0
287
+ version: 2.6.0
286
288
  required_rubygems_version: !ruby/object:Gem::Requirement
287
289
  requirements:
288
290
  - - ">="
289
291
  - !ruby/object:Gem::Version
290
292
  version: '0'
291
293
  requirements: []
292
- rubygems_version: 3.2.2
294
+ rubygems_version: 3.1.6
293
295
  signing_key:
294
296
  specification_version: 4
295
297
  summary: Prometheus Exporter