fluent-plugin-prometheus 1.6.0 → 1.7.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f079822fa7231b77912e5ac1324b691f38dbf1f1f32fd8700dee19bc636fd8c
4
- data.tar.gz: 58ad623a333cc1600e971fdd35b9b2c7d4a381c03a954d99206233758e4562f6
3
+ metadata.gz: 1f14f2b2162bace2a18a80f885df28261ed260a7b8e865a12e7828d46af9b02f
4
+ data.tar.gz: 20749cd6ee743ac3726719e654e1a4f046aa2f953245604e6aa75cd87ed00e62
5
5
  SHA512:
6
- metadata.gz: 4394983e398e9e4b116f54927b7ad82778ab672b27e41bfc1ef3eb5421f3f3cde7899ce906d86de8862179c0293c1831c0a555553f21c10787dddba22c68fe1e
7
- data.tar.gz: f8d52d61a493f4c8abfc88a1cfdc415779a4946eeed639b03545574b2a86304d7a560c026391897ab93596280be306a02c24d3dcdbc161c30f625c211d237711
6
+ metadata.gz: 28ca37140a4b634665a9ba184c1615380bc7c458ed745d5548cb75b92d75d3b1069cd342d3119785a11d1beb369e7b41b5fa0319021bb0f69400ffe327dea83e
7
+ data.tar.gz: b04770fec96856fc37b6943c7129c5e8ef16ce8b1415b425932e4419594e58a3255406de54e3aad1c28af1e3d62074391bd2b40e4aded8b5f598fbece53cab82
data/README.md CHANGED
@@ -369,7 +369,13 @@ Reserved placeholders are:
369
369
  - `${worker_id}`: fluent worker id
370
370
  - `${tag}`: tag name
371
371
  - only available in Prometheus output/filter plugin
372
-
372
+ - `${tag_parts[N]}` refers to the Nth part of the tag.
373
+ - only available in Prometheus output/filter plugin
374
+ - `${tag_prefix[N]}` refers to the [0..N] part of the tag.
375
+ - only available in Prometheus output/filter plugin
376
+ - `${tag_suffix[N]}` refers to the [`tagsize`-1-N..] part of the tag.
377
+ - where `tagsize` is the size of tag which is splitted with `.` (when tag is `1.2.3`, then `tagsize` is 3)
378
+ - only available in Prometheus output/filter plugin
373
379
 
374
380
  ### top-level labels and labels inside metric
375
381
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "fluent-plugin-prometheus"
3
- spec.version = "1.6.0"
3
+ spec.version = "1.7.3"
4
4
  spec.authors = ["Masahiro Sano"]
5
5
  spec.email = ["sabottenda@gmail.com"]
6
6
  spec.summary = %q{A fluent plugin that collects metrics and exposes for Prometheus.}
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.require_paths = ["lib"]
15
15
 
16
16
  spec.add_dependency "fluentd", ">= 0.14.20", "< 2"
17
- spec.add_dependency "prometheus-client"
17
+ spec.add_dependency "prometheus-client", "< 0.10"
18
18
  spec.add_development_dependency "bundler"
19
19
  spec.add_development_dependency "rake"
20
20
  spec.add_development_dependency "rspec"
@@ -22,9 +22,9 @@ module Fluent::Plugin
22
22
  @metrics = Fluent::Plugin::Prometheus.parse_metrics_elements(conf, @registry, labels)
23
23
  end
24
24
 
25
- def filter_stream(tag, es)
26
- instrument(tag, es, @metrics)
27
- es
25
+ def filter(tag, time, record)
26
+ instrument_single(tag, time, record, @metrics)
27
+ record
28
28
  end
29
29
  end
30
30
  end
@@ -24,14 +24,14 @@ module Fluent::Plugin
24
24
  def configure(conf)
25
25
  super
26
26
  hostname = Socket.gethostname
27
- expander = Fluent::Plugin::Prometheus.placeholder_expander(log)
28
- placeholders = expander.prepare_placeholders({'hostname' => hostname, 'worker_id' => fluentd_worker_id})
27
+ expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
28
+ expander = expander_builder.build({ 'hostname' => hostname, 'worker_id' => fluentd_worker_id })
29
29
  @base_labels = parse_labels_elements(conf)
30
30
  @base_labels.each do |key, value|
31
31
  unless value.is_a?(String)
32
32
  raise Fluent::ConfigError, "record accessor syntax is not available in prometheus_monitor"
33
33
  end
34
- @base_labels[key] = expander.expand(value, placeholders)
34
+ @base_labels[key] = expander.expand(value)
35
35
  end
