prometheus_exporter 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -0
- data/lib/prometheus_exporter/client.rb +161 -145
- data/lib/prometheus_exporter/instrumentation/process.rb +13 -1
- data/lib/prometheus_exporter/instrumentation.rb +0 -1
- data/lib/prometheus_exporter/server/collector.rb +4 -17
- data/lib/prometheus_exporter/version.rb +1 -1
- metadata +2 -3
- data/lib/prometheus_exporter/instrumentation/global.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b60596255e84ca5aedae095d4ae7dda21f1c324e49a495e73e5f8e8a6adb3472
|
4
|
+
data.tar.gz: 990f03708492b5a2b5d13cf6a9bf67e091bdf62c819b1a559a0a240e6d9660bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d914be65e9857dcf5173e03a8fd230d0a2004aea8cf454fd1671229c856730ee72a9793624eddcf01ee561768d228e9e2e06eddd1a72629726d1dc994f34614b
|
7
|
+
data.tar.gz: 6a05a048eaa75641b749761ed823be1aa694e83d3c2f0d35e860e9237dd0f82569dc31c4c3d0e24293b10d1dcd5d36f16d893348c620f21efae9c16596b29b3c
|
data/README.md
CHANGED
@@ -56,10 +56,20 @@ Simplest way of consuming Prometheus exporter is in a single process mode.
|
|
56
56
|
```ruby
|
57
57
|
require 'prometheus_exporter/server'
|
58
58
|
|
59
|
+
# client allows instrumentation to send info to server
|
60
|
+
require 'prometheus_exporter/client'
|
61
|
+
require 'prometheus_exporter/instrumentation'
|
62
|
+
|
59
63
|
# port is the port that will provide the /metrics route
|
60
64
|
server = PrometheusExporter::Server::WebServer.new port: 12345
|
61
65
|
server.start
|
62
66
|
|
67
|
+
# wire up a default local client
|
68
|
+
PrometheusExporter::Client.default = PrometheusExporter::LocalClient.new(collector: server.collector)
|
69
|
+
|
70
|
+
# this ensures basic process instrumentation metrics are added such as RSS and Ruby metrics
|
71
|
+
PrometheusExporter::Instrumentation::Process.start
|
72
|
+
|
63
73
|
gauge = PrometheusExporter::Metric::Gauge.new("rss", "used RSS for process")
|
64
74
|
counter = PrometheusExporter::Metric::Counter.new("web_requests", "number of web requests")
|
65
75
|
summary = PrometheusExporter::Metric::Summary.new("page_load_time", "time it took to load page")
|
@@ -3,197 +3,213 @@
|
|
3
3
|
require 'socket'
|
4
4
|
require 'thread'
|
5
5
|
|
6
|
-
|
6
|
+
module PrometheusExporter
|
7
|
+
|
8
|
+
class Client
|
9
|
+
class RemoteMetric
|
10
|
+
def initialize(name:, help:, type:, client:)
|
11
|
+
@name = name
|
12
|
+
@help = help
|
13
|
+
@client = client
|
14
|
+
@type = type
|
15
|
+
end
|
7
16
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
def standard_values(value, keys, prometheus_exporter_action = nil)
|
18
|
+
values = {
|
19
|
+
type: @type,
|
20
|
+
help: @help,
|
21
|
+
name: @name,
|
22
|
+
keys: keys,
|
23
|
+
value: value
|
24
|
+
}
|
25
|
+
values[:prometheus_exporter_action] = prometheus_exporter_action if prometheus_exporter_action
|
26
|
+
values
|
27
|
+
end
|
15
28
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
def observe(value = 1, keys = nil)
|
30
|
+
@client.send_json(standard_values(value, keys))
|
31
|
+
end
|
32
|
+
|
33
|
+
def increment(keys = nil, value = 1)
|
34
|
+
@client.send_json(standard_values(value, keys, :increment))
|
35
|
+
end
|
36
|
+
|
37
|
+
def decrement(keys = nil, value = 1)
|
38
|
+
@client.send_json(standard_values(value, keys, :decrement))
|
39
|
+
end
|
27
40
|
|
28
|
-
def observe(value = 1, keys = nil)
|
29
|
-
@client.send_json(standard_values(value, keys))
|
30
41
|
end
|
31
42
|
|
32
|
-
def
|
33
|
-
@
|
43
|
+
def self.default
|
44
|
+
@default ||= new
|
34
45
|
end
|
35
46
|
|
36
|
-
def
|
37
|
-
@
|
47
|
+
def self.default=(client)
|
48
|
+
@default = client
|
38
49
|
end
|
39
50
|
|
40
|
-
|
51
|
+
MAX_SOCKET_AGE = 25
|
52
|
+
MAX_QUEUE_SIZE = 10_000
|
41
53
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
54
|
+
def initialize(host: 'localhost', port: PrometheusExporter::DEFAULT_PORT, max_queue_size: nil, thread_sleep: 0.5, json_serializer: nil, custom_labels: nil)
|
55
|
+
@metrics = []
|
45
56
|
|
46
|
-
|
47
|
-
|
48
|
-
|
57
|
+
@queue = Queue.new
|
58
|
+
@socket = nil
|
59
|
+
@socket_started = nil
|
49
60
|
|
50
|
-
|
51
|
-
|
61
|
+
max_queue_size ||= MAX_QUEUE_SIZE
|
62
|
+
max_queue_size = max_queue_size.to_i
|
52
63
|
|
53
|
-
|
54
|
-
|
64
|
+
if max_queue_size.to_i <= 0
|
65
|
+
raise ArgumentError, "max_queue_size must be larger than 0"
|
66
|
+
end
|
55
67
|
|
56
|
-
|
57
|
-
|
58
|
-
|
68
|
+
@max_queue_size = max_queue_size
|
69
|
+
@host = host
|
70
|
+
@port = port
|
71
|
+
@worker_thread = nil
|
72
|
+
@mutex = Mutex.new
|
73
|
+
@thread_sleep = thread_sleep
|
59
74
|
|
60
|
-
|
61
|
-
max_queue_size = max_queue_size.to_i
|
75
|
+
@json_serializer = json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
|
62
76
|
|
63
|
-
|
64
|
-
raise ArgumentError, "max_queue_size must be larger than 0"
|
77
|
+
@custom_labels = custom_labels
|
65
78
|
end
|
66
79
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@worker_thread = nil
|
71
|
-
@mutex = Mutex.new
|
72
|
-
@thread_sleep = thread_sleep
|
73
|
-
|
74
|
-
@json_serializer = json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
|
75
|
-
|
76
|
-
@custom_labels = custom_labels
|
77
|
-
end
|
78
|
-
|
79
|
-
def custom_labels=(custom_labels)
|
80
|
-
@custom_labels = custom_labels
|
81
|
-
end
|
82
|
-
|
83
|
-
def register(type, name, help)
|
84
|
-
metric = RemoteMetric.new(type: type, name: name, help: help, client: self)
|
85
|
-
@metrics << metric
|
86
|
-
metric
|
87
|
-
end
|
80
|
+
def custom_labels=(custom_labels)
|
81
|
+
@custom_labels = custom_labels
|
82
|
+
end
|
88
83
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
def register(type, name, help)
|
85
|
+
metric = RemoteMetric.new(type: type, name: name, help: help, client: self)
|
86
|
+
@metrics << metric
|
87
|
+
metric
|
88
|
+
end
|
93
89
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
STDERR.puts "Prometheus Exporter client is dropping message cause queue is full"
|
98
|
-
@queue.pop
|
90
|
+
def send_json(obj)
|
91
|
+
payload = @custom_labels.nil? ? obj : obj.merge(custom_labels: @custom_labels)
|
92
|
+
send(@json_serializer.dump(payload))
|
99
93
|
end
|
100
94
|
|
101
|
-
|
102
|
-
|
95
|
+
def send(str)
|
96
|
+
@queue << str
|
97
|
+
if @queue.length > @max_queue_size
|
98
|
+
STDERR.puts "Prometheus Exporter client is dropping message cause queue is full"
|
99
|
+
@queue.pop
|
100
|
+
end
|
103
101
|
|
104
|
-
|
105
|
-
|
106
|
-
ensure_socket!
|
102
|
+
ensure_worker_thread!
|
103
|
+
end
|
107
104
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
105
|
+
def process_queue
|
106
|
+
while @queue.length > 0
|
107
|
+
ensure_socket!
|
108
|
+
|
109
|
+
begin
|
110
|
+
message = @queue.pop
|
111
|
+
@socket.write(message.bytesize.to_s(16).upcase)
|
112
|
+
@socket.write("\r\n")
|
113
|
+
@socket.write(message)
|
114
|
+
@socket.write("\r\n")
|
115
|
+
rescue => e
|
116
|
+
STDERR.puts "Prometheus Exporter is dropping a message: #{e}"
|
117
|
+
@socket = nil
|
118
|
+
raise
|
119
|
+
end
|
118
120
|
end
|
119
121
|
end
|
120
|
-
end
|
121
122
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
123
|
+
def stop
|
124
|
+
@mutex.synchronize do
|
125
|
+
@worker_thread&.kill
|
126
|
+
while @worker_thread.alive?
|
127
|
+
sleep 0.001
|
128
|
+
end
|
129
|
+
@worker_thread = nil
|
127
130
|
end
|
128
|
-
@worker_thread = nil
|
129
|
-
end
|
130
131
|
|
131
|
-
|
132
|
-
|
132
|
+
close_socket!
|
133
|
+
end
|
133
134
|
|
134
|
-
|
135
|
+
private
|
135
136
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
def worker_loop
|
138
|
+
close_socket_if_old!
|
139
|
+
process_queue
|
140
|
+
rescue => e
|
141
|
+
STDERR.puts "Prometheus Exporter, failed to send message #{e}"
|
142
|
+
end
|
142
143
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
144
|
+
def ensure_worker_thread!
|
145
|
+
unless @worker_thread&.alive?
|
146
|
+
@mutex.synchronize do
|
147
|
+
return if @worker_thread&.alive?
|
147
148
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
149
|
+
@worker_thread = Thread.new do
|
150
|
+
while true
|
151
|
+
worker_loop
|
152
|
+
sleep @thread_sleep
|
153
|
+
end
|
152
154
|
end
|
153
155
|
end
|
154
156
|
end
|
155
157
|
end
|
156
|
-
end
|
157
158
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
159
|
+
def close_socket!
|
160
|
+
begin
|
161
|
+
if @socket
|
162
|
+
@socket.write("0\r\n")
|
163
|
+
@socket.write("\r\n")
|
164
|
+
@socket.flush
|
165
|
+
@socket.close
|
166
|
+
end
|
167
|
+
rescue Errno::EPIPE
|
168
|
+
end
|
169
|
+
|
170
|
+
@socket = nil
|
171
|
+
@socket_started = nil
|
172
|
+
end
|
173
|
+
|
174
|
+
def close_socket_if_old!
|
175
|
+
if @socket && ((@socket_started + MAX_SOCKET_AGE) < Time.now.to_f)
|
176
|
+
close_socket!
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def ensure_socket!
|
181
|
+
close_socket_if_old!
|
182
|
+
if !@socket
|
183
|
+
@socket = TCPSocket.new @host, @port
|
184
|
+
@socket.write("POST /send-metrics HTTP/1.1\r\n")
|
185
|
+
@socket.write("Transfer-Encoding: chunked\r\n")
|
186
|
+
@socket.write("Host: #{@host}\r\n")
|
187
|
+
@socket.write("Connection: Close\r\n")
|
188
|
+
@socket.write("Content-Type: application/octet-stream\r\n")
|
162
189
|
@socket.write("\r\n")
|
163
|
-
@
|
164
|
-
@socket.close
|
190
|
+
@socket_started = Time.now.to_f
|
165
191
|
end
|
166
|
-
|
192
|
+
|
193
|
+
nil
|
194
|
+
rescue
|
195
|
+
@socket = nil
|
196
|
+
@socket_started = nil
|
197
|
+
raise
|
167
198
|
end
|
168
199
|
|
169
|
-
@socket = nil
|
170
|
-
@socket_started = nil
|
171
200
|
end
|
172
201
|
|
173
|
-
|
174
|
-
|
175
|
-
|
202
|
+
class LocalClient < Client
|
203
|
+
attr_reader :collector
|
204
|
+
|
205
|
+
def initialize(collector:, json_serializer: nil, custom_labels: nil)
|
206
|
+
@collector = collector
|
207
|
+
super(json_serializer: json_serializer, custom_labels: custom_labels)
|
176
208
|
end
|
177
|
-
end
|
178
209
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
@socket = TCPSocket.new @host, @port
|
183
|
-
@socket.write("POST /send-metrics HTTP/1.1\r\n")
|
184
|
-
@socket.write("Transfer-Encoding: chunked\r\n")
|
185
|
-
@socket.write("Host: #{@host}\r\n")
|
186
|
-
@socket.write("Connection: Close\r\n")
|
187
|
-
@socket.write("Content-Type: application/octet-stream\r\n")
|
188
|
-
@socket.write("\r\n")
|
189
|
-
@socket_started = Time.now.to_f
|
190
|
-
end
|
191
|
-
|
192
|
-
nil
|
193
|
-
rescue
|
194
|
-
@socket = nil
|
195
|
-
@socket_started = nil
|
196
|
-
raise
|
210
|
+
def send(json)
|
211
|
+
@collector.process(json)
|
212
|
+
end
|
197
213
|
end
|
198
214
|
|
199
215
|
end
|
@@ -1,10 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# collects stats from currently running process
|
2
4
|
module PrometheusExporter::Instrumentation
|
3
5
|
class Process
|
4
6
|
def self.start(client: nil, type: "ruby", frequency: 30)
|
5
7
|
process_collector = new(type)
|
6
8
|
client ||= PrometheusExporter::Client.default
|
7
|
-
|
9
|
+
|
10
|
+
stop if @thread
|
11
|
+
|
12
|
+
@thread = Thread.new do
|
8
13
|
while true
|
9
14
|
begin
|
10
15
|
metric = process_collector.collect
|
@@ -18,6 +23,13 @@ module PrometheusExporter::Instrumentation
|
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
26
|
+
def self.stop
|
27
|
+
if t = @thread
|
28
|
+
t.kill
|
29
|
+
@thread = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
21
33
|
def initialize(type)
|
22
34
|
@type = type
|
23
35
|
end
|
@@ -2,6 +2,5 @@ require_relative "instrumentation/process"
|
|
2
2
|
require_relative "instrumentation/method_profiler"
|
3
3
|
require_relative "instrumentation/sidekiq"
|
4
4
|
require_relative "instrumentation/delayed_job"
|
5
|
-
require_relative "instrumentation/global"
|
6
5
|
require_relative "instrumentation/puma"
|
7
6
|
require_relative "instrumentation/hutch"
|
@@ -3,22 +3,6 @@
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
|
5
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_ops_total: "Major GC operations by process.",
|
19
|
-
minor_gc_ops_total: "Minor GC operations by process.",
|
20
|
-
allocated_objects_total: "Total number of allocated objects by process.",
|
21
|
-
}
|
22
6
|
|
23
7
|
def initialize(json_serializer: nil)
|
24
8
|
@process_metrics = []
|
@@ -39,7 +23,10 @@ module PrometheusExporter::Server
|
|
39
23
|
end
|
40
24
|
|
41
25
|
def process(str)
|
42
|
-
|
26
|
+
process_hash(@json_serializer.parse(str))
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_hash(obj)
|
43
30
|
@mutex.synchronize do
|
44
31
|
if collector = @collectors[obj["type"]]
|
45
32
|
collector.collect(obj)
|
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.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -162,7 +162,6 @@ files:
|
|
162
162
|
- lib/prometheus_exporter/client.rb
|
163
163
|
- lib/prometheus_exporter/instrumentation.rb
|
164
164
|
- lib/prometheus_exporter/instrumentation/delayed_job.rb
|
165
|
-
- lib/prometheus_exporter/instrumentation/global.rb
|
166
165
|
- lib/prometheus_exporter/instrumentation/hutch.rb
|
167
166
|
- lib/prometheus_exporter/instrumentation/method_profiler.rb
|
168
167
|
- lib/prometheus_exporter/instrumentation/process.rb
|
File without changes
|