prometheus-client 0.6.0 → 0.7.0.pre.rc.1

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
  SHA1:
3
- metadata.gz: 1ca1c148a74709f3c2fbbba112f651babdf43fbe
4
- data.tar.gz: e55c7b9be73218b6a252a5a624fc8a865d00d851
3
+ metadata.gz: c7df7b1114d199d5da7e201aa5a3d42898c5db9a
4
+ data.tar.gz: 5bea74f933571336d6dba20f9b5a28aa12321b52
5
5
  SHA512:
6
- metadata.gz: 337ad7b5c4315463492d5120b4652bd0e4d58c1b2a53fa8be05c5f1d02c7b567133196e31e5b43899001f25fd896c7f60d4cccda717d1d8fede675aaa21276e0
7
- data.tar.gz: fcbb6c9963f8f0e5c5c532209fecf8643fad16a8943b74fd601644972ff25bb30f8c0e0be02b307c0247fb7a83549d21a54fe1f75850df795000d7756c9092ab
6
+ metadata.gz: bdfb89648fb64ee9c9bbfaaf4d4158834b3cbdc79d562530fa61d4511f8e95b2e15f4e01c374fedeb985dbf1c69fa3d5748260d29eaf5e87ed784350e1613437
7
+ data.tar.gz: acb12ac03128cb6387930d6f4fb43997be306fa590cd9cad706c319f514509cc631e2103152d57294e68738a4a30f42e0c6102a21db4d6ca9335d1bca646bec5
data/README.md CHANGED
@@ -35,7 +35,7 @@ http_requests.increment
35
35
  ### Rack middleware
36
36
 
37
37
  There are two [Rack][2] middlewares available, one to expose a metrics HTTP
38
- endpoint to be scraped by a prometheus server ([Exporter][9]) and one to trace all HTTP
38
+ endpoint to be scraped by a Prometheus server ([Exporter][9]) and one to trace all HTTP
39
39
  requests ([Collector][10]).
40
40
 
41
41
  It's highly recommended to enable gzip compression for the metrics endpoint,
@@ -45,14 +45,14 @@ for example by including the `Rack::Deflater` middleware.
45
45
  # config.ru
46
46
 
47
47
  require 'rack'
48
- require 'prometheus/client/rack/collector'
49
- require 'prometheus/client/rack/exporter'
48
+ require 'prometheus/middleware/collector'
49
+ require 'prometheus/middleware/exporter'
50
50
 
51
- use Rack::Deflater, if: ->(env, status, headers, body) { body.any? && body[0].length > 512 }
52
- use Prometheus::Client::Rack::Collector
53
- use Prometheus::Client::Rack::Exporter
51
+ use Rack::Deflater, if: ->(_, _, _, body) { body.any? && body[0].length > 512 }
52
+ use Prometheus::Middleware::Collector
53
+ use Prometheus::Middleware::Exporter
54
54
 
55
- run ->(env) { [200, {'Content-Type' => 'text/html'}, ['OK']] }
55
+ run ->(_) { [200, {'Content-Type' => 'text/html'}, ['OK']] }
56
56
  ```
57
57
 
58
58
  Start the server and have a look at the metrics endpoint:
@@ -85,6 +85,10 @@ Prometheus::Client::Push.new(
85
85
  # If you want to replace any previously pushed metrics for a given instance,
86
86
  # use the #replace method.
87
87
  Prometheus::Client::Push.new('my-batch-job', 'instance').replace(prometheus)
88
+
89
+ # If you want to delete all previously pushed metrics for a given instance,
90
+ # use the #delete method.
91
+ Prometheus::Client::Push.new('my-batch-job', 'instance').delete
88
92
  ```
89
93
 
90
94
  ## Metrics
@@ -137,7 +141,7 @@ histogram = Prometheus::Client::Histogram.new(:service_latency_seconds, '...')
137
141
  # record a value
138
142
  histogram.observe({ service: 'users' }, Benchmark.realtime { service.call(arg) })
139
143
 
140
- # retrieve the current quantile values
144
+ # retrieve the current bucket values
141
145
  histogram.get({ service: 'users' })
142
146
  # => { 0.005 => 3, 0.01 => 15, 0.025 => 18, ..., 2.5 => 42, 5 => 42, 10 = >42 }
