prometheus-client 0.5.0 → 0.6.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
  SHA1:
3
- metadata.gz: 525f1dcd2ca63486f95a2d2fd44b7f047625edc3
4
- data.tar.gz: 0480ce9fd716c715553e8c1a0877345f2124b563
3
+ metadata.gz: 1ca1c148a74709f3c2fbbba112f651babdf43fbe
4
+ data.tar.gz: e55c7b9be73218b6a252a5a624fc8a865d00d851
5
5
  SHA512:
6
- metadata.gz: 362f0157652258f5270dc815434d7ef4768e4842a68a5a344e9dcff41ee494fadcab29d0c15c5764310bfa8d9b1655e27a829a7c62782505ea3c6f611402796e
7
- data.tar.gz: 548905e7ec71056de3e6a622073bf0fc342f6dca7a6d9c0c783411c35ab7f306aa87f71aecab7fc128a4dfd232f62dcb72dca0682a3421451d162844dab66960
6
+ metadata.gz: 337ad7b5c4315463492d5120b4652bd0e4d58c1b2a53fa8be05c5f1d02c7b567133196e31e5b43899001f25fd896c7f60d4cccda717d1d8fede675aaa21276e0
7
+ data.tar.gz: fcbb6c9963f8f0e5c5c532209fecf8643fad16a8943b74fd601644972ff25bb30f8c0e0be02b307c0247fb7a83549d21a54fe1f75850df795000d7756c9092ab
data/README.md CHANGED
@@ -73,7 +73,7 @@ require 'prometheus/client'
73
73
  require 'prometheus/client/push'
74
74
 
75
75
  prometheus = Prometheus::Client.registry
76
- # ... register some metrics, set/add/increment/etc. their values
76
+ # ... register some metrics, set/increment/observe/etc. their values
77
77
 
78
78
  # push the registry state to the default gateway
79
79
  Prometheus::Client::Push.new('my-batch-job').add(prometheus)
@@ -96,19 +96,16 @@ The following metric types are currently supported.
96
96
  Counter is a metric that exposes merely a sum or tally of things.
97
97
 
98
98
  ```ruby
99
- counter = Prometheus::Client::Counter.new(:foo, '...')
99
+ counter = Prometheus::Client::Counter.new(:service_requests_total, '...')
100
100
 
101
101
  # increment the counter for a given label set
102
- counter.increment(service: 'foo')
102
+ counter.increment({ service: 'foo' })
103
103
 
104
104
  # increment by a given value
105
105
  counter.increment({ service: 'bar' }, 5)
106
106
 
107
- # decrement the counter
108
- counter.decrement(service: 'exceptional')
109
-
110
107
  # get current value for a given label set
111
- counter.get(service: 'bar')
108
+ counter.get({ service: 'bar' })
112
109
  # => 5
113
110
  ```
114
111
 
@@ -118,30 +115,47 @@ Gauge is a metric that exposes merely an instantaneous value or some snapshot
118
115
  thereof.
119
116
 
120
117
  ```ruby
121
- gauge = Prometheus::Client::Gauge.new(:bar, '...')
118
+ gauge = Prometheus::Client::Gauge.new(:room_temperature_celsius, '...')
122
119
 
123
120
  # set a value
124
- gauge.set({ role: 'base' }, 'up')
121
+ gauge.set({ room: 'kitchen' }, 21.534)
125
122
 
126
123
  # retrieve the current value for a given label set
127
- gauge.get({ role: 'problematic' })
128
- # => 'down'
124
+ gauge.get({ room: 'kitchen' })
125
+ # => 21.534
126
+ ```
127
+
128
+ ### Histogram
129
+
130
+ A histogram samples observations (usually things like request durations or
131
+ response sizes) and counts them in configurable buckets. It also provides a sum
132
+ of all observed values.
133
+
134
+ ```ruby
135
+ histogram = Prometheus::Client::Histogram.new(:service_latency_seconds, '...')
136
+
137
+ # record a value
138
+ histogram.observe({ service: 'users' }, Benchmark.realtime { service.call(arg) })
139
+
140
+ # retrieve the current quantile values
141
+ histogram.get({ service: 'users' })
142
+ # => { 0.005 => 3, 0.01 => 15, 0.025 => 18, ..., 2.5 => 42, 5 => 42, 10 = >42 }
129
143
  ```
130
144
 
