prometheus-client-mmap 0.7.0.beta8 → 0.7.0.beta9
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/lib/prometheus/client/configuration.rb +18 -0
- data/lib/prometheus/client/counter.rb +1 -2
- data/lib/prometheus/client/formats/text.rb +36 -29
- data/lib/prometheus/client/gauge.rb +2 -2
- data/lib/prometheus/client/histogram.rb +5 -5
- data/lib/prometheus/client/metric.rb +11 -12
- data/lib/prometheus/client/mmaped_dict.rb +123 -0
- data/lib/prometheus/client/mmaped_value.rb +80 -0
- data/lib/prometheus/client/rack/exporter.rb +6 -3
- data/lib/prometheus/client/simple_value.rb +28 -0
- data/lib/prometheus/client/summary.rb +6 -5
- data/lib/prometheus/client/uses_value_type.rb +19 -0
- data/lib/prometheus/client/version.rb +1 -1
- data/lib/prometheus/client.rb +20 -5
- metadata +7 -3
- data/lib/prometheus/client/valuetype.rb +0 -207
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24351728cd7ccff16974ea7cf72a781f3b393831
|
4
|
+
data.tar.gz: aba591bd2540b26541949b3536a2b5fbc2850e72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a6d7c3669a14ff150f3a7d79c119db34ed7a30404a6e69f0c6183e8c50b53325f6147fad8bbf5efd55137d00eef3c6e4c89b60c54e4dad8618753edc69bf06b
|
7
|
+
data.tar.gz: ff9e4880b4847076b887d5c2ee07fe09fefb88d0806b840d6a65f21a8d4e1c3aca05e07ca94561ec97bf79f6450a95f4129a48c1eb9cdf982ac898797d4101a4
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'prometheus/client/registry'
|
2
|
+
require 'prometheus/client/mmaped_value'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Prometheus
|
6
|
+
module Client
|
7
|
+
class Configuration
|
8
|
+
attr_accessor :value_class, :multiprocess_files_dir, :initial_mmap_file_size, :logger
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@value_class = ::Prometheus::Client::MmapedValue
|
12
|
+
@multiprocess_files_dir = ENV['prometheus_multiproc_dir']
|
13
|
+
@initial_mmap_file_size = 4 * 1024
|
14
|
+
@logger = Logger.new($stdout)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
require 'prometheus/client/metric'
|
4
|
-
require 'prometheus/client/valuetype'
|
5
4
|
|
6
5
|
module Prometheus
|
7
6
|
module Client
|
@@ -21,7 +20,7 @@ module Prometheus
|
|
21
20
|
private
|
22
21
|
|
23
22
|
def default(labels)
|
24
|
-
|
23
|
+
value_object(type, @name, @name, labels)
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'prometheus/client/valuetype'
|
1
|
+
require 'prometheus/client/uses_value_type'
|
4
2
|
|
5
3
|
module Prometheus
|
6
4
|
module Client
|
@@ -38,32 +36,8 @@ module Prometheus
|
|
38
36
|
(lines << nil).join(DELIMITER)
|
39
37
|
end
|
40
38
|
|
41
|
-
def self.marshal_multiprocess(path=Prometheus::Client
|
42
|
-
metrics =
|
43
|
-
Dir.glob(File.join(path, "*.db")).each do |f|
|
44
|
-
parts = File.basename(f,'.db').split("_")
|
45
|
-
type = parts[0].to_sym
|
46
|
-
d = MmapedDict.new(f)
|
47
|
-
d.all_values.each do |key, value|
|
48
|
-
metric_name, name, labelnames, labelvalues = JSON.parse(key)
|
49
|
-
metric = metrics.fetch(metric_name, {
|
50
|
-
metric_name: metric_name,
|
51
|
-
help: 'Multiprocess metric',
|
52
|
-
type: type,
|
53
|
-
samples: [],
|
54
|
-
})
|
55
|
-
if type == :gauge
|
56
|
-
pid = parts[2]
|
57
|
-
metric[:multiprocess_mode] = parts[1]
|
58
|
-
metric[:samples] += [[name, labelnames.zip(labelvalues) + [['pid', pid]], value]]
|
59
|
-
else
|
60
|
-
# The duplicates and labels are fixed in the next for.
|
61
|
-
metric[:samples] += [[name, labelnames.zip(labelvalues), value]]
|
62
|
-
end
|
63
|
-
metrics[metric_name] = metric
|
64
|
-
end
|
65
|
-
d.close
|
66
|
-
end
|
39
|
+
def self.marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir)
|
40
|
+
metrics = load_metrics(path)
|
67
41
|
|
68
42
|
metrics.each_value do |metric|
|
69
43
|
samples = {}
|
@@ -118,6 +92,39 @@ module Prometheus
|
|
118
92
|
class << self
|
119
93
|
private
|
120
94
|
|
95
|
+
def load_metrics(path)
|
96
|
+
metrics = {}
|
97
|
+
Dir.glob(File.join(path, '*.db')).each do |f|
|
98
|
+
parts = File.basename(f, '.db').split('_')
|
99
|
+
type = parts[0].to_sym
|
100
|
+
d = MmapedDict.new(f)
|
101
|
+
|
102
|
+
begin
|
103
|
+
d.all_values.each do |key, value|
|
104
|
+
metric_name, name, labelnames, labelvalues = JSON.parse(key)
|
105
|
+
metric = metrics.fetch(metric_name,
|
106
|
+
metric_name: metric_name,
|
107
|
+
help: 'Multiprocess metric',
|
108
|
+
type: type,
|
109
|
+
samples: []
|
110
|
+
)
|
111
|
+
if type == :gauge
|
112
|
+
pid = parts[2]
|
113
|
+
metric[:multiprocess_mode] = parts[1]
|
114
|
+
metric[:samples] += [[name, labelnames.zip(labelvalues) + [['pid', pid]], value]]
|
115
|
+
else
|
116
|
+
# The duplicates and labels are fixed in the next for.
|
117
|
+
metric[:samples] += [[name, labelnames.zip(labelvalues), value]]
|
118
|
+
end
|
119
|
+
metrics[metric_name] = metric
|
120
|
+
end
|
121
|
+
ensure
|
122
|
+
d.close
|
123
|
+
end
|
124
|
+
end
|
125
|
+
metrics
|
126
|
+
end
|
127
|
+
|
121
128
|
def representation(metric, label_set, value, &block)
|
122
129
|
set = metric.base_labels.merge(label_set)
|
123
130
|
|
@@ -9,7 +9,7 @@ module Prometheus
|
|
9
9
|
class Gauge < Metric
|
10
10
|
def initialize(name, docstring, base_labels = {}, multiprocess_mode=:all)
|
11
11
|
super(name, docstring, base_labels)
|
12
|
-
if
|
12
|
+
if value_class.multiprocess and ![:min, :max, :livesum, :liveall, :all].include?(multiprocess_mode)
|
13
13
|
raise ArgumentError, 'Invalid multiprocess mode: ' + multiprocess_mode
|
14
14
|
end
|
15
15
|
@multiprocess_mode = multiprocess_mode
|
@@ -20,7 +20,7 @@ module Prometheus
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def default(labels)
|
23
|
-
|
23
|
+
value_object(type, @name, @name, labels, @multiprocess_mode)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Sets the value for the given label set
|
@@ -1,6 +1,5 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'prometheus/client/metric'
|
2
|
+
require 'prometheus/client/uses_value_type'
|
4
3
|
|
5
4
|
module Prometheus
|
6
5
|
module Client
|
@@ -10,15 +9,16 @@ module Prometheus
|
|
10
9
|
class Histogram < Metric
|
11
10
|
# Value represents the state of a Histogram at a given point.
|
12
11
|
class Value < Hash
|
12
|
+
include UsesValueType
|
13
13
|
attr_accessor :sum, :total
|
14
14
|
|
15
15
|
def initialize(type, name, labels, buckets)
|
16
|
-
@sum =
|
16
|
+
@sum = value_object(type, name, "#{name}_sum", labels)
|
17
17
|
# TODO: get rid of total and use +Inf bucket instead.
|
18
|
-
@total =
|
18
|
+
@total = value_object(type, name, "#{name}_count", labels)
|
19
19
|
|
20
20
|
buckets.each do |bucket|
|
21
|
-
self[bucket] =
|
21
|
+
self[bucket] = value_object(type, name, "#{name}_bucket", labels.merge({ :le => bucket.to_s }))
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -1,25 +1,24 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'thread'
|
4
2
|
require 'prometheus/client/label_set_validator'
|
5
|
-
require 'prometheus/client/
|
3
|
+
require 'prometheus/client/uses_value_type'
|
6
4
|
|
7
5
|
module Prometheus
|
8
6
|
module Client
|
9
7
|
# Metric
|
10
8
|
class Metric
|
9
|
+
include UsesValueType
|
11
10
|
attr_reader :name, :docstring, :base_labels
|
12
11
|
|
13
12
|
def initialize(name, docstring, base_labels = {})
|
14
13
|
@mutex = Mutex.new
|
15
|
-
case type
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
@validator = case type
|
15
|
+
when :summary
|
16
|
+
LabelSetValidator.new(['quantile'])
|
17
|
+
when :histogram
|
18
|
+
LabelSetValidator.new(['le'])
|
19
|
+
else
|
20
|
+
LabelSetValidator.new
|
21
|
+
end
|
23
22
|
@values = Hash.new { |hash, key| hash[key] = default(key) }
|
24
23
|
|
25
24
|
validate_name(name)
|
@@ -50,7 +49,7 @@ module Prometheus
|
|
50
49
|
private
|
51
50
|
|
52
51
|
def default(labels)
|
53
|
-
|
52
|
+
value_object(type, @name, @name, labels)
|
54
53
|
end
|
55
54
|
|
56
55
|
def validate_name(name)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Prometheus
|
2
|
+
module Client
|
3
|
+
class ParsingError < StandardError; end
|
4
|
+
|
5
|
+
# A dict of doubles, backed by an mmapped file.
|
6
|
+
#
|
7
|
+
# The file starts with a 4 byte int, indicating how much of it is used.
|
8
|
+
# Then 4 bytes of padding.
|
9
|
+
# There's then a number of entries, consisting of a 4 byte int which is the
|
10
|
+
# size of the next field, a utf-8 encoded string key, padding to an 8 byte
|
11
|
+
# alignment, and then a 8 byte float which is the value.
|
12
|
+
#
|
13
|
+
# TODO(julius): dealing with Mmap.new, truncate etc. errors?
|
14
|
+
class MmapedDict
|
15
|
+
MINIMUM_SIZE = 4.freeze
|
16
|
+
attr_reader :m, :capacity, :used, :positions
|
17
|
+
|
18
|
+
def initialize(filename)
|
19
|
+
@mutex = Mutex.new
|
20
|
+
@f = File.open(filename, 'a+b')
|
21
|
+
process_file_wrappe_error
|
22
|
+
end
|
23
|
+
|
24
|
+
# Yield (key, value, pos). No locking is performed.
|
25
|
+
def all_values
|
26
|
+
read_all_values.map { |k, v, p| [k, v] }
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_value(key)
|
30
|
+
@mutex.synchronize do
|
31
|
+
init_value(key) unless @positions.has_key?(key)
|
32
|
+
end
|
33
|
+
pos = @positions[key]
|
34
|
+
# We assume that reading from an 8 byte aligned value is atomic.
|
35
|
+
@m[pos..pos+7].unpack('d')[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_value(key, value)
|
39
|
+
@mutex.synchronize do
|
40
|
+
init_value(key) unless @positions.has_key?(key)
|
41
|
+
end
|
42
|
+
pos = @positions[key]
|
43
|
+
# We assume that writing to an 8 byte aligned value is atomic.
|
44
|
+
@m[pos..pos+7] = [value].pack('d')
|
45
|
+
end
|
46
|
+
|
47
|
+
def close()
|
48
|
+
@m.munmap
|
49
|
+
@f.close
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def process_file_wrappe_error
|
55
|
+
process_file
|
56
|
+
rescue StandardError => e
|
57
|
+
raise ParsingError.new("exception #{e} while processing metrics file #{@f.path}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_file
|
61
|
+
if @f.size < MINIMUM_SIZE
|
62
|
+
@f.truncate(initial_mmap_file_size)
|
63
|
+
end
|
64
|
+
@f.truncate(initial_mmap_file_size)
|
65
|
+
|
66
|
+
@capacity = @f.size
|
67
|
+
@m = Mmap.new(@f.path, 'rw', Mmap::MAP_SHARED)
|
68
|
+
# @m.mlock # TODO: Why does this raise an error?
|
69
|
+
|
70
|
+
@positions = {}
|
71
|
+
@used = @m[0..3].unpack('l')[0]
|
72
|
+
if @used == 0
|
73
|
+
@used = 8
|
74
|
+
@m[0..3] = [@used].pack('l')
|
75
|
+
else
|
76
|
+
read_all_values.each do |key, _, pos|
|
77
|
+
@positions[key] = pos
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def initial_mmap_file_size
|
83
|
+
Prometheus::Client.configuration.initial_mmap_file_size
|
84
|
+
end
|
85
|
+
|
86
|
+
# Initialize a value. Lock must be held by caller.
|
87
|
+
def init_value(key)
|
88
|
+
# Pad to be 8-byte aligned.
|
89
|
+
padded = key + (' ' * (8 - (key.length + 4) % 8))
|
90
|
+
value = [key.length, padded, 0.0].pack("lA#{padded.length}d")
|
91
|
+
while @used + value.length > @capacity
|
92
|
+
@capacity *= 2
|
93
|
+
@f.truncate(@capacity)
|
94
|
+
@m.unmap
|
95
|
+
@m = Mmap.new(@f.path, 'rw', Mmap::MAP_SHARED)
|
96
|
+
end
|
97
|
+
@m[@used..@used + value.length] = value
|
98
|
+
|
99
|
+
# Update how much space we've used.
|
100
|
+
@used += value.length
|
101
|
+
@m[0..3] = [@used].pack('l')
|
102
|
+
@positions[key] = @used - 8
|
103
|
+
end
|
104
|
+
|
105
|
+
# Yield (key, value, pos). No locking is performed.
|
106
|
+
def read_all_values
|
107
|
+
pos = 8
|
108
|
+
values = []
|
109
|
+
while pos < @used
|
110
|
+
encoded_len = @m[pos..-1].unpack('l')[0]
|
111
|
+
pos += 4
|
112
|
+
encoded = @m[pos..-1].unpack("A#{encoded_len}")[0]
|
113
|
+
padded_len = encoded_len + (8 - (encoded_len + 4) % 8)
|
114
|
+
pos += padded_len
|
115
|
+
value = @m[pos..-1].unpack('d')[0]
|
116
|
+
values << [encoded, value, pos]
|
117
|
+
pos += 8
|
118
|
+
end
|
119
|
+
values
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'prometheus/client/mmaped_dict'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Prometheus
|
5
|
+
module Client
|
6
|
+
# A float protected by a mutex backed by a per-process mmaped file.
|
7
|
+
class MmapedValue
|
8
|
+
@@files = {}
|
9
|
+
@@files_lock = Mutex.new
|
10
|
+
@@pid = Process.pid
|
11
|
+
|
12
|
+
def initialize(type, metric_name, name, labels, multiprocess_mode='')
|
13
|
+
file_prefix = type.to_s
|
14
|
+
if type == :gauge
|
15
|
+
file_prefix += '_' + multiprocess_mode.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
@@files_lock.synchronize do
|
19
|
+
unless @@files.has_key?(file_prefix)
|
20
|
+
filename = File.join(Prometheus::Client.configuration.multiprocess_files_dir, "#{file_prefix}_#{@@pid}.db")
|
21
|
+
@@files[file_prefix] = MmapedDict.new(filename)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@file = @@files[file_prefix]
|
26
|
+
labelnames = []
|
27
|
+
labelvalues = []
|
28
|
+
labels.each do |k, v|
|
29
|
+
labelnames << k
|
30
|
+
labelvalues << v
|
31
|
+
end
|
32
|
+
|
33
|
+
@key = [metric_name, name, labelnames, labelvalues].to_json
|
34
|
+
@value = read_value(@key)
|
35
|
+
@mutex = Mutex.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment(amount=1)
|
39
|
+
@mutex.synchronize do
|
40
|
+
@value += amount
|
41
|
+
write_value(@key, @value)
|
42
|
+
@value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def set(value)
|
47
|
+
@mutex.synchronize do
|
48
|
+
@value = value
|
49
|
+
write_value(@key, @value)
|
50
|
+
@value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def get
|
55
|
+
@mutex.synchronize do
|
56
|
+
return @value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.multiprocess
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def write_value(key, val)
|
67
|
+
@file.write_value(key, val)
|
68
|
+
rescue StandardError => e
|
69
|
+
Prometheus::Client.logger.warn("writing value to #{@file.path} failed with #{e}")
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_value(key)
|
73
|
+
@file.read_value(key)
|
74
|
+
rescue StandardError => e
|
75
|
+
Prometheus::Client.logger.warn("readomg value from #{@file.path} failed with #{e}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -11,7 +11,7 @@ module Prometheus
|
|
11
11
|
class Exporter
|
12
12
|
attr_reader :app, :registry, :path
|
13
13
|
|
14
|
-
FORMATS
|
14
|
+
FORMATS = [Formats::Text].freeze
|
15
15
|
FALLBACK = Formats::Text
|
16
16
|
|
17
17
|
def initialize(app, options = {})
|
@@ -62,8 +62,11 @@ module Prometheus
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def respond_with(format)
|
65
|
-
|
66
|
-
|
65
|
+
response = if Prometheus::Client.configuration.value_class.multiprocess
|
66
|
+
format.marshal_multiprocess
|
67
|
+
else
|
68
|
+
format.marshal
|
69
|
+
end
|
67
70
|
[
|
68
71
|
200,
|
69
72
|
{ 'Content-Type' => format::CONTENT_TYPE },
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'mmap'
|
3
|
+
|
4
|
+
module Prometheus
|
5
|
+
module Client
|
6
|
+
class SimpleValue
|
7
|
+
def initialize(type, metric_name, name, labels, value = 0)
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(value)
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def increment(by = 1)
|
16
|
+
@value += by
|
17
|
+
end
|
18
|
+
|
19
|
+
def get
|
20
|
+
@value
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.multiprocess
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'prometheus/client/metric'
|
2
|
+
require 'prometheus/client/uses_value_type'
|
4
3
|
|
5
4
|
module Prometheus
|
6
5
|
module Client
|
@@ -11,16 +10,17 @@ module Prometheus
|
|
11
10
|
|
12
11
|
# Value represents the state of a Summary at a given point.
|
13
12
|
class Value < Hash
|
13
|
+
include UsesValueType
|
14
14
|
attr_accessor :sum, :total
|
15
15
|
|
16
16
|
def initialize(type, name, labels)
|
17
|
-
@sum =
|
18
|
-
@total =
|
17
|
+
@sum = value_object(type, name, "#{name}_sum", labels)
|
18
|
+
@total = value_object(type, name, "#{name}_count", labels)
|
19
19
|
end
|
20
20
|
|
21
21
|
def observe(value)
|
22
22
|
@sum.increment(value)
|
23
|
-
@total.increment
|
23
|
+
@total.increment
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -37,6 +37,7 @@ module Prometheus
|
|
37
37
|
label_set = label_set_for(labels)
|
38
38
|
synchronize { @values[label_set].observe(value) }
|
39
39
|
end
|
40
|
+
|
40
41
|
alias add observe
|
41
42
|
deprecate :add, :observe, 2016, 10
|
42
43
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'prometheus/client/simple_value'
|
2
|
+
|
3
|
+
module Prometheus
|
4
|
+
module Client
|
5
|
+
# Module providing convenience methods for creating value_object
|
6
|
+
module UsesValueType
|
7
|
+
def value_class
|
8
|
+
Prometheus::Client.configuration.value_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def value_object(type, metric_name, name, labels, *args)
|
12
|
+
value_class.new(type, metric_name, name, labels, *args)
|
13
|
+
rescue StandardError => e
|
14
|
+
Prometheus::Client.logger.info("error #{e} while creating instance of #{value_class} defaultig to SimpleValue")
|
15
|
+
Prometheus::Client::SimpleValue.new(type, metric_name, name, labels)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/prometheus/client.rb
CHANGED
@@ -1,13 +1,28 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'prometheus/client/registry'
|
2
|
+
require 'prometheus/client/configuration'
|
4
3
|
|
5
4
|
module Prometheus
|
6
5
|
# Client is a ruby implementation for a Prometheus compatible client.
|
7
6
|
module Client
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class << self
|
8
|
+
attr_writer :configuration
|
9
|
+
|
10
|
+
def configuration
|
11
|
+
@configuration ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def configure
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a default registry object
|
19
|
+
def registry
|
20
|
+
@registry ||= Registry.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
configuration.logger
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
13
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus-client-mmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.beta9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Schmidt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mmap2
|
@@ -40,18 +40,22 @@ files:
|
|
40
40
|
- README.md
|
41
41
|
- lib/prometheus.rb
|
42
42
|
- lib/prometheus/client.rb
|
43
|
+
- lib/prometheus/client/configuration.rb
|
43
44
|
- lib/prometheus/client/counter.rb
|
44
45
|
- lib/prometheus/client/formats/text.rb
|
45
46
|
- lib/prometheus/client/gauge.rb
|
46
47
|
- lib/prometheus/client/histogram.rb
|
47
48
|
- lib/prometheus/client/label_set_validator.rb
|
48
49
|
- lib/prometheus/client/metric.rb
|
50
|
+
- lib/prometheus/client/mmaped_dict.rb
|
51
|
+
- lib/prometheus/client/mmaped_value.rb
|
49
52
|
- lib/prometheus/client/push.rb
|
50
53
|
- lib/prometheus/client/rack/collector.rb
|
51
54
|
- lib/prometheus/client/rack/exporter.rb
|
52
55
|
- lib/prometheus/client/registry.rb
|
56
|
+
- lib/prometheus/client/simple_value.rb
|
53
57
|
- lib/prometheus/client/summary.rb
|
54
|
-
- lib/prometheus/client/
|
58
|
+
- lib/prometheus/client/uses_value_type.rb
|
55
59
|
- lib/prometheus/client/version.rb
|
56
60
|
homepage: https://gitlab.com/gitlab-org/prometheus-client-mmap
|
57
61
|
licenses:
|
@@ -1,207 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "mmap"
|
5
|
-
|
6
|
-
module Prometheus
|
7
|
-
module Client
|
8
|
-
class SimpleValue
|
9
|
-
def initialize(type, metric_name, name, labels, value = 0)
|
10
|
-
@value = value
|
11
|
-
end
|
12
|
-
|
13
|
-
def set(value)
|
14
|
-
@value = value
|
15
|
-
end
|
16
|
-
|
17
|
-
def increment(by = 1)
|
18
|
-
@value += by
|
19
|
-
end
|
20
|
-
|
21
|
-
def get
|
22
|
-
@value
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.multiprocess
|
26
|
-
false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# A float protected by a mutex backed by a per-process mmaped file.
|
31
|
-
class MmapedValue
|
32
|
-
@@files = {}
|
33
|
-
@@files_lock = Mutex.new
|
34
|
-
@@pid = Process.pid
|
35
|
-
|
36
|
-
def initialize(type, metric_name, name, labels, multiprocess_mode='')
|
37
|
-
@@pid = Process.pid
|
38
|
-
file_prefix = type.to_s
|
39
|
-
if type == :gauge
|
40
|
-
file_prefix += '_' + multiprocess_mode.to_s
|
41
|
-
end
|
42
|
-
|
43
|
-
@@files_lock.synchronize do
|
44
|
-
if !@@files.has_key?(file_prefix)
|
45
|
-
filename = File.join(Prometheus::Client::Multiprocdir, "#{file_prefix}_#{@@pid}.db")
|
46
|
-
@@files[file_prefix] = MmapedDict.new(filename)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
@file = @@files[file_prefix]
|
51
|
-
labelnames = []
|
52
|
-
labelvalues = []
|
53
|
-
labels.each do |k, v|
|
54
|
-
labelnames << k
|
55
|
-
labelvalues << v
|
56
|
-
end
|
57
|
-
|
58
|
-
@key = [metric_name, name, labelnames, labelvalues].to_json
|
59
|
-
@value = @file.read_value(@key)
|
60
|
-
@mutex = Mutex.new
|
61
|
-
end
|
62
|
-
|
63
|
-
def increment(amount=1)
|
64
|
-
@mutex.synchronize do
|
65
|
-
@value += amount
|
66
|
-
@file.write_value(@key, @value)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def set(value)
|
71
|
-
@mutex.synchronize do
|
72
|
-
@value = value
|
73
|
-
@file.write_value(@key, @value)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def get
|
78
|
-
@mutex.synchronize do
|
79
|
-
return @value
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.multiprocess
|
84
|
-
true
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Should we enable multi-process mode?
|
89
|
-
# This needs to be chosen before the first metric is constructed,
|
90
|
-
# and as that may be in some arbitrary library the user/admin has
|
91
|
-
# no control over we use an enviroment variable.
|
92
|
-
if ENV.has_key?('prometheus_multiproc_dir')
|
93
|
-
Multiprocdir = ENV['prometheus_multiproc_dir']
|
94
|
-
ValueClass = MmapedValue
|
95
|
-
else
|
96
|
-
# Only support mmap values for now.
|
97
|
-
Multiprocdir = '/var/run/gitlab/unicorn'
|
98
|
-
ValueClass = MmapedValue
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# A dict of doubles, backed by an mmapped file.
|
104
|
-
#
|
105
|
-
# The file starts with a 4 byte int, indicating how much of it is used.
|
106
|
-
# Then 4 bytes of padding.
|
107
|
-
# There's then a number of entries, consisting of a 4 byte int which is the
|
108
|
-
# size of the next field, a utf-8 encoded string key, padding to an 8 byte
|
109
|
-
# alignment, and then a 8 byte float which is the value.
|
110
|
-
#
|
111
|
-
# TODO(julius): dealing with Mmap.new, truncate etc. errors?
|
112
|
-
class MmapedDict
|
113
|
-
@@INITIAL_MMAP_SIZE = 1024*1024
|
114
|
-
|
115
|
-
attr_reader :m, :capacity, :used, :positions
|
116
|
-
|
117
|
-
def initialize(filename)
|
118
|
-
@mutex = Mutex.new
|
119
|
-
@f = File.open(filename, 'a+b')
|
120
|
-
if @f.size == 0
|
121
|
-
@f.truncate(@@INITIAL_MMAP_SIZE)
|
122
|
-
end
|
123
|
-
@capacity = @f.size
|
124
|
-
@m = Mmap.new(filename, 'rw', Mmap::MAP_SHARED)
|
125
|
-
# @m.mlock # TODO: Why does this raise an error?
|
126
|
-
|
127
|
-
@positions = {}
|
128
|
-
@used = @m[0..3].unpack('l')[0]
|
129
|
-
if @used == 0
|
130
|
-
@used = 8
|
131
|
-
@m[0..3] = [@used].pack('l')
|
132
|
-
else
|
133
|
-
read_all_values.each do |key, _, pos|
|
134
|
-
@positions[key] = pos
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
# Yield (key, value, pos). No locking is performed.
|
140
|
-
def all_values
|
141
|
-
read_all_values.map { |k, v, p| [k, v] }
|
142
|
-
end
|
143
|
-
|
144
|
-
def read_value(key)
|
145
|
-
@mutex.synchronize do
|
146
|
-
if !@positions.has_key?(key)
|
147
|
-
init_value(key)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
pos = @positions[key]
|
151
|
-
# We assume that reading from an 8 byte aligned value is atomic.
|
152
|
-
@m[pos..pos+7].unpack('d')[0]
|
153
|
-
end
|
154
|
-
|
155
|
-
def write_value(key, value)
|
156
|
-
@mutex.synchronize do
|
157
|
-
if !@positions.has_key?(key)
|
158
|
-
init_value(key)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
pos = @positions[key]
|
162
|
-
# We assume that writing to an 8 byte aligned value is atomic.
|
163
|
-
@m[pos..pos+7] = [value].pack('d')
|
164
|
-
end
|
165
|
-
|
166
|
-
def close()
|
167
|
-
@m.munmap
|
168
|
-
@f.close
|
169
|
-
end
|
170
|
-
|
171
|
-
private
|
172
|
-
|
173
|
-
# Initialize a value. Lock must be held by caller.
|
174
|
-
def init_value(key)
|
175
|
-
# Pad to be 8-byte aligned.
|
176
|
-
padded = key + (' ' * (8 - (key.length + 4) % 8))
|
177
|
-
value = [key.length, padded, 0.0].pack("lA#{padded.length}d")
|
178
|
-
while @used + value.length > @capacity
|
179
|
-
@capacity *= 2
|
180
|
-
@f.truncate(@capacity)
|
181
|
-
@m = Mmap.new(@f.path, 'rw', Mmap::MAP_SHARED)
|
182
|
-
end
|
183
|
-
@m[@used..@used + value.length] = value
|
184
|
-
|
185
|
-
# Update how much space we've used.
|
186
|
-
@used += value.length
|
187
|
-
@m[0..3] = [@used].pack('l')
|
188
|
-
@positions[key] = @used - 8
|
189
|
-
end
|
190
|
-
|
191
|
-
# Yield (key, value, pos). No locking is performed.
|
192
|
-
def read_all_values
|
193
|
-
pos = 8
|
194
|
-
values = []
|
195
|
-
while pos < @used
|
196
|
-
encoded_len = @m[pos..-1].unpack('l')[0]
|
197
|
-
pos += 4
|
198
|
-
encoded = @m[pos..-1].unpack("A#{encoded_len}")[0]
|
199
|
-
padded_len = encoded_len + (8 - (encoded_len + 4) % 8)
|
200
|
-
pos += padded_len
|
201
|
-
value = @m[pos..-1].unpack('d')[0]
|
202
|
-
values << [encoded, value, pos]
|
203
|
-
pos += 8
|
204
|
-
end
|
205
|
-
values
|
206
|
-
end
|
207
|
-
end
|