143
147
  ```
@@ -158,10 +162,6 @@ summary.get({ service: 'database' })
158
162
  # => { 0.5 => 0.1233122, 0.9 => 3.4323, 0.99 => 5.3428231 }
159
163
  ```
160
164
 
161
- ## Todo
162
-
163
- * add protobuf support
164
-
165
165
  ## Tests
166
166
 
167
167
  Install necessary development gems with `bundle install` and run tests with
@@ -173,11 +173,11 @@ rake
173
173
 
174
174
  [1]: https://github.com/prometheus/prometheus
175
175
  [2]: http://rack.github.io/
176
- [3]: https://secure.travis-ci.org/prometheus/client_ruby.png?branch=master
176
+ [3]: https://secure.travis-ci.org/prometheus/client_ruby.svg?branch=master
177
177
  [4]: https://badge.fury.io/rb/prometheus-client.svg
178
178
  [5]: https://gemnasium.com/prometheus/client_ruby.svg
179
- [6]: https://codeclimate.com/github/prometheus/client_ruby.png
180
- [7]: https://coveralls.io/repos/prometheus/client_ruby/badge.png?branch=master
179
+ [6]: https://codeclimate.com/github/prometheus/client_ruby.svg
180
+ [7]: https://coveralls.io/repos/prometheus/client_ruby/badge.svg?branch=master
181
181
  [8]: https://github.com/prometheus/pushgateway
182
- [9]: lib/prometheus/client/rack/exporter.rb
183
- [10]: lib/prometheus/client/rack/collector.rb
182
+ [9]: lib/prometheus/middleware/exporter.rb
183
+ [10]: lib/prometheus/middleware/collector.rb
@@ -20,7 +20,7 @@ module Prometheus
20
20
  private
21
21
 
22
22
  def default
23
- 0
23
+ 0.0
24
24
  end
25
25
  end
26
26
  end
@@ -13,7 +13,11 @@ module Prometheus
13
13
 
14
14
  # Sets the value for the given label set
15
15
  def set(labels, value)
16
- @values[label_set_for(labels)] = value
16
+ unless value.is_a?(Numeric)
17
+ raise ArgumentError, 'value must be a number'
18
+ end
19
+
20
+ @values[label_set_for(labels)] = value.to_f
17
21
  end
18
22
  end
19
23
  end
@@ -14,10 +14,10 @@ module Prometheus
14
14
 
15
15
  def initialize(buckets)
16
16
  @sum = 0.0
17
- @total = 0
17
+ @total = 0.0
18
18
 
19
19
  buckets.each do |bucket|
20
- self[bucket] = 0
20
+ self[bucket] = 0.0
21
21
  end
22
22
  end
23
23
 
@@ -23,11 +23,6 @@ module Prometheus
23
23
  @base_labels = base_labels
24
24
  end
25
25
 
26
- # Returns the metric type
27
- def type
28
- raise NotImplementedError
29
- end
30
-
31
26
  # Returns the value for the given label set
32
27
  def get(labels = {})
33
28
  @validator.valid?(labels)
@@ -16,6 +16,7 @@ module Prometheus
16
16
  PATH = '/metrics/jobs/%s'.freeze
17
17
  INSTANCE_PATH = '/metrics/jobs/%s/instances/%s'.freeze
18
18
  HEADER = { 'Content-Type' => Formats::Text::CONTENT_TYPE }.freeze
19
+ SUPPORTED_SCHEMES = %w(http https).freeze
19
20
 
20
21
  attr_reader :job, :instance, :gateway, :path
21
22
 
@@ -26,6 +27,7 @@ module Prometheus
26
27
  @uri = parse(@gateway)
27
28
  @path = build_path(job, instance)
28
29
  @http = Net::HTTP.new(@uri.host, @uri.port)
30
+ @http.use_ssl = @uri.scheme == 'https'
29
31
  end
30
32
 
31
33
  def add(registry)
@@ -36,16 +38,20 @@ module Prometheus
36
38
  request('PUT', registry)
37
39
  end
38
40
 
41
+ def delete
42
+ @http.send_request('DELETE', path)
43
+ end
44
+
39
45
  private
40
46
 
41
47
  def parse(url)
42
48
  uri = URI.parse(url)
43
49
 
