prometheus-client 0.1.0 → 0.2.0

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