prometheus_exporter 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +50 -6
- data/bin/prometheus_exporter +58 -0
- data/lib/prometheus_exporter.rb +2 -0
- data/lib/prometheus_exporter/client.rb +9 -1
- data/lib/prometheus_exporter/instrumentation.rb +2 -0
- data/lib/prometheus_exporter/instrumentation/method_profiler.rb +55 -0
- data/lib/prometheus_exporter/instrumentation/process.rb +77 -0
- data/lib/prometheus_exporter/middleware.rb +47 -0
- data/lib/prometheus_exporter/server.rb +1 -0
- data/lib/prometheus_exporter/server/collector.rb +117 -7
- data/lib/prometheus_exporter/server/collector_base.rb +14 -0
- data/lib/prometheus_exporter/server/web_server.rb +21 -4
- data/lib/prometheus_exporter/version.rb +1 -1
- data/prometheus_exporter.gemspec +2 -1
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6957162d157420b2b8fb6d2a4b9e08483ce846497fe6bb21f1695b0e70460dc8
|
4
|
+
data.tar.gz: 9a74c21c97efad9ef3720df426d14a5e1d879eb34bebcafc625de8d45f3b232b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 695472a08f8f9ede18d6f0fdaa8722d2e4efb01b20bf23269d26afae580cb559da4f409bb3499da7a80dcd5d47e6156a6e8c7e54f01c770485212e21369fb61d
|
7
|
+
data.tar.gz: af55f7e9d025a925f0add89a61628d236691df0a613d82d8c4e622fc9b83b7651187d9e78121113de872632459e95015772ed9af43a12b85ce4a1f820be9b7fc
|
data/README.md
CHANGED
@@ -61,20 +61,20 @@ In some cases, for example unicorn or puma clusters you may want to aggregate me
|
|
61
61
|
|
62
62
|
Simplest way to acheive this is use the built-in collector.
|
63
63
|
|
64
|
-
First, run an exporter on your desired port:
|
64
|
+
First, run an exporter on your desired port, we use the default port of 9394:
|
65
65
|
|
66
66
|
```
|
67
|
-
# prometheus_exporter
|
67
|
+
# prometheus_exporter
|
68
68
|
```
|
69
69
|
|
70
|
-
At this point an exporter is running on port
|
70
|
+
At this point an exporter is running on port 9394
|
71
71
|
|
72
72
|
In your application:
|
73
73
|
|
74
74
|
```ruby
|
75
75
|
require 'prometheus_exporter/client'
|
76
76
|
|
77
|
-
client = PrometheusExporter::Client.
|
77
|
+
client = PrometheusExporter::Client.default
|
78
78
|
gauge = client.register(:gauge, "awesome", "amount of awesome")
|
79
79
|
|
80
80
|
gauge.observe(10)
|
@@ -85,7 +85,7 @@ gauge.observe(99, day: "friday")
|
|
85
85
|
Then you will get the metrics:
|
86
86
|
|
87
87
|
```bash
|
88
|
-
% curl localhost:
|
88
|
+
% curl localhost:9394/metrics
|
89
89
|
# HELP collector_working Is the master process collector able to collect metrics
|
90
90
|
# TYPE collector_working gauge
|
91
91
|
collector_working 1
|
@@ -97,6 +97,50 @@ awesome 10
|
|
97
97
|
|
98
98
|
```
|
99
99
|
|
100
|
+
### Easy integration into Rails
|
101
|
+
|
102
|
+
You can easily integrate into any Rack application:
|
103
|
+
|
104
|
+
In your Gemfile:
|
105
|
+
|
106
|
+
```
|
107
|
+
gem 'prometheus_exporter'
|
108
|
+
```
|
109
|
+
|
110
|
+
|
111
|
+
```
|
112
|
+
# in an initializer
|
113
|
+
|
114
|
+
unless Rails.env == "test"
|
115
|
+
require 'prometheus_exporter/middleware'
|
116
|
+
# insert in position 1
|
117
|
+
# instrument means method profiler will be injected in Redis and PG
|
118
|
+
Rails.application.middleware.unshift PrometheusExporter::Middleware
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
You may also be interested in per-process stats, this collects memory and GC stats
|
123
|
+
|
124
|
+
```
|
125
|
+
# in an initializer
|
126
|
+
unless Rails.env == "test"
|
127
|
+
require 'prometheus_exporter/instrumentation'
|
128
|
+
PrometheusExporter::Instrumentation::Process.start(type: "master")
|
129
|
+
end
|
130
|
+
|
131
|
+
after_fork do
|
132
|
+
require 'prometheus_exporter/instrumentation'
|
133
|
+
PrometheusExporter::Instrumentation::Process.start(type:"web")
|
134
|
+
end
|
135
|
+
|
136
|
+
```
|
137
|
+
|
138
|
+
Ensure you run the exporter via
|
139
|
+
|
140
|
+
```
|
141
|
+
% bundle exec prometheus_exporter
|
142
|
+
```
|
143
|
+
|
100
144
|
### Multi process mode with custom collector
|
101
145
|
|
102
146
|
You can opt for custom collector logic in a multi process environment.
|
@@ -108,7 +152,7 @@ The standard collector ships "help", "type" and "name" for every metric, in some
|
|
108
152
|
First, define a custom collector, it is critical you inherit off `PrometheusExporter::Server::Collector`, also it is critical you have custom implementations for #process and #prometheus_metrics_text
|
109
153
|
|
110
154
|
```ruby
|
111
|
-
class MyCustomCollector < PrometheusExporter::Server::
|
155
|
+
class MyCustomCollector < PrometheusExporter::Server::CollectorBase
|
112
156
|
def initialize
|
113
157
|
@gauge1 = PrometheusExporter::Metric::Gauge.new("thing1", "I am thing 1")
|
114
158
|
@gauge2 = PrometheusExporter::Metric::Gauge.new("thing2", "I am thing 2")
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
require_relative "./../lib/prometheus_exporter"
|
6
|
+
require_relative "./../lib/prometheus_exporter/server"
|
7
|
+
|
8
|
+
def run
|
9
|
+
port = PrometheusExporter::DEFAULT_PORT
|
10
|
+
prefix = "ruby_"
|
11
|
+
collector = nil
|
12
|
+
verbose = false
|
13
|
+
|
14
|
+
OptionParser.new do |opt|
|
15
|
+
opt.on('-p',
|
16
|
+
'--port INTEGER',
|
17
|
+
Integer,
|
18
|
+
"Port exporter should listen on (default: #{port})") do |o|
|
19
|
+
port = o.to_i
|
20
|
+
end
|
21
|
+
opt.on('--prefix METRIC_PREFIX', String, "Prefix to apply to all metrics (default: #{prefix})") do |o|
|
22
|
+
prefix = o.to_s
|
23
|
+
end
|
24
|
+
opt.on('-c', '--collector CUSTOM_COLLECTOR', String, "(optional) Custom collector to run") do |o|
|
25
|
+
collector = o.to_s
|
26
|
+
end
|
27
|
+
opt.on('-v', '--verbose') do |o|
|
28
|
+
verbose = true
|
29
|
+
end
|
30
|
+
|
31
|
+
end.parse!
|
32
|
+
|
33
|
+
PrometheusExporter::Metric::Base.default_prefix = prefix
|
34
|
+
|
35
|
+
if collector
|
36
|
+
eval File.read(collector)
|
37
|
+
|
38
|
+
ObjectSpace.each_object(Class) do |klass|
|
39
|
+
if klass < PrometheusExporter::Server::CollectorBase
|
40
|
+
collector = klass
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if !collector
|
45
|
+
STDERR.puts "Can not find a class inheriting off PrometheusExporter::Server::Collector"
|
46
|
+
usage
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "#{Time.now} Starting prometheus exporter on port #{port}"
|
52
|
+
server = PrometheusExporter::Server::WebServer.new port: port, collector: collector&.new, verbose: verbose
|
53
|
+
server.start
|
54
|
+
sleep
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
run
|
data/lib/prometheus_exporter.rb
CHANGED
@@ -24,10 +24,18 @@ class PrometheusExporter::Client
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def self.default
|
28
|
+
@default ||= new
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.default=(client)
|
32
|
+
@default = client
|
33
|
+
end
|
34
|
+
|
27
35
|
MAX_SOCKET_AGE = 25
|
28
36
|
MAX_QUEUE_SIZE = 10_000
|
29
37
|
|
30
|
-
def initialize(host: 'localhost', port
|
38
|
+
def initialize(host: 'localhost', port: PrometheusExporter::DEFAULT_PORT, max_queue_size: nil, thread_sleep: 0.5)
|
31
39
|
@metrics = []
|
32
40
|
|
33
41
|
@queue = Queue.new
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# see https://samsaffron.com/archive/2017/10/18/fastest-way-to-profile-a-method-in-ruby
|
2
|
+
module PrometheusExporter::Instrumentation; end
|
3
|
+
|
4
|
+
class PrometheusExporter::Instrumentation::MethodProfiler
|
5
|
+
def self.patch(klass, methods, name)
|
6
|
+
patches = methods.map do |method_name|
|
7
|
+
<<~RUBY
|
8
|
+
unless defined?(#{method_name}__mp_unpatched)
|
9
|
+
alias_method :#{method_name}__mp_unpatched, :#{method_name}
|
10
|
+
def #{method_name}(*args, &blk)
|
11
|
+
unless prof = Thread.current[:_method_profiler]
|
12
|
+
return #{method_name}__mp_unpatched(*args, &blk)
|
13
|
+
end
|
14
|
+
begin
|
15
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
16
|
+
#{method_name}__mp_unpatched(*args, &blk)
|
17
|
+
ensure
|
18
|
+
data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
|
19
|
+
data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
20
|
+
data[:calls] += 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
end.join("\n")
|
26
|
+
|
27
|
+
klass.class_eval patches
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.transfer
|
31
|
+
result = Thread.current[:_method_profiler]
|
32
|
+
Thread.current[:_method_profiler] = nil
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.start(transfer = nil)
|
37
|
+
Thread.current[:_method_profiler] = transfer || {
|
38
|
+
__start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.clear
|
43
|
+
Thread.current[:_method_profiler] = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.stop
|
47
|
+
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
48
|
+
if data = Thread.current[:_method_profiler]
|
49
|
+
Thread.current[:_method_profiler] = nil
|
50
|
+
start = data.delete(:__start)
|
51
|
+
data[:total_duration] = finish - start
|
52
|
+
end
|
53
|
+
data
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# collects stats from currently running process
|
2
|
+
module PrometheusExporter::Instrumentation
|
3
|
+
class Process
|
4
|
+
def self.start(client: nil, type: "ruby", frequency: 30)
|
5
|
+
process_collector = new(type)
|
6
|
+
client ||= PrometheusExporter::Client.default
|
7
|
+
Thread.new do
|
8
|
+
while true
|
9
|
+
begin
|
10
|
+
metric = process_collector.collect
|
11
|
+
client.send_json metric
|
12
|
+
rescue => e
|
13
|
+
STDERR.puts("Prometheus Discoruse Failed To Collect Process Stats #{e}")
|
14
|
+
ensure
|
15
|
+
sleep frequency
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(type)
|
22
|
+
@type = type
|
23
|
+
end
|
24
|
+
|
25
|
+
def collect
|
26
|
+
metric = {}
|
27
|
+
metric[:type] = "process"
|
28
|
+
metric[:process_type] = @type
|
29
|
+
collect_gc_stats(metric)
|
30
|
+
collect_v8_stats(metric)
|
31
|
+
collect_process_stats(metric)
|
32
|
+
metric
|
33
|
+
end
|
34
|
+
|
35
|
+
def pid
|
36
|
+
@pid = ::Process.pid
|
37
|
+
end
|
38
|
+
|
39
|
+
def rss
|
40
|
+
@pagesize ||= `getconf PAGESIZE`.to_i rescue 4096
|
41
|
+
File.read("/proc/#{pid}/statm").split(' ')[1].to_i * @pagesize rescue 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def collect_process_stats(metric)
|
45
|
+
metric[:pid] = pid
|
46
|
+
metric[:rss] = rss
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def collect_gc_stats(metric)
|
51
|
+
stat = GC.stat
|
52
|
+
metric[:heap_live_slots] = stat[:heap_live_slots]
|
53
|
+
metric[:heap_free_slots] = stat[:heap_free_slots]
|
54
|
+
metric[:major_gc_count] = stat[:major_gc_count]
|
55
|
+
metric[:minor_gc_count] = stat[:minor_gc_count]
|
56
|
+
metric[:total_allocated_objects] = stat[:total_allocated_objects]
|
57
|
+
end
|
58
|
+
|
59
|
+
def collect_v8_stats(metric)
|
60
|
+
return if !defined? MiniRacer
|
61
|
+
|
62
|
+
metric[:v8_heap_count] = metric[:v8_heap_size] = 0
|
63
|
+
metric[:v8_heap_size] = metric[:v8_physical_size] = 0
|
64
|
+
metric[:v8_used_heap_size] = 0
|
65
|
+
|
66
|
+
ObjectSpace.each_object(MiniRacer::Context) do |context|
|
67
|
+
stats = context.heap_stats
|
68
|
+
if stats
|
69
|
+
metric[:v8_heap_count] += 1
|
70
|
+
metric[:v8_heap_size] += stats[:total_heap_size].to_i
|
71
|
+
metric[:v8_used_heap_size] += stats[:used_heap_size].to_i
|
72
|
+
metric[:v8_physical_size] += stats[:total_physical_size].to_i
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prometheus_exporter/instrumentation/method_profiler'
|
4
|
+
require 'prometheus_exporter/client'
|
5
|
+
|
6
|
+
class PrometheusExporter::Middleware
|
7
|
+
MethodProfiler = PrometheusExporter::Instrumentation::MethodProfiler
|
8
|
+
|
9
|
+
def initialize(app, config = { instrument: true, client: nil })
|
10
|
+
@app = app
|
11
|
+
@client = config[:client] || PrometheusExporter::Client.default
|
12
|
+
|
13
|
+
if config[:instrument]
|
14
|
+
if defined? Redis::Client
|
15
|
+
MethodProfiler.patch(Redis::Client, [:call, :call_pipeline], :redis)
|
16
|
+
end
|
17
|
+
if defined? PG::Connection
|
18
|
+
MethodProfiler.patch(PG::Connection, [
|
19
|
+
:exec, :async_exec, :exec_prepared, :send_query_prepared, :query
|
20
|
+
], :sql)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(env)
|
26
|
+
MethodProfiler.start
|
27
|
+
result = @app.call(env)
|
28
|
+
info = MethodProfiler.stop
|
29
|
+
result
|
30
|
+
ensure
|
31
|
+
status = (result && result[0]) || -1
|
32
|
+
params = env["action_dispatch.request.parameters"]
|
33
|
+
action, controller = nil
|
34
|
+
if params
|
35
|
+
action = params["action"]
|
36
|
+
controller = params["controller"]
|
37
|
+
end
|
38
|
+
|
39
|
+
@client.send_json(
|
40
|
+
type: "web",
|
41
|
+
timings: info,
|
42
|
+
action: action,
|
43
|
+
controller: controller,
|
44
|
+
status: status
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
@@ -2,9 +2,26 @@
|
|
2
2
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
|
5
|
-
class Collector
|
5
|
+
class Collector < CollectorBase
|
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
|
+
}
|
6
22
|
|
7
23
|
def initialize
|
24
|
+
@process_metrics = []
|
8
25
|
@metrics = {}
|
9
26
|
@buffer = []
|
10
27
|
@mutex = Mutex.new
|
@@ -13,27 +30,120 @@ module PrometheusExporter::Server
|
|
13
30
|
def process(str)
|
14
31
|
obj = JSON.parse(str)
|
15
32
|
@mutex.synchronize do
|
16
|
-
|
17
|
-
|
18
|
-
|
33
|
+
if obj["type"] == "web"
|
34
|
+
observe_web(obj)
|
35
|
+
elsif obj["type"] == "process"
|
36
|
+
observe_process(obj)
|
37
|
+
else
|
38
|
+
metric = @metrics[obj["name"]]
|
39
|
+
if !metric
|
40
|
+
metric = register_metric_unsafe(obj)
|
41
|
+
end
|
42
|
+
metric.observe(obj["value"], obj["keys"])
|
19
43
|
end
|
20
|
-
metric.observe(obj["value"], obj["keys"])
|
21
44
|
end
|
22
45
|
end
|
23
46
|
|
24
47
|
def prometheus_metrics_text
|
25
48
|
@mutex.synchronize do
|
26
|
-
@metrics.values.map(&:to_prometheus_text).join("\n")
|
49
|
+
val = @metrics.values.map(&:to_prometheus_text).join("\n")
|
50
|
+
|
51
|
+
metrics = {}
|
52
|
+
|
53
|
+
if @process_metrics.length > 0
|
54
|
+
val << "\n"
|
55
|
+
|
56
|
+
@process_metrics.map do |m|
|
57
|
+
metric_key = { pid: m["pid"], type: m["process_type"] }
|
58
|
+
|
59
|
+
PROCESS_GAUGES.map do |k, help|
|
60
|
+
k = k.to_s
|
61
|
+
if v = m[k]
|
62
|
+
g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new(k, help)
|
63
|
+
g.observe(v, metric_key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
PROCESS_COUNTERS.map do |k, help|
|
68
|
+
k = k.to_s
|
69
|
+
if v = m[k]
|
70
|
+
c = metrics[k] ||= PrometheusExporter::Metric::Counter.new(k, help)
|
71
|
+
c.observe(v, metric_key)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
val << metrics.values.map(&:to_prometheus_text).join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
val
|
27
81
|
end
|
28
82
|
end
|
29
83
|
|
84
|
+
protected
|
85
|
+
|
30
86
|
def register_metric(metric)
|
31
87
|
@mutex.synchronize do
|
32
88
|
@metrics << metric
|
33
89
|
end
|
34
90
|
end
|
35
91
|
|
36
|
-
|
92
|
+
def ensure_web_metrics
|
93
|
+
unless @http_requests
|
94
|
+
@metrics["http_requests"] = @http_requests = PrometheusExporter::Metric::Counter.new(
|
95
|
+
"http_requests",
|
96
|
+
"Total HTTP requests from web app"
|
97
|
+
)
|
98
|
+
|
99
|
+
@metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
100
|
+
"http_duration_seconds",
|
101
|
+
"Time spent in HTTP reqs in seconds"
|
102
|
+
)
|
103
|
+
|
104
|
+
@metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
105
|
+
"http_redis_duration_seconds",
|
106
|
+
"Time spent in HTTP reqs in redis seconds"
|
107
|
+
)
|
108
|
+
|
109
|
+
@metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Summary.new(
|
110
|
+
"http_sql_duration_seconds",
|
111
|
+
"Time spent in HTTP reqs in SQL in seconds"
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def observe_web(obj)
|
117
|
+
ensure_web_metrics
|
118
|
+
|
119
|
+
labels = {
|
120
|
+
controller: obj["controller"] || "other",
|
121
|
+
action: obj["action"] || "other"
|
122
|
+
}
|
123
|
+
|
124
|
+
@http_requests.observe(1, labels.merge(status: obj["status"]))
|
125
|
+
|
126
|
+
if timings = obj["timings"]
|
127
|
+
@http_duration_seconds.observe(timings["total_duration"], labels)
|
128
|
+
if redis = timings["redis"]
|
129
|
+
@http_redis_duration_seconds.observe(redis["duration"], labels)
|
130
|
+
end
|
131
|
+
if sql = timings["sql"]
|
132
|
+
@http_sql_duration_seconds.observe(sql["duration"], labels)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def observe_process(obj)
|
138
|
+
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
139
|
+
|
140
|
+
obj["created_at"] = now
|
141
|
+
|
142
|
+
@process_metrics.delete_if do |current|
|
143
|
+
obj["pid"] == current["pid"] || (current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
|
144
|
+
end
|
145
|
+
@process_metrics << obj
|
146
|
+
end
|
37
147
|
|
38
148
|
def register_metric_unsafe(obj)
|
39
149
|
name = obj["name"]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PrometheusExporter::Server
|
2
|
+
|
3
|
+
# minimal interface to implement a customer collector
|
4
|
+
class CollectorBase
|
5
|
+
|
6
|
+
# called each time a string is delivered from the web
|
7
|
+
def process(str)
|
8
|
+
end
|
9
|
+
|
10
|
+
# a string denoting the metrics
|
11
|
+
def prometheus_metrics_text(str)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -9,7 +9,9 @@ module PrometheusExporter::Server
|
|
9
9
|
class WebServer
|
10
10
|
attr_reader :collector
|
11
11
|
|
12
|
-
def initialize(port: , collector: nil)
|
12
|
+
def initialize(port: , collector: nil, verbose: false)
|
13
|
+
|
14
|
+
@verbose = verbose
|
13
15
|
|
14
16
|
@total_metrics = PrometheusExporter::Metric::Counter.new("total_collector_metrics", "total metrics processed by exporter web")
|
15
17
|
|
@@ -21,10 +23,19 @@ module PrometheusExporter::Server
|
|
21
23
|
@total_sessions.observe(0)
|
22
24
|
@total_bad_metrics.observe(0)
|
23
25
|
|
26
|
+
access_log = []
|
27
|
+
if verbose
|
28
|
+
access_log = [
|
29
|
+
[$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT],
|
30
|
+
[$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT],
|
31
|
+
]
|
32
|
+
logger = WEBrick::Log.new($stderr)
|
33
|
+
end
|
34
|
+
|
24
35
|
@server = WEBrick::HTTPServer.new(
|
25
36
|
Port: port,
|
26
|
-
|
27
|
-
|
37
|
+
Logger: logger,
|
38
|
+
AccessLog: access_log
|
28
39
|
)
|
29
40
|
|
30
41
|
@collector = collector || Collector.new
|
@@ -52,7 +63,7 @@ module PrometheusExporter::Server
|
|
52
63
|
handle_metrics(req, res)
|
53
64
|
else
|
54
65
|
res.status = 404
|
55
|
-
res.body = "Not Found! The Prometheus
|
66
|
+
res.body = "Not Found! The Prometheus Ruby Exporter only listens on /metrics and /send-metrics"
|
56
67
|
end
|
57
68
|
end
|
58
69
|
end
|
@@ -64,6 +75,12 @@ module PrometheusExporter::Server
|
|
64
75
|
@total_metrics.observe
|
65
76
|
@collector.process(block)
|
66
77
|
rescue => e
|
78
|
+
if @verbose
|
79
|
+
STDERR.puts
|
80
|
+
STDERR.puts e.inspect
|
81
|
+
STDERR.puts e.backtrace
|
82
|
+
STDERR.puts
|
83
|
+
end
|
67
84
|
@total_bad_metrics.observe
|
68
85
|
res.body = "Bad Metrics #{e}"
|
69
86
|
res.status = e.respond_to?(:status_code) ? e.status_code : 500
|
data/prometheus_exporter.gemspec
CHANGED
@@ -18,12 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
f.match(%r{^(test|spec|features|bin)/})
|
19
19
|
end
|
20
20
|
spec.bindir = "bin"
|
21
|
-
spec.executables =
|
21
|
+
spec.executables = ["prometheus_exporter"]
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.16"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "minitest", "~> 5.0"
|
27
27
|
spec.add_development_dependency "guard", "~> 2.0"
|
28
|
+
spec.add_development_dependency "mini_racer", "~> 0.1"
|
28
29
|
spec.add_development_dependency "guard-minitest", "~> 2.0"
|
29
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mini_racer
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: guard-minitest
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,7 +97,8 @@ dependencies:
|
|
83
97
|
description: Prometheus metric collector and exporter for Ruby
|
84
98
|
email:
|
85
99
|
- sam.saffron@gmail.com
|
86
|
-
executables:
|
100
|
+
executables:
|
101
|
+
- prometheus_exporter
|
87
102
|
extensions: []
|
88
103
|
extra_rdoc_files: []
|
89
104
|
files:
|
@@ -97,16 +112,22 @@ files:
|
|
97
112
|
- README.md
|
98
113
|
- Rakefile
|
99
114
|
- bench/bench.rb
|
115
|
+
- bin/prometheus_exporter
|
100
116
|
- examples/custom_collector.rb
|
101
117
|
- lib/prometheus_exporter.rb
|
102
118
|
- lib/prometheus_exporter/client.rb
|
119
|
+
- lib/prometheus_exporter/instrumentation.rb
|
120
|
+
- lib/prometheus_exporter/instrumentation/method_profiler.rb
|
121
|
+
- lib/prometheus_exporter/instrumentation/process.rb
|
103
122
|
- lib/prometheus_exporter/metric.rb
|
104
123
|
- lib/prometheus_exporter/metric/base.rb
|
105
124
|
- lib/prometheus_exporter/metric/counter.rb
|
106
125
|
- lib/prometheus_exporter/metric/gauge.rb
|
107
126
|
- lib/prometheus_exporter/metric/summary.rb
|
127
|
+
- lib/prometheus_exporter/middleware.rb
|
108
128
|
- lib/prometheus_exporter/server.rb
|
109
129
|
- lib/prometheus_exporter/server/collector.rb
|
130
|
+
- lib/prometheus_exporter/server/collector_base.rb
|
110
131
|
- lib/prometheus_exporter/server/web_server.rb
|
111
132
|
- lib/prometheus_exporter/version.rb
|
112
133
|
- prometheus_exporter.gemspec
|