prometheus_exporter 2.0.8 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +51 -4
- data/.rubocop +1 -0
- data/.rubocop.yml +12 -1
- data/Appraisals +6 -2
- data/CHANGELOG +12 -0
- data/README.md +45 -8
- data/bin/prometheus_exporter +2 -2
- data/gemfiles/ar_70.gemfile +7 -0
- data/gemfiles/ar_71.gemfile +7 -0
- data/lib/prometheus_exporter/client.rb +34 -19
- data/lib/prometheus_exporter/instrumentation/delayed_job.rb +8 -5
- data/lib/prometheus_exporter/instrumentation/good_job.rb +30 -0
- data/lib/prometheus_exporter/instrumentation/method_profiler.rb +4 -4
- data/lib/prometheus_exporter/instrumentation/process.rb +2 -0
- data/lib/prometheus_exporter/instrumentation/sidekiq.rb +16 -14
- data/lib/prometheus_exporter/instrumentation.rb +1 -0
- data/lib/prometheus_exporter/metric/histogram.rb +1 -1
- data/lib/prometheus_exporter/middleware.rb +1 -1
- data/lib/prometheus_exporter/server/active_record_collector.rb +9 -13
- data/lib/prometheus_exporter/server/collector.rb +1 -0
- data/lib/prometheus_exporter/server/delayed_job_collector.rb +7 -1
- data/lib/prometheus_exporter/server/good_job_collector.rb +52 -0
- data/lib/prometheus_exporter/server/process_collector.rb +10 -13
- data/lib/prometheus_exporter/server/puma_collector.rb +1 -1
- data/lib/prometheus_exporter/server/resque_collector.rb +3 -7
- data/lib/prometheus_exporter/server/sidekiq_process_collector.rb +2 -2
- data/lib/prometheus_exporter/server/sidekiq_queue_collector.rb +2 -2
- data/lib/prometheus_exporter/server/sidekiq_stats_collector.rb +2 -2
- data/lib/prometheus_exporter/server/unicorn_collector.rb +32 -33
- data/lib/prometheus_exporter/server/web_server.rb +65 -39
- data/lib/prometheus_exporter/server.rb +1 -0
- data/lib/prometheus_exporter/version.rb +1 -1
- data/lib/prometheus_exporter.rb +12 -13
- data/prometheus_exporter.gemspec +5 -7
- metadata +19 -16
- data/.github/workflows/docker.yml +0 -47
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
class ActiveRecordCollector < TypeCollector
|
5
|
-
|
5
|
+
MAX_METRIC_AGE = 60
|
6
|
+
|
6
7
|
ACTIVE_RECORD_GAUGES = {
|
7
8
|
connections: "Total connections in pool",
|
8
9
|
busy: "Connections in use in pool",
|
@@ -13,7 +14,12 @@ module PrometheusExporter::Server
|
|
13
14
|
}
|
14
15
|
|
15
16
|
def initialize
|
16
|
-
@active_record_metrics =
|
17
|
+
@active_record_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
18
|
+
@active_record_metrics.filter = -> (new_metric, old_metric) do
|
19
|
+
new_metric["pid"] == old_metric["pid"] &&
|
20
|
+
new_metric["hostname"] == old_metric["hostname"] &&
|
21
|
+
new_metric["metric_labels"]["pool_name"] == old_metric["metric_labels"]["pool_name"]
|
22
|
+
end
|
17
23
|
end
|
18
24
|
|
19
25
|
def type
|
@@ -26,7 +32,7 @@ module PrometheusExporter::Server
|
|
26
32
|
metrics = {}
|
27
33
|
|
28
34
|
@active_record_metrics.map do |m|
|
29
|
-
metric_key = (m["metric_labels"] || {}).merge("pid" => m["pid"])
|
35
|
+
metric_key = (m["metric_labels"] || {}).merge("pid" => m["pid"], "hostname" => m["hostname"])
|
30
36
|
metric_key.merge!(m["custom_labels"]) if m["custom_labels"]
|
31
37
|
|
32
38
|
ACTIVE_RECORD_GAUGES.map do |k, help|
|
@@ -42,16 +48,6 @@ module PrometheusExporter::Server
|
|
42
48
|
end
|
43
49
|
|
44
50
|
def collect(obj)
|
45
|
-
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
46
|
-
|
47
|
-
obj["created_at"] = now
|
48
|
-
|
49
|
-
@active_record_metrics.delete_if do |current|
|
50
|
-
(obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"] &&
|
51
|
-
obj["metric_labels"]["pool_name"] == current["metric_labels"]["pool_name"]) ||
|
52
|
-
(current["created_at"] + MAX_ACTIVERECORD_METRIC_AGE < now)
|
53
|
-
end
|
54
|
-
|
55
51
|
@active_record_metrics << obj
|
56
52
|
end
|
57
53
|
end
|
@@ -23,6 +23,7 @@ module PrometheusExporter::Server
|
|
23
23
|
register_collector(ActiveRecordCollector.new)
|
24
24
|
register_collector(ShoryukenCollector.new)
|
25
25
|
register_collector(ResqueCollector.new)
|
26
|
+
register_collector(GoodJobCollector.new)
|
26
27
|
end
|
27
28
|
|
28
29
|
def register_collector(collector)
|
@@ -5,6 +5,7 @@ module PrometheusExporter::Server
|
|
5
5
|
def initialize
|
6
6
|
@delayed_jobs_total = nil
|
7
7
|
@delayed_job_duration_seconds = nil
|
8
|
+
@delayed_job_latency_seconds_total = nil
|
8
9
|
@delayed_jobs_total = nil
|
9
10
|
@delayed_failed_jobs_total = nil
|
10
11
|
@delayed_jobs_max_attempts_reached_total = nil
|
@@ -25,6 +26,7 @@ module PrometheusExporter::Server
|
|
25
26
|
|
26
27
|
ensure_delayed_job_metrics
|
27
28
|
@delayed_job_duration_seconds.observe(obj["duration"], counter_labels)
|
29
|
+
@delayed_job_latency_seconds_total.observe(obj["latency"], counter_labels)
|
28
30
|
@delayed_jobs_total.observe(1, counter_labels)
|
29
31
|
@delayed_failed_jobs_total.observe(1, counter_labels) if !obj["success"]
|
30
32
|
@delayed_jobs_max_attempts_reached_total.observe(1, counter_labels) if obj["attempts"] >= obj["max_attempts"]
|
@@ -38,7 +40,7 @@ module PrometheusExporter::Server
|
|
38
40
|
|
39
41
|
def metrics
|
40
42
|
if @delayed_jobs_total
|
41
|
-
[@delayed_job_duration_seconds, @delayed_jobs_total, @delayed_failed_jobs_total,
|
43
|
+
[@delayed_job_duration_seconds, @delayed_job_latency_seconds_total, @delayed_jobs_total, @delayed_failed_jobs_total,
|
42
44
|
@delayed_jobs_max_attempts_reached_total, @delayed_job_duration_seconds_summary, @delayed_job_attempts_summary,
|
43
45
|
@delayed_jobs_enqueued, @delayed_jobs_pending]
|
44
46
|
else
|
@@ -55,6 +57,10 @@ module PrometheusExporter::Server
|
|
55
57
|
PrometheusExporter::Metric::Counter.new(
|
56
58
|
"delayed_job_duration_seconds", "Total time spent in delayed jobs.")
|
57
59
|
|
60
|
+
@delayed_job_latency_seconds_total =
|
61
|
+
PrometheusExporter::Metric::Counter.new(
|
62
|
+
"delayed_job_latency_seconds_total", "Total delayed jobs latency.")
|
63
|
+
|
58
64
|
@delayed_jobs_total =
|
59
65
|
PrometheusExporter::Metric::Counter.new(
|
60
66
|
"delayed_jobs_total", "Total number of delayed jobs executed.")
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrometheusExporter::Server
|
4
|
+
class GoodJobCollector < TypeCollector
|
5
|
+
MAX_METRIC_AGE = 30
|
6
|
+
GOOD_JOB_GAUGES = {
|
7
|
+
scheduled: "Total number of scheduled GoodJob jobs.",
|
8
|
+
retried: "Total number of retried GoodJob jobs.",
|
9
|
+
queued: "Total number of queued GoodJob jobs.",
|
10
|
+
running: "Total number of running GoodJob jobs.",
|
11
|
+
finished: "Total number of finished GoodJob jobs.",
|
12
|
+
succeeded: "Total number of succeeded GoodJob jobs.",
|
13
|
+
discarded: "Total number of discarded GoodJob jobs."
|
14
|
+
}
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@good_job_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
18
|
+
@gauges = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def type
|
22
|
+
"good_job"
|
23
|
+
end
|
24
|
+
|
25
|
+
def metrics
|
26
|
+
return [] if good_job_metrics.length == 0
|
27
|
+
|
28
|
+
good_job_metrics.map do |metric|
|
29
|
+
labels = metric.fetch("custom_labels", {})
|
30
|
+
|
31
|
+
GOOD_JOB_GAUGES.map do |name, help|
|
32
|
+
value = metric[name.to_s]
|
33
|
+
|
34
|
+
if value
|
35
|
+
gauge = gauges[name] ||= PrometheusExporter::Metric::Gauge.new("good_job_#{name}", help)
|
36
|
+
gauge.observe(value, labels)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
gauges.values
|
42
|
+
end
|
43
|
+
|
44
|
+
def collect(object)
|
45
|
+
@good_job_metrics << object
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :good_job_metrics, :gauges
|
51
|
+
end
|
52
|
+
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
|
5
5
|
class ProcessCollector < TypeCollector
|
6
|
-
|
6
|
+
MAX_METRIC_AGE = 60
|
7
|
+
|
7
8
|
PROCESS_GAUGES = {
|
8
9
|
heap_free_slots: "Free ruby heap slots.",
|
9
10
|
heap_live_slots: "Used ruby heap slots.",
|
@@ -12,6 +13,8 @@ module PrometheusExporter::Server
|
|
12
13
|
v8_physical_size: "Physical size consumed by V8 heaps.",
|
13
14
|
v8_heap_count: "Number of V8 contexts running.",
|
14
15
|
rss: "Total RSS used by process.",
|
16
|
+
malloc_increase_bytes_limit: 'Limit before Ruby triggers a GC against current objects (bytes).',
|
17
|
+
oldmalloc_increase_bytes_limit: 'Limit before Ruby triggers a major GC against old objects (bytes).'
|
15
18
|
}
|
16
19
|
|
17
20
|
PROCESS_COUNTERS = {
|
@@ -21,7 +24,10 @@ module PrometheusExporter::Server
|
|
21
24
|
}
|
22
25
|
|
23
26
|
def initialize
|
24
|
-
@process_metrics =
|
27
|
+
@process_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
28
|
+
@process_metrics.filter = -> (new_metric, old_metric) do
|
29
|
+
new_metric["pid"] == old_metric["pid"] && new_metric["hostname"] == old_metric["hostname"]
|
30
|
+
end
|
25
31
|
end
|
26
32
|
|
27
33
|
def type
|
@@ -34,8 +40,8 @@ module PrometheusExporter::Server
|
|
34
40
|
metrics = {}
|
35
41
|
|
36
42
|
@process_metrics.map do |m|
|
37
|
-
metric_key = m["metric_labels"].merge("pid" => m["pid"])
|
38
|
-
metric_key.merge!(m["custom_labels"]
|
43
|
+
metric_key = (m["metric_labels"] || {}).merge("pid" => m["pid"], "hostname" => m["hostname"])
|
44
|
+
metric_key.merge!(m["custom_labels"]) if m["custom_labels"]
|
39
45
|
|
40
46
|
PROCESS_GAUGES.map do |k, help|
|
41
47
|
k = k.to_s
|
@@ -58,15 +64,6 @@ module PrometheusExporter::Server
|
|
58
64
|
end
|
59
65
|
|
60
66
|
def collect(obj)
|
61
|
-
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
62
|
-
|
63
|
-
obj["created_at"] = now
|
64
|
-
|
65
|
-
@process_metrics.delete_if do |current|
|
66
|
-
(obj["pid"] == current["pid"] && obj["hostname"] == current["hostname"]) ||
|
67
|
-
(current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
|
68
|
-
end
|
69
|
-
|
70
67
|
@process_metrics << obj
|
71
68
|
end
|
72
69
|
end
|
@@ -14,7 +14,7 @@ module PrometheusExporter::Server
|
|
14
14
|
}
|
15
15
|
|
16
16
|
def initialize
|
17
|
-
@puma_metrics = MetricsContainer.new
|
17
|
+
@puma_metrics = MetricsContainer.new(ttl: MAX_PUMA_METRIC_AGE)
|
18
18
|
@puma_metrics.filter = -> (new_metric, old_metric) do
|
19
19
|
new_metric["pid"] == old_metric["pid"] && new_metric["hostname"] == old_metric["hostname"]
|
20
20
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
class ResqueCollector < TypeCollector
|
5
|
-
|
5
|
+
MAX_METRIC_AGE = 30
|
6
6
|
RESQUE_GAUGES = {
|
7
7
|
processed_jobs: "Total number of processed Resque jobs.",
|
8
8
|
failed_jobs: "Total number of failed Resque jobs.",
|
@@ -13,7 +13,7 @@ module PrometheusExporter::Server
|
|
13
13
|
}
|
14
14
|
|
15
15
|
def initialize
|
16
|
-
@resque_metrics =
|
16
|
+
@resque_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
17
17
|
@gauges = {}
|
18
18
|
end
|
19
19
|
|
@@ -40,11 +40,7 @@ module PrometheusExporter::Server
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def collect(object)
|
43
|
-
|
44
|
-
|
45
|
-
object["created_at"] = now
|
46
|
-
resque_metrics.delete_if { |metric| metric["created_at"] + MAX_RESQUE_METRIC_AGE < now }
|
47
|
-
resque_metrics << object
|
43
|
+
@resque_metrics << object
|
48
44
|
end
|
49
45
|
|
50
46
|
private
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
class SidekiqProcessCollector < PrometheusExporter::Server::TypeCollector
|
5
|
-
|
5
|
+
MAX_METRIC_AGE = 60
|
6
6
|
|
7
7
|
SIDEKIQ_PROCESS_GAUGES = {
|
8
8
|
'busy' => 'Number of running jobs',
|
@@ -12,7 +12,7 @@ module PrometheusExporter::Server
|
|
12
12
|
attr_reader :sidekiq_metrics, :gauges
|
13
13
|
|
14
14
|
def initialize
|
15
|
-
@sidekiq_metrics = MetricsContainer.new(ttl:
|
15
|
+
@sidekiq_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
16
16
|
@gauges = {}
|
17
17
|
end
|
18
18
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module PrometheusExporter::Server
|
3
3
|
class SidekiqQueueCollector < TypeCollector
|
4
|
-
|
4
|
+
MAX_METRIC_AGE = 60
|
5
5
|
|
6
6
|
SIDEKIQ_QUEUE_GAUGES = {
|
7
7
|
'backlog' => 'Size of the sidekiq queue.',
|
@@ -11,7 +11,7 @@ module PrometheusExporter::Server
|
|
11
11
|
attr_reader :sidekiq_metrics, :gauges
|
12
12
|
|
13
13
|
def initialize
|
14
|
-
@sidekiq_metrics = MetricsContainer.new
|
14
|
+
@sidekiq_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
15
15
|
@gauges = {}
|
16
16
|
end
|
17
17
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module PrometheusExporter::Server
|
4
4
|
class SidekiqStatsCollector < TypeCollector
|
5
|
-
|
5
|
+
MAX_METRIC_AGE = 60
|
6
6
|
|
7
7
|
SIDEKIQ_STATS_GAUGES = {
|
8
8
|
'dead_size' => 'Size of dead the queue',
|
@@ -18,7 +18,7 @@ module PrometheusExporter::Server
|
|
18
18
|
attr_reader :sidekiq_metrics, :gauges
|
19
19
|
|
20
20
|
def initialize
|
21
|
-
@sidekiq_metrics = MetricsContainer.new(ttl:
|
21
|
+
@sidekiq_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
22
22
|
@gauges = {}
|
23
23
|
end
|
24
24
|
|
@@ -2,47 +2,46 @@
|
|
2
2
|
|
3
3
|
# custom type collector for prometheus_exporter for handling the metrics sent from
|
4
4
|
# PrometheusExporter::Instrumentation::Unicorn
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
module PrometheusExporter::Server
|
6
|
+
class UnicornCollector < PrometheusExporter::Server::TypeCollector
|
7
|
+
MAX_METRIC_AGE = 60
|
8
|
+
|
9
|
+
UNICORN_GAUGES = {
|
10
|
+
workers: 'Number of unicorn workers.',
|
11
|
+
active_workers: 'Number of active unicorn workers',
|
12
|
+
request_backlog: 'Number of requests waiting to be processed by a unicorn worker.'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@unicorn_metrics = MetricsContainer.new(ttl: MAX_METRIC_AGE)
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def type
|
20
|
+
'unicorn'
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
def metrics
|
24
|
+
return [] if @unicorn_metrics.length.zero?
|
24
25
|
|
25
|
-
|
26
|
+
metrics = {}
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
@unicorn_metrics.map do |m|
|
29
|
+
labels = m["custom_labels"] || {}
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
UNICORN_GAUGES.map do |k, help|
|
32
|
+
k = k.to_s
|
33
|
+
if (v = m[k])
|
34
|
+
g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new("unicorn_#{k}", help)
|
35
|
+
g.observe(v, labels)
|
36
|
+
end
|
35
37
|
end
|
36
38
|
end
|
37
|
-
end
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
metrics.values
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@unicorn_metrics.delete_if { |m| m['created_at'] + MAX_UNICORN_METRIC_AGE < now }
|
46
|
-
@unicorn_metrics << obj
|
43
|
+
def collect(obj)
|
44
|
+
@unicorn_metrics << obj
|
45
|
+
end
|
47
46
|
end
|
48
47
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "webrick"
|
4
|
+
require "timeout"
|
5
|
+
require "zlib"
|
6
|
+
require "stringio"
|
7
7
|
|
8
8
|
module PrometheusExporter::Server
|
9
9
|
class WebServer
|
@@ -18,11 +18,23 @@ module PrometheusExporter::Server
|
|
18
18
|
@auth = opts[:auth]
|
19
19
|
@realm = opts[:realm] || PrometheusExporter::DEFAULT_REALM
|
20
20
|
|
21
|
-
@metrics_total =
|
21
|
+
@metrics_total =
|
22
|
+
PrometheusExporter::Metric::Counter.new(
|
23
|
+
"collector_metrics_total",
|
24
|
+
"Total metrics processed by exporter web."
|
25
|
+
)
|
22
26
|
|
23
|
-
@sessions_total =
|
27
|
+
@sessions_total =
|
28
|
+
PrometheusExporter::Metric::Counter.new(
|
29
|
+
"collector_sessions_total",
|
30
|
+
"Total send_metric sessions processed by exporter web."
|
31
|
+
)
|
24
32
|
|
25
|
-
@bad_metrics_total =
|
33
|
+
@bad_metrics_total =
|
34
|
+
PrometheusExporter::Metric::Counter.new(
|
35
|
+
"collector_bad_metrics_total",
|
36
|
+
"Total mis-handled metrics by collector."
|
37
|
+
)
|
26
38
|
|
27
39
|
@metrics_total.observe(0)
|
28
40
|
@sessions_total.observe(0)
|
@@ -34,7 +46,7 @@ module PrometheusExporter::Server
|
|
34
46
|
if @verbose
|
35
47
|
@access_log = [
|
36
48
|
[$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT],
|
37
|
-
[$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT]
|
49
|
+
[$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT]
|
38
50
|
]
|
39
51
|
@logger = WEBrick::Log.new(log_target || $stderr)
|
40
52
|
else
|
@@ -42,23 +54,26 @@ module PrometheusExporter::Server
|
|
42
54
|
@logger = WEBrick::Log.new(log_target || "/dev/null")
|
43
55
|
end
|
44
56
|
|
45
|
-
|
57
|
+
if @verbose && @auth
|
58
|
+
@logger.info "Using Basic Authentication via #{@auth}"
|
59
|
+
end
|
46
60
|
|
47
|
-
if %w
|
61
|
+
if %w[ALL ANY].include?(@bind)
|
48
62
|
@logger.info "Listening on both 0.0.0.0/:: network interfaces"
|
49
63
|
@bind = nil
|
50
64
|
end
|
51
65
|
|
52
|
-
@server =
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
@server =
|
67
|
+
WEBrick::HTTPServer.new(
|
68
|
+
Port: @port,
|
69
|
+
BindAddress: @bind,
|
70
|
+
Logger: @logger,
|
71
|
+
AccessLog: @access_log
|
72
|
+
)
|
73
|
+
|
74
|
+
@server.mount_proc "/" do |req, res|
|
75
|
+
res["Content-Type"] = "text/plain; charset=utf-8"
|
76
|
+
if req.path == "/metrics"
|
62
77
|
authenticate(req, res) if @auth
|
63
78
|
|
64
79
|
res.status = 200
|
@@ -76,13 +91,14 @@ module PrometheusExporter::Server
|
|
76
91
|
else
|
77
92
|
res.body = metrics
|
78
93
|
end
|
79
|
-
elsif req.path ==
|
94
|
+
elsif req.path == "/send-metrics"
|
80
95
|
handle_metrics(req, res)
|
81
|
-
elsif req.path ==
|
82
|
-
res.body =
|
96
|
+
elsif req.path == "/ping"
|
97
|
+
res.body = "PONG"
|
83
98
|
else
|
84
99
|
res.status = 404
|
85
|
-
res.body =
|
100
|
+
res.body =
|
101
|
+
"Not Found! The Prometheus Ruby Exporter only listens on /ping, /metrics and /send-metrics"
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
@@ -94,13 +110,11 @@ module PrometheusExporter::Server
|
|
94
110
|
@metrics_total.observe
|
95
111
|
@collector.process(block)
|
96
112
|
rescue => e
|
97
|
-
if @verbose
|
98
|
-
@logger.error "\n\n#{e.inspect}\n#{e.backtrace}\n\n"
|
99
|
-
end
|
113
|
+
@logger.error "\n\n#{e.inspect}\n#{e.backtrace}\n\n" if @verbose
|
100
114
|
@bad_metrics_total.observe
|
101
115
|
res.body = "Bad Metrics #{e}"
|
102
116
|
res.status = e.respond_to?(:status_code) ? e.status_code : 500
|
103
|
-
|
117
|
+
break
|
104
118
|
end
|
105
119
|
end
|
106
120
|
|
@@ -109,13 +123,14 @@ module PrometheusExporter::Server
|
|
109
123
|
end
|
110
124
|
|
111
125
|
def start
|
112
|
-
@runner ||=
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
126
|
+
@runner ||=
|
127
|
+
Thread.start do
|
128
|
+
begin
|
129
|
+
@server.start
|
130
|
+
rescue => e
|
131
|
+
@logger.error "Failed to start prometheus collector web on port #{@port}: #{e}"
|
132
|
+
end
|
117
133
|
end
|
118
|
-
end
|
119
134
|
end
|
120
135
|
|
121
136
|
def stop
|
@@ -125,7 +140,7 @@ module PrometheusExporter::Server
|
|
125
140
|
def metrics
|
126
141
|
metric_text = nil
|
127
142
|
begin
|
128
|
-
Timeout
|
143
|
+
Timeout.timeout(@timeout) do
|
129
144
|
metric_text = @collector.prometheus_metrics_text
|
130
145
|
end
|
131
146
|
rescue Timeout::Error
|
@@ -158,9 +173,18 @@ module PrometheusExporter::Server
|
|
158
173
|
end
|
159
174
|
|
160
175
|
def get_rss
|
161
|
-
@pagesize ||=
|
176
|
+
@pagesize ||=
|
177
|
+
begin
|
178
|
+
`getconf PAGESIZE`.to_i
|
179
|
+
rescue StandardError
|
180
|
+
4096
|
181
|
+
end
|
162
182
|
@pid ||= Process.pid
|
163
|
-
|
183
|
+
begin
|
184
|
+
File.read("/proc/#{@pid}/statm").split(" ")[1].to_i * @pagesize
|
185
|
+
rescue StandardError
|
186
|
+
0
|
187
|
+
end
|
164
188
|
end
|
165
189
|
|
166
190
|
def add_gauge(name, help, value)
|
@@ -171,10 +195,12 @@ module PrometheusExporter::Server
|
|
171
195
|
|
172
196
|
def authenticate(req, res)
|
173
197
|
htpasswd = WEBrick::HTTPAuth::Htpasswd.new(@auth)
|
174
|
-
basic_auth =
|
198
|
+
basic_auth =
|
199
|
+
WEBrick::HTTPAuth::BasicAuth.new(
|
200
|
+
{ Realm: @realm, UserDB: htpasswd, Logger: @logger }
|
201
|
+
)
|
175
202
|
|
176
203
|
basic_auth.authenticate(req, res)
|
177
204
|
end
|
178
|
-
|
179
205
|
end
|
180
206
|
end
|
data/lib/prometheus_exporter.rb
CHANGED
@@ -2,21 +2,21 @@
|
|
2
2
|
|
3
3
|
require_relative "prometheus_exporter/version"
|
4
4
|
require "json"
|
5
|
-
require "thread"
|
6
5
|
|
7
6
|
module PrometheusExporter
|
8
7
|
# per: https://github.com/prometheus/prometheus/wiki/Default-port-allocations
|
9
8
|
DEFAULT_PORT = 9394
|
10
|
-
DEFAULT_BIND_ADDRESS =
|
11
|
-
DEFAULT_PREFIX =
|
9
|
+
DEFAULT_BIND_ADDRESS = "localhost"
|
10
|
+
DEFAULT_PREFIX = "ruby_"
|
12
11
|
DEFAULT_LABEL = {}
|
13
12
|
DEFAULT_TIMEOUT = 2
|
14
|
-
DEFAULT_REALM =
|
13
|
+
DEFAULT_REALM = "Prometheus Exporter"
|
15
14
|
|
16
15
|
class OjCompat
|
17
16
|
def self.parse(obj)
|
18
17
|
Oj.compat_load(obj)
|
19
18
|
end
|
19
|
+
|
20
20
|
def self.dump(obj)
|
21
21
|
Oj.dump(obj, mode: :compat)
|
22
22
|
end
|
@@ -25,7 +25,7 @@ module PrometheusExporter
|
|
25
25
|
def self.hostname
|
26
26
|
@hostname ||=
|
27
27
|
begin
|
28
|
-
require
|
28
|
+
require "socket"
|
29
29
|
Socket.gethostname
|
30
30
|
rescue => e
|
31
31
|
STDERR.puts "Unable to lookup hostname #{e}"
|
@@ -45,13 +45,12 @@ module PrometheusExporter
|
|
45
45
|
def self.has_oj?
|
46
46
|
(
|
47
47
|
@@has_oj ||=
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
) == :
|
48
|
+
begin
|
49
|
+
require "oj"
|
50
|
+
:T
|
51
|
+
rescue LoadError
|
52
|
+
:F
|
53
|
+
end
|
54
|
+
) == :T
|
55
55
|
end
|
56
|
-
|
57
56
|
end
|
data/prometheus_exporter.gemspec
CHANGED
@@ -15,8 +15,6 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.homepage = "https://github.com/discourse/prometheus_exporter"
|
16
16
|
spec.license = "MIT"
|
17
17
|
|
18
|
-
spec.post_install_message = "prometheus_exporter will only bind to localhost by default as of v0.5"
|
19
|
-
|
20
18
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
19
|
f.match(%r{^(test|spec|features|bin)/})
|
22
20
|
end
|
@@ -29,14 +27,14 @@ Gem::Specification.new do |spec|
|
|
29
27
|
spec.add_development_dependency "rubocop", ">= 0.69"
|
30
28
|
spec.add_development_dependency "bundler", ">= 2.1.4"
|
31
29
|
spec.add_development_dependency "rake", "~> 13.0"
|
32
|
-
spec.add_development_dependency "minitest", "~> 5.
|
30
|
+
spec.add_development_dependency "minitest", "~> 5.23.0"
|
33
31
|
spec.add_development_dependency "guard", "~> 2.0"
|
34
|
-
spec.add_development_dependency "mini_racer", "~> 0.
|
32
|
+
spec.add_development_dependency "mini_racer", "~> 0.12.0"
|
35
33
|
spec.add_development_dependency "guard-minitest", "~> 2.0"
|
36
34
|
spec.add_development_dependency "oj", "~> 3.0"
|
37
|
-
spec.add_development_dependency "rack-test", "~>
|
35
|
+
spec.add_development_dependency "rack-test", "~> 2.1.0"
|
38
36
|
spec.add_development_dependency "minitest-stub-const", "~> 0.6"
|
39
|
-
spec.add_development_dependency "rubocop-discourse", "
|
37
|
+
spec.add_development_dependency "rubocop-discourse", ">= 3"
|
40
38
|
spec.add_development_dependency "appraisal", "~> 2.3"
|
41
39
|
spec.add_development_dependency "activerecord", "~> 6.0.0"
|
42
40
|
spec.add_development_dependency "redis", "> 5"
|
@@ -44,5 +42,5 @@ Gem::Specification.new do |spec|
|
|
44
42
|
if !RUBY_ENGINE == 'jruby'
|
45
43
|
spec.add_development_dependency "raindrops", "~> 0.19"
|
46
44
|
end
|
47
|
-
spec.required_ruby_version = '>=
|
45
|
+
spec.required_ruby_version = '>= 3.0.0'
|
48
46
|
end
|