36
36
 
37
37
  if defined?(Fluent::Plugin) && defined?(Fluent::Plugin::MonitorAgentInput)
@@ -43,14 +43,14 @@ module Fluent::Plugin
43
43
  def configure(conf)
44
44
  super
45
45
  hostname = Socket.gethostname
46
- expander = Fluent::Plugin::Prometheus.placeholder_expander(log)
47
- placeholders = expander.prepare_placeholders({'hostname' => hostname, 'worker_id' => fluentd_worker_id})
46
+ expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
47
+ expander = expander_builder.build({ 'hostname' => hostname, 'worker_id' => fluentd_worker_id })
48
48
  @base_labels = parse_labels_elements(conf)
49
49
  @base_labels.each do |key, value|
50
50
  unless value.is_a?(String)
51
51
  raise Fluent::ConfigError, "record accessor syntax is not available in prometheus_output_monitor"
52
52
  end
53
- @base_labels[key] = expander.expand(value, placeholders)
53
+ @base_labels[key] = expander.expand(value)
54
54
  end
55
55
 
56
56
  if defined?(Fluent::Plugin) && defined?(Fluent::Plugin::MonitorAgentInput)
@@ -28,14 +28,14 @@ module Fluent::Plugin
28
28
  def configure(conf)
29
29
  super
30
30
  hostname = Socket.gethostname
31
- expander = Fluent::Plugin::Prometheus.placeholder_expander(log)
32
- placeholders = expander.prepare_placeholders({'hostname' => hostname, 'worker_id' => fluentd_worker_id})
31
+ expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
32
+ expander = expander_builder.build({ 'hostname' => hostname, 'worker_id' => fluentd_worker_id })
33
33
  @base_labels = parse_labels_elements(conf)
34
34
  @base_labels.each do |key, value|
35
35
  unless value.is_a?(String)
36
36
  raise Fluent::ConfigError, "record accessor syntax is not available in prometheus_tail_monitor"
37
37
  end
38
- @base_labels[key] = expander.expand(value, placeholders)
38
+ @base_labels[key] = expander.expand(value)
39
39
  end
40
40
 
41
41
  if defined?(Fluent::Plugin) && defined?(Fluent::Plugin::MonitorAgentInput)
@@ -1,6 +1,6 @@
1
1
  require 'prometheus/client'
2
2
  require 'prometheus/client/formats/text'
3
- require 'fluent/plugin/filter_record_transformer'
3
+ require 'fluent/plugin/prometheus/placeholder_expander'
4
4
 
5
5
  module Fluent
6
6
  module Plugin
@@ -82,16 +82,35 @@ module Fluent
82
82
  end
83
83
 
84
84
  def self.placeholder_expander(log)
85
- # Use internal class in order to expand placeholder
86
- Fluent::Plugin::RecordTransformerFilter::PlaceholderExpander.new(log: log)
85
+ Fluent::Plugin::Prometheus::ExpandBuilder.new(log: log)
87
86
  end
88
87
 
89
88
  def configure(conf)
90
89
  super
91
- @placeholder_expander = Fluent::Plugin::Prometheus.placeholder_expander(log)
90
+ @placeholder_values = {}
91
+ @placeholder_expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
92
92
  @hostname = Socket.gethostname
93
93
  end
94
94
 
95
+ def instrument_single(tag, time, record, metrics)
96
+ @placeholder_values[tag] ||= {
97
+ 'tag' => tag,
98
+ 'hostname' => @hostname,
99
+ 'worker_id' => fluentd_worker_id,
100
+ }
101
+
102
+ placeholders = record.merge(@placeholder_values[tag])
103
+ expander = @placeholder_expander_builder.build(placeholders)
104
+ metrics.each do |metric|
105
+ begin
106
+ metric.instrument(record, expander)
107
+ rescue => e
108
+ log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
109
+ router.emit_error_event(tag, time, record, e)
110
+ end
111
+ end
112
+ end
113
+
95
114
  def instrument(tag, es, metrics)
