fluent-plugin-prometheus 1.7.0 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95008f0031db355e5a5bbf4ed21f8f9f0a07839480e40bf44e974046b04f562f
4
- data.tar.gz: 994a30da7b1ff0ad77f12e5b47d9218a2deb97effc31a36fa83175077cc6ed69
3
+ metadata.gz: 32e8a803f1394e445d273b41ec9bde5f238f4193956c431a0866c93b2a2a0426
4
+ data.tar.gz: 281024e977c3aee02030691ffb7cd86361b759dae259bb8103934db7e195f751
5
5
  SHA512:
6
- metadata.gz: e0dc26e78e242e51e0fa87257b85c68af2ae32c11f7dabd1b4245e3ad7e1b1a7182b1b8432578f98af8a5a22d14c14e664285a238f74ebc40df0c0461026f323
7
- data.tar.gz: fb31a79f327ce31e8203fa1fa9a5b9d9166410d71f9b62d4fd5411d246c768dfde5c9c076ff6e72f0d9f5bd4c7657c9180eb91ba4ee383729856da0704930404
6
+ metadata.gz: 6e784fec1bca9a2ea8f725b5970ed5f08069b6d457ae11b23e1c22017d188046cab4718573c67a94b230acff1583ac12d6378a372a3713f311470c2b9a5818e3
7
+ data.tar.gz: 805673a26e562f4607cefbbf256c12e3557317e115a88096357ddde30f6d5f8dea67835e580100c6bfb5aa9abfb542d0f5b0bff9374b6d4b48046034fea5527a
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.7.0"
3
+ spec.version = "1.7.1"
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.}
@@ -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.buidl({ '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.prepare_placeholders({ '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,14 +82,13 @@ 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
90
  @placeholder_values = {}
92
- @placeholder_expander = Fluent::Plugin::Prometheus.placeholder_expander(log)
91
+ @placeholder_expander_builder = Fluent::Plugin::Prometheus.placeholder_expander(log)
93
92
  @hostname = Socket.gethostname
94
93
  end
95
94
 
@@ -101,10 +100,10 @@ module Fluent
101
100
  }
102
101
 
103
102
  placeholders = record.merge(@placeholder_values[tag])
104
- placeholders = @placeholder_expander.prepare_placeholders(placeholders)
103
+ expander = @placeholder_expander_builder.build(placeholders)
105
104
  metrics.each do |metric|
106
105
  begin
107
- metric.instrument(record, @placeholder_expander, placeholders)
106
+ metric.instrument(record, expander)
108
107
  rescue => e
109
108
  log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
110
109
  router.emit_error_event(tag, time, record, e)
@@ -121,10 +120,10 @@ module Fluent
121
120
 
122
121
  es.each do |time, record|
123
122
  placeholders = record.merge(placeholder_values)
124
- placeholders = @placeholder_expander.prepare_placeholders(placeholders)
123
+ expander = @placeholder_expander_builder.build(placeholders)
125
124
  metrics.each do |metric|
126
125
  begin
127
- metric.instrument(record, @placeholder_expander, placeholders)
126
+ metric.instrument(record, expander)
128
127
  rescue => e
129
128
  log.warn "prometheus: failed to instrument a metric.", error_class: e.class, error: e, tag: tag, name: metric.name
130
129
  router.emit_error_event(tag, time, record, e)
@@ -154,11 +153,11 @@ module Fluent
154
153
  @base_labels = labels.merge(@base_labels)
155
154
  end
156
155
 
157
- def labels(record, expander, placeholders)
156
+ def labels(record, expander)
158
157
  label = {}
159
158
  @base_labels.each do |k, v|
160
159
  if v.is_a?(String)
161
- label[k] = expander.expand(v, placeholders)
160
+ label[k] = expander.expand(v)
162
161
  else
163
162
  label[k] = v.call(record)
164
163
  end
@@ -195,14 +194,14 @@ module Fluent
195
194
  end
196
195
  end
197
196
 
198
- def instrument(record, expander, placeholders)
197
+ def instrument(record, expander)
199
198
  if @key.is_a?(String)
200
199
  value = record[@key]
201
200
  else
202
201
  value = @key.call(record)
203
202
  end
204
203
  if value
205
- @gauge.set(labels(record, expander, placeholders), value)
204
+ @gauge.set(labels(record, expander), value)
206
205
  end
207
206
  end
208
207
  end
@@ -217,7 +216,7 @@ module Fluent
217
216
  end
218
217
  end
219
218
 
220
- def instrument(record, expander, placeholders)
219
+ def instrument(record, expander)
221
220
  # use record value of the key if key is specified, otherwise just increment
222
221
  if @key.nil?
223
222
  value = 1
@@ -230,7 +229,7 @@ module Fluent
230
229
  # ignore if record value is nil
231
230
  return if value.nil?
232
231
 
233
- @counter.increment(labels(record, expander, placeholders), value)
232
+ @counter.increment(labels(record, expander), value)
234
233
  end
235
234
  end
236
235
 
@@ -248,14 +247,14 @@ module Fluent
248
247
  end
249
248
  end
250
249
 
251
- def instrument(record, expander, placeholders)
250
+ def instrument(record, expander)
252
251
  if @key.is_a?(String)
253
252
  value = record[@key]
254
253
  else
255
254
  value = @key.call(record)
256
255
  end
257
256
  if value
258
- @summary.observe(labels(record, expander, placeholders), value)
257
+ @summary.observe(labels(record, expander), value)
259
258
  end
260
259
  end
261
260
  end
@@ -281,14 +280,14 @@ module Fluent
281
280
  end
282
281
  end
283
282
 
284
- def instrument(record, expander, placeholders)
283
+ def instrument(record, expander)
285
284
  if @key.is_a?(String)
286
285
  value = record[@key]
287
286
  else
288
287
  value = @key.call(record)
289
288
  end
290
289
  if value
291
- @histogram.observe(labels(record, expander, placeholders), value)
290
+ @histogram.observe(labels(record, expander), value)
292
291
  end
293
292
  end
294
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
@@ -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.7.0
4
+ version: 1.7.1
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-10-31 00:00:00.000000000 Z
11
+ date: 2020-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -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,10 @@ 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
132
134
  - spec/fluent/plugin/out_prometheus_spec.rb
135
+ - spec/fluent/plugin/prometheus/placeholder_expander_spec.rb
133
136
  - spec/fluent/plugin/prometheus_metrics_spec.rb
134
- - spec/fluent/plugin/prometheus_spec.rb
135
137
  - spec/fluent/plugin/shared.rb
136
138
  - spec/spec_helper.rb
137
139
  homepage: https://github.com/fluent/fluent-plugin-prometheus
@@ -160,8 +162,9 @@ summary: A fluent plugin that collects metrics and exposes for Prometheus.
160
162
  test_files:
161
163
  - spec/fluent/plugin/filter_prometheus_spec.rb
162
164
  - spec/fluent/plugin/in_prometheus_monitor_spec.rb
165
+ - spec/fluent/plugin/in_prometheus_spec.rb
163
166
  - spec/fluent/plugin/out_prometheus_spec.rb
167
+ - spec/fluent/plugin/prometheus/placeholder_expander_spec.rb
164
168
  - spec/fluent/plugin/prometheus_metrics_spec.rb
165
- - spec/fluent/plugin/prometheus_spec.rb
166
169
  - spec/fluent/plugin/shared.rb
167
170
  - spec/spec_helper.rb