44
- if uri.scheme == 'http'
45
- uri
46
- else
50
+ unless SUPPORTED_SCHEMES.include?(uri.scheme)
47
51
  raise ArgumentError, 'only HTTP gateway URLs are supported currently.'
48
52
  end
53
+
54
+ uri
49
55
  rescue URI::InvalidURIError => e
50
56
  raise ArgumentError, "#{url} is not a valid URL: #{e}"
51
57
  end
@@ -24,9 +24,8 @@ module Prometheus
24
24
  @mutex.synchronize do
25
25
  if exist?(name.to_sym)
26
26
  raise AlreadyRegisteredError, "#{name} has already been registered"
27
- else
28
- @metrics[name.to_sym] = metric
29
27
  end
28
+ @metrics[name.to_sym] = metric
30
29
  end
31
30
 
32
31
  metric
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Prometheus
4
4
  module Client
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0-rc.1'
6
6
  end
7
7
  end
@@ -0,0 +1,91 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'prometheus/client'
4
+
5
+ module Prometheus
6
+ module Middleware
7
+ # Collector is a Rack middleware that provides a sample implementation of a
8
+ # HTTP tracer.
9
+ #
10
+ # By default metrics are registered on the global registry. Set the
11
+ # `:registry` option to use a custom registry.
12
+ #
13
+ # The request counter metric is broken down by code, method and path by
14
+ # default. Set the `:counter_label_builder` option to use a custom label
15
+ # builder.
16
+ #
17
+ # The request duration metric is broken down by method and path by default.
18
+ # Set the `:duration_label_builder` option to use a custom label builder.
19
+ class Collector
20
+ attr_reader :app, :registry
21
+
22
+ def initialize(app, options = {})
23
+ @app = app
24
+ @registry = options[:registry] || Client.registry
25
+ @counter_lb = options[:counter_label_builder] || COUNTER_LB
26
+ @duration_lb = options[:duration_label_builder] || DURATION_LB
27
+
28
+ init_request_metrics
29
+ init_exception_metrics
30
+ end
31
+
32
+ def call(env) # :nodoc:
33
+ trace(env) { @app.call(env) }
34
+ end
35
+
36
+ protected
37
+
38
+ COUNTER_LB = proc do |env, code|
39
+ {
40
+ code: code,
41
+ method: env['REQUEST_METHOD'].downcase,
42
+ path: env['PATH_INFO'].to_s,
43
+ }
44
+ end
45
+
46
+ DURATION_LB = proc do |env, _|
47
+ {
48
+ method: env['REQUEST_METHOD'].downcase,
49
+ path: env['PATH_INFO'].to_s,
50
+ }
51
+ end
52
+
53
+ def init_request_metrics
54
+ @requests = @registry.counter(
55
+ :http_server_requests_total,
56
+ 'The total number of HTTP requests handled by the Rack application.',
57
+ )
58
+ @durations = @registry.histogram(
59
+ :http_server_request_duration_seconds,
60
+ 'The HTTP response duration of the Rack application.',
61
+ )
62
+ end
63
+
64
+ def init_exception_metrics
65
+ @exceptions = @registry.counter(
66
+ :http_server_exceptions_total,
67
+ 'The total number of exceptions raised by the Rack application.',
68
+ )
69
+ end
70
+
71
+ def trace(env)
72
+ start = Time.now
73
+ yield.tap do |response|
74
+ duration = [(Time.now - start).to_f, 0.0].max
75
+ record(env, response.first.to_s, duration)
76
+ end
77
+ rescue => exception
78
+ @exceptions.increment(exception: exception.class.name)
79
+ raise
80
+ end
81
+
82
+ def record(env, code, duration)
83
+ @requests.increment(@counter_lb.call(env, code))
84
+ @durations.observe(@duration_lb.call(env, code), duration)
85
+ rescue
86
+ # TODO: log unexpected exception during request recording
87
+ nil
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'prometheus/client'
4
+ require 'prometheus/client/formats/text'
5
+
6
+ module Prometheus
7
+ module Middleware
8
+ # Exporter is a Rack middleware that provides a sample implementation of a
9
+ # Prometheus HTTP exposition endpoint.
10
+ #
11
+ # By default it will export the state of the global registry and expose it
12
+ # under `/metrics`. Use the `:registry` and `:path` options to change the
13
+ # defaults.
14
+ class Exporter
15
+ attr_reader :app, :registry, :path
16
+
17
+ FORMATS = [Client::Formats::Text].freeze
18
+ FALLBACK = Client::Formats::Text
19
+
20
+ def initialize(app, options = {})
21
+ @app = app
22
+ @registry = options[:registry] || Client.registry
23
+ @path = options[:path] || '/metrics'
24
+ @acceptable = build_dictionary(FORMATS, FALLBACK)
25
+ end
26
+
27
+ def call(env)
28
+ if env['PATH_INFO'] == @path
29
+ format = negotiate(env, @acceptable)
30
+ format ? respond_with(format) : not_acceptable(FORMATS)
31
+ else
32
+ @app.call(env)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def negotiate(env, formats)
39
+ parse(env.fetch('HTTP_ACCEPT', '*/*')).each do |content_type, _|
40
+ return formats[content_type] if formats.key?(content_type)
41
+ end
42
+
43
+ nil
44
+ end
45
+
46
+ def parse(header)
47
+ header.split(/\s*,\s*/).map do |type|
48
+ attributes = type.split(/\s*;\s*/)
49
+ quality = extract_quality(attributes)
50
+
51
+ [attributes.join('; '), quality]
52
+ end.sort_by(&:last).reverse
53
+ end
54
+
55
+ def extract_quality(attributes, default = 1.0)
56
+ quality = default
57
+
58
+ attributes.delete_if do |attr|
59
+ quality = attr.split('q=').last.to_f if attr.start_with?('q=')
60
+ end
61
+
62
+ quality
63
+ end
64
+
65
+ def respond_with(format)
66
+ [
67
+ 200,
68
+ { 'Content-Type' => format::CONTENT_TYPE },
69
+ [format.marshal(@registry)],
70
+ ]
71
+ end
72
+
73
+ def not_acceptable(formats)
74
+ types = formats.map { |format| format::MEDIA_TYPE }
75
+
76
+ [
77
+ 406,
78
+ { 'Content-Type' => 'text/plain' },
79
+ ["Supported media types: #{types.join(', ')}"],
80
+ ]
81
+ end
82
+
83
+ def build_dictionary(formats, fallback)
84
+ formats.each_with_object('*/*' => fallback) do |format, memo|
85
+ memo[format::CONTENT_TYPE] = format
86
+ memo[format::MEDIA_TYPE] = format
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0.pre.rc.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schmidt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2017-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: quantile
@@ -41,11 +41,11 @@ files:
41
41
  - lib/prometheus/client/label_set_validator.rb