96
115
  placeholder_values = {
97
116
  'tag' => tag,
@@ -101,10 +120,10 @@ module Fluent
101
120
 
102
121
  es.each do |time, record|
103
122
  placeholders = record.merge(placeholder_values)
104
- placeholders = @placeholder_expander.prepare_placeholders(placeholders)
123
+ expander = @placeholder_expander_builder.build(placeholders)
105
124
  metrics.each do |metric|
106
125
  begin
107
- metric.instrument(record, @placeholder_expander, placeholders)
126
+ metric.instrument(record, expander)
108
127
  rescue => e
109
128
  log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
110
129
  router.emit_error_event(tag, time, record, e)
@@ -134,11 +153,11 @@ module Fluent
134
153
  @base_labels = labels.merge(@base_labels)
135
154
  end
136
155
 
137
- def labels(record, expander, placeholders)
156
+ def labels(record, expander)
138
157
  label = {}
139
158
  @base_labels.each do |k, v|
140
159
  if v.is_a?(String)
141
- label[k] = expander.expand(v, placeholders)
160
+ label[k] = expander.expand(v)
142
161
  else
143
162
  label[k] = v.call(record)
144
163
  end
@@ -175,14 +194,14 @@ module Fluent
175
194
  end
176
195
  end
177
196
 
178
- def instrument(record, expander, placeholders)
197
+ def instrument(record, expander)
179
198
  if @key.is_a?(String)
180
199
  value = record[@key]
181
200
  else
182
201
  value = @key.call(record)
183
202
  end
184
203
  if value
185
- @gauge.set(labels(record, expander, placeholders), value)
204
+ @gauge.set(labels(record, expander), value)
186
205
  end
187
206
  end
188
207
  end
@@ -197,7 +216,7 @@ module Fluent
197
216
  end
198
217
  end
199
218
 
200
- def instrument(record, expander, placeholders)
219
+ def instrument(record, expander)
201
220
  # use record value of the key if key is specified, otherwise just increment
202
221
  if @key.nil?
203
222
  value = 1
@@ -210,7 +229,7 @@ module Fluent
210
229
  # ignore if record value is nil
211
230
  return if value.nil?
212
231
 
213
- @counter.increment(labels(record, expander, placeholders), value)
232
+ @counter.increment(labels(record, expander), value)
214
233
  end
215
234
  end
216
235
 
@@ -228,14 +247,14 @@ module Fluent
228
247
  end
229
248
  end
230
249
 
231
- def instrument(record, expander, placeholders)
250
+ def instrument(record, expander)
232
251
  if @key.is_a?(String)
233
252
  value = record[@key]
234
253
  else
235
254
  value = @key.call(record)
236
255
  end
237
256
  if value
238
- @summary.observe(labels(record, expander, placeholders), value)
257
+ @summary.observe(labels(record, expander), value)
239
258
  end
240
259
  end
241
260
  end
@@ -261,14 +280,14 @@ module Fluent
261
280
  end
262
281
  end
263
282
 
264
- def instrument(record, expander, placeholders)
283
+ def instrument(record, expander)
265
284
  if @key.is_a?(String)
266
285
  value = record[@key]
267
286
  else
268
287
  value = @key.call(record)
269
288
  end
270
289
  if value
271
- @histogram.observe(labels(record, expander, placeholders), value)
290
+ @histogram.observe(labels(record, expander), value)
272
291
  end
273
292
  end
274
293
  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
@@ -6,7 +6,11 @@ require_relative 'shared'
6
6
  describe Fluent::Plugin::PrometheusFilter do
7
7
  let(:tag) { 'prometheus.test' }
8
8
  let(:driver) { Fluent::Test::Driver::Filter.new(Fluent::Plugin::PrometheusFilter).configure(config) }
9
- let(:registry) { ::Prometheus::Client.registry }
9
+ let(:registry) { ::Prometheus::Client::Registry.new }
10
+
11
+ before do
12
+ allow(Prometheus::Client).to receive(:registry).and_return(registry)
13
+ end
10
14
 
11
15
  describe '#configure' do
12
16
  it_behaves_like 'output configuration'
@@ -14,22 +18,28 @@ describe Fluent::Plugin::PrometheusFilter do
14
18
 
15
19
  describe '#run' do
16
20
  let(:message) { {"foo" => 100, "bar" => 100, "baz" => 100, "qux" => 10} }
17
- let(:es) {
18
- driver.run(default_tag: tag) { driver.feed(event_time, message) }
19
- driver.filtered_records
20
- }
21
21
 
22
22
  context 'simple config' do
23
- include_context 'simple_config'
23
+ let(:config) {
24
+ BASE_CONFIG + %(
25
+ <metric>
26
+ name simple
27
+ type counter
28
+ desc Something foo.
29
+ key foo
30
+ </metric>
31
+ )
32
+ }
24
33
 
25
34
  it 'adds a new counter metric' do
26
- expect(registry.metrics.map(&:name)).not_to include(name)
27
- es
28
- expect(registry.metrics.map(&:name)).to include(name)
35
+ expect(registry.metrics.map(&:name)).not_to eq([:simple])
36
+ driver.run(default_tag: tag) { driver.feed(event_time, message) }
37
+ expect(registry.metrics.map(&:name)).to eq([:simple])
29
38
  end
30
39
 
31
40
  it 'should keep original message' do
32
- expect(es.first).to eq(message)
41
+ driver.run(default_tag: tag) { driver.feed(event_time, message) }
42
+ expect(driver.filtered_records.first).to eq(message)
33
43
  end
34
44
  end
35
45
 
@@ -23,7 +23,6 @@ describe Fluent::Plugin::PrometheusMonitorInput do
23
23
  ]
