prometheus-client-mmap 0.7.0.beta8 → 0.7.0.beta9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|