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 +4 -4
- data/README.md +30 -16
- data/lib/prometheus/client/counter.rb +1 -1
- data/lib/prometheus/client/formats/text.rb +53 -38
- data/lib/prometheus/client/histogram.rb +73 -0
- data/lib/prometheus/client/label_set_validator.rb +6 -6
- data/lib/prometheus/client/metric.rb +3 -3
- data/lib/prometheus/client/push.rb +5 -5
- data/lib/prometheus/client/rack/collector.rb +7 -8
- data/lib/prometheus/client/rack/exporter.rb +1 -1
- data/lib/prometheus/client/registry.rb +7 -1
- data/lib/prometheus/client/summary.rb +5 -1
- data/lib/prometheus/client/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ca1c148a74709f3c2fbbba112f651babdf43fbe
|
4
|
+
data.tar.gz: e55c7b9be73218b6a252a5a624fc8a865d00d851
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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(:
|
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(:
|
118
|
+
gauge = Prometheus::Client::Gauge.new(:room_temperature_celsius, '...')
|
122
119
|
|
123
120
|
# set a value
|
124
|
-
gauge.set({
|
121
|
+
gauge.set({ room: 'kitchen' }, 21.534)
|
125
122
|
|
126
123
|
# retrieve the current value for a given label set
|
127
|
-
gauge.get({
|
128
|
-
# =>
|
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
|
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(:
|
151
|
+
summary = Prometheus::Client::Summary.new(:service_latency_seconds, '...')
|
138
152
|
|
139
153
|
# record a value
|
140
|
-
summary.
|
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
|
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
|
-
|
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
|
-
|
39
|
+
class << self
|
40
|
+
private
|
40
41
|
|
41
|
-
|
42
|
-
|
42
|
+
def representation(metric, label_set, value, &block)
|
43
|
+
set = metric.base_labels.merge(label_set)
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
69
|
-
|
75
|
+
def metric(name, labels, value)
|
76
|
+
format(METRIC_LINE, name, labels, value)
|
70
77
|
end
|
71
78
|
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
@
|
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
|
@@ -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
|
-
|
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
|
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 = {})
|
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.
|
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-
|
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
|