24
24
 
25
25
  let(:config) { MONITOR_CONFIG }
26
- let(:port) { 24231 }
27
26
  let(:driver) { Fluent::Test::Driver::Input.new(Fluent::Plugin::PrometheusMonitorInput).configure(config) }
28
27
 
29
28
  describe '#configure' do
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'fluent/plugin/in_prometheus_tail_monitor'
3
+ require 'fluent/test/driver/input'
4
+
5
+ describe Fluent::Plugin::PrometheusTailMonitorInput do
6
+ MONITOR_CONFIG = %[
7
+ @type prometheus_tail_monitor
8
+ <labels>
9
+ host ${hostname}
10
+ foo bar
11
+ </labels>
12
+ ]
13
+
14
+ INVALID_MONITOR_CONFIG = %[
15
+ @type prometheus_tail_monitor
16
+
17
+ <labels>
18
+ host ${hostname}
19
+ foo bar
20
+ invalid_use1 $.foo.bar
21
+ invalid_use2 $[0][1]
22
+ </labels>
23
+ ]
24
+
25
+ let(:config) { MONITOR_CONFIG }
26
+ let(:driver) { Fluent::Test::Driver::Input.new(Fluent::Plugin::PrometheusTailMonitorInput).configure(config) }
27
+
28
+ describe '#configure' do
29
+ describe 'valid' do
30
+ it 'does not raise error' do
31
+ expect { driver }.not_to raise_error
32
+ end
33
+ end
34
+
35
+ describe 'invalid' do
36
+ let(:config) { INVALID_MONITOR_CONFIG }
37
+ it 'expect raise error' do
38
+ expect { driver }.to raise_error
39
+ end
40
+ end
41
+ end
42
+ end
@@ -6,7 +6,11 @@ require_relative 'shared'
6
6
  describe Fluent::Plugin::PrometheusOutput do
7
7
  let(:tag) { 'prometheus.test' }
8
8
  let(:driver) { Fluent::Test::Driver::Output.new(Fluent::Plugin::PrometheusOutput).configure(config) }
9
- let(:registry) { ::Prometheus::Client.registry }
9
+ let(:registry) { ::Prometheus::Client::Registry.new }
10
+
11
+ before do
12
+ allow(Prometheus::Client).to receive(:registry).and_return(registry)
13
+ end
10
14
 
11
15
  describe '#configure' do
12
16
  it_behaves_like 'output configuration'
@@ -14,18 +18,23 @@ describe Fluent::Plugin::PrometheusOutput do
14
18
 
15
19
  describe '#run' do
16
20
  let(:message) { {"foo" => 100, "bar" => 100, "baz" => 100, "qux" => 10} }
17
- let(:es) {
18
- driver.run(default_tag: tag) { driver.feed(event_time, message) }
19
- driver.events
20
- }
21
21
 
22
22
  context 'simple config' do
23
- include_context 'simple_config'
23
+ let(:config) {
24
+ BASE_CONFIG + %(
25
+ <metric>
26
+ name simple
27
+ type counter
28
+ desc Something foo.
29
+ key foo
30
+ </metric>
31
+ )
32
+ }
24
33
 
25
34
  it 'adds a new counter metric' do
26
- expect(registry.metrics.map(&:name)).not_to include(name)
27
- es
28
- expect(registry.metrics.map(&:name)).to include(name)
35
+ expect(registry.metrics.map(&:name)).not_to eq([:simple])
36
+ driver.run(default_tag: tag) { driver.feed(event_time, message) }
37
+ expect(registry.metrics.map(&:name)).to eq([:simple])
29
38
  end
30
39
  end
31
40
 