131
145
  ### Summary
132
146
 
133
- Summary is an accumulator for samples. It captures Numeric data and provides
134
- an efficient percentile calculation mechanism.
147
+ Summary, similar to histograms, is an accumulator for samples. It captures
148
+ Numeric data and provides an efficient percentile calculation mechanism.
135
149
 
136
150
  ```ruby
137
- summary = Prometheus::Client::Summary.new(:baz, '...')
151
+ summary = Prometheus::Client::Summary.new(:service_latency_seconds, '...')
138
152
 
139
153
  # record a value
140
- summary.add({ service: 'slow' }, Benchmark.realtime { service.call(arg) })
154
+ summary.observe({ service: 'database' }, Benchmark.realtime { service.call() })
141
155
 
142
156
  # retrieve the current quantile values
143
157
  summary.get({ service: 'database' })
144
- # => { 0.5: 1.233122, 0.9: 83.4323, 0.99: 341.3428231 }
158
+ # => { 0.5 => 0.1233122, 0.9 => 3.4323, 0.99 => 5.3428231 }
145
159
  ```
146
160
 
147
161
  ## Todo
@@ -11,7 +11,7 @@ module Prometheus
11
11
  end
12
12
 
13
13
  def increment(labels = {}, by = 1)
14
- fail ArgumentError, 'increment must be a non-negative number' if by < 0
14
+ raise ArgumentError, 'increment must be a non-negative number' if by < 0
15
15
 
16
16
  label_set = label_set_for(labels)
17
17
  synchronize { @values[label_set] += by }
@@ -5,20 +5,20 @@ module Prometheus
5
5
  module Formats
6
6
  # Text format is human readable mainly used for manual inspection.
7
7
  module Text
8
- MEDIA_TYPE = 'text/plain'
9
- VERSION = '0.0.4'
10
- CONTENT_TYPE = "#{MEDIA_TYPE}; version=#{VERSION}"
8
+ MEDIA_TYPE = 'text/plain'.freeze
9
+ VERSION = '0.0.4'.freeze
10
+ CONTENT_TYPE = "#{MEDIA_TYPE}; version=#{VERSION}".freeze
11
11
 
12
- METRIC_LINE = '%s%s %s'
13
- TYPE_LINE = '# TYPE %s %s'
14
- HELP_LINE = '# HELP %s %s'
12
+ METRIC_LINE = '%s%s %s'.freeze
13
+ TYPE_LINE = '# TYPE %s %s'.freeze
14
+ HELP_LINE = '# HELP %s %s'.freeze
15
15
 
16
- LABEL = '%s="%s"'
17
- SEPARATOR = ','
18
- DELIMITER = "\n"
16
+ LABEL = '%s="%s"'.freeze
17
+ SEPARATOR = ','.freeze
18
+ DELIMITER = "\n".freeze
19
19
 
20
- REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }
21
- REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }
20
+ REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }.freeze
21
+ REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }.freeze
22
22
 
23
23
  def self.marshal(registry)
24
24
  lines = []
@@ -36,44 +36,59 @@ module Prometheus
36
36
  (lines << nil).join(DELIMITER)
37
37
  end
38
38
 
39
- private
39
+ class << self
40
+ private
40
41
 
41
- def self.representation(metric, label_set, value, &block)
42
- set = metric.base_labels.merge(label_set)
42
+ def representation(metric, label_set, value, &block)
43
+ set = metric.base_labels.merge(label_set)
43
44
 
44
- if metric.type == :summary
45
- summary(metric.name, set, value, &block)
46
- else
47
- yield metric(metric.name, labels(set), value)
45
+ if metric.type == :summary
46
+ summary(metric.name, set, value, &block)
47
+ elsif metric.type == :histogram
48
+ histogram(metric.name, set, value, &block)
49
+ else
50
+ yield metric(metric.name, labels(set), value)
51
+ end
48
52
  end
49
- end
50
53
 
51
- def self.summary(name, set, value)
52
- value.each do |q, v|
53
- yield metric(name, labels(set.merge(quantile: q)), v)
54
- end
54
+ def summary(name, set, value)
55
+ value.each do |q, v|
56
+ yield metric(name, labels(set.merge(quantile: q)), v)
57
+ end
55
58
 
