prometheus-client 2.1.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.