prometheus-client 2.1.0 → 4.0.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: 26ff1f489c371d04487bbd7fe478ac70f43ed79f7c462d18b46d9475136f63f4
4
- data.tar.gz: ddc03d2b15acfa3bf44ebc4661dd7653c2fd45a951d8ef2e2b015469a73d89c1
3
+ metadata.gz: d86fc24651a441455a293ead401451df5f6954499943f9c752117fe5f879b5cd
4
+ data.tar.gz: 5aaa58ced22e1050e7a4687a44b251710d5f12281f1572a2375d71f733c4ffc0
5
5
  SHA512:
6
- metadata.gz: 54dc0dedfb667d7bf49c909fdf043209b5508473876b6d5df019db192dd5d788397424972e5ba8c6eab6e4162940bff1d43313dc8ea12379d84018b910840044
7
- data.tar.gz: 98d74089ed076100bc845721c318944bd620b19a9228405b0160d81c248ea6023eab79c60019eea661c92ace20e7f59cc05b237f6bbbe43fd27500427242dd5a
6
+ metadata.gz: 81ddb196287e2d977e6210c6b56a58459cbf4eab56e6e38ac55929a4a59d9749c64f043153b64ae120c591ed35a364b088df3f156b9825b79c69df8bfecbb240
7
+ data.tar.gz: dfe9c36fc511b3adde97198d6e53705d62d7502af016e6636cd9191cb94434126ee4a0d80e73eccddf8ad3c2d0fe116319c1f769b8fdedfcd0213a3d1176abce
data/README.md CHANGED
@@ -5,8 +5,7 @@ through a HTTP interface. Intended to be used together with a
5
5
  [Prometheus server][1].
6
6
 