56
- l = labels(set)
57
- yield metric("#{name}_sum", l, value.sum)
58
- yield metric("#{name}_count", l, value.total)
59
- end
59
+ l = labels(set)
60
+ yield metric("#{name}_sum", l, value.sum)
61
+ yield metric("#{name}_count", l, value.total)
62
+ end
60
63
 
61
- def self.metric(name, labels, value)
62
- format(METRIC_LINE, name, labels, value)
63
- end
64
+ def histogram(name, set, value)
65
+ value.each do |q, v|
66
+ yield metric(name, labels(set.merge(le: q)), v)
67
+ end
68
+ yield metric(name, labels(set.merge(le: '+Inf')), value.total)
64
69
 
65
- def self.labels(set)
66
- return if set.empty?
70
+ l = labels(set)
71
+ yield metric("#{name}_sum", l, value.sum)
72
+ yield metric("#{name}_count", l, value.total)
73
+ end
67
74
 
68
- strings = set.each_with_object([]) do |(key, value), memo|
69
- memo << format(LABEL, key, escape(value, :label))
75
+ def metric(name, labels, value)
76
+ format(METRIC_LINE, name, labels, value)
70
77
  end
71
78
 
72
- "{#{strings.join(SEPARATOR)}}"
73
- end
79
+ def labels(set)
80
+ return if set.empty?
81
+
82
+ strings = set.each_with_object([]) do |(key, value), memo|
83
+ memo << format(LABEL, key, escape(value, :label))
84
+ end
74
85
 
75
- def self.escape(string, format = :doc)
76
- string.to_s.gsub(REGEX[format], REPLACE)
86
+ "{#{strings.join(SEPARATOR)}}"
87
+ end
88
+
89
+ def escape(string, format = :doc)
90
+ string.to_s.gsub(REGEX[format], REPLACE)
91
+ end
77
92
  end
78
93
  end
79
94
  end
@@ -0,0 +1,73 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'prometheus/client/metric'
4
+
5
+ module Prometheus
6
+ module Client
7
+ # A histogram samples observations (usually things like request durations
8
+ # or response sizes) and counts them in configurable buckets. It also
9
+ # provides a sum of all observed values.
10
+ class Histogram < Metric
11
+ # Value represents the state of a Histogram at a given point.
12
+ class Value < Hash
13
+ attr_accessor :sum, :total
14
+
15
+ def initialize(buckets)
16
+ @sum = 0.0
17
+ @total = 0
18
+
19
+ buckets.each do |bucket|
20
+ self[bucket] = 0
21
+ end
22
+ end
23
+
24
+ def observe(value)
25
+ @sum += value
26
+ @total += 1
27
+
28
+ each_key do |bucket|
29
+ self[bucket] += 1 if value <= bucket
30
+ end
31
+ end
32
+ end
33
+
34
+ # DEFAULT_BUCKETS are the default Histogram buckets. The default buckets
35
+ # are tailored to broadly measure the response time (in seconds) of a
36
+ # network service. (From DefBuckets client_golang)
37
+ DEFAULT_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
38
+ 2.5, 5, 10].freeze
39
+
40
+ # Offer a way to manually specify buckets
41
+ def initialize(name, docstring, base_labels = {},
42
+ buckets = DEFAULT_BUCKETS)
43
+ raise ArgumentError, 'Unsorted buckets, typo?' unless sorted? buckets
44
+
45
+ @buckets = buckets
46
+ super(name, docstring, base_labels)
47
+ end
48
+
49
+ def type
50
+ :histogram
51
+ end
52
+
53
+ def observe(labels, value)
54
+ if labels[:le]
55
+ raise ArgumentError, 'Label with name "le" is not permitted'
56
+ end
57
+
58
+ label_set = label_set_for(labels)
59
+ synchronize { @values[label_set].observe(value) }
60
+ end
61
+
62
+ private
63
+
64
+ def default
65
+ Value.new(@buckets)
66
+ end
67
+
68
+ def sorted?(bucket)
69
+ bucket.each_cons(2).all? { |i, j| i <= j }
70
+ end
71
+ end
72
+ end
73
+ end
@@ -6,7 +6,7 @@ module Prometheus
6
6
  # Prometheus specification.
7
7
  class LabelSetValidator
8
8
  # TODO: we might allow setting :instance in the future
9
- RESERVED_LABELS = [:job, :instance]
9
+ RESERVED_LABELS = [:job, :instance].freeze
10
10
 