@@ -0,0 +1,110 @@
1
+ require 'logger'
2
+
3
+ require 'spec_helper'
4
+ require 'fluent/plugin/prometheus/placeholder_expander'
5
+ require_relative '../shared'
6
+
7
+ describe Fluent::Plugin::Prometheus::ExpandBuilder::PlaceholderExpander do
8
+ let(:log) do
9
+ Logger.new('/dev/null')
10
+ end
11
+
12
+ let(:builder) do
13
+ Fluent::Plugin::Prometheus::ExpandBuilder.new(log: log)
14
+ end
15
+
16
+ describe '#expand' do
17
+ context 'with static placeholder' do
18
+ let(:static_placeholder) do
19
+ {
20
+ 'hostname' => 'host_value',
21
+ 'tag' => '1.2.3',
22
+ 'ary_value' => ['1', '2', '3'],
23
+ 'hash_value' => { 'key1' => 'val1' },
24
+ }
25
+ end
26
+
27
+ let(:dynamic_placeholder) do
28
+ end
29
+
30
+ it 'expands values' do
31
+ expander = builder.build(static_placeholder)
32
+ expect(expander.expand('${hostname}')).to eq('host_value')
33
+ expect(expander.expand('${ary_value[0]}.${ary_value[1]}.${ary_value[2]}')).to eq('1.2.3')
34
+ expect(expander.expand('${ary_value[-3]}.${ary_value[-2]}.${ary_value[-1]}')).to eq('1.2.3')
35
+ expect(expander.expand('${hash_value["key1"]}')).to eq('val1')
36
+
37
+ expect(expander.expand('${tag}')).to eq('1.2.3')
38
+ expect(expander.expand('${tag_parts[0]}.${tag_parts[1]}.${tag_parts[2]}')).to eq('1.2.3')
39
+ expect(expander.expand('${tag_parts[-3]}.${tag_parts[-2]}.${tag_parts[-1]}')).to eq('1.2.3')
40
+ expect(expander.expand('${tag_prefix[0]}.${tag_prefix[1]}.${tag_prefix[2]}')).to eq('1.1.2.1.2.3')
41
+ expect(expander.expand('${tag_suffix[0]}.${tag_suffix[1]}.${tag_suffix[2]}')).to eq('3.2.3.1.2.3')
42
+ end
43
+
44
+ it 'does not create new expander' do
45
+ builder # cached before mock
46
+
47
+ expect(Fluent::Plugin::Prometheus::ExpandBuilder).to receive(:build).with(anything, log: anything).never
48
+ expander = builder.build(static_placeholder)
49
+ expander.expand('${hostname}')
50
+ expander.expand('${hostname}')
51
+ end
52
+
53
+ context 'when not found placeholder' do
54
+ it 'prints wanring log and as it is' do
55
+ expect(log).to receive(:warn).with('unknown placeholder `${tag_prefix[100]}` found').once
56
+
57
+ expander = builder.build(static_placeholder)
58
+ expect(expander.expand('${tag_prefix[100]}')).to eq('${tag_prefix[100]}')
59
+ end
60
+ end
61
+ end
62
+
63
+ context 'with dynamic placeholder' do
64
+ let(:static_placeholder) do
65
+ {
66
+ 'hostname' => 'host_value',
67
+ 'ary_value' => ['1', '2', '3'],
68
+ 'hash_value' => { 'key1' => 'val1' },
69
+ }
70
+ end
71
+
72
+ let(:dynamic_placeholder) do
73
+ { 'tag' => '1.2.3'}
74
+ end
75
+
76
+ it 'expands values' do
77
+ expander = builder.build(static_placeholder)
78
+ expect(expander.expand('${hostname}', dynamic_placeholders: dynamic_placeholder)).to eq('host_value')
79
+ expect(expander.expand('${ary_value[0]}.${ary_value[1]}.${ary_value[2]}', dynamic_placeholders: dynamic_placeholder)).to eq('1.2.3')
80
+ expect(expander.expand('${ary_value[-3]}.${ary_value[-2]}.${ary_value[-1]}', dynamic_placeholders: dynamic_placeholder)).to eq('1.2.3')
81
+ expect(expander.expand('${hash_value["key1"]}', dynamic_placeholders: dynamic_placeholder)).to eq('val1')
82
+
83
+ expect(expander.expand('${tag}', dynamic_placeholders: dynamic_placeholder)).to eq('1.2.3')
84
+ expect(expander.expand('${tag_parts[0]}.${tag_parts[1]}.${tag_parts[2]}', dynamic_placeholders: dynamic_placeholder)).to eq('1.2.3')
85
+ expect(expander.expand('${tag_parts[-3]}.${tag_parts[-2]}.${tag_parts[-1]}', dynamic_placeholders: dynamic_placeholder)).to eq('1.2.3')
86
+ expect(expander.expand('${tag_prefix[0]}.${tag_prefix[1]}.${tag_prefix[2]}', dynamic_placeholders: dynamic_placeholder)).to eq('1.1.2.1.2.3')
87
+ expect(expander.expand('${tag_suffix[0]}.${tag_suffix[1]}.${tag_suffix[2]}', dynamic_placeholders: dynamic_placeholder)).to eq('3.2.3.1.2.3')
88
+ end
89
+
90
+ it 'does not create expander twice if given the same placeholder' do
91
+ builder # cached before mock
92
+
93
+ expect(Fluent::Plugin::Prometheus::ExpandBuilder).to receive(:build).with(anything, log: anything).once.and_call_original
94
+ expander = builder.build(static_placeholder)
95
+ placeholder = { 'tag' => 'val.test' }
96
+ expander.expand('${hostname}', dynamic_placeholders: placeholder)
97
+ expander.expand('${hostname}', dynamic_placeholders: placeholder)
98
+ end
99
+
100
+ it 'creates new expander for each placeholder' do
101
+ builder # cached before mock
102
+
103
+ expect(Fluent::Plugin::Prometheus::ExpandBuilder).to receive(:build).with(anything, log: anything).twice.and_call_original
104
+ expander = builder.build(static_placeholder)
105
+ expander.expand('${hostname}', dynamic_placeholders: { 'tag' => 'val.test' })
106
+ expander.expand('${hostname}', dynamic_placeholders: { 'tag' => 'val.test2' })
107
+ end
108
+ end
109
+ end
110
+ end
@@ -3,9 +3,7 @@ BASE_CONFIG = %[
3
3
  type prometheus
4
4
  ]
