phihos-fluent-plugin-prometheus 2.0.3.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/linux.yml +34 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +14 -0
  6. data/ChangeLog +43 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +202 -0
  9. data/README.md +537 -0
  10. data/Rakefile +7 -0
  11. data/fluent-plugin-prometheus.gemspec +22 -0
  12. data/lib/fluent/plugin/filter_prometheus.rb +50 -0
  13. data/lib/fluent/plugin/in_prometheus/async_wrapper.rb +47 -0
  14. data/lib/fluent/plugin/in_prometheus.rb +230 -0
  15. data/lib/fluent/plugin/in_prometheus_monitor.rb +107 -0
  16. data/lib/fluent/plugin/in_prometheus_output_monitor.rb +234 -0
  17. data/lib/fluent/plugin/in_prometheus_tail_monitor.rb +98 -0
  18. data/lib/fluent/plugin/out_prometheus.rb +49 -0
  19. data/lib/fluent/plugin/prometheus/data_store.rb +103 -0
  20. data/lib/fluent/plugin/prometheus/placeholder_expander.rb +132 -0
  21. data/lib/fluent/plugin/prometheus.rb +445 -0
  22. data/lib/fluent/plugin/prometheus_metrics.rb +77 -0
  23. data/misc/fluentd_sample.conf +170 -0
  24. data/misc/nginx_proxy.conf +22 -0
  25. data/misc/prometheus.yaml +13 -0
  26. data/misc/prometheus_alerts.yaml +59 -0
  27. data/spec/fluent/plugin/filter_prometheus_spec.rb +145 -0
  28. data/spec/fluent/plugin/in_prometheus_monitor_spec.rb +42 -0
  29. data/spec/fluent/plugin/in_prometheus_spec.rb +225 -0
  30. data/spec/fluent/plugin/in_prometheus_tail_monitor_spec.rb +42 -0
  31. data/spec/fluent/plugin/out_prometheus_spec.rb +166 -0
  32. data/spec/fluent/plugin/prometheus/placeholder_expander_spec.rb +110 -0
  33. data/spec/fluent/plugin/prometheus_metrics_spec.rb +138 -0
  34. data/spec/fluent/plugin/shared.rb +248 -0
  35. data/spec/spec_helper.rb +10 -0
  36. metadata +176 -0