11
11
  class LabelSetError < StandardError; end
12
12
  class InvalidLabelSetError < LabelSetError; end
@@ -19,7 +19,7 @@ module Prometheus
19
19
 
20
20
  def valid?(labels)
21
21
  unless labels.respond_to?(:all?)
22
- fail InvalidLabelSetError, "#{labels} is not a valid label set"
22
+ raise InvalidLabelSetError, "#{labels} is not a valid label set"
23
23
  end
24
24
 
25
25
  labels.all? do |key, _|
@@ -35,7 +35,7 @@ module Prometheus
35
35
  valid?(labels)
36
36
 
37
37
  unless @validated.empty? || match?(labels, @validated.first.last)
38
- fail InvalidLabelSetError, 'labels must have the same signature'
38
+ raise InvalidLabelSetError, 'labels must have the same signature'
39
39
  end
40
40
 
41
41
  @validated[labels.hash] = labels
@@ -50,19 +50,19 @@ module Prometheus
50
50
  def validate_symbol(key)
51
51
  return true if key.is_a?(Symbol)
52
52
 
53
- fail InvalidLabelError, "label #{key} is not a symbol"
53
+ raise InvalidLabelError, "label #{key} is not a symbol"
54
54
  end
55
55
 
56
56
  def validate_name(key)
57
57
  return true unless key.to_s.start_with?('__')
58
58
 
59
- fail ReservedLabelError, "label #{key} must not start with __"
59
+ raise ReservedLabelError, "label #{key} must not start with __"
60
60
  end
61
61
 
62
62
  def validate_reserved_key(key)
63
63
  return true unless RESERVED_LABELS.include?(key)
64
64
 
65
- fail ReservedLabelError, "#{key} is reserved"
65
+ raise ReservedLabelError, "#{key} is reserved"
66
66
  end
67
67
  end
68
68
  end
@@ -25,7 +25,7 @@ module Prometheus
25
25
 
26
26
  # Returns the metric type
27
27
  def type
28
- fail NotImplementedError
28
+ raise NotImplementedError
29
29
  end
30
30
 
31
31
  # Returns the value for the given label set
@@ -53,13 +53,13 @@ module Prometheus
53
53
  def validate_name(name)
54
54
  return true if name.is_a?(Symbol)
55
55
 
56
- fail ArgumentError, 'given name must be a symbol'
56
+ raise ArgumentError, 'given name must be a symbol'
57
57
  end
58
58
 
59
59
  def validate_docstring(docstring)
60
60
  return true if docstring.respond_to?(:empty?) && !docstring.empty?
61
61
 
62
- fail ArgumentError, 'docstring must be given'
62
+ raise ArgumentError, 'docstring must be given'
63
63
  end
64
64
 
65
65
  def label_set_for(labels)
@@ -12,10 +12,10 @@ module Prometheus
12
12
  # Push implements a simple way to transmit a given registry to a given
13
13
  # Pushgateway.
14
14
  class Push
15
- DEFAULT_GATEWAY = 'http://localhost:9091'
16
- PATH = '/metrics/jobs/%s'
17
- INSTANCE_PATH = '/metrics/jobs/%s/instances/%s'
18
- HEADER = { 'Content-Type' => Formats::Text::CONTENT_TYPE }
15
+ DEFAULT_GATEWAY = 'http://localhost:9091'.freeze
16
+ PATH = '/metrics/jobs/%s'.freeze
17
+ INSTANCE_PATH = '/metrics/jobs/%s/instances/%s'.freeze
18
+ HEADER = { 'Content-Type' => Formats::Text::CONTENT_TYPE }.freeze
19
19
 
20
20
  attr_reader :job, :instance, :gateway, :path
21
21
 
@@ -44,7 +44,7 @@ module Prometheus
44
44
  if uri.scheme == 'http'
45
45
  uri
46
46
  else
47
- fail ArgumentError, 'only HTTP gateway URLs are supported currently.'
47
+ raise ArgumentError, 'only HTTP gateway URLs are supported currently.'
48
48
  end
49
49
  rescue URI::InvalidURIError => e
50
50
  raise ArgumentError, "#{url} is not a valid URL: #{e}"
@@ -37,19 +37,19 @@ module Prometheus
37
37
  def init_request_metrics
