1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
module Longboat
class CollectorTransactionError < Exception; end
class Collector
def initialize(config)
@metrics = {}
@config = config
@transaction = nil
end
def report!(name, value, help: nil, type: nil, labels: {}, timestamp: Time.now)
raise CollectorTransactionError if @transaction.nil?
name = prefix(name)
@transaction[name] ||= {help: help, type: type}
@transaction[name][labels] = {value: value, timestamp: timestamp}
end
def redact!(name, labels: nil)
raise CollectorTransactionError if @transaction.nil?
name = prefix(name)
if labels.nil?
@transaction.delete(name)
else
@transaction[name].delete(labels)
end
end
def begin!
@transaction = clone_metrics
end
def abort!
@transaction = nil
end
def commit!
@metrics = @transaction
@transaction = nil
end
def prometheus_metrics
res = ""
@metrics.each do |name, metric|
res << ?\n unless res.empty?
res << "#HELP #{name} #{metric[:help]}\n" unless metric[:help].nil?
res << "#TYPE #{name} #{metric[:type]}\n" unless metric[:type].nil?
case metric[:type]
when "gauge", "counter"
metric.each do |labelset, value|
next if labelset == :help
next if labelset == :type
res << stringify_gaugecounter(name, labelset, value[:value], value[:timestamp])
end
when "histogram"
metric.each do |labelset, value|
next if labelset == :help
next if labelset == :type
res << stringify_histogram(name, labelset, value[:value], value[:timestamp])
end
when "summary"
metric.each do |labelset, value|
next if labelset == :help
next if labelset == :type
res << stringify_summary(name, labelset, value[:value], value[:timestamp])
end
end
end
res
end
private
def stringify_gaugecounter(name, labelset, value, timestamp)
labels = prometheus_labels(labelset)
timestamp = (timestamp.to_f * 1000).to_i
"#{name}{#{labels}} #{value} #{timestamp}\n"
end
def stringify_histogram(name, labelset, data, timestamp)
res = ""
data[:buckets].each do |bkt, cnt|
res << stringify_gaugecounter("#{name}_bucket", labelset.merge({le: bkt.to_s}), cnt, timestamp)
end
res << stringify_gaugecounter("#{name}_bucket", labelset.merge({le: "+Inf"}), data[:count], timestamp)
res << stringify_gaugecounter("#{name}_count", labelset, data[:count], timestamp)
res << stringify_gaugecounter("#{name}_sum", labelset, data[:sum], timestamp)
res
end
def stringify_summary(name, labelset, data, timestamp)
res = ""
data[:quantiles].each do |qtl, val|
res << stringify_gaugecounter(name, labelset.merge({quantile: qtl.to_s}), val, timestamp)
end
res << stringify_gaugecounter("#{name}_count", labelset, data[:count], timestamp)
res << stringify_gaugecounter("#{name}_sum", labelset, data[:sum], timestamp)
res
end
def prometheus_labels(labelset)
labellist = []
labelset.each do |k, v|
labellist << "#{k}=\"#{v}\""
end
labellist.join(",")
end
def prefix(name)
"#{@config[:metric_prefix]}#{name}"
end
def clone_metrics
clone = {}
@metrics.each do |metric, attributes|
clone[metric] = {}
clone[metric][:help] = attributes[:help]
clone[metric][:type] = attributes[:type]
attributes.each do |labels, value|
next if [:help, :type].include?(labels)
clone[metric][labels.clone] = value.clone
end
end
clone
end
end
end
|