phobos_prometheus 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +9 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +19 -0
- data/.rubocop_common.yml +23 -0
- data/.rubosync.yml +2 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +44 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +176 -0
- data/PULL_REQUEST_TEMPLATE.md +29 -0
- data/README.md +126 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/config/phobos_prometheus.yml.example +46 -0
- data/lib/phobos_prometheus.rb +69 -0
- data/lib/phobos_prometheus/collector.rb +14 -0
- data/lib/phobos_prometheus/collector/counter.rb +34 -0
- data/lib/phobos_prometheus/collector/error_logger.rb +28 -0
- data/lib/phobos_prometheus/collector/gauge.rb +59 -0
- data/lib/phobos_prometheus/collector/helper.rb +35 -0
- data/lib/phobos_prometheus/collector/histogram.rb +48 -0
- data/lib/phobos_prometheus/config_parser.rb +205 -0
- data/lib/phobos_prometheus/errors.rb +8 -0
- data/lib/phobos_prometheus/exporter.rb +43 -0
- data/lib/phobos_prometheus/exporter_helper.rb +42 -0
- data/lib/phobos_prometheus/logger.rb +18 -0
- data/lib/phobos_prometheus/version.rb +5 -0
- data/phobos_prometheus.gemspec +45 -0
- metadata +216 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'phobos_prometheus'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
metrics_prefix: phobos_app
|
2
|
+
|
3
|
+
counters:
|
4
|
+
- instrumentation: listener.process_message
|
5
|
+
- instrumentation: listener.process_batch
|
6
|
+
|
7
|
+
histograms:
|
8
|
+
- instrumentation: listener.process_message
|
9
|
+
bucket_name: message
|
10
|
+
- instrumentation: listener.process_batch
|
11
|
+
bucket_name: batch
|
12
|
+
|
13
|
+
gauges:
|
14
|
+
- label: number_of_handlers
|
15
|
+
increment: listener.start_handler
|
16
|
+
decrement: listener.stop_handler
|
17
|
+
|
18
|
+
buckets:
|
19
|
+
- name: message
|
20
|
+
bins:
|
21
|
+
- 2
|
22
|
+
- 4
|
23
|
+
- 8
|
24
|
+
- 16
|
25
|
+
- 32
|
26
|
+
- 64
|
27
|
+
- 128
|
28
|
+
- 256
|
29
|
+
- 512
|
30
|
+
- 1024
|
31
|
+
- 2048
|
32
|
+
- 4096
|
33
|
+
- name: batch
|
34
|
+
bins:
|
35
|
+
- 64
|
36
|
+
- 128
|
37
|
+
- 256
|
38
|
+
- 512
|
39
|
+
- 1024
|
40
|
+
- 2048
|
41
|
+
- 4096
|
42
|
+
- 8192
|
43
|
+
- 16384
|
44
|
+
- 32768
|
45
|
+
- 65536
|
46
|
+
- 131072
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require 'phobos'
|
7
|
+
require 'prometheus/client'
|
8
|
+
require 'prometheus/client/formats/text'
|
9
|
+
require 'sinatra/base'
|
10
|
+
|
11
|
+
require 'phobos_prometheus/version'
|
12
|
+
require 'phobos_prometheus/errors'
|
13
|
+
require 'phobos_prometheus/logger'
|
14
|
+
require 'phobos_prometheus/config_parser'
|
15
|
+
require 'phobos_prometheus/collector/helper'
|
16
|
+
require 'phobos_prometheus/collector/error_logger'
|
17
|
+
require 'phobos_prometheus/collector/histogram'
|
18
|
+
require 'phobos_prometheus/collector/counter'
|
19
|
+
require 'phobos_prometheus/collector/gauge'
|
20
|
+
require 'phobos_prometheus/collector'
|
21
|
+
require 'phobos_prometheus/exporter_helper'
|
22
|
+
require 'phobos_prometheus/exporter'
|
23
|
+
|
24
|
+
# Prometheus collector for Phobos
|
25
|
+
module PhobosPrometheus
|
26
|
+
class << self
|
27
|
+
include Logger
|
28
|
+
attr_reader :config, :metrics
|
29
|
+
|
30
|
+
# Public - configure and validate configuration
|
31
|
+
def configure(path)
|
32
|
+
@metrics ||= []
|
33
|
+
@config = ConfigParser.new(path).config
|
34
|
+
|
35
|
+
log_info('PhobosPrometheus configured')
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public - after configured create the prometheus metrics
|
39
|
+
def subscribe
|
40
|
+
subscribe_counters
|
41
|
+
subscribe_histograms
|
42
|
+
subscribe_gauges
|
43
|
+
|
44
|
+
log_info('PhobosPrometheus subscribed') unless @metrics.empty?
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def subscribe_counters
|
52
|
+
@config.counters.each do |counter|
|
53
|
+
@metrics << PhobosPrometheus::Collector::Counter.create(counter)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def subscribe_histograms
|
58
|
+
@config.histograms.each do |histogram|
|
59
|
+
@metrics << PhobosPrometheus::Collector::Histogram.create(histogram)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def subscribe_gauges
|
64
|
+
@config.gauges.each do |gauge|
|
65
|
+
@metrics << PhobosPrometheus::Collector::Gauge.create(gauge)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
# Collector houses common metrics code
|
5
|
+
module Collector
|
6
|
+
EVENT_LABEL_BUILDER = proc do |event|
|
7
|
+
{
|
8
|
+
topic: event.payload[:topic],
|
9
|
+
group_id: event.payload[:group_id],
|
10
|
+
handler: event.payload[:handler]
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
module Collector
|
5
|
+
# Collector class to track counter events
|
6
|
+
class Counter
|
7
|
+
include Helper
|
8
|
+
attr_reader :counter
|
9
|
+
|
10
|
+
def self.create(config)
|
11
|
+
instrumentation = config[:instrumentation]
|
12
|
+
raise(InvalidConfigurationError, 'Counter requires :instrumentation') \
|
13
|
+
unless instrumentation
|
14
|
+
new(instrumentation: instrumentation)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(instrumentation:)
|
18
|
+
@metrics_prefix = @instrumentation = @registry = @counter = nil
|
19
|
+
setup_collector_module(instrumentation: instrumentation)
|
20
|
+
end
|
21
|
+
|
22
|
+
def init_metrics(prometheus_label)
|
23
|
+
@counter = @registry.counter(
|
24
|
+
:"#{@metrics_prefix}_#{prometheus_label}_total",
|
25
|
+
"The total number of #{@instrumentation} events handled."
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_metrics(event_label, _event)
|
30
|
+
@counter.increment(event_label)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
module Collector
|
5
|
+
# ErrorLogger logs errors to stdout
|
6
|
+
class ErrorLogger
|
7
|
+
include Logger
|
8
|
+
def initialize(error, event, instrumentation_label)
|
9
|
+
@error = error
|
10
|
+
@event = event
|
11
|
+
@instrumentation_label = instrumentation_label
|
12
|
+
end
|
13
|
+
|
14
|
+
def log
|
15
|
+
log_error(
|
16
|
+
Hash(
|
17
|
+
message: 'PhobosPrometheus: Error occured in metrics handler for subscribed event',
|
18
|
+
instrumentation_label: @instrumentation_label,
|
19
|
+
event: @event,
|
20
|
+
exception_class: @error.class.to_s,
|
21
|
+
exception_message: @error.message,
|
22
|
+
backtrace: @error.backtrace
|
23
|
+
)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
module Collector
|
5
|
+
# Collector class to track gauge events
|
6
|
+
class Gauge
|
7
|
+
attr_reader :gauge, :registry
|
8
|
+
|
9
|
+
def self.create(config)
|
10
|
+
label = config[:label]
|
11
|
+
increment = config[:increment]
|
12
|
+
decrement = config[:decrement]
|
13
|
+
|
14
|
+
raise(InvalidConfigurationError, 'Gauge requires :label, :increment and :decrement') \
|
15
|
+
unless label && increment && decrement
|
16
|
+
new(label: label, increment: increment, decrement: decrement)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(label:, increment:, decrement:)
|
20
|
+
@registry = Prometheus::Client.registry
|
21
|
+
@metrics_prefix = PhobosPrometheus.config.metrics_prefix || 'phobos_client'
|
22
|
+
@increment = increment
|
23
|
+
@decrement = decrement
|
24
|
+
@label = label
|
25
|
+
@gauge = @registry.gauge(
|
26
|
+
:"#{@metrics_prefix}_#{label}", "The current count of #{@label}"
|
27
|
+
)
|
28
|
+
|
29
|
+
subscribe_metrics
|
30
|
+
end
|
31
|
+
|
32
|
+
def subscribe_metrics
|
33
|
+
Phobos::Instrumentation.subscribe(@increment) do |event|
|
34
|
+
safely_update_metrics(event, :increment)
|
35
|
+
end
|
36
|
+
|
37
|
+
Phobos::Instrumentation.subscribe(@decrement) do |event|
|
38
|
+
safely_update_metrics(event, :decrement)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# rubocop:disable Lint/RescueWithoutErrorClass
|
43
|
+
def safely_update_metrics(event, operation)
|
44
|
+
event_label = EVENT_LABEL_BUILDER.call(event)
|
45
|
+
# .increment and .decrement is not released yet
|
46
|
+
# @gauge.public_send(operation, event_label)
|
47
|
+
current = @gauge.get(event_label) || 0
|
48
|
+
if operation == :increment
|
49
|
+
@gauge.set(event_label, current + 1)
|
50
|
+
else
|
51
|
+
@gauge.set(event_label, current - 1)
|
52
|
+
end
|
53
|
+
rescue => error
|
54
|
+
ErrorLogger.new(error, event, @label).log
|
55
|
+
end
|
56
|
+
# rubocop:enable Lint/RescueWithoutErrorClass
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
module Collector
|
5
|
+
# Shared code between collectors.
|
6
|
+
# Using module to avoid introducing inheritance
|
7
|
+
module Helper
|
8
|
+
attr_reader :registry
|
9
|
+
|
10
|
+
def setup_collector_module(instrumentation:)
|
11
|
+
@instrumentation = instrumentation
|
12
|
+
@registry = Prometheus::Client.registry
|
13
|
+
@metrics_prefix = PhobosPrometheus.config.metrics_prefix || 'phobos_client'
|
14
|
+
|
15
|
+
init_metrics(instrumentation.sub('.', '_'))
|
16
|
+
subscribe_metrics
|
17
|
+
end
|
18
|
+
|
19
|
+
def subscribe_metrics
|
20
|
+
Phobos::Instrumentation.subscribe(@instrumentation) do |event|
|
21
|
+
safely_update_metrics(event)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:disable Lint/RescueWithoutErrorClass
|
26
|
+
def safely_update_metrics(event)
|
27
|
+
event_label = EVENT_LABEL_BUILDER.call(event)
|
28
|
+
update_metrics(event_label, event)
|
29
|
+
rescue => error
|
30
|
+
ErrorLogger.new(error, event, @instrumentation).log
|
31
|
+
end
|
32
|
+
# rubocop:enable Lint/RescueWithoutErrorClass
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
module Collector
|
5
|
+
# Collector class to track histogram events
|
6
|
+
class Histogram
|
7
|
+
include Helper
|
8
|
+
attr_reader :histogram
|
9
|
+
|
10
|
+
# Buckets in ms for histogram
|
11
|
+
BUCKETS = [5, 10, 25, 50, 100, 250, 500, 750, 1500, 3000, 5000].freeze
|
12
|
+
|
13
|
+
def self.create(config)
|
14
|
+
instrumentation = config[:instrumentation]
|
15
|
+
bucket_name = config[:bucket_name]
|
16
|
+
raise(InvalidConfigurationError, 'Histogram requires :bucket_name and :instrumentation') \
|
17
|
+
unless instrumentation && bucket_name
|
18
|
+
new(
|
19
|
+
instrumentation: instrumentation,
|
20
|
+
bucket_name: bucket_name
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(instrumentation:, bucket_name:)
|
25
|
+
@metrics_prefix = @instrumentation = @registry = @histogram = nil
|
26
|
+
@buckets = fetch_bucket_size(bucket_name) || BUCKETS
|
27
|
+
setup_collector_module(instrumentation: instrumentation)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_bucket_size(bucket_name)
|
31
|
+
PhobosPrometheus.config.buckets.find { |bucket| bucket.name == bucket_name }.bins
|
32
|
+
end
|
33
|
+
|
34
|
+
def init_metrics(prometheus_label)
|
35
|
+
@histogram = @registry.histogram(
|
36
|
+
:"#{@metrics_prefix}_#{prometheus_label}_duration",
|
37
|
+
"The duration spent (in ms) consuming #{@instrumentation} events.",
|
38
|
+
{},
|
39
|
+
@buckets
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_metrics(event_label, event)
|
44
|
+
@histogram.observe(event_label, event.duration)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PhobosPrometheus
|
4
|
+
# Validate Counters
|
5
|
+
class CountersValidator
|
6
|
+
include Logger
|
7
|
+
COUNTER_INSTRUMENTATION_MISSING = 'Missing required key :instrumentation for counter'
|
8
|
+
COUNTER_INVALID_KEY = 'Invalid configuration option detected at counter level, ignoring'
|
9
|
+
COUNTER_KEYS = [:instrumentation].freeze
|
10
|
+
|
11
|
+
def initialize(counters)
|
12
|
+
@counters = counters
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
@counters.map do |counter|
|
17
|
+
validate_counter(counter)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_counter(counter)
|
22
|
+
Helper.assert_required_key(counter, :instrumentation) || \
|
23
|
+
Helper.fail_config(COUNTER_INSTRUMENTATION_MISSING)
|
24
|
+
Helper.check_invalid_keys(COUNTER_KEYS, counter) || \
|
25
|
+
log_warn(COUNTER_INVALID_KEY)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Validate Histograms
|
30
|
+
class HistogramsValidator
|
31
|
+
include Logger
|
32
|
+
HISTOGRAM_INSTRUMENTATION_MISSING = 'Missing required key :instrumentation for histogram'
|
33
|
+
HISTOGRAM_BUCKET_NAME_MISSING = 'Missing required key :bucket_name for histogram'
|
34
|
+
HISTOGRAM_INVALID_BUCKET = 'Invalid bucket reference specified for histogram'
|
35
|
+
HISTOGRAM_INVALID_KEY = 'Invalid configuration option detected at histogram level, ignoring'
|
36
|
+
HISTOGRAM_KEYS = [:instrumentation, :bucket_name].freeze
|
37
|
+
|
38
|
+
def initialize(histograms, buckets)
|
39
|
+
@histograms = histograms
|
40
|
+
@buckets = buckets
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate
|
44
|
+
@histograms.map do |histogram|
|
45
|
+
validate_histogram(histogram)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_histogram(histogram)
|
50
|
+
Helper.assert_required_key(histogram, :instrumentation) || \
|
51
|
+
Helper.fail_config(HISTOGRAM_INSTRUMENTATION_MISSING)
|
52
|
+
Helper.assert_required_key(histogram, :bucket_name) || \
|
53
|
+
Helper.fail_config(HISTOGRAM_BUCKET_NAME_MISSING)
|
54
|
+
assert_bucket_exists(histogram['bucket_name']) || Helper.fail_config(HISTOGRAM_INVALID_BUCKET)
|
55
|
+
Helper.check_invalid_keys(HISTOGRAM_KEYS, histogram) || \
|
56
|
+
log_warn(HISTOGRAM_INVALID_KEY)
|
57
|
+
end
|
58
|
+
|
59
|
+
def assert_bucket_exists(name)
|
60
|
+
@buckets.any? { |key| key.name == name }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Validate buckets
|
65
|
+
class BucketsValidator
|
66
|
+
include Logger
|
67
|
+
BUCKET_NAME_MISSING = 'Missing required key :name for bucket'
|
68
|
+
BUCKET_BINS_MISSING = 'Missing required key :bins for bucket'
|
69
|
+
BUCKET_BINS_EMPTY = 'Bucket config bad, bins are empty'
|
70
|
+
BUCKET_INVALID_KEY = 'Invalid configuration option detected at bucket level, ignoring'
|
71
|
+
BUCKET_KEYS = [:name, :bins].freeze
|
72
|
+
|
73
|
+
def initialize(buckets)
|
74
|
+
@buckets = buckets
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate
|
78
|
+
@buckets.map do |bucket|
|
79
|
+
validate_bucket(bucket)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate_bucket(bucket)
|
84
|
+
Helper.assert_required_key(bucket, :name) || Helper.fail_config(BUCKET_NAME_MISSING)
|
85
|
+
Helper.assert_required_key(bucket, :bins) || Helper.fail_config(BUCKET_BINS_MISSING)
|
86
|
+
Helper.assert_array_of_type(bucket, :bins, Integer) || Helper.fail_config(BUCKET_BINS_EMPTY)
|
87
|
+
Helper.check_invalid_keys(BUCKET_KEYS, bucket) || \
|
88
|
+
log_warn(BUCKET_INVALID_KEY)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Validate gauges
|
93
|
+
class GaugesValidator
|
94
|
+
include Logger
|
95
|
+
GAUGE_LABEL_MISSING = 'Missing required key :label for gauge'
|
96
|
+
GAUGE_INCREMENT_MISSING = 'Missing required key :increment for gauge'
|
97
|
+
GAUGE_DECREMENT_MISSING = 'Missing required key :decrement for gauge'
|
98
|
+
GAUGE_INVALID_KEY = 'Invalid configuration option detected at gauge level, ignoring'
|
99
|
+
GAUGE_KEYS = [:label, :increment, :decrement].freeze
|
100
|
+
|
101
|
+
def initialize(gauges)
|
102
|
+
@gauges = gauges
|
103
|
+
end
|
104
|
+
|
105
|
+
def validate
|
106
|
+
@gauges.map do |gauge|
|
107
|
+
validate_gauge(gauge)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_gauge(gauge)
|
112
|
+
Helper.assert_required_key(gauge, :label) || Helper.fail_config(GAUGE_LABEL_MISSING)
|
113
|
+
Helper.assert_required_key(gauge, :increment) || Helper.fail_config(GAUGE_INCREMENT_MISSING)
|
114
|
+
Helper.assert_required_key(gauge, :decrement) || Helper.fail_config(GAUGE_DECREMENT_MISSING)
|
115
|
+
Helper.check_invalid_keys(GAUGE_KEYS, gauge) || \
|
116
|
+
log_warn(GAUGE_INVALID_KEY)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Helper for operations not dependent on instance state
|
121
|
+
module Helper
|
122
|
+
def self.read_config(path)
|
123
|
+
Phobos::DeepStruct.new(
|
124
|
+
YAML.safe_load(
|
125
|
+
ERB.new(
|
126
|
+
File.read(File.expand_path(path))
|
127
|
+
).result
|
128
|
+
)
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.assert_required_key(metric, required)
|
133
|
+
metric.keys.any? { |key| key.to_sym == required }
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.assert_array_of_type(metric, key, type)
|
137
|
+
ary = metric[key.to_s]
|
138
|
+
ary.is_a?(Array) && \
|
139
|
+
ary.all? { |value| value.is_a? type }
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.fail_config(message)
|
143
|
+
raise(InvalidConfigurationError, message)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.check_invalid_keys(keys, metric)
|
147
|
+
metric.keys.all? { |key| keys.include?(key.to_sym) }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Config validates and parses configuration yml
|
152
|
+
class ConfigParser
|
153
|
+
include Logger
|
154
|
+
attr_reader :config
|
155
|
+
|
156
|
+
ROOT_MISSING_COLLECTORS = 'No histograms, gauges nor counters are configured. ' \
|
157
|
+
'Metrics will not be recorded'
|
158
|
+
ROOT_INVALID_KEY = 'Invalid configuration option detected at root level, ignoring'
|
159
|
+
ROOT_KEYS = [:metrics_prefix, :counters, :histograms, :buckets, :gauges].freeze
|
160
|
+
|
161
|
+
def initialize(path)
|
162
|
+
@config = Helper.read_config(path)
|
163
|
+
validate_config
|
164
|
+
@config.counters = [] unless @config.counters
|
165
|
+
@config.histograms = [] unless @config.histograms
|
166
|
+
@config.gauges = [] unless @config.gauges
|
167
|
+
@config.freeze
|
168
|
+
end
|
169
|
+
|
170
|
+
def validate_config
|
171
|
+
validate_root
|
172
|
+
validate_counters
|
173
|
+
validate_histograms
|
174
|
+
validate_buckets
|
175
|
+
validate_gauges
|
176
|
+
end
|
177
|
+
|
178
|
+
def validate_root
|
179
|
+
assert_required_root_keys
|
180
|
+
Helper.check_invalid_keys(ROOT_KEYS, @config.to_h) || \
|
181
|
+
log_warn(ROOT_INVALID_KEY)
|
182
|
+
end
|
183
|
+
|
184
|
+
def validate_counters
|
185
|
+
CountersValidator.new(@config.to_h[:counters] || []).validate
|
186
|
+
end
|
187
|
+
|
188
|
+
def validate_histograms
|
189
|
+
HistogramsValidator.new(@config.to_h[:histograms] || [], @config.buckets).validate
|
190
|
+
end
|
191
|
+
|
192
|
+
def validate_buckets
|
193
|
+
BucketsValidator.new(@config.to_h[:buckets] || []).validate
|
194
|
+
end
|
195
|
+
|
196
|
+
def validate_gauges
|
197
|
+
GaugesValidator.new(@config.to_h[:gauges] || []).validate
|
198
|
+
end
|
199
|
+
|
200
|
+
def assert_required_root_keys
|
201
|
+
@config.counters || @config.histograms || @config.gauges || \
|
202
|
+
log_warn(ROOT_MISSING_COLLECTORS)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|