7
7
  [![Gem Version][4]](http://badge.fury.io/rb/prometheus-client)
8
- [![Build Status][3]](http://travis-ci.org/prometheus/client_ruby)
9
- [![Coverage Status][7]](https://coveralls.io/r/prometheus/client_ruby)
8
+ [![Build Status][3]](https://circleci.com/gh/prometheus/client_ruby/tree/master.svg?style=svg)
10
9
 
11
10
  ## Usage
12
11
 
@@ -71,7 +70,7 @@ integrated [example application](examples/rack/README.md).
71
70
  The Ruby client can also be used to push its collected metrics to a
72
71
  [Pushgateway][8]. This comes in handy with batch jobs or in other scenarios
73
72
  where it's not possible or feasible to let a Prometheus server scrape a Ruby
74
- process. TLS and basic access authentication are supported.
73
+ process. TLS and HTTP basic authentication are supported.
75
74
 
76
75
  ```ruby
77
76
  require 'prometheus/client'
@@ -81,18 +80,59 @@ registry = Prometheus::Client.registry
81
80
  # ... register some metrics, set/increment/observe/etc. their values
82
81
 
83
82
  # push the registry state to the default gateway
84
- Prometheus::Client::Push.new('my-batch-job').add(registry)
83
+ Prometheus::Client::Push.new(job: 'my-batch-job').add(registry)
84
+
85
+ # optional: specify a grouping key that uniquely identifies a job instance, and gateway.
86
+ #
87
+ # Note: the labels you use in the grouping key must not conflict with labels set on the
88
+ # metrics being pushed. If they do, an error will be raised.
89
+ Prometheus::Client::Push.new(
90
+ job: 'my-batch-job',
91
+ gateway: 'https://example.domain:1234',
92
+ grouping_key: { instance: 'some-instance', extra_key: 'foobar' }
93
+ ).add(registry)
94
+
95
+ # If you want to replace any previously pushed metrics for a given grouping key,
96
+ # use the #replace method.
97
+ #
98
+ # Unlike #add, this will completely replace the metrics under the specified grouping key
99
+ # (i.e. anything currently present in the pushgateway for the specified grouping key, but
100
+ # not present in the registry for that grouping key will be removed).
101
+ #
102
+ # See https://github.com/prometheus/pushgateway#put-method for a full explanation.
103
+ Prometheus::Client::Push.new(job: 'my-batch-job').replace(registry)
104
+
105
+ # If you want to delete all previously pushed metrics for a given grouping key,
106
+ # use the #delete method.
107
+ Prometheus::Client::Push.new(job: 'my-batch-job').delete
108
+ ```
85
109
 
86
- # optional: specify the instance name (instead of IP) and gateway.
87
- Prometheus::Client::Push.new('my-batch-job', 'foobar', 'https://example.domain:1234').add(registry)
110
+ #### Basic authentication
88
111
 
89
- # If you want to replace any previously pushed metrics for a given instance,
90
- # use the #replace method.
91
- Prometheus::Client::Push.new('my-batch-job').replace(registry)
112
+ By design, `Prometheus::Client::Push` doesn't read credentials for HTTP basic
113
+ authentication when they are passed in via the gateway URL using the
114
+ `http://user:password@example.com:9091` syntax, and will in fact raise an error if they're
115
+ supplied that way.
92
116
 
93
- # If you want to delete all previously pushed metrics for a given instance,
94
- # use the #delete method.
95
- Prometheus::Client::Push.new('my-batch-job').delete
117
+ The reason for this is that when using that syntax, the username and password
118
+ have to follow the usual rules for URL encoding of characters [per RFC
119
+ 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1).
120
+
121
+ Rather than place the burden of correctly performing that encoding on users of this gem,
122
+ we decided to have a separate method for supplying HTTP basic authentication credentials,
123
+ with no requirement to URL encode the characters in them.
124
+
125
+ Instead of passing credentials like this:
126
+
127
+ ```ruby
128
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://user:password@localhost:9091")
129
+ ```
130
+
131
+ please pass them like this:
132
+
133
+ ```ruby
134
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://localhost:9091")
135
+ push.basic_auth("user", "password")
96
136
  ```
97
137
 
98
138
  ## Metrics
@@ -389,6 +429,17 @@ If you are running in pre-fork servers (such as Unicorn or Puma with multiple pr
389
429
  make sure you do this **before** the server forks. Otherwise, each child process may delete
390
430
  files created by other processes on *this* run, instead of deleting old files.
391
431
 
432
+ **Declare metrics before fork**: As well as deleting files before your process forks, you
433
+ should make sure to declare your metrics before forking too. Because the metric registry
434
+ is held in memory, any metrics declared after forking will only be present in child
435
+ processes where the code declaring them ran, and as a result may not be consistently
436
+ exported when scraped (i.e. they will only appear when a child process that declared them
437
+ is scraped).
438
+
439
+ If you're absolutely sure that every child process will run the metric declaration code,
440
+ then you won't run into this issue, but the simplest approach is to declare the metrics
441
+ before forking.
442
+
392
443
  **Large numbers of files**: Because there is an individual file per metric and per process
393
444
  (which is done to optimize for observation performance), you may end up with a large number
394
445
  of files. We don't currently have a solution for this problem, but we're working on it.
@@ -6,7 +6,7 @@ module Prometheus
6
6
  module Client
7
7
  # A histogram samples observations (usually things like request durations
8
8
  # or response sizes) and counts them in configurable buckets. It also
9
- # provides a sum of all observed values.
9
+ # provides a total count and sum of all observed values.
10
10
  class Histogram < Metric
11
11
  # DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
12
12
  # are tailored to broadly measure the response time (in seconds) of a
@@ -42,18 +42,30 @@ module Prometheus
42
42
  end
43
43
 
44
44
  def with_labels(labels)
45
- self.class.new(name,
46
- docstring: docstring,
47
- labels: @labels,
48
- preset_labels: preset_labels.merge(labels),
49
- buckets: @buckets,
50
- store_settings: @store_settings)
45
+ new_metric = self.class.new(name,
46
+ docstring: docstring,
47
+ labels: @labels,
48
+ preset_labels: preset_labels.merge(labels),
49
+ buckets: @buckets,
50
+ store_settings: @store_settings)
51
+
52
+ # The new metric needs to use the same store as the "main" declared one, otherwise
53
+ # any observations on that copy with the pre-set labels won't actually be exported.
54
+ new_metric.replace_internal_store(@store)
55
+
56
+ new_metric
51
57
  end
52
58
 
53
59
  def type
54
60
  :histogram
55
61
  end
56
62
 
63
+ # Records a given value. The recorded value is usually positive
64
+ # or zero. A negative value is accepted but prevents current
65
+ # versions of Prometheus from properly detecting counter resets
66
+ # in the sum of observations. See
67
+ # https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
68
+ # for details.
57
69
  def observe(value, labels: {})
58
70
  bucket = buckets.find {|upper_limit| upper_limit >= value }
59
71
  bucket = "+Inf" if bucket.nil?
@@ -7,6 +7,7 @@ module Prometheus
7
7
  class LabelSetValidator
8
8
  # TODO: we might allow setting :instance in the future
9
9
  BASE_RESERVED_LABELS = [:job, :instance, :pid].freeze
10
+ LABEL_NAME_REGEX = /\A[a-zA-Z_][a-zA-Z0-9_]*\Z/
10
11
 
11
12
  class LabelSetError < StandardError; end
12
13
  class InvalidLabelSetError < LabelSetError; end
@@ -59,9 +60,15 @@ module Prometheus
59
60
  end
60
61
 
61
62
  def validate_name(key)
62
- return true unless key.to_s.start_with?('__')
63
+ if key.to_s.start_with?('__')
64
+ raise ReservedLabelError, "label #{key} must not start with __"
65
+ end
66
+
67
+ unless key.to_s =~ LABEL_NAME_REGEX
68
+ raise InvalidLabelError, "label name must match /#{LABEL_NAME_REGEX}/"
69
+ end
63
70
 
64
- raise ReservedLabelError, "label #{key} must not start with __"
71
+ true
65
72
  end
66
73
 
67
74
  def validate_reserved_key(key)
@@ -7,7 +7,7 @@ module Prometheus
7
7
  module Client
8
8
  # Metric
9
9
  class Metric
10
- attr_reader :name, :docstring, :preset_labels
10
+ attr_reader :name, :docstring, :labels, :preset_labels
11
11
 
12
12
  def initialize(name,
13
13
  docstring:,
@@ -40,8 +40,17 @@ module Prometheus
40
40
  metric_type: type,
41
41
  metric_settings: store_settings
42
42
  )
43
+
44
+ # WARNING: Our internal store can be replaced later by `with_labels`
45
+ # Everything we do after this point needs to still work if @store gets replaced
46
+ init_label_set({}) if labels.empty?
47
+ end
48
+
49
+ protected def replace_internal_store(new_store)
50
+ @store = new_store
43
51
  end
44
52
 
53
+
45
54
  # Returns the value for the given label set
46
55
  def get(labels: {})
47
56
  label_set = label_set_for(labels)
@@ -49,11 +58,17 @@ module Prometheus
49
58
  end
50
59
 
51
60
  def with_labels(labels)
52
- self.class.new(name,
53
- docstring: docstring,
54
- labels: @labels,
55
- preset_labels: preset_labels.merge(labels),
56
- store_settings: @store_settings)
61
+ new_metric = self.class.new(name,
62
+ docstring: docstring,
63
+ labels: @labels,
64
+ preset_labels: preset_labels.merge(labels),
65
+ store_settings: @store_settings)
66
+
67
+ # The new metric needs to use the same store as the "main" declared one, otherwise
68
+ # any observations on that copy with the pre-set labels won't actually be exported.
69
+ new_metric.replace_internal_store(@store)
70
+
71
+ new_metric
57
72
  end
58
73
 
59
74
  def init_label_set(labels)
@@ -1,11 +1,15 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ require 'base64'
3
4
  require 'thread'
4
5
  require 'net/http'
5
6
  require 'uri'
7
+ require 'erb'
8
+ require 'set'
6
9
 
7
10
  require 'prometheus/client'
8
11
  require 'prometheus/client/formats/text'
12
+ require 'prometheus/client/label_set_validator'
9
13
 
10
14
  module Prometheus
11
15
  # Client is a ruby implementation for a Prometheus compatible client.
@@ -13,23 +17,41 @@ module Prometheus
13
17
  # Push implements a simple way to transmit a given registry to a given
14
18
  # Pushgateway.
15
19
  class Push
20
+ class HttpError < StandardError; end
21
+ class HttpRedirectError < HttpError; end
22
+ class HttpClientError < HttpError; end
23
+ class HttpServerError < HttpError; end
24
+
16
25
  DEFAULT_GATEWAY = 'http://localhost:9091'.freeze
17
26
  PATH = '/metrics/job/%s'.freeze
18
- INSTANCE_PATH = '/metrics/job/%s/instance/%s'.freeze
19
27
  SUPPORTED_SCHEMES = %w(http https).freeze
20
28
 
21
- attr_reader :job, :instance, :gateway, :path
29
+ attr_reader :job, :gateway, :path
30
+
31
+ def initialize(job:, gateway: DEFAULT_GATEWAY, grouping_key: {}, **kwargs)
32
+ raise ArgumentError, "job cannot be nil" if job.nil?
33
+ raise ArgumentError, "job cannot be empty" if job.empty?
34
+ @validator = LabelSetValidator.new(expected_labels: grouping_key.keys)
35
+ @validator.validate_symbols!(grouping_key)
22
36
 
23
- def initialize(job, instance = nil, gateway = nil)
24
37
  @mutex = Mutex.new
25
38
  @job = job
26
- @instance = instance
27
39
  @gateway = gateway || DEFAULT_GATEWAY
28
- @path = build_path(job, instance)
40
+ @grouping_key = grouping_key
41
+ @path = build_path(job, grouping_key)
42
+
29
43
  @uri = parse("#{@gateway}#{@path}")
44
+ validate_no_basic_auth!(@uri)
30
45
 
31
46
  @http = Net::HTTP.new(@uri.host, @uri.port)
32
47
  @http.use_ssl = (@uri.scheme == 'https')
48
+ @http.open_timeout = kwargs[:open_timeout] if kwargs[:open_timeout]
49
+ @http.read_timeout = kwargs[:read_timeout] if kwargs[:read_timeout]
50
+ end
51
+
52
+ def basic_auth(user, password)
53
+ @user = user
54
+ @password = password
33
55
  end
34
56
 
35
57
  def add(registry)
@@ -64,26 +86,118 @@ module Prometheus
64
86
  raise ArgumentError, "#{url} is not a valid URL: #{e}"
65
87
  end
66
88
 
67
- def build_path(job, instance)
68
- if instance
69
- format(INSTANCE_PATH, CGI::escape(job), CGI::escape(instance))
70
- else
71
- format(PATH, CGI::escape(job))
89
+ def build_path(job, grouping_key)
90
+ path = format(PATH, ERB::Util::url_encode(job))
91
+
92
+ grouping_key.each do |label, value|
93
+ if value.include?('/')
94
+ encoded_value = Base64.urlsafe_encode64(value)
95
+ path += "/#{label}@base64/#{encoded_value}"
96
+ # While it's valid for the urlsafe_encode64 function to return an
97
+ # empty string when the input string is empty, it doesn't work for
98
+ # our specific use case as we're putting the result into a URL path
99
+ # segment. A double slash (`//`) can be normalised away by HTTP
100
+ # libraries, proxies, and web servers.
101
+ #
102
+ # For empty strings, we use a single padding character (`=`) as the
103
+ # value.
104
+ #
105
+ # See the pushgateway docs for more details:
106
+ #
107
+ # https://github.com/prometheus/pushgateway/blob/6393a901f56d4dda62cd0f6ab1f1f07c495b6354/README.md#url
108
+ elsif value.empty?
109
+ path += "/#{label}@base64/="
110
+ else
111
+ path += "/#{label}/#{ERB::Util::url_encode(value)}"
112
+ end
72
113
  end
114
+
115
+ path
73
116
  end
74
117
 
75
118
  def request(req_class, registry = nil)
119
+ validate_no_label_clashes!(registry) if registry
120
+
76
121
  req = req_class.new(@uri)
77
122
  req.content_type = Formats::Text::CONTENT_TYPE
78
- req.basic_auth(@uri.user, @uri.password) if @uri.user
123
+ req.basic_auth(@user, @password) if @user
79
124
  req.body = Formats::Text.marshal(registry) if registry
80
125
 
81
- @http.request(req)
126
+ response = @http.request(req)
127
+ validate_response!(response)
128
+
129
+ response
82
130
  end
83
131
 
84
132
  def synchronize
85
133
  @mutex.synchronize { yield }
86
134
  end
135
+
136
+ def validate_no_basic_auth!(uri)
137
+ if uri.user || uri.password
138
+ raise ArgumentError, <<~EOF
139
+ Setting Basic Auth credentials in the gateway URL is not supported, please call the `basic_auth` method.
140
+
141
+ Received username `#{uri.user}` in gateway URL. Instead of passing
142
+ Basic Auth credentials like this:
143
+
144
+ ```
145
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://user:password@localhost:9091")
146
+ ```
147
+
148
+ please pass them like this:
149
+
150
+ ```
151
+ push = Prometheus::Client::Push.new(job: "my-job", gateway: "http://localhost:9091")
152
+ push.basic_auth("user", "password")
153
+ ```
154
+
155
+ While URLs do support passing Basic Auth credentials using the
156
+ `http://user:password@example.com/` syntax, the username and
157
+ password in that syntax have to follow the usual rules for URL
158
+ encoding of characters per RFC 3986
159
+ (https://datatracker.ietf.org/doc/html/rfc3986#section-2.1).
160
+
161
+ Rather than place the burden of correctly performing that encoding
162
+ on users of this gem, we decided to have a separate method for
163
+ supplying Basic Auth credentials, with no requirement to URL encode
164
+ the characters in them.
165
+ EOF
166
+ end
167
+ end
168
+
169
+ def validate_no_label_clashes!(registry)
170
+ # There's nothing to check if we don't have a grouping key
171
+ return if @grouping_key.empty?
172
+
173
+ # We could be doing a lot of comparisons, so let's do them against a
174
+ # set rather than an array
175
+ grouping_key_labels = @grouping_key.keys.to_set
176
+
177
+ registry.metrics.each do |metric|
178
+ metric.labels.each do |label|
179
+ if grouping_key_labels.include?(label)
180
+ raise LabelSetValidator::InvalidLabelSetError,
181
+ "label :#{label} from grouping key collides with label of the " \
182
+ "same name from metric :#{metric.name} and would overwrite it"
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ def validate_response!(response)
189
+ status = Integer(response.code)
190
+ if status >= 300
191
+ message = "status: #{response.code}, message: #{response.message}, body: #{response.body}"
192
+ if status <= 399
193
+ raise HttpRedirectError, message
194
+ elsif status <= 499
195
+ raise HttpClientError, message
196
+ else
197
+ raise HttpServerError, message
198
+ end
199
+ end
200
+ end
87
201
  end
88
202
  end
89
203
  end
@@ -22,7 +22,7 @@ module Prometheus
22
22
  name = metric.name
23
23
 
24
24
  @mutex.synchronize do
25
- if exist?(name.to_sym)
25
+ if @metrics.key?(name.to_sym)
26
26
  raise AlreadyRegisteredError, "#{name} has already been registered"
27
27
  end
28
28
  @metrics[name.to_sym] = metric
@@ -73,15 +73,15 @@ module Prometheus
73
73
  end
74
74
 
75
75
  def exist?(name)
76
- @metrics.key?(name)
76
+ @mutex.synchronize { @metrics.key?(name) }
77
77
  end
78
78
 
79
79
  def get(name)
80
- @metrics[name.to_sym]
80
+ @mutex.synchronize { @metrics[name.to_sym] }
81
81
  end
82
82
 
83
83
  def metrics
84
- @metrics.values
84
+ @mutex.synchronize { @metrics.values }
85
85
  end
86
86
  end
87
87
  end
@@ -11,7 +11,12 @@ module Prometheus
11
11
  :summary
12
12
  end
13
13
 
14
- # Records a given value.
14
+ # Records a given value. The recorded value is usually positive
15
+ # or zero. A negative value is accepted but prevents current
16
+ # versions of Prometheus from properly detecting counter resets
17
+ # in the sum of observations. See
18
+ # https://prometheus.io/docs/practices/histograms/#count-and-sum-of-observations
19
+ # for details.
15
20
  def observe(value, labels: {})
16
21
  base_label_set = label_set_for(labels)
17
22
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Prometheus
4
4
  module Client
5
- VERSION = '2.1.0'
5
+ VERSION = '4.0.0'
6
6
  end
7
7
  end
@@ -67,15 +67,17 @@ module Prometheus
67
67
  end
68
68
 
69
69
  def record(env, code, duration)
70
+ path = generate_path(env)
71
+
70
72
  counter_labels = {
71
73
  code: code,
72
74
  method: env['REQUEST_METHOD'].downcase,
73
- path: strip_ids_from_path(env['PATH_INFO']),
75
+ path: path,
74
76
  }
75
77
 
76
78
  duration_labels = {
77
79
  method: env['REQUEST_METHOD'].downcase,
78
- path: strip_ids_from_path(env['PATH_INFO']),
80
+ path: path,
79
81
  }
80
82
 
81
83
  @requests.increment(labels: counter_labels)
@@ -85,10 +87,16 @@ module Prometheus
85
87
  nil
86
88
  end
87
89
 
90
+ def generate_path(env)
91
+ full_path = [env['SCRIPT_NAME'], env['PATH_INFO']].join
92
+
93
+ strip_ids_from_path(full_path)
94
+ end
95
+
88
96
  def strip_ids_from_path(path)
89
97
  path
90
- .gsub(%r{/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(/|$)}, '/:uuid\\1')
91
- .gsub(%r{/\d+(/|$)}, '/:id\\1')
98
+ .gsub(%r{/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=/|$)}, '/:uuid\\1')
99
+ .gsub(%r{/\d+(?=/|$)}, '/:id\\1')
92
100
  end
93
101
  end
94
102
  end
@@ -21,11 +21,12 @@ module Prometheus
21
21
  @app = app
22
22
  @registry = options[:registry] || Client.registry
23
23
  @path = options[:path] || '/metrics'
24
+ @port = options[:port]
24
25
  @acceptable = build_dictionary(FORMATS, FALLBACK)
25
26
  end
26
27
 
27
28
  def call(env)
28
- if env['PATH_INFO'] == @path
29
+ if metrics_port?(env['SERVER_PORT']) && env['PATH_INFO'] == @path
29
30
  format = negotiate(env, @acceptable)
30
31
  format ? respond_with(format) : not_acceptable(FORMATS)
31
32
  else
@@ -86,6 +87,10 @@ module Prometheus
86
87
  memo[format::MEDIA_TYPE] = format
87
88
  end
88
89
  end
90
+
91
+ def metrics_port?(request_port)
92
+ @port.nil? || @port.to_s == request_port
93
+ end
89
94
  end
90
95
  end
91
96
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Kochie
8
8
  - Chris Sinjakli
9
9
  - Daniel Magliola
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-06-29 00:00:00.000000000 Z
13
+ date: 2022-03-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: benchmark-ips
@@ -40,10 +40,10 @@ dependencies:
40
40
  - - ">="
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0'
43
- description:
43
+ description:
44
44
  email:
45
45
  - superq@gmail.com
46
- - chris@gocardless.com
46
+ - chris@sinjakli.co.uk
47
47
  - dmagliola@crystalgears.com
48
48
  executables: []
49
49
  extensions: []
@@ -71,9 +71,9 @@ files:
71
71
  - lib/prometheus/middleware/exporter.rb
72
72
  homepage: https://github.com/prometheus/client_ruby
73
73
  licenses:
74
- - Apache 2.0
74
+ - Apache-2.0
75
75
  metadata: {}
76
- post_install_message:
76
+ post_install_message:
77
77
  rdoc_options: []
78
78
  require_paths:
79
79
  - lib
@@ -88,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubygems_version: 3.1.2
92
- signing_key:
91
+ rubygems_version: 3.3.4
92
+ signing_key:
93
93
  specification_version: 4
94
94
  summary: A suite of instrumentation metric primitivesthat can be exposed through a
95
95
  web services interface.