@@ -0,0 +1,49 @@
1
+ require 'fluent/plugin/output'
2
+ require 'fluent/plugin/prometheus'
3
+
4
+ module Fluent::Plugin
5
+ class PrometheusOutput < Fluent::Plugin::Output
6
+ Fluent::Plugin.register_output('prometheus', self)
7
+ include Fluent::Plugin::PrometheusLabelParser
8
+ include Fluent::Plugin::Prometheus
9
+
10
+ helpers :thread
11
+
12
+ def initialize
13
+ super
14
+ @registry = ::Prometheus::Client.registry
15
+ end
16
+
17
+ def multi_workers_ready?
18
+ true
19
+ end
20
+
21
+ def configure(conf)
22
+ super
23
+ labels = parse_labels_elements(conf)
24
+ @metrics = Fluent::Plugin::Prometheus.parse_metrics_elements(conf, @registry, labels)
25
+ end
26
+
27
+ def start
28
+ super
29
+ Fluent::Plugin::Prometheus.start_retention_threads(
30
+ @metrics,
31
+ @registry,
32
+ method(:thread_create),
33
+ method(:thread_current_running?),
34
+ @log
35
+ )
36
+ Fluent::Plugin::Prometheus.start_reset_threads(
37
+ @metrics,
38
+ @registry,
39
+ method(:thread_create),
40
+ method(:thread_current_running?),
41
+ @log
42
+ )
43
+ end
44
+
45
+ def process(tag, es)
46
+ instrument(tag, es, @metrics)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,103 @@
1
+ # The default Prometheus client data store has no means of removing values.
2
+ # For the "retention" feature we need to be able to remove metrics with specific labels after some time of inactivity.
3
+ # By patching the Metric class and using our own DataStore we implement that missing feature.
4
+ module Prometheus
5
+ module Client
6
+ class Metric
7
+ def remove(labels)
8
+ label_set = label_set_for(labels)
9
+ @store.remove(labels: label_set)
10
+ end
11
+
12
+ def reset_values
13
+ @store.reset_values
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ module Fluent
20
+ module Plugin
21
+ module Prometheus
22
+ # Stores all the data in simple hashes, one per metric. Each of these metrics
23
+ # synchronizes access to their hash, but multiple metrics can run observations
24
+ # concurrently.
25
+ class DataStore
26
+ class InvalidStoreSettingsError < StandardError; end
27
+ DEFAULT_METRIC_SETTINGS = { topk: 0 }
28
+
29
+ def for_metric(metric_name, metric_type:, metric_settings: {})
30
+ settings = DEFAULT_METRIC_SETTINGS.merge(metric_settings)
31
+ validate_metric_settings(metric_settings: settings)
32
+ MetricStore.new(metric_settings: settings)
33
+ end
34
+
35
+ private
36
+
37
+ def validate_metric_settings(metric_settings:)
38
+ unless metric_settings.has_key?(:topk) &&
39
+ (metric_settings[:topk].is_a? Integer) &&
40
+ metric_settings[:topk] >= 0
41
+ raise InvalidStoreSettingsError,
42
+ "Metrics need a valid :topk key"
43
+ end
44
+ end
45
+
46
+ class MetricStore
47
+ def initialize(metric_settings:)
48
+ @internal_store = Hash.new { |hash, key| hash[key] = 0.0 }
49
+ @topk = metric_settings[:topk]
50
+ @lock = Monitor.new
51
+ end
52
+
53
+ def synchronize
54
+ @lock.synchronize { yield }
55
+ end
56
+
57
+ def set(labels:, val:)
58
+ synchronize do
59
+ @internal_store[labels] = val.to_f
60
+ end
61
+ end
62
+
63
+ def increment(labels:, by: 1)
64
+ synchronize do
65
+ @internal_store[labels] += by
66
+ end
67
+ end
68
+
69
+ def get(labels:)
70
+ synchronize do
71
+ @internal_store[labels]
72
+ end
73
+ end
74
+
75
+ def remove(labels:)
76
+ synchronize do
77
+ @internal_store.delete(labels)
78
+ end
79
+ end
80
+
81
+ def all_values
82
+ synchronize do
83
+ store = @internal_store.dup
84
+ if @topk > 0
85
+ store.sort_by { |_, value| -value }.first(@topk).to_h
86
+ else
87
+ store
88
+ end
89
+ end
90
+ end
91
+
92
+ def reset_values
93
+ synchronize do
94
+ @internal_store.transform_values! { |_| 0.0 }
95
+ end
96
+ end
97
+ end
98
+
99
+ private_constant :MetricStore
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,132 @@
1
+ module Fluent
2
+ module Plugin
3
+ module Prometheus
4
+ class ExpandBuilder
5
+ def self.build(placeholder, log:)
6
+ new(log: log).build(placeholder)
7
+ end
8
+
9
+ def initialize(log:)
10
+ @log = log
11
+ end
12
+
13
+ def build(placeholder_values)
14
+ placeholders = {}
15
+ placeholder_values.each do |key, value|
16
+ case value
17
+ when Array
18
+ size = value.size
19
+ value.each_with_index do |v, i|
20
+ placeholders["${#{key}[#{i}]}"] = v
21
+ placeholders["${#{key}[#{i - size}]}"] = v
22
+ end
23
+ when Hash
24
+ value.each do |k, v|
25
+ placeholders[%(${#{key}["#{k}"]})] = v
26
+ end
27
+ else
28
+ if key == 'tag'
29
+ placeholders.merge!(build_tag(value))
30
+ else
31
+ placeholders["${#{key}}"] = value
32
+ end
33
+ end
34
+ end
35
+
36
+ Fluent::Plugin::Prometheus::ExpandBuilder::PlaceholderExpander.new(@log, placeholders)
37
+ end
38
+
39
+ private
40
+
41
+ def build_tag(tag)
42
+ tags = tag.split('.')
43
+
44
+ placeholders = { '${tag}' => tag }
45
+
46
+ size = tags.size
47
+
48
+ tags.each_with_index do |v, i|
49
+ placeholders["${tag_parts[#{i}]}"] = v
50
+ placeholders["${tag_parts[#{i - size}]}"] = v
51
+ end
52
+
53
+ tag_prefix(tags).each_with_index do |v, i|
54
+ placeholders["${tag_prefix[#{i}]}"] = v
55
+ end
56
+
57
+ tag_suffix(tags).each_with_index do |v, i|
58
+ placeholders["${tag_suffix[#{i}]}"] = v
59
+ end
60
+
61
+ placeholders
62
+ end
63
+
64
+ def tag_prefix(tags)
65
+ tags = tags.dup
66
+ return [] if tags.empty?
67
+
68
+ ret = [tags.shift]
69
+ tags.each.with_index(1) do |tag, i|
70
+ ret[i] = "#{ret[i-1]}.#{tag}"
71
+ end
72
+ ret
73
+ end
74
+
75
+ def tag_suffix(tags)
76
+ return [] if tags.empty?
77
+
78
+ tags = tags.dup.reverse
79
+ ret = [tags.shift]
80
+ tags.each.with_index(1) do |tag, i|
81
+ ret[i] = "#{tag}.#{ret[i-1]}"
82
+ end
83
+ ret
84
+ end
85
+
86
+ class PlaceholderExpander
87
+ PLACEHOLDER_REGEX = /(\${[^\[}]+(\[[^\]]+\])?})/.freeze
88
+
89
+ attr_reader :placeholder
90
+
91
+ def initialize(log, placeholder)
92
+ @placeholder = placeholder
93
+ @log = log
94
+ @expander_cache = {}
95
+ end
96
+
97
+ def merge_placeholder(placeholder)
98
+ @placeholder.merge!(placeholder)
99
+ end
100
+
101
+ def expand(str, dynamic_placeholders: nil)
102
+ expander = if dynamic_placeholders
103
+ if @expander_cache[dynamic_placeholders]
104
+ @expander_cache[dynamic_placeholders]
105
+ else
106
+ e = ExpandBuilder.build(dynamic_placeholders, log: @log)
107
+ e.merge_placeholder(@placeholder)
108
+ @expander_cache[dynamic_placeholders] = e
109
+ e
110
+ end
111
+ else
112
+ self
113
+ end
114
+
115
+ expander.expand!(str)
116
+ end
117
+
118
+ protected
119
+
120
+ def expand!(str)
121
+ str.gsub(PLACEHOLDER_REGEX) { |value|
122
+ @placeholder.fetch(value) do
123
+ @log.warn("unknown placeholder `#{value}` found")
124
+ value # return as it is
125
+ end
126
+ }
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end