38
38
  @requests = @registry.counter(
39
39
  :http_requests_total,
40
- 'A counter of the total number of HTTP requests made.')
41
- @requests_duration = @registry.counter(
42
- :http_request_duration_total_seconds,
43
- 'The total amount of time spent answering HTTP requests.')
40
+ 'A counter of the total number of HTTP requests made.',
41
+ )
44
42
  @durations = @registry.summary(
45
43
  :http_request_duration_seconds,
46
- 'A histogram of the response latency.')
44
+ 'A histogram of the response latency.',
45
+ )
47
46
  end
48
47
 
49
48
  def init_exception_metrics
50
49
  @exceptions = @registry.counter(
51
50
  :http_exceptions_total,
52
- 'A counter of the total number of exceptions raised.')
51
+ 'A counter of the total number of exceptions raised.',
52
+ )
53
53
  end
54
54
 
55
55
  def trace(env)
@@ -71,8 +71,7 @@ module Prometheus
71
71
 
72
72
  def record(labels, duration)
73
73
  @requests.increment(labels)
74
- @requests_duration.increment(labels, duration)
75
- @durations.add(labels, duration)
74
+ @durations.observe(labels, duration)
76
75
  rescue
77
76
  # TODO: log unexpected exception during request recording
78
77
  nil
@@ -11,7 +11,7 @@ module Prometheus
11
11
  class Exporter
12
12
  attr_reader :app, :registry, :path
13
13
 
14
- FORMATS = [Formats::Text]
14
+ FORMATS = [Formats::Text].freeze
15
15
  FALLBACK = Formats::Text
16
16
 
17
17
  def initialize(app, options = {})
@@ -5,6 +5,7 @@ require 'thread'
5
5
  require 'prometheus/client/counter'
6
6
  require 'prometheus/client/summary'
7
7
  require 'prometheus/client/gauge'
8
+ require 'prometheus/client/histogram'
8
9
 
9
10
  module Prometheus
10
11
  module Client
@@ -22,7 +23,7 @@ module Prometheus
22
23
 
23
24
  @mutex.synchronize do
24
25
  if exist?(name.to_sym)
25
- fail AlreadyRegisteredError, "#{name} has already been registered"
26
+ raise AlreadyRegisteredError, "#{name} has already been registered"
26
27
  else
27
28
  @metrics[name.to_sym] = metric
28
29
  end
@@ -43,6 +44,11 @@ module Prometheus
43
44
  register(Gauge.new(name, docstring, base_labels))
44
45
  end
45
46
 
47
+ def histogram(name, docstring, base_labels = {},
48
+ buckets = Histogram::DEFAULT_BUCKETS)
49
+ register(Histogram.new(name, docstring, base_labels, buckets))
50
+ end
51
+
46
52
  def exist?(name)
47
53
  @metrics.key?(name)
48
54
  end
@@ -8,6 +8,8 @@ module Prometheus
8
8
  # Summary is an accumulator for samples. It captures Numeric data and
9
9
  # provides an efficient quantile calculation mechanism.
10
10
  class Summary < Metric
11
+ extend Gem::Deprecate
12
+
11
13
  # Value represents the state of a Summary at a given point.
12
14
  class Value < Hash
13
15
  attr_accessor :sum, :total
@@ -27,10 +29,12 @@ module Prometheus
27
29
  end
28
30
 
29
31
  # Records a given value.
30
- def add(labels, value)
32
+ def observe(labels, value)
31
33
  label_set = label_set_for(labels)
32
34
  synchronize { @values[label_set].observe(value) }
33
35
  end
36
+ alias add observe
37
+ deprecate :add, :observe, 2016, 10
34
38
 
35
39
  # Returns the value for the given label set
36
40
  def get(labels = {})
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Prometheus
4
4
  module Client
5
- VERSION = '0.5.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  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.5.0
4
+ version: 0.6.0
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-01-12 00:00:00.000000000 Z
11
+ date: 2016-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: quantile
@@ -37,6 +37,7 @@ files:
37
37
  - lib/prometheus/client/counter.rb
38
38
  - lib/prometheus/client/formats/text.rb
39
39
  - lib/prometheus/client/gauge.rb
40
+ - lib/prometheus/client/histogram.rb
40
41
  - lib/prometheus/client/label_set_validator.rb
41
42
  - lib/prometheus/client/metric.rb
42
43
  - lib/prometheus/client/push.rb