5
5
 
6
-
7
6
  SIMPLE_CONFIG = BASE_CONFIG + %[
8
- type prometheus
9
7
  <metric>
10
8
  name simple_foo
11
9
  type counter
@@ -111,109 +109,65 @@ COUNTER_WITHOUT_KEY_CONFIG = BASE_CONFIG + %[
111
109
  </metric>
112
110
  ]
113
111
 
114
- def gen_time_suffix
115
- return Time.now.to_f.to_s.gsub('.', '')
116
- end
117
-
118
- shared_context 'simple_config' do
119
- let(:orig_name) { 'simple_foo' }
120
- let(:config) { SIMPLE_CONFIG.gsub(orig_name, name.to_s) }
121
- let(:name) { "#{orig_name}_#{gen_time_suffix}".to_sym }
122
- let(:counter) { registry.get(name) }
123
- end
124
-
125
- shared_context 'full_config' do
126
- let(:config) { FULL_CONFIG }
127
- let(:counter) { registry.get(:full_foo) }
128
- let(:gauge) { registry.get(:full_bar) }
129
- let(:summary) { registry.get(:full_baz) }
130
- let(:histogram) { registry.get(:full_qux) }
131
- let(:summary_with_accessor) { registry.get(:full_accessor1) }
132
- let(:counter_with_accessor) { registry.get(:full_accessor2) }
133
- end
134
-
135
- shared_context 'placeholder_config' do
136
- let(:orig_name) { 'placeholder_foo' }
137
- let(:config) { PLACEHOLDER_CONFIG.gsub(orig_name, name.to_s) }
138
- let(:name) { "#{orig_name}_#{gen_time_suffix}".to_sym }
139
- let(:counter) { registry.get(name) }
140
- end
141
-
142
- shared_context 'accessor_config' do
143
- let(:orig_name) { 'accessor_foo' }
144
- let(:config) { ACCESSOR_CONFIG.gsub(orig_name, name.to_s) }
145
- let(:name) { "#{orig_name}_#{gen_time_suffix}".to_sym }
146
- let(:counter) { registry.get(name) }
147
- end
148
-
149
- shared_context 'counter_without_key_config' do
150
- let(:orig_name) { 'without_key_foo' }
151
- let(:config) { COUNTER_WITHOUT_KEY_CONFIG.gsub(orig_name, name.to_s) }
152
- let(:name) { "#{orig_name}_#{gen_time_suffix}".to_sym }
153
- let(:counter) { registry.get(name) }
154
- end
155
-
156
112
  shared_examples_for 'output configuration' do
157
113
  context 'base config' do
158
114
  let(:config) { BASE_CONFIG }
159
- it 'does not raise error' do
160
- expect{driver}.not_to raise_error
161
- end
115
+ it { expect { driver }.not_to raise_error }
162
116
  end
163
117
 
164
- describe 'configure simple configuration' do
165
- include_context 'simple_config'
166
- it { expect{driver}.not_to raise_error }
118
+ context 'with simple configuration' do
119
+ let(:config) { SIMPLE_CONFIG }
120
+ it { expect { driver }.not_to raise_error }
167
121
  end
168
122
 
169
- describe 'configure full configuration' do
170
- include_context 'full_config'
171
- it { expect{driver}.not_to raise_error }
123
+ context 'with full configuration' do
124
+ let(:config) { FULL_CONFIG }
125
+ it { expect { driver }.not_to raise_error }
172
126
  end
173
127
 
174
- describe 'configure placeholder configuration' do
175
- include_context 'placeholder_config'
176
- it { expect{driver}.not_to raise_error }
128
+ context 'with placeholder configuration' do
129
+ let(:config) { PLACEHOLDER_CONFIG }
130
+ it { expect { driver }.not_to raise_error }
177
131
  end
178
132
 
179
- describe 'configure accessor configuration' do
180
- include_context 'accessor_config'
181
- it { expect{driver}.not_to raise_error }
133
+ context 'with accessor configuration' do
134
+ let(:config) { ACCESSOR_CONFIG }
135
+ it { expect { driver }.not_to raise_error }
182
136
  end
183
137
 
184
- describe 'configure counter without key configuration' do
185
- include_context 'counter_without_key_config'
186
- it { expect{driver}.not_to raise_error }
138
+ describe 'with counter without key configuration' do
139
+ let(:config) { COUNTER_WITHOUT_KEY_CONFIG }
140
+ it { expect { driver }.not_to raise_error }
187
141
  end
188
142
 
189
- context 'unknown type' do
190
- let(:config) { BASE_CONFIG + %[
191
- <metric>
192
- type foo
193
- </metric>
194
- ] }
195
- it 'raises ConfigError' do
196
- expect{driver}.to raise_error Fluent::ConfigError
143
+ context 'with unknown type' do
144
+ let(:config) do
145
+ BASE_CONFIG + %[
146
+ <metric>
147
+ type foo
148
+ </metric>
149
+ ]
197
150
  end
151
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
198
152
  end
199
153
  end
200
154
 
201
- emit_count = 0
202
155
  shared_examples_for 'instruments record' do
203
- context 'full config' do
204
- include_context 'full_config'
156
+ before do
157
+ driver.run(default_tag: tag) { driver.feed(event_time, message) }
158
+ end
205
159
 
206
- before :each do
207
- es
208
- emit_count += 1
209
- end
160
+ context 'full config' do
161
+ let(:config) { FULL_CONFIG }
162
+ let(:counter) { registry.get(:full_foo) }
163
+ let(:gauge) { registry.get(:full_bar) }
164
+ let(:summary) { registry.get(:full_baz) }
165
+ let(:histogram) { registry.get(:full_qux) }
166
+ let(:summary_with_accessor) { registry.get(:full_accessor1) }
167
+ let(:counter_with_accessor) { registry.get(:full_accessor2) }
210
168
 
211
169
  it 'adds all metrics' do
212
- expect(registry.metrics.map(&:name)).to include(:full_foo)
213
- expect(registry.metrics.map(&:name)).to include(:full_bar)
214
- expect(registry.metrics.map(&:name)).to include(:full_baz)
215
- expect(registry.metrics.map(&:name)).to include(:full_accessor1)
216
- expect(registry.metrics.map(&:name)).to include(:full_accessor2)
170
+ expect(registry.metrics.map(&:name)).to eq(%i[full_foo full_bar full_baz full_qux full_accessor1 full_accessor2])
217
171
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
218
172
  expect(gauge).to be_kind_of(::Prometheus::Client::Metric)
219
173
  expect(summary).to be_kind_of(::Prometheus::Client::Metric)
@@ -241,21 +195,22 @@ shared_examples_for 'instruments record' do
241
195
  end
242
196
 
243
197
  it 'instruments histogram metric' do
198
+ driver.run(default_tag: tag) do
199
+ 4.times { driver.feed(event_time, message) }
200
+ end
201
+
244
202
  expect(histogram.type).to eq(:histogram)
245
203
  expect(histogram.get({test_key: 'test_value', key: 'foo4'})).to be_kind_of(Hash)
246
- expect(histogram.get({test_key: 'test_value', key: 'foo4'})[10]).to eq(emit_count)
204
+ expect(histogram.get({test_key: 'test_value', key: 'foo4'})[10]).to eq(5) # 4 + `es` in before
247
205
  end
248
206
  end
249
207
 
250
208
  context 'placeholder config' do
251
- include_context 'placeholder_config'
252
-
253
- before :each do
254
- es
255
- end
209
+ let(:config) { PLACEHOLDER_CONFIG }
210
+ let(:counter) { registry.get(:placeholder_foo) }
256
211
 
257
212
  it 'expands placeholders with record values' do
258
- expect(registry.metrics.map(&:name)).to include(name)
213
+ expect(registry.metrics.map(&:name)).to eq([:placeholder_foo])
259
214
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
260
215
  key, _ = counter.values.find {|k,v| v == 100 }
261
216
  expect(key).to be_kind_of(Hash)
@@ -268,14 +223,11 @@ shared_examples_for 'instruments record' do
268
223
  end
269
224
 
270
225
  context 'accessor config' do
271
- include_context 'accessor_config'
272
-
273
- before :each do
274
- es
275
- end
226
+ let(:config) { ACCESSOR_CONFIG }
227
+ let(:counter) { registry.get(:accessor_foo) }
276
228
 
277
229
  it 'expands accessor with record values' do
278
- expect(registry.metrics.map(&:name)).to include(name)
230
+ expect(registry.metrics.map(&:name)).to eq([:accessor_foo])
279
231
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
280
232
  key, _ = counter.values.find {|k,v| v == 100 }
281
233
  expect(key).to be_kind_of(Hash)
@@ -284,14 +236,11 @@ shared_examples_for 'instruments record' do
284
236
  end
285
237
 
286
238
  context 'counter_without config' do
287
- include_context 'counter_without_key_config'
288
-
289
- before :each do
290
- es
291
- end
239
+ let(:config) { COUNTER_WITHOUT_KEY_CONFIG }
240
+ let(:counter) { registry.get(:without_key_foo) }
292
241
 
293
242
  it 'just increments by 1' do
294
- expect(registry.metrics.map(&:name)).to include(name)
243
+ expect(registry.metrics.map(&:name)).to eq([:without_key_foo])
295
244
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
296
245
  _, value = counter.values.find {|k,v| k == {} }
297
246
  expect(value).to eq(1)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-prometheus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Sano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-15 00:00:00.000000000 Z
11
+ date: 2020-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -34,16 +34,16 @@ dependencies:
34
34
  name: prometheus-client
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "<"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: '0.10'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: '0.10'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,7 @@ files:
122
122
  - lib/fluent/plugin/in_prometheus_tail_monitor.rb
123
123
  - lib/fluent/plugin/out_prometheus.rb
124
124
  - lib/fluent/plugin/prometheus.rb
125
+ - lib/fluent/plugin/prometheus/placeholder_expander.rb
125
126
  - lib/fluent/plugin/prometheus_metrics.rb
126
127
  - misc/fluentd_sample.conf
127
128
  - misc/nginx_proxy.conf
@@ -129,9 +130,11 @@ files:
129
130
  - misc/prometheus_alerts.yaml
130
131
  - spec/fluent/plugin/filter_prometheus_spec.rb
131
132
  - spec/fluent/plugin/in_prometheus_monitor_spec.rb
133
+ - spec/fluent/plugin/in_prometheus_spec.rb
134
+ - spec/fluent/plugin/in_prometheus_tail_monitor_spec.rb
132
135
  - spec/fluent/plugin/out_prometheus_spec.rb
136
+ - spec/fluent/plugin/prometheus/placeholder_expander_spec.rb
133
137
  - spec/fluent/plugin/prometheus_metrics_spec.rb
134
- - spec/fluent/plugin/prometheus_spec.rb
135
138
  - spec/fluent/plugin/shared.rb
136
139
  - spec/spec_helper.rb
137
140
  homepage: https://github.com/fluent/fluent-plugin-prometheus
@@ -153,16 +156,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
156
  - !ruby/object:Gem::Version
154
157
  version: '0'
155
158
  requirements: []
156
- rubyforge_project:
157
- rubygems_version: 2.7.6
159
+ rubygems_version: 3.0.3
158
160
  signing_key:
159
161
  specification_version: 4
160
162
  summary: A fluent plugin that collects metrics and exposes for Prometheus.
161
163
  test_files:
162
164
  - spec/fluent/plugin/filter_prometheus_spec.rb
163
165
  - spec/fluent/plugin/in_prometheus_monitor_spec.rb
166
+ - spec/fluent/plugin/in_prometheus_spec.rb
167
+ - spec/fluent/plugin/in_prometheus_tail_monitor_spec.rb
164
168
  - spec/fluent/plugin/out_prometheus_spec.rb
169
+ - spec/fluent/plugin/prometheus/placeholder_expander_spec.rb
165
170
  - spec/fluent/plugin/prometheus_metrics_spec.rb
166
- - spec/fluent/plugin/prometheus_spec.rb
167
171
  - spec/fluent/plugin/shared.rb
168
172
  - spec/spec_helper.rb