42
42
  - lib/prometheus/client/metric.rb
43
43
  - lib/prometheus/client/push.rb
44
- - lib/prometheus/client/rack/collector.rb
45
- - lib/prometheus/client/rack/exporter.rb
46
44
  - lib/prometheus/client/registry.rb
47
45
  - lib/prometheus/client/summary.rb
48
46
  - lib/prometheus/client/version.rb
47
+ - lib/prometheus/middleware/collector.rb
48
+ - lib/prometheus/middleware/exporter.rb
49
49
  homepage: https://github.com/prometheus/client_ruby
50
50
  licenses:
51
51
  - Apache 2.0
@@ -61,12 +61,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
61
61
  version: '0'
62
62
  required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  requirements:
64
- - - ">="
64
+ - - ">"
65
65
  - !ruby/object:Gem::Version
66
- version: '0'
66
+ version: 1.3.1
67
67
  requirements: []
68
68
  rubyforge_project:
69
- rubygems_version: 2.5.1
69
+ rubygems_version: 2.6.8
70
70
  signing_key:
71
71
  specification_version: 4
72
72
  summary: A suite of instrumentation metric primitivesthat can be exposed through a
@@ -1,82 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'prometheus/client'
4
-
5
- module Prometheus
6
- module Client
7
- module Rack
8
- # Collector is a Rack middleware that provides a sample implementation of
9
- # a HTTP tracer. The default label builder can be modified to export a
10
- # different set of labels per recorded metric.
11
- class Collector
12
- attr_reader :app, :registry
13
-
14
- def initialize(app, options = {}, &label_builder)
15
- @app = app
16
- @registry = options[:registry] || Client.registry
17
- @label_builder = label_builder || DEFAULT_LABEL_BUILDER
18
-
19
- init_request_metrics
20
- init_exception_metrics
21
- end
22
-
23
- def call(env) # :nodoc:
24
- trace(env) { @app.call(env) }
25
- end
26
-
27
- protected
28
-
29
- DEFAULT_LABEL_BUILDER = proc do |env|
30
- {
31
- method: env['REQUEST_METHOD'].downcase,
32
- host: env['HTTP_HOST'].to_s,
33
- path: env['PATH_INFO'].to_s,
34
- }
35
- end
36
-
37
- def init_request_metrics
38
- @requests = @registry.counter(
39
- :http_requests_total,
40
- 'A counter of the total number of HTTP requests made.',
41
- )
42
- @durations = @registry.summary(
43
- :http_request_duration_seconds,
44
- 'A histogram of the response latency.',
45
- )
46
- end
47
-
48
- def init_exception_metrics
49
- @exceptions = @registry.counter(
50
- :http_exceptions_total,
51
- 'A counter of the total number of exceptions raised.',
52
- )
53
- end
54
-
55
- def trace(env)
56
- start = Time.now
57
- yield.tap do |response|
58
- duration = (Time.now - start).to_f
59
- record(labels(env, response), duration)
60
- end
61
- rescue => exception
62
- @exceptions.increment(exception: exception.class.name)
63
- raise
64
- end
65
-
66
- def labels(env, response)
67
- @label_builder.call(env).tap do |labels|
68
- labels[:code] = response.first.to_s
69
- end
70
- end
71
-
72
- def record(labels, duration)
73
- @requests.increment(labels)
74
- @durations.observe(labels, duration)
75
- rescue
76
- # TODO: log unexpected exception during request recording
77
- nil
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,91 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'prometheus/client'
4
- require 'prometheus/client/formats/text'
5
-
6
- module Prometheus
7
- module Client
8
- module Rack
9
- # Exporter is a Rack middleware that provides a sample implementation of
10
- # a Prometheus HTTP client API.
11
- class Exporter
12
- attr_reader :app, :registry, :path
13
-
14
- FORMATS = [Formats::Text].freeze
15
- FALLBACK = Formats::Text
16
-
17
- def initialize(app, options = {})
18
- @app = app
19
- @registry = options[:registry] || Client.registry
20
- @path = options[:path] || '/metrics'
21
- @acceptable = build_dictionary(FORMATS, FALLBACK)
22
- end
23
-
24
- def call(env)
25
- if env['PATH_INFO'] == @path
26
- format = negotiate(env['HTTP_ACCEPT'], @acceptable)
27
- format ? respond_with(format) : not_acceptable(FORMATS)
28
- else
29
- @app.call(env)
30
- end
31
- end
32
-
33
- private
34
-
35
- def negotiate(accept, formats)
36
- accept = '*/*' if accept.to_s.empty?
37
-
38
- parse(accept).each do |content_type, _|
39
- return formats[content_type] if formats.key?(content_type)
40
- end
41
-
42
- nil
43
- end
44
-
45
- def parse(header)
46
- header.to_s.split(/\s*,\s*/).map do |type|
47
- attributes = type.split(/\s*;\s*/)
48
- quality = extract_quality(attributes)
49
-
50
- [attributes.join('; '), quality]
51
- end.sort_by(&:last).reverse
52
- end
53
-
54
- def extract_quality(attributes, default = 1.0)
55
- quality = default
56
-
57
- attributes.delete_if do |attr|
58
- quality = attr.split('q=').last.to_f if attr.start_with?('q=')
59
- end
60
-
61
- quality
62
- end
63
-
64
- def respond_with(format)
65
- [
66
- 200,
67
- { 'Content-Type' => format::CONTENT_TYPE },
68
- [format.marshal(@registry)],
69
- ]
70
- end
71
-
72
- def not_acceptable(formats)
73
- types = formats.map { |format| format::MEDIA_TYPE }
74
-
75
- [
76
- 406,
77
- { 'Content-Type' => 'text/plain' },
78
- ["Supported media types: #{types.join(', ')}"],
79
- ]
80
- end
81
-
82
- def build_dictionary(formats, fallback)
83
- formats.each_with_object('*/*' => fallback) do |format, memo|
84
- memo[format::CONTENT_TYPE] = format
85
- memo[format::MEDIA_TYPE] = format
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end