phobos_prometheus 0.3.2
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 +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
|