prometheus-client 0.1.0 → 0.2.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.
data/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # Prometheus Ruby Client
2
2
 
3
+ A suite of instrumentation metric primitives for Ruby that can be exposed
4
+ through a JSON web services interface. Intended to be used together with a
5
+ [Prometheus server][1].
6
+
3
7
  ## Usage
4
8
 
9
+ ### Library
10
+
5
11
  ```ruby
6
12
  require 'prometheus/client'
7
13
 
@@ -10,14 +16,40 @@ prometheus = Prometheus::Client.registry
10
16
 
11
17
  # create a new counter metric
12
18
  http_requests = Prometheus::Client::Counter.new
13
-
14
19
  # register the metric
15
- prometheus.register(:http_requests, 'A counter of the total number of HTTP requests made', http_requests)
20
+ prometheus.register(:http_requests, 'A counter of HTTP requests made', http_requests)
21
+
22
+ # equivalent helper function
23
+ http_requests = prometheus.counter(:http_requests, 'A counter of HTTP requests made')
16
24
 
17
25
  # start using the counter
18
26
  http_requests.increment
19
27
  ```
20
28
 
29
+ ### Rack middleware
30
+
31
+ There are two [Rack][2] middlewares available, one to expose a metrics HTTP
32
+ endpoint to be scraped by a prometheus server and one to trace all HTTP
33
+ requests.
34
+
35
+ ```ruby
36
+ # config.ru
37
+
38
+ require 'rack'
39
+ require 'prometheus/client/rack/collector'
40
+ require 'prometheus/client/rack/exporter'
41
+
42
+ use Prometheus::Client::Rack::Collector
43
+ use Prometheus::Client::Rack::Exporter
44
+ run lambda { |env| [200, {'Content-Type' => 'text/html'}, ['OK']] }
45
+ ```
46
+
47
+ Start the server and have a look at the metrics endpoint:
48
+ [http://localhost:5000/metrics](http://localhost:5000/metrics).
49
+
50
+ For further instructions and other scripts to get started, have a look at the
51
+ integrated [example application](examples/rack/README.md).
52
+
21
53
  ## Metrics
22
54
 
23
55
  The following metric types are currently supported.
@@ -26,21 +58,64 @@ The following metric types are currently supported.
26
58
 
27
59
  A Counter is a metric that exposes merely a sum or tally of things.
28
60
 
61
+ ```ruby
62
+ counter = Prometheus::Client::Counter.new
63
+
64
+ # increment the counter for a given label set
65
+ counter.increment(service: 'foo')
66
+
67
+ # increment by a given value
68
+ counter.increment({ service: 'bar' }, 5)
69
+
70
+ # decrement the counter
71
+ counter.decrement(service: 'exceptional')
72
+
73
+ # get current value for a given label set
74
+ counter.get(service: 'bar')
75
+ # => 5
76
+ ```
77
+
29
78
  ### Gauge
30
79
 
31
80
  A Gauge is a metric that exposes merely an instantaneous value or some
32
81
  snapshot thereof.
33
82
 
83
+ ```ruby
84
+ gauge = Prometheus::Client::Gauge.new
85
+
86
+ # set a value
87
+ gauge.set({ role: 'base' }, 'up')
88
+
89
+ # retrieve the current value for a given label set
90
+ gauge.get({ role: 'problematic' })
91
+ # => 'down'
92
+ ```
93
+
94
+ ### Summary
95
+
96
+ The Summary is an accumulator for samples. It captures Numeric data and provides
97
+ an efficient percentile calculation mechanism.
98
+
99
+ ```ruby
100
+ summary = Prometheus::Client::Summary.new
101
+
102
+ # record a value
103
+ summary.add({ service: 'slow' }, Benchmark.realtime { service.call(arg) })
104
+
105
+ # retrieve the current quantile values
106
+ summary.get({ service: 'database' })
107
+ # => { 0.5: 1.233122, 0.9: 83.4323, 0.99: 341.3428231 }
108
+ ```
109
+
34
110
  ## Todo
35
111
 
36
- * add histogram support
37
112
  * add push support to a vanilla prometheus exporter
38
- * add tests for Rack middlewares
39
113
  * use a more performant JSON library
114
+ * add protobuf support
40
115
 
41
116
  ## Tests
42
117
 
43
- [![Build Status][1]](http://travis-ci.org/prometheus/client_ruby)
118
+ [![Build Status][3]](http://travis-ci.org/prometheus/client_ruby)
44
119
 
45
120
  Install necessary development gems with `bundle install` and run tests with
46
121
  rspec:
@@ -49,4 +124,6 @@ rspec:
49
124
  rspec
50
125
  ```
51
126
 
52
- [1]: https://secure.travis-ci.org/prometheus/client_ruby.png?branch=master
127
+ [1]: https://github.com/prometheus/prometheus
128
+ [2]: http://rack.github.io/
129
+ [3]: https://secure.travis-ci.org/prometheus/client_ruby.png?branch=master
@@ -3,10 +3,6 @@ require 'prometheus/client/metric'
3
3
  module Prometheus
4
4
  module Client
5
5
  class Counter < Metric
6
- def default
7
- 0
8
- end
9
-
10
6
  def type
11
7
  :counter
12
8
  end
@@ -19,6 +15,13 @@ module Prometheus
19
15
  def decrement(labels = {}, by = 1)
20
16
  increment(labels, -by)
21
17
  end
18
+
19
+ private
20
+
21
+ def default
22
+ 0
23
+ end
24
+
22
25
  end
23
26
  end
24
27
  end
@@ -6,6 +6,11 @@ module Prometheus
6
6
  def type
7
7
  :gauge
8
8
  end
9
+
10
+ # Sets the value for the given label set
11
+ def set(labels, value)
12
+ @values[label_set_for(labels)] = value
13
+ end
9
14
  end
10
15
  end
11
16
  end
@@ -7,12 +7,7 @@ module Prometheus
7
7
  class Metric
8
8
  def initialize
9
9
  @mutex = Mutex.new
10
- @values = Hash.new(default)
11
- end
12
-
13
- # Default value
14
- def default
15
- nil
10
+ @values = Hash.new { |hash, key| hash[key] = default }
16
11
  end
17
12
 
18
13
  # Returns the metric type
@@ -20,23 +15,11 @@ module Prometheus
20
15
  raise NotImplementedError
21
16
  end
22
17
 
23
- # Returns all values
24
- def value
25
- @values.map do |labels, value|
26
- { :labels => labels, :value => value }
27
- end
28
- end
29
-
30
18
  # Returns the value for the given label set
31
19
  def get(labels = {})
32
20
  @values[label_set_for(labels)]
33
21
  end
34
22
 
35
- # Sets the value for the given label set
36
- def set(labels, value)
37
- @values[label_set_for(labels)] = value
38
- end
39
-
40
23
  # Generates JSON representation
41
24
  def to_json(*json)
42
25
  {
@@ -45,7 +28,17 @@ module Prometheus
45
28
  }.to_json(*json)
46
29
  end
47
30
 
48
- protected
31
+ private
32
+
33
+ def default
34
+ nil
35
+ end
36
+
37
+ def value
38
+ @values.map do |labels, value|
39
+ { :labels => labels, :value => get(labels) }
40
+ end
41
+ end
49
42
 
50
43
  def label_set_for(labels)
51
44
  LabelSet.new(labels)
@@ -14,11 +14,7 @@ module Prometheus
14
14
  end
15
15
 
16
16
  def call(env) # :nodoc:
17
- start = Time.now
18
- @app.call(env)
19
- ensure
20
- @requests.increment
21
- @requests_duration.increment({}, ((Time.now - start) * 1_000_000).to_i)
17
+ trace(env) { @app.call(env) }
22
18
  end
23
19
 
24
20
  protected
@@ -26,6 +22,36 @@ module Prometheus
26
22
  def init_metrics
27
23
  @requests = @registry.counter(:http_requests_total, 'A counter of the total number of HTTP requests made')
28
24
  @requests_duration = @registry.counter(:http_request_durations_total_microseconds, 'The total amount of time Rack has spent answering HTTP requests (microseconds).')
25
+ @durations = @registry.summary(:http_request_durations_microseconds, 'A histogram of the response latency for requests made (microseconds).')
26
+ end
27
+
28
+ def trace(env, &block)
29
+ start = Time.now
30
+ response = yield
31
+ rescue => exception
32
+ raise
33
+ ensure
34
+ duration = ((Time.now - start) * 1_000_000).to_i
35
+ record(duration, env, response, exception)
36
+ end
37
+
38
+ def record(duration, env, response, exception)
39
+ labels = {
40
+ :method => env['REQUEST_METHOD'].downcase,
41
+ :path => env['PATH_INFO'].to_s,
42
+ }
43
+
44
+ if response
45
+ labels[:code] = response.first.to_s
46
+ else
47
+ labels[:exception] = exception.class.name
48
+ end
49
+
50
+ @requests.increment(labels)
51
+ @requests_duration.increment(labels, duration)
52
+ @durations.add(labels, duration)
53
+ rescue => error
54
+ # TODO: log unexpected exception during request recording
29
55
  end
30
56
 
31
57
  end
@@ -6,10 +6,14 @@ module Prometheus
6
6
  class Exporter
7
7
  attr_reader :app, :registry, :path
8
8
 
9
+ API_VERSION = '0.0.2'
10
+ CONTENT_TYPE = 'application/json; schema="prometheus/telemetry"; version=' + API_VERSION
11
+ HEADERS = { 'Content-Type' => CONTENT_TYPE }
12
+
9
13
  def initialize(app, options = {})
10
14
  @app = app
11
15
  @registry = options[:registry] || Client.registry
12
- @path = options[:path] || '/metrics.json'
16
+ @path = options[:path] || '/metrics'
13
17
  end
14
18
 
15
19
  def call(env)
@@ -23,13 +27,7 @@ module Prometheus
23
27
  protected
24
28
 
25
29
  def metrics_response
26
- json = @registry.to_json
27
- headers = {
28
- 'Content-Type' => 'application/json',
29
- 'Content-Length' => json.size.to_s
30
- }
31
-
32
- [200, headers, [json]]
30
+ [200, HEADERS, [@registry.to_json]]
33
31
  end
34
32
 
35
33
  end
@@ -3,6 +3,7 @@ require 'thread'
3
3
 
4
4
  require 'prometheus/client/container'
5
5
  require 'prometheus/client/counter'
6
+ require 'prometheus/client/summary'
6
7
  require 'prometheus/client/gauge'
7
8
 
8
9
  module Prometheus
@@ -36,6 +37,10 @@ module Prometheus
36
37
  register(name, docstring, Counter.new, base_labels).metric
37
38
  end
38
39
 
40
+ def summary(name, docstring, base_labels = {})
41
+ register(name, docstring, Summary.new, base_labels).metric
42
+ end
43
+
39
44
  def gauge(name, docstring, base_labels = {})
40
45
  register(name, docstring, Gauge.new, base_labels).metric
41
46
  end
@@ -0,0 +1,36 @@
1
+ require 'quantile'
2
+ require 'prometheus/client/metric'
3
+
4
+ module Prometheus
5
+ module Client
6
+ class Summary < Metric
7
+ def type
8
+ :histogram
9
+ end
10
+
11
+ # Records a given value.
12
+ def add(labels, value)
13
+ label_set = label_set_for(labels)
14
+ synchronize { @values[label_set].observe(value) }
15
+ end
16
+
17
+ # Returns the value for the given label set
18
+ def get(labels = {})
19
+ synchronize do
20
+ estimator = @values[label_set_for(labels)]
21
+ estimator.invariants.inject({}) do |memo, invariant|
22
+ memo[invariant.quantile] = estimator.query(invariant.quantile)
23
+ memo
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def default
31
+ Quantile::Estimator.new
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,15 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Tobias Schmidt
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-05 00:00:00.000000000 Z
12
- dependencies: []
12
+ date: 2013-11-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: quantile
16
+ requirement: &19137520 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *19137520
13
25
  description:
14
26
  email:
15
27
  - ts@soundcloud.com
@@ -20,40 +32,40 @@ files:
20
32
  - README.md
21
33
  - lib/prometheus/client/counter.rb
22
34
  - lib/prometheus/client/gauge.rb
23
- - lib/prometheus/client/histogram/bucket.rb
24
35
  - lib/prometheus/client/metric.rb
25
36
  - lib/prometheus/client/container.rb
26
37
  - lib/prometheus/client/registry.rb
27
38
  - lib/prometheus/client/rack/collector.rb
28
39
  - lib/prometheus/client/rack/exporter.rb
29
- - lib/prometheus/client/histogram.rb
30
40
  - lib/prometheus/client/version.rb
31
41
  - lib/prometheus/client/label_set.rb
42
+ - lib/prometheus/client/summary.rb
32
43
  - lib/prometheus/client.rb
33
44
  - lib/prometheus.rb
34
45
  homepage: https://github.com/prometheus/client_ruby
35
46
  licenses:
36
47
  - Apache 2.0
37
- metadata: {}
38
48
  post_install_message:
39
49
  rdoc_options: []
40
50
  require_paths:
41
51
  - lib
42
52
  required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
43
54
  requirements:
44
55
  - - ! '>='
45
56
  - !ruby/object:Gem::Version
46
57
  version: '0'
47
58
  required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
48
60
  requirements:
49
61
  - - ! '>='
50
62
  - !ruby/object:Gem::Version
51
63
  version: '0'
52
64
  requirements: []
53
65
  rubyforge_project:
54
- rubygems_version: 2.1.2
66
+ rubygems_version: 1.8.5
55
67
  signing_key:
56
- specification_version: 4
68
+ specification_version: 3
57
69
  summary: A suite of instrumentation metric primitives for Ruby that can be exposed
58
70
  through a JSON web services interface.
59
71
  test_files: []
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OTQ3NjhkOGY4ZjA3Zjg0MDZiNWIyMjc1MDc2YmZmMjI5Y2IxMGIxYg==
5
- data.tar.gz: !binary |-
6
- ZjBhYjc4OTA2YWIxNzdjYmY5YjNjYTYwM2I2ZDBiMjJiNTc1NTEwOA==
7
- SHA512:
8
- metadata.gz: !binary |-
9
- MTFlNDlkZDZhNjA1ZGE4M2EzN2Q2NTQ2M2M3N2E5NGZkOWFmNjg0YzEyZmQy
10
- NGRiNDgyNTVhMDU1ODkzMmUzYjBhN2E1MjNkNDc4M2ExMjVjY2ZjMmVkMWU5
11
- MDZmMThkMjE4ZGE1MzNiZDcwMWUxMjU3N2EzOGJmNmRiMWJkZTI=
12
- data.tar.gz: !binary |-
13
- YWE3ZWI4MmU4MjUyOTkyMjk3MWRkMzNkYWIxZTc4MDUxMWQ0OTk0NzIxZTNl
14
- OTMzNGIxNTkxOTgyNDc3ODczM2E2ODM0YjM2MTFhYTIzZjdjOTRhMDI0N2Yy
15
- ODkzNTQyYTg4MjcwMmM4OTdmNDcyNzgzOTZkYmNlMGFiOTQ4MzM=
@@ -1,19 +0,0 @@
1
- require 'prometheus/client/metric'
2
-
3
- module Prometheus
4
- module Client
5
- class Histogram < Metric
6
- def type
7
- :histogram
8
- end
9
-
10
- def add(labels, timing)
11
- raise NotImplementedError
12
- end
13
-
14
- def measure(labels, &block)
15
- raise NotImplementedError
16
- end
17
- end
18
- end
19
- end
@@ -1,19 +0,0 @@
1
- module Prometheus
2
- module Client
3
- class Histogram
4
- class Bucket
5
- attr_reader :observations
6
-
7
- def initialize
8
- @observations = 0
9
- @timings = []
10
- end
11
-
12
- def add(timing)
13
- # TODO: mutex
14
- @timmings << timing
15
- end
16
- end
17
- end
18
- end
19
- end