prometheus_exporter 0.1.9 → 0.1.10
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/bin/prometheus_exporter +32 -9
- data/lib/prometheus_exporter/server.rb +4 -0
- data/lib/prometheus_exporter/server/collector.rb +12 -128
- data/lib/prometheus_exporter/server/process_collector.rb +70 -0
- data/lib/prometheus_exporter/server/sidekiq_collector.rb +42 -0
- data/lib/prometheus_exporter/server/type_collector.rb +15 -0
- data/lib/prometheus_exporter/server/web_collector.rb +68 -0
- data/lib/prometheus_exporter/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0670768fb402bef5d0e563cf3fb0a8299637f03f975f73ed7bf58ee8571bb682'
|
4
|
+
data.tar.gz: 1db696d86b7de07df16fd2abd3b35cc44d0d19e041d98cd6dc6738f245eccea4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 062d0dbedc25eb1f4c267f434b4f4f1a76b9a70c3c1b0eb8d8c4d44afe63003737152afff75af41099a7e48b81c8c6206e9ace60cd26f92a8a3664030dfd13ee
|
7
|
+
data.tar.gz: 1a2afb4920a3cd2d15214b1b212d7828e62b022a804fba37fb1a8797a6ca93c6c90da8333524474e2dc32271e1a8a1abbb4e554be40d5df52c1ec4d65e723d7b
|
data/bin/prometheus_exporter
CHANGED
@@ -8,8 +8,10 @@ require_relative "./../lib/prometheus_exporter/server"
|
|
8
8
|
def run
|
9
9
|
port = PrometheusExporter::DEFAULT_PORT
|
10
10
|
prefix = "ruby_"
|
11
|
-
|
11
|
+
collector_filename = nil
|
12
12
|
verbose = false
|
13
|
+
type_collectors = []
|
14
|
+
collector_class = PrometheusExporter::Server::Collector
|
13
15
|
|
14
16
|
OptionParser.new do |opt|
|
15
17
|
opt.on('-p',
|
@@ -21,8 +23,11 @@ def run
|
|
21
23
|
opt.on('--prefix METRIC_PREFIX', String, "Prefix to apply to all metrics (default: #{prefix})") do |o|
|
22
24
|
prefix = o.to_s
|
23
25
|
end
|
24
|
-
opt.on('-c', '--collector
|
25
|
-
|
26
|
+
opt.on('-c', '--collector FILE', String, "(optional) Custom collector to run") do |o|
|
27
|
+
collector_filename = o.to_s
|
28
|
+
end
|
29
|
+
opt.on('-a', '--type-collector FILE', String, "(optional) Custom type collectors to run in main collector") do |o|
|
30
|
+
type_collectors << o
|
26
31
|
end
|
27
32
|
opt.on('-v', '--verbose') do |o|
|
28
33
|
verbose = true
|
@@ -32,24 +37,42 @@ def run
|
|
32
37
|
|
33
38
|
PrometheusExporter::Metric::Base.default_prefix = prefix
|
34
39
|
|
35
|
-
if
|
36
|
-
eval File.read(
|
40
|
+
if collector_filename
|
41
|
+
eval File.read(collector_filename)
|
42
|
+
|
43
|
+
found = false
|
37
44
|
|
38
45
|
ObjectSpace.each_object(Class) do |klass|
|
39
46
|
if klass < PrometheusExporter::Server::CollectorBase
|
40
|
-
|
47
|
+
collector_class = klass
|
48
|
+
found = true
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
44
|
-
if !
|
45
|
-
STDERR.puts "Can not find a class inheriting off PrometheusExporter::Server::
|
52
|
+
if !found
|
53
|
+
STDERR.puts "Can not find a class inheriting off PrometheusExporter::Server::CollectorBase"
|
46
54
|
usage
|
47
55
|
exit 1
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
59
|
+
collector = collector_class.new
|
60
|
+
|
61
|
+
if type_collectors.length > 0
|
62
|
+
type_collectors.each do |t|
|
63
|
+
eval File.read(t)
|
64
|
+
end
|
65
|
+
|
66
|
+
ObjectSpace.each_object(Class) do |klass|
|
67
|
+
if klass < PrometheusExporter::Server::TypeCollector
|
68
|
+
collector.register_collector klass.new
|
69
|
+
STDERR.puts "Registered TypeCollector: #{klass}" if verbose
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
51
74
|
puts "#{Time.now} Starting prometheus exporter on port #{port}"
|
52
|
-
server = PrometheusExporter::Server::WebServer.new port: port, collector: collector
|
75
|
+
server = PrometheusExporter::Server::WebServer.new port: port, collector: collector, verbose: verbose
|
53
76
|
server.start
|
54
77
|
sleep
|
55
78
|
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require_relative "server/type_collector"
|
2
|
+
require_relative "server/web_collector"
|
3
|
+
require_relative "server/process_collector"
|
4
|
+
require_relative "server/sidekiq_collector"
|
1
5
|
require_relative "server/collector_base"
|
2
6
|
require_relative "server/collector"
|
3
7
|
require_relative "server/web_server"
|
@@ -23,19 +23,22 @@ module PrometheusExporter::Server
|
|
23
23
|
def initialize
|
24
24
|
@process_metrics = []
|
25
25
|
@metrics = {}
|
26
|
-
@buffer = []
|
27
26
|
@mutex = Mutex.new
|
27
|
+
@collectors = {}
|
28
|
+
register_collector(WebCollector.new)
|
29
|
+
register_collector(ProcessCollector.new)
|
30
|
+
register_collector(SidekiqCollector.new)
|
31
|
+
end
|
32
|
+
|
33
|
+
def register_collector(collector)
|
34
|
+
@collectors[collector.type] = collector
|
28
35
|
end
|
29
36
|
|
30
37
|
def process(str)
|
31
38
|
obj = JSON.parse(str)
|
32
39
|
@mutex.synchronize do
|
33
|
-
if obj["type"]
|
34
|
-
|
35
|
-
elsif obj["type"] == "process"
|
36
|
-
observe_process(obj)
|
37
|
-
elsif obj["type"] == "sidekiq"
|
38
|
-
observe_sidekiq(obj)
|
40
|
+
if collector = @collectors[obj["type"]]
|
41
|
+
collector.observe(obj)
|
39
42
|
else
|
40
43
|
metric = @metrics[obj["name"]]
|
41
44
|
if !metric
|
@@ -48,132 +51,13 @@ module PrometheusExporter::Server
|
|
48
51
|
|
49
52
|
def prometheus_metrics_text
|
50
53
|
@mutex.synchronize do
|
51
|
-
|
52
|
-
|
53
|
-
metrics = {}
|
54
|
-
|
55
|
-
if @process_metrics.length > 0
|
56
|
-
val << "\n"
|
57
|
-
|
58
|
-
@process_metrics.map do |m|
|
59
|
-
metric_key = { pid: m["pid"], type: m["process_type"] }
|
60
|
-
|
61
|
-
PROCESS_GAUGES.map do |k, help|
|
62
|
-
k = k.to_s
|
63
|
-
if v = m[k]
|
64
|
-
g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new(k, help)
|
65
|
-
g.observe(v, metric_key)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
PROCESS_COUNTERS.map do |k, help|
|
70
|
-
k = k.to_s
|
71
|
-
if v = m[k]
|
72
|
-
c = metrics[k] ||= PrometheusExporter::Metric::Counter.new(k, help)
|
73
|
-
c.observe(v, metric_key)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
val << metrics.values.map(&:to_prometheus_text).join("\n")
|
80
|
-
end
|
81
|
-
|
82
|
-
val
|
54
|
+
(@metrics.values + @collectors.values.map(&:metrics).flatten)
|
55
|
+
.map(&:to_prometheus_text).join("\n")
|
83
56
|
end
|
84
57
|
end
|
85
58
|
|
86
59
|
protected
|
87
60
|
|
88
|
-
def register_metric(metric)
|
89
|
-
@mutex.synchronize do
|
90
|
-
@metrics << metric
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def ensure_web_metrics
|
95
|
-
unless @http_requests
|
96
|
-
@metrics["http_requests"] = @http_requests = PrometheusExporter::Metric::Counter.new(
|
97
|
-
"http_requests",
|
98
|
-
"Total HTTP requests from web app"
|
99
|
-
)
|
100
|
-
|
101
|
-
@metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
102
|
-
"http_duration_seconds",
|
103
|
-
"Time spent in HTTP reqs in seconds"
|
104
|
-
)
|
105
|
-
|
106
|
-
@metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
107
|
-
"http_redis_duration_seconds",
|
108
|
-
"Time spent in HTTP reqs in redis seconds"
|
109
|
-
)
|
110
|
-
|
111
|
-
@metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
112
|
-
"http_sql_duration_seconds",
|
113
|
-
"Time spent in HTTP reqs in SQL in seconds"
|
114
|
-
)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def observe_web(obj)
|
119
|
-
ensure_web_metrics
|
120
|
-
|
121
|
-
labels = {
|
122
|
-
controller: obj["controller"] || "other",
|
123
|
-
action: obj["action"] || "other"
|
124
|
-
}
|
125
|
-
|
126
|
-
@http_requests.observe(1, labels.merge(status: obj["status"]))
|
127
|
-
|
128
|
-
if timings = obj["timings"]
|
129
|
-
@http_duration_seconds.observe(timings["total_duration"], labels)
|
130
|
-
if redis = timings["redis"]
|
131
|
-
@http_redis_duration_seconds.observe(redis["duration"], labels)
|
132
|
-
end
|
133
|
-
if sql = timings["sql"]
|
134
|
-
@http_sql_duration_seconds.observe(sql["duration"], labels)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def observe_process(obj)
|
140
|
-
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
141
|
-
|
142
|
-
obj["created_at"] = now
|
143
|
-
|
144
|
-
@process_metrics.delete_if do |current|
|
145
|
-
obj["pid"] == current["pid"] || (current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
|
146
|
-
end
|
147
|
-
@process_metrics << obj
|
148
|
-
end
|
149
|
-
|
150
|
-
def observe_sidekiq(obj)
|
151
|
-
ensure_sidekiq_metrics
|
152
|
-
@sidekiq_job_duration_seconds.observe(obj["duration"], job_name: obj["name"])
|
153
|
-
@sidekiq_job_count.observe(1, job_name: obj["name"])
|
154
|
-
@sidekiq_failed_job_count.observe(1, job_name: obj["name"]) if !obj["success"]
|
155
|
-
end
|
156
|
-
|
157
|
-
def ensure_sidekiq_metrics
|
158
|
-
if !@sidekiq_job_count
|
159
|
-
|
160
|
-
@metrics["sidekiq_job_duration_seconds"] =
|
161
|
-
@sidekiq_job_duration_seconds =
|
162
|
-
PrometheusExporter::Metric::Counter.new(
|
163
|
-
"sidekiq_job_duration_seconds", "Total time spent in sidekiq jobs")
|
164
|
-
|
165
|
-
@metrics["sidekiq_job_count"] =
|
166
|
-
@sidekiq_job_count =
|
167
|
-
PrometheusExporter::Metric::Counter.new(
|
168
|
-
"sidekiq_job_count", "Total number of sidekiq jobs executed")
|
169
|
-
|
170
|
-
@metrics["sidekiq_failed_job_count"] =
|
171
|
-
@sidekiq_failed_job_count =
|
172
|
-
PrometheusExporter::Metric::Counter.new(
|
173
|
-
"sidekiq_failed_job_count", "Total number failed sidekiq jobs executed")
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
61
|
def register_metric_unsafe(obj)
|
178
62
|
name = obj["name"]
|
179
63
|
help = obj["help"]
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrometheusExporter::Server
|
4
|
+
|
5
|
+
class ProcessCollector < TypeCollector
|
6
|
+
MAX_PROCESS_METRIC_AGE = 60
|
7
|
+
PROCESS_GAUGES = {
|
8
|
+
heap_free_slots: "Free ruby heap slots",
|
9
|
+
heap_live_slots: "Used ruby heap slots",
|
10
|
+
v8_heap_size: "Total JavaScript V8 heap size (bytes)",
|
11
|
+
v8_used_heap_size: "Total used JavaScript V8 heap size (bytes)",
|
12
|
+
v8_physical_size: "Physical size consumed by V8 heaps",
|
13
|
+
v8_heap_count: "Number of V8 contexts running",
|
14
|
+
rss: "Total RSS used by process",
|
15
|
+
}
|
16
|
+
|
17
|
+
PROCESS_COUNTERS = {
|
18
|
+
major_gc_count: "Major GC operations by process",
|
19
|
+
minor_gc_count: "Minor GC operations by process",
|
20
|
+
total_allocated_objects: "Total number of allocateds objects by process",
|
21
|
+
}
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@process_metrics = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def type
|
28
|
+
"process"
|
29
|
+
end
|
30
|
+
|
31
|
+
def metrics
|
32
|
+
return [] if @process_metrics.length == 0
|
33
|
+
|
34
|
+
metrics = {}
|
35
|
+
|
36
|
+
@process_metrics.map do |m|
|
37
|
+
metric_key = { pid: m["pid"], type: m["process_type"] }
|
38
|
+
|
39
|
+
PROCESS_GAUGES.map do |k, help|
|
40
|
+
k = k.to_s
|
41
|
+
if v = m[k]
|
42
|
+
g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new(k, help)
|
43
|
+
g.observe(v, metric_key)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
PROCESS_COUNTERS.map do |k, help|
|
48
|
+
k = k.to_s
|
49
|
+
if v = m[k]
|
50
|
+
c = metrics[k] ||= PrometheusExporter::Metric::Counter.new(k, help)
|
51
|
+
c.observe(v, metric_key)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
metrics.values
|
57
|
+
end
|
58
|
+
|
59
|
+
def observe(obj)
|
60
|
+
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
61
|
+
|
62
|
+
obj["created_at"] = now
|
63
|
+
|
64
|
+
@process_metrics.delete_if do |current|
|
65
|
+
obj["pid"] == current["pid"] || (current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
|
66
|
+
end
|
67
|
+
@process_metrics << obj
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module PrometheusExporter::Server
|
2
|
+
class SidekiqCollector < TypeCollector
|
3
|
+
|
4
|
+
def type
|
5
|
+
"sidekiq"
|
6
|
+
end
|
7
|
+
|
8
|
+
def observe(obj)
|
9
|
+
ensure_sidekiq_metrics
|
10
|
+
@sidekiq_job_duration_seconds.observe(obj["duration"], job_name: obj["name"])
|
11
|
+
@sidekiq_job_count.observe(1, job_name: obj["name"])
|
12
|
+
@sidekiq_failed_job_count.observe(1, job_name: obj["name"]) if !obj["success"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def metrics
|
16
|
+
if @sidekiq_job_count
|
17
|
+
[@sidekiq_job_duration_seconds, @sidekiq_job_count, @sidekiq_failed_job_count]
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def ensure_sidekiq_metrics
|
26
|
+
if !@sidekiq_job_count
|
27
|
+
|
28
|
+
@sidekiq_job_duration_seconds =
|
29
|
+
PrometheusExporter::Metric::Counter.new(
|
30
|
+
"sidekiq_job_duration_seconds", "Total time spent in sidekiq jobs")
|
31
|
+
|
32
|
+
@sidekiq_job_count =
|
33
|
+
PrometheusExporter::Metric::Counter.new(
|
34
|
+
"sidekiq_job_count", "Total number of sidekiq jobs executed")
|
35
|
+
|
36
|
+
@sidekiq_failed_job_count =
|
37
|
+
PrometheusExporter::Metric::Counter.new(
|
38
|
+
"sidekiq_failed_job_count", "Total number failed sidekiq jobs executed")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrometheusExporter::Server
|
4
|
+
class WebCollector < TypeCollector
|
5
|
+
def initialize
|
6
|
+
@metrics = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def type
|
10
|
+
"web"
|
11
|
+
end
|
12
|
+
|
13
|
+
def collect(obj)
|
14
|
+
ensure_metrics
|
15
|
+
observe(obj)
|
16
|
+
end
|
17
|
+
|
18
|
+
def metrics
|
19
|
+
@metrics.values
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def ensure_metrics
|
25
|
+
unless @http_requests
|
26
|
+
@metrics["http_requests"] = @http_requests = PrometheusExporter::Metric::Counter.new(
|
27
|
+
"http_requests",
|
28
|
+
"Total HTTP requests from web app"
|
29
|
+
)
|
30
|
+
|
31
|
+
@metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
32
|
+
"http_duration_seconds",
|
33
|
+
"Time spent in HTTP reqs in seconds"
|
34
|
+
)
|
35
|
+
|
36
|
+
@metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
37
|
+
"http_redis_duration_seconds",
|
38
|
+
"Time spent in HTTP reqs in redis seconds"
|
39
|
+
)
|
40
|
+
|
41
|
+
@metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
42
|
+
"http_sql_duration_seconds",
|
43
|
+
"Time spent in HTTP reqs in SQL in seconds"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def observe(obj)
|
49
|
+
|
50
|
+
labels = {
|
51
|
+
controller: obj["controller"] || "other",
|
52
|
+
action: obj["action"] || "other"
|
53
|
+
}
|
54
|
+
|
55
|
+
@http_requests.observe(1, labels.merge(status: obj["status"]))
|
56
|
+
|
57
|
+
if timings = obj["timings"]
|
58
|
+
@http_duration_seconds.observe(timings["total_duration"], labels)
|
59
|
+
if redis = timings["redis"]
|
60
|
+
@http_redis_duration_seconds.observe(redis["duration"], labels)
|
61
|
+
end
|
62
|
+
if sql = timings["sql"]
|
63
|
+
@http_sql_duration_seconds.observe(sql["duration"], labels)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus_exporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
@@ -130,6 +130,10 @@ files:
|
|
130
130
|
- lib/prometheus_exporter/server.rb
|
131
131
|
- lib/prometheus_exporter/server/collector.rb
|
132
132
|
- lib/prometheus_exporter/server/collector_base.rb
|
133
|
+
- lib/prometheus_exporter/server/process_collector.rb
|
134
|
+
- lib/prometheus_exporter/server/sidekiq_collector.rb
|
135
|
+
- lib/prometheus_exporter/server/type_collector.rb
|
136
|
+
- lib/prometheus_exporter/server/web_collector.rb
|
133
137
|
- lib/prometheus_exporter/server/web_server.rb
|
134
138
|
- lib/prometheus_exporter/version.rb
|
135
139
|
- prometheus_exporter.gemspec
|