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

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.
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