fluent-plugin-prometheus 1.8.5 → 2.1.0

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: 5679e5c8d8872be0448075a8c1e05c395b1f9c06a0267f568a788c77dc12dc62
4
- data.tar.gz: 786191b3092cf3745ab97425e4aebb4b1cb524c907433d0f4e091dbb6d14b7f9
3
+ metadata.gz: 366793cd93075c7e1d63cb2641d2654f5eef5304aaa1293bb9e1dabed7d93e2f
4
+ data.tar.gz: 4db5c9fc80dcf10b013239bfdb6e098b53bf26c34a135f944850c70d2a36b7d9
5
5
  SHA512:
6
- metadata.gz: 1b7c49f483ad596641edf01226e4282db10b72dd0117867884d0df4cf6ca0cf23c48e54eab6a2a22ca3ecae845ad8edda6e75d3532853a200b7a57ac8caca423
7
- data.tar.gz: 344b9a363c49067056c4366f46b6b9f3bddf955e22a0155215158b13420027479c0a5e41da1a86b2baaf71e0b01256d90d0a0c8269c322ebe3616c861ce35a98
6
+ metadata.gz: 47de7cabc21f0365b8629714dbd7044e95346d5fb9ce13f7c830a8aeab66bd078769792e6af2ddf52bfc41be3b052f1f1ef912dc71d34c3db0dc94b283832238
7
+ data.tar.gz: 00c98825475166ae5f53e6bd85081766a38aa12b5ecddf079e0f1efcbb0eb729deacf996ee975893897aa2aeca84e35a741d23c2bf7e1f602c75876e08d317ee
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -0,0 +1,30 @@
1
+ name: linux
2
+ on:
3
+ push:
4
+ branches: [master]
5
+ pull_request:
6
+ branches: [master]
7
+ jobs:
8
+ build:
9
+ runs-on: ${{ matrix.os }}
10
+ continue-on-error: ${{ matrix.experimental }}
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ ruby: [ '3.2', '3.1', '3.0', '2.7' ]
15
+ os:
16
+ - ubuntu-latest
17
+ experimental: [false]
18
+ name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v3
21
+ - uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ - name: unit testing
25
+ env:
26
+ CI: true
27
+ run: |
28
+ gem install bundler rake
29
+ bundle install --jobs 4 --retry 3
30
+ bundle exec rake spec
data/ChangeLog CHANGED
@@ -1,3 +1,24 @@
1
+ Release 2.1.0 - 2023/06/15
2
+
3
+ * Add `initialized` and `initlabels` parameters to `<metric>` element
4
+ * in_prometheus_tail_monitor: Add file open/closed/rotation metrics
5
+
6
+ Release 2.0.3 - 2022/05/06
7
+
8
+ * in_prometheus_output_monitor: Fix a bug where output_status_num_errors metric is missing
9
+
10
+ Release 2.0.2 - 2021/08/02
11
+
12
+ * in_prometheus_output_monitor: Follow Fluentd's core metrics mechanism
13
+
14
+ Release 2.0.1 - 2021/04/08
15
+
16
+ * out_prometheus: Allow for lookup by symbol as well as string
17
+
18
+ Release 2.0.0 - 2021/02/18
19
+
20
+ * Update prometheus-client dependency to 2.1.0 or later
21
+
1
22
  Release 1.8.5 - 2020/11/24
2
23
 
3
24
  * in_prometheus_monitor: Support USR2 reload
data/README.md CHANGED
@@ -162,10 +162,11 @@ This plugin uses internal class of Fluentd, so it's easy to break.
162
162
 
163
163
  #### Exposed metrics
164
164
 
165
- - `fluentd_tail_file_position`
166
- - Current bytes which plugin reads from the file
167
- - `fluentd_tail_file_inode`
168
- - inode of the file
165
+ - `fluentd_tail_file_position`: Current bytes which plugin reads from the file
166
+ - `fluentd_tail_file_inode`: inode of the file
167
+ - `fluentd_tail_file_closed`: Number of closed files
168
+ - `fluentd_tail_file_opened`: Number of opened files
169
+ - `fluentd_tail_file_rotated`: Number of rotated files
169
170
 
170
171
  Default labels:
171
172
 
@@ -286,7 +287,9 @@ For details of each metric type, see [Prometheus documentation](http://prometheu
286
287
  - `type`: metric type (required)
287
288
  - `desc`: description of this metric (required)
288
289
  - `key`: key name of record for instrumentation (**optional**)
290
+ - `initialized`: boolean controlling initilization of metric (**optional**). See [Metric initialization](#metric-initialization)
289
291
  - `<labels>`: additional labels for this metric (optional). See [Labels](#labels)
292
+ - `<initlabels>`: labels to use for initialization of ReccordAccessors/Placeholder labels (**optional**). See [Metric initialization](#metric-initialization)
290
293
 
291
294
  If key is empty, the metric values is treated as 1, so the counter increments by 1 on each record regardless of contents of the record.
292
295
 
@@ -310,7 +313,9 @@ If key is empty, the metric values is treated as 1, so the counter increments by
310
313
  - `type`: metric type (required)
311
314
  - `desc`: description of metric (required)
312
315
  - `key`: key name of record for instrumentation (required)
316
+ - `initialized`: boolean controlling initilization of metric (**optional**). See [Metric initialization](#metric-initialization)
313
317
  - `<labels>`: additional labels for this metric (optional). See [Labels](#labels)
318
+ - `<initlabels>`: labels to use for initialization of ReccordAccessors/Placeholder labels (**optional**). See [Metric initialization](#metric-initialization)
314
319
 
315
320
  ### summary type
316
321
 
@@ -332,7 +337,9 @@ If key is empty, the metric values is treated as 1, so the counter increments by
332
337
  - `type`: metric type (required)
333
338
  - `desc`: description of metric (required)
334
339
  - `key`: key name of record for instrumentation (required)
340
+ - `initialized`: boolean controlling initilization of metric (**optional**). See [Metric initialization](#metric-initialization)
335
341
  - `<labels>`: additional labels for this metric (optional). See [Labels](#labels)
342
+ - `<initlabels>`: labels to use for initialization of ReccordAccessors/Placeholder labels (**optional**). See [Metric initialization](#metric-initialization)
336
343
 
337
344
  ### histogram type
338
345
 
@@ -355,8 +362,10 @@ If key is empty, the metric values is treated as 1, so the counter increments by
355
362
  - `type`: metric type (required)
356
363
  - `desc`: description of metric (required)
357
364
  - `key`: key name of record for instrumentation (required)
365
+ - `initialized`: boolean controlling initilization of metric (**optional**). See [Metric initialization](#metric-initialization)
358
366
  - `buckets`: buckets of record for instrumentation (optional)
359
367
  - `<labels>`: additional labels for this metric (optional). See [Labels](#labels)
368
+ - `<initlabels>`: labels to use for initialization of ReccordAccessors/Placeholder labels (**optional**). See [Metric initialization](#metric-initialization)
360
369
 
361
370
  ## Labels
362
371
 
@@ -395,6 +404,53 @@ Reserved placeholders are:
395
404
  - where `tagsize` is the size of tag which is splitted with `.` (when tag is `1.2.3`, then `tagsize` is 3)
396
405
  - only available in Prometheus output/filter plugin
397
406
 
407
+ ### Metric initialization
408
+
409
+ You can configure if a metric should be initialized to its zero value before receiving any event. To do so you just need to specify `initialized true`.
410
+
411
+ ```
412
+ <metric>
413
+ name message_bar_counter
414
+ type counter
415
+ desc The total number of bar in message.
416
+ key bar
417
+ initialized true
418
+ <labels>
419
+ foo bar
420
+ </labels>
421
+ </metric>
422
+ ```
423
+
424
+ If your labels contains ReccordAccessors or Placeholders, you must use `<initlabels>` to specify the values your ReccordAccessors/Placeholders will take. This feature is useful only if your Placeholders/ReccordAccessors contain deterministic values. Initialization will create as many zero value metrics as `<initlabels>` blocks you defined.
425
+ Potential reserved placeholders `${hostname}` and `${worker_id}`, as well as static labels, are automatically added and should not be specified in `<initlabels>` configuration.
426
+
427
+ ```
428
+ <metric>
429
+ name message_bar_counter
430
+ type counter
431
+ desc The total number of bar in message.
432
+ key bar
433
+ initialized true
434
+ <labels>
435
+ key $.foo
436
+ tag ${tag}
437
+ foo bar
438
+ worker_id ${worker_id}
439
+ </labels>
440
+ <initlabels>
441
+ key foo1
442
+ tag tag1
443
+ </initlabels>
444
+ <initlabels>
445
+ key foo2
446
+ tag tag2
447
+ </initlabels>
448
+ </metric>
449
+ <labels>
450
+ hostname ${hostname}
451
+ </labels>
452
+ ```
453
+
398
454
  ### top-level labels and labels inside metric
399
455
 
400
456
  Prometheus output/filter plugin can have multiple metric section. Top-level labels section specifies labels for all metrics. Labels section inside metric section specifies labels for the metric. Both are specified, labels are merged.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "fluent-plugin-prometheus"
3
- spec.version = "1.8.5"
3
+ spec.version = "2.1.0"
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", ">= 1.9.1", "< 2"
17
- spec.add_dependency "prometheus-client", "< 0.10"
17
+ spec.add_dependency "prometheus-client", ">= 2.1.0"
18
18
  spec.add_development_dependency "bundler"
19
19
  spec.add_development_dependency "rake"
20
20
  spec.add_development_dependency "rspec"
@@ -76,14 +76,14 @@ module Fluent::Plugin
76
76
 
77
77
  @monitor_info.each do |name, metric|
78
78
  if info[name]
79
- metric.set(label, info[name])
79
+ metric.set(info[name], labels: label)
80
80
  end
81
81
  end
82
82
 
83
83
  timekeys = info["buffer_timekeys"]
84
84
  if timekeys && !timekeys.empty?
85
- @buffer_newest_timekey.set(label, timekeys.max)
86
- @buffer_oldest_timekey.set(label, timekeys.min)
85
+ @buffer_newest_timekey.set(timekeys.max, labels: label)
86
+ @buffer_oldest_timekey.set(timekeys.min, labels: label)
87
87
  end
88
88
  end
89
89
  end
@@ -100,7 +100,7 @@ module Fluent::Plugin
100
100
  if @registry.exist?(name)
101
101
  @registry.get(name)
102
102
  else
103
- @registry.gauge(name, docstring)
103
+ @registry.gauge(name, docstring: docstring, labels: @base_labels.keys + [:plugin_id, :plugin_category, :type])
104
104
  end
105
105
  end
106
106
  end
@@ -133,18 +133,26 @@ module Fluent::Plugin
133
133
 
134
134
  monitor_info = {
135
135
  # buffer metrics
136
- 'buffer_total_queued_size' => @metrics[:buffer_total_queued_size],
137
- 'buffer_stage_length' => @metrics[:buffer_stage_length],
138
- 'buffer_stage_byte_size' => @metrics[:buffer_stage_byte_size],
139
- 'buffer_queue_length' => @metrics[:buffer_queue_length],
140
- 'buffer_queue_byte_size' => @metrics[:buffer_queue_byte_size],
141
- 'buffer_available_buffer_space_ratios' => @metrics[:buffer_available_buffer_space_ratios],
142
- 'buffer_newest_timekey' => @metrics[:buffer_newest_timekey],
143
- 'buffer_oldest_timekey' => @metrics[:buffer_oldest_timekey],
136
+ 'buffer_total_queued_size' => [@metrics[:buffer_total_queued_size]],
137
+ 'buffer_stage_length' => [@metrics[:buffer_stage_length]],
138
+ 'buffer_stage_byte_size' => [@metrics[:buffer_stage_byte_size]],
139
+ 'buffer_queue_length' => [@metrics[:buffer_queue_length]],
140
+ 'buffer_queue_byte_size' => [@metrics[:buffer_queue_byte_size]],
141
+ 'buffer_available_buffer_space_ratios' => [@metrics[:buffer_available_buffer_space_ratios]],
142
+ 'buffer_newest_timekey' => [@metrics[:buffer_newest_timekey]],
143
+ 'buffer_oldest_timekey' => [@metrics[:buffer_oldest_timekey]],
144
144
 
145
145
  # output metrics
146
- 'retry_count' => @metrics[:retry_counts],
146
+ 'retry_count' => [@metrics[:retry_counts], @metrics[:num_errors]],
147
+ # Needed since Fluentd v1.14 due to metrics extensions.
148
+ 'write_count' => [@metrics[:write_count]],
149
+ 'emit_count' => [@metrics[:emit_count]],
150
+ 'emit_records' => [@metrics[:emit_records]],
151
+ 'rollback_count' => [@metrics[:rollback_count]],
152
+ 'flush_time_count' => [@metrics[:flush_time_count]],
153
+ 'slow_flush_count' => [@metrics[:slow_flush_count]],
147
154
  }
155
+ # No needed for Fluentd v1.14 but leave as-is for backward compatibility.
148
156
  instance_vars_info = {
149
157
  num_errors: @metrics[:num_errors],
150
158
  write_count: @metrics[:write_count],
@@ -158,12 +166,14 @@ module Fluent::Plugin
158
166
  agent_info.each do |info|
159
167
  label = labels(info)
160
168
 
161
- monitor_info.each do |name, metric|
162
- if info[name]
163
- if metric.is_a?(::Prometheus::Client::Gauge)
164
- metric.set(label, info[name])
165
- elsif metric.is_a?(::Prometheus::Client::Counter)
166
- metric.increment(label, info[name] - metric.get(label))
169
+ monitor_info.each do |name, metrics|
170
+ metrics.each do |metric|
171
+ if info[name]
172
+ if metric.is_a?(::Prometheus::Client::Gauge)
173
+ metric.set(info[name], labels: label)
174
+ elsif metric.is_a?(::Prometheus::Client::Counter)
175
+ metric.increment(by: info[name] - metric.get(labels: label), labels: label)
176
+ end
167
177
  end
168
178
  end
169
179
  end
@@ -172,9 +182,9 @@ module Fluent::Plugin
172
182
  instance_vars_info.each do |name, metric|
173
183
  if info['instance_variables'][name]
174
184
  if metric.is_a?(::Prometheus::Client::Gauge)
175
- metric.set(label, info['instance_variables'][name])
185
+ metric.set(info['instance_variables'][name], labels: label)
176
186
  elsif metric.is_a?(::Prometheus::Client::Counter)
177
- metric.increment(label, info['instance_variables'][name] - metric.get(label))
187
+ metric.increment(by: info['instance_variables'][name] - metric.get(labels: label), labels: label)
178
188
  end
179
189
  end
180
190
  end
@@ -193,7 +203,7 @@ module Fluent::Plugin
193
203
  if next_time && start_time
194
204
  wait = next_time - start_time
195
205
  end
196
- @metrics[:retry_wait].set(label, wait.to_f)
206
+ @metrics[:retry_wait].set(wait.to_f, labels: label)
197
207
  end
198
208
  end
199
209
  end
@@ -209,7 +219,7 @@ module Fluent::Plugin
209
219
  if @registry.exist?(name)
210
220
  @registry.get(name)
211
221
  else
212
- @registry.gauge(name, docstring)
222
+ @registry.gauge(name, docstring: docstring, labels: @base_labels.keys + [:plugin_id, :type])
213
223
  end
214
224
  end
215
225
 
@@ -217,7 +227,7 @@ module Fluent::Plugin
217
227
  if @registry.exist?(name)
218
228
  @registry.get(name)
219
229
  else
220
- @registry.public_send(@gauge_or_counter, name, docstring)
230
+ @registry.public_send(@gauge_or_counter, name, docstring: docstring, labels: @base_labels.keys + [:plugin_id, :type])
221
231
  end
222
232
  end
223
233
  end
@@ -51,6 +51,15 @@ module Fluent::Plugin
51
51
  inode: get_gauge(
52
52
  :fluentd_tail_file_inode,
53
53
  'Current inode of file.'),
54
+ closed_file_metrics: get_gauge(
55
+ :fluentd_tail_file_closed,
56
+ 'Number of files closed.'),
57
+ opened_file_metrics: get_gauge(
58
+ :fluentd_tail_file_opened,
59
+ 'Number of files opened.'),
60
+ rotated_file_metrics: get_gauge(
61
+ :fluentd_tail_file_rotated,
62
+ 'Number of files rotated.'),
54
63
  }
55
64
  timer_execute(:in_prometheus_tail_monitor, @interval, &method(:update_monitor_info))
56
65
  end
@@ -72,9 +81,15 @@ module Fluent::Plugin
72
81
  # Access to internal variable of internal class...
73
82
  # Very fragile implementation
74
83
  pe = watcher.instance_variable_get(:@pe)
84
+ monitor_info = watcher.instance_variable_get(:@metrics)
75
85
  label = labels(info, watcher.path)
76
- @metrics[:inode].set(label, pe.read_inode)
77
- @metrics[:position].set(label, pe.read_pos)
86
+ @metrics[:inode].set(pe.read_inode, labels: label)
87
+ @metrics[:position].set(pe.read_pos, labels: label)
88
+ unless monitor_info.nil?
89
+ @metrics[:closed_file_metrics].set(monitor_info.closed.get, labels: label)
90
+ @metrics[:opened_file_metrics].set(monitor_info.opened.get, labels: label)
91
+ @metrics[:rotated_file_metrics].set(monitor_info.rotated.get, labels: label)
92
+ end
78
93
  end
79
94
  end
80
95
  end
@@ -91,7 +106,7 @@ module Fluent::Plugin
91
106
  if @registry.exist?(name)
92
107
  @registry.get(name)
93
108
  else
94
- @registry.gauge(name, docstring)
109
+ @registry.gauge(name, docstring: docstring, labels: @base_labels.keys + [:plugin_id, :type, :path])
95
110
  end
96
111
  end
97
112
  end
@@ -56,6 +56,69 @@ module Fluent
56
56
  base_labels
57
57
  end
58
58
 
59
+ def self.parse_initlabels_elements(conf, base_labels)
60
+ base_initlabels = []
61
+
62
+ # We first treat the special case of RecordAccessors and Placeholders labels if any declared
63
+ conf.elements.select { |e| e.name == 'initlabels' }.each { |block|
64
+ initlabels = {}
65
+
66
+ block.each do |key, value|
67
+ if not base_labels.has_key? key.to_sym
68
+ raise ConfigError, "Key #{key} in <initlabels> is non existent in <labels> for metric #{conf['name']}"
69
+ end
70
+
71
+ if value.start_with?('$.') || value.start_with?('$[') || value.start_with?('${')
72
+ raise ConfigError, "Cannot use RecordAccessor or placeholder #{value} (key #{key}) in a <initlabels> in metric #{conf['name']}"
73
+ end
74
+
75
+ base_label_value = base_labels[key.to_sym]
76
+
77
+ if !(base_label_value.class == Fluent::PluginHelper::RecordAccessor::Accessor) && ! (base_label_value.start_with?('${') )
78
+ raise ConfigError, "Cannot set <initlabels> on non RecordAccessor/Placeholder key #{key} (value #{value}) in metric #{conf['name']}"
79
+ end
80
+
81
+ if base_label_value == '${worker_id}' || base_label_value == '${hostname}'
82
+ raise ConfigError, "Cannot set <initlabels> on reserved placeholder #{base_label_value} for key #{key} in metric #{conf['name']}"
83
+ end
84
+
85
+ initlabels[key.to_sym] = value
86
+ end
87
+
88
+ # Now adding all labels that are not RecordAccessor nor Placeholder labels as is
89
+ base_labels.each do |key, value|
90
+ if base_labels[key.to_sym].class != Fluent::PluginHelper::RecordAccessor::Accessor
91
+ if value == '${worker_id}'
92
+ # We retrieve fluentd_worker_id this way to not overcomplicate the code
93
+ initlabels[key.to_sym] = (ENV['SERVERENGINE_WORKER_ID'] || 0).to_i
94
+ elsif value == '${hostname}'
95
+ initlabels[key.to_sym] = Socket.gethostname
96
+ elsif !(value.start_with?('${'))
97
+ initlabels[key.to_sym] = value
98
+ end
99
+ end
100
+ end
101
+
102
+ base_initlabels << initlabels
103
+ }
104
+
105
+ # Testing for RecordAccessor/Placeholder labels missing a declaration in <initlabels> blocks
106
+ base_labels.each do |key, value|
107
+ if value.class == Fluent::PluginHelper::RecordAccessor::Accessor || value.start_with?('${')
108
+ if not base_initlabels.map(&:keys).flatten.include? (key.to_sym)
109
+ raise ConfigError, "RecordAccessor/Placeholder key #{key} with value #{value} has not been set in a <initlabels> for initialized metric #{conf['name']}"
110
+ end
111
+ end
112
+ end
113
+
114
+ if base_initlabels.length == 0
115
+ # There were no RecordAccessor nor Placeholder labels, we blunty retrieve the static base_labels
116
+ base_initlabels << base_labels
117
+ end
118
+
119
+ base_initlabels
120
+ end
121
+
59
122
  def self.parse_metrics_elements(conf, registry, labels = {})
60
123
  metrics = []
61
124
  conf.elements.select { |element|
@@ -85,6 +148,18 @@ module Fluent
85
148
  Fluent::Plugin::Prometheus::ExpandBuilder.new(log: log)
86
149
  end
87
150
 
151
+ def stringify_keys(hash_to_stringify)
152
+ # Adapted from: https://www.jvt.me/posts/2019/09/07/ruby-hash-keys-string-symbol/
153
+ hash_to_stringify.map do |k,v|
154
+ value_or_hash = if v.instance_of? Hash
155
+ stringify_keys(v)
156
+ else
157
+ v
158
+ end
159
+ [k.to_s, value_or_hash]
160
+ end.to_h
161
+ end
162
+
88
163
  def configure(conf)
89
164
  super
90
165
  @placeholder_values = {}
@@ -99,6 +174,7 @@ module Fluent
99
174
  'worker_id' => fluentd_worker_id,
100
175
  }
101
176
 
177
+ record = stringify_keys(record)
102
178
  placeholders = record.merge(@placeholder_values[tag])
103
179
  expander = @placeholder_expander_builder.build(placeholders)
104
180
  metrics.each do |metric|
@@ -119,6 +195,7 @@ module Fluent
119
195
  }
120
196
 
121
197
  es.each do |time, record|
198
+ record = stringify_keys(record)
122
199
  placeholders = record.merge(placeholder_values)
123
200
  expander = @placeholder_expander_builder.build(placeholders)
124
201
  metrics.each do |metric|
@@ -148,9 +225,27 @@ module Fluent
148
225
  @name = element['name']
149
226
  @key = element['key']
150
227
  @desc = element['desc']
151
-
228
+ element['initialized'].nil? ? @initialized = false : @initialized = element['initialized'] == 'true'
229
+
152
230
  @base_labels = Fluent::Plugin::Prometheus.parse_labels_elements(element)
153
231
  @base_labels = labels.merge(@base_labels)
232
+
233
+ if @initialized
234
+ @base_initlabels = Fluent::Plugin::Prometheus.parse_initlabels_elements(element, @base_labels)
235
+ end
236
+ end
237
+
238
+ def self.init_label_set(metric, base_initlabels, base_labels)
239
+ base_initlabels.each { |initlabels|
240
+ # Should never happen, but handy test should code evolution break current implementation
241
+ if initlabels.keys.sort != base_labels.keys.sort
242
+ raise ConfigError, "initlabels for metric #{metric.name} must have the same signature than labels " \
243
+ "(initlabels given: #{initlabels.keys} vs." \
244
+ " expected from labels: #{base_labels.keys})"
245
+ end
246
+
247
+ metric.init_label_set(initlabels)
248
+ }
154
249
  end
155
250
 
156
251
  def labels(record, expander)
@@ -188,10 +283,14 @@ module Fluent
188
283
  end
189
284
 
190
285
  begin
191
- @gauge = registry.gauge(element['name'].to_sym, element['desc'])
286
+ @gauge = registry.gauge(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
192
287
  rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
193
288
  @gauge = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :gauge, element['desc'])
194
289
  end
290
+
291
+ if @initialized
292
+ Fluent::Plugin::Prometheus::Metric.init_label_set(@gauge, @base_initlabels, @base_labels)
293
+ end
195
294
  end
196
295
 
197
296
  def instrument(record, expander)
@@ -201,7 +300,7 @@ module Fluent
201
300
  value = @key.call(record)
202
301
  end
203
302
  if value
204
- @gauge.set(labels(record, expander), value)
303
+ @gauge.set(value, labels: labels(record, expander))
205
304
  end
206
305
  end
207
306
  end
@@ -210,10 +309,14 @@ module Fluent
210
309
  def initialize(element, registry, labels)
211
310
  super
212
311
  begin
213
- @counter = registry.counter(element['name'].to_sym, element['desc'])
312
+ @counter = registry.counter(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
214
313
  rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
215
314
  @counter = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :counter, element['desc'])
216
315
  end
316
+
317
+ if @initialized
318
+ Fluent::Plugin::Prometheus::Metric.init_label_set(@counter, @base_initlabels, @base_labels)
319
+ end
217
320
  end
218
321
 
219
322
  def instrument(record, expander)
@@ -229,7 +332,7 @@ module Fluent
229
332
  # ignore if record value is nil
230
333
  return if value.nil?
231
334
 
232
- @counter.increment(labels(record, expander), value)
335
+ @counter.increment(by: value, labels: labels(record, expander))
233
336
  end
234
337
  end
235
338
 
@@ -241,10 +344,14 @@ module Fluent
241
344
  end
242
345
 
243
346
  begin
244
- @summary = registry.summary(element['name'].to_sym, element['desc'])
347
+ @summary = registry.summary(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
245
348
  rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
246
349
  @summary = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :summary, element['desc'])
247
350
  end
351
+
352
+ if @initialized
353
+ Fluent::Plugin::Prometheus::Metric.init_label_set(@summary, @base_initlabels, @base_labels)
354
+ end
248
355
  end
249
356
 
250
357
  def instrument(record, expander)
@@ -254,7 +361,7 @@ module Fluent
254
361
  value = @key.call(record)
255
362
  end
256
363
  if value
257
- @summary.observe(labels(record, expander), value)
364
+ @summary.observe(value, labels: labels(record, expander))
258
365
  end
259
366
  end
260
367
  end
@@ -271,13 +378,17 @@ module Fluent
271
378
  buckets = element['buckets'].split(/,/).map(&:strip).map do |e|
272
379
  e[/\A\d+.\d+\Z/] ? e.to_f : e.to_i
273
380
  end
274
- @histogram = registry.histogram(element['name'].to_sym, element['desc'], {}, buckets)
381
+ @histogram = registry.histogram(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys, buckets: buckets)
275
382
  else
276
- @histogram = registry.histogram(element['name'].to_sym, element['desc'])
383
+ @histogram = registry.histogram(element['name'].to_sym, docstring: element['desc'], labels: @base_labels.keys)
277
384
  end
278
385
  rescue ::Prometheus::Client::Registry::AlreadyRegisteredError
279
386
  @histogram = Fluent::Plugin::Prometheus::Metric.get(registry, element['name'].to_sym, :histogram, element['desc'])
280
387
  end
388
+
389
+ if @initialized
390
+ Fluent::Plugin::Prometheus::Metric.init_label_set(@histogram, @base_initlabels, @base_labels)
391
+ end
281
392
  end
282
393
 
283
394
  def instrument(record, expander)
@@ -287,7 +398,7 @@ module Fluent
287
398
  value = @key.call(record)
288
399
  end
289
400
  if value
290
- @histogram.observe(labels(record, expander), value)
401
+ @histogram.observe(value, labels: labels(record, expander))
291
402
  end
292
403
  end
293
404
  end
@@ -202,7 +202,7 @@ describe Fluent::Plugin::PrometheusInput do
202
202
  describe '#run_multi_workers' do
203
203
  context '/metrics' do
204
204
  Fluent::SystemConfig.overwrite_system_config('workers' => 4) do
205
- let(:config) { CONFIG + %[
205
+ let(:config) { FULL_CONFIG + %[
206
206
  port #{port - 2}
207
207
  ] }
208
208
 
@@ -16,6 +16,10 @@ describe Fluent::Plugin::PrometheusOutput do
16
16
  it_behaves_like 'output configuration'
17
17
  end
18
18
 
19
+ describe '#testinitlabels' do
20
+ it_behaves_like 'initalized metrics'
21
+ end
22
+
19
23
  describe '#run' do
20
24
  let(:message) { {"foo" => 100, "bar" => 100, "baz" => 100, "qux" => 10} }
21
25
 
@@ -40,4 +44,29 @@ describe Fluent::Plugin::PrometheusOutput do
40
44
 
41
45
  it_behaves_like 'instruments record'
42
46
  end
47
+
48
+ describe '#run with symbolized keys' do
49
+ let(:message) { {:foo => 100, :bar => 100, :baz => 100, :qux => 10} }
50
+
51
+ context 'simple config' do
52
+ let(:config) {
53
+ BASE_CONFIG + %(
54
+ <metric>
55
+ name simple
56
+ type counter
57
+ desc Something foo.
58
+ key foo
59
+ </metric>
60
+ )
61
+ }
62
+
63
+ it 'adds a new counter metric' do
64
+ expect(registry.metrics.map(&:name)).not_to eq([:simple])
65
+ driver.run(default_tag: tag) { driver.feed(event_time, message) }
66
+ expect(registry.metrics.map(&:name)).to eq([:simple])
67
+ end
68
+ end
69
+
70
+ it_behaves_like 'instruments record'
71
+ end
43
72
  end
@@ -27,6 +27,7 @@ FULL_CONFIG = BASE_CONFIG + %[
27
27
  type gauge
28
28
  desc Something bar.
29
29
  key bar
30
+ initialized true
30
31
  <labels>
31
32
  key foo2
32
33
  </labels>
@@ -36,6 +37,7 @@ FULL_CONFIG = BASE_CONFIG + %[
36
37
  type summary
37
38
  desc Something baz.
38
39
  key baz
40
+ initialized true
39
41
  <labels>
40
42
  key foo3
41
43
  </labels>
@@ -46,6 +48,7 @@ FULL_CONFIG = BASE_CONFIG + %[
46
48
  desc Something qux.
47
49
  key qux
48
50
  buckets 0.1, 1, 5, 10
51
+ initialized true
49
52
  <labels>
50
53
  key foo4
51
54
  </labels>
@@ -62,12 +65,32 @@ FULL_CONFIG = BASE_CONFIG + %[
62
65
  <metric>
63
66
  name full_accessor2
64
67
  type counter
65
- desc Something with accessor.
68
+ desc Something with accessor
66
69
  key $.foo
70
+ initialized true
67
71
  <labels>
68
72
  key foo6
69
73
  </labels>
70
74
  </metric>
75
+ <metric>
76
+ name full_accessor3
77
+ type counter
78
+ desc Something with accessor and several initialized metrics
79
+ initialized true
80
+ <labels>
81
+ key $.foo
82
+ key2 $.foo2
83
+ key3 footix
84
+ </labels>
85
+ <initlabels>
86
+ key foo6
87
+ key2 foo7
88
+ </initlabels>
89
+ <initlabels>
90
+ key foo8
91
+ key2 foo9
92
+ </initlabels>
93
+ </metric>
71
94
  <labels>
72
95
  test_key test_value
73
96
  </labels>
@@ -79,13 +102,20 @@ PLACEHOLDER_CONFIG = BASE_CONFIG + %[
79
102
  type counter
80
103
  desc Something foo.
81
104
  key foo
105
+ initialized true
82
106
  <labels>
83
107
  foo ${foo}
108
+ foo2 foo2
84
109
  </labels>
110
+ <initlabels>
111
+ tag tag
112
+ foo foo
113
+ </initlabels>
85
114
  </metric>
86
115
  <labels>
87
116
  tag ${tag}
88
117
  hostname ${hostname}
118
+ workerid ${worker_id}
89
119
  </labels>
90
120
  ]
91
121
 
@@ -150,6 +180,111 @@ shared_examples_for 'output configuration' do
150
180
  end
151
181
  it { expect { driver }.to raise_error(Fluent::ConfigError) }
152
182
  end
183
+
184
+
185
+ context 'with missing <initlabels>' do
186
+ let(:config) do
187
+ BASE_CONFIG + %[
188
+ <metric>
189
+ name simple_foo
190
+ type counter
191
+ desc Something foo but incorrect
192
+ key foo
193
+ initialized true
194
+ <labels>
195
+ key $.accessor
196
+ </labels>
197
+ </metric>
198
+ ]
199
+ end
200
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
201
+ end
202
+
203
+ context 'with RecordAccessor set in <initlabels>' do
204
+ let(:config) do
205
+ BASE_CONFIG + %[
206
+ <metric>
207
+ name simple_foo
208
+ type counter
209
+ desc Something foo but incorrect
210
+ key foo
211
+ initialized true
212
+ <labels>
213
+ key $.accessor
214
+ </labels>
215
+ <initlabels>
216
+ key $.accessor2
217
+ <initlabels>
218
+ </metric>
219
+ ]
220
+ end
221
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
222
+ end
223
+
224
+ context 'with PlaceHolder set in <initlabels>' do
225
+ let(:config) do
226
+ BASE_CONFIG + %[
227
+ <metric>
228
+ name simple_foo
229
+ type counter
230
+ desc Something foo but incorrect
231
+ key foo
232
+ initialized true
233
+ <labels>
234
+ key ${foo}
235
+ </labels>
236
+ <initlabels>
237
+ key ${foo}
238
+ <initlabels>
239
+ </metric>
240
+ ]
241
+ end
242
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
243
+ end
244
+
245
+ context 'with non RecordAccessor label set in <initlabels>' do
246
+ let(:config) do
247
+ BASE_CONFIG + %[
248
+ <metric>
249
+ name simple_foo
250
+ type counter
251
+ desc Something foo but incorrect
252
+ key foo
253
+ initialized true
254
+ <labels>
255
+ key $.accessor
256
+ key2 foo2
257
+ </labels>
258
+ <initlabels>
259
+ key foo
260
+ key2 foo2
261
+ <initlabels>
262
+ </metric>
263
+ ]
264
+ end
265
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
266
+ end
267
+
268
+ context 'with non-matching label keys set in <initlabels>' do
269
+ let(:config) do
270
+ BASE_CONFIG + %[
271
+ <metric>
272
+ name simple_foo
273
+ type counter
274
+ desc Something foo but incorrect
275
+ key foo
276
+ initialized true
277
+ <labels>
278
+ key $.accessor
279
+ </labels>
280
+ <initlabels>
281
+ key2 foo
282
+ <initlabels>
283
+ </metric>
284
+ ]
285
+ end
286
+ it { expect { driver }.to raise_error(Fluent::ConfigError) }
287
+ end
153
288
  end
154
289
 
155
290
  shared_examples_for 'instruments record' do
@@ -165,33 +300,35 @@ shared_examples_for 'instruments record' do
165
300
  let(:histogram) { registry.get(:full_qux) }
166
301
  let(:summary_with_accessor) { registry.get(:full_accessor1) }
167
302
  let(:counter_with_accessor) { registry.get(:full_accessor2) }
303
+ let(:counter_with_two_accessors) { registry.get(:full_accessor3) }
168
304
 
169
305
  it 'adds all metrics' do
170
- expect(registry.metrics.map(&:name)).to eq(%i[full_foo full_bar full_baz full_qux full_accessor1 full_accessor2])
306
+ expect(registry.metrics.map(&:name)).to eq(%i[full_foo full_bar full_baz full_qux full_accessor1 full_accessor2 full_accessor3])
171
307
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
172
308
  expect(gauge).to be_kind_of(::Prometheus::Client::Metric)
173
309
  expect(summary).to be_kind_of(::Prometheus::Client::Metric)
174
310
  expect(summary_with_accessor).to be_kind_of(::Prometheus::Client::Metric)
175
311
  expect(counter_with_accessor).to be_kind_of(::Prometheus::Client::Metric)
312
+ expect(counter_with_two_accessors).to be_kind_of(::Prometheus::Client::Metric)
176
313
  expect(histogram).to be_kind_of(::Prometheus::Client::Metric)
177
314
  end
178
315
 
179
316
  it 'instruments counter metric' do
180
317
  expect(counter.type).to eq(:counter)
181
- expect(counter.get({test_key: 'test_value', key: 'foo1'})).to be_kind_of(Numeric)
182
- expect(counter_with_accessor.get({test_key: 'test_value', key: 'foo6'})).to be_kind_of(Numeric)
318
+ expect(counter.get(labels: {test_key: 'test_value', key: 'foo1'})).to be_kind_of(Numeric)
319
+ expect(counter_with_accessor.get(labels: {test_key: 'test_value', key: 'foo6'})).to be_kind_of(Numeric)
320
+ expect(counter_with_two_accessors.get(labels: {test_key: 'test_value', key: 'foo6', key2: 'foo7', key3: 'footix'})).to be_kind_of(Numeric)
183
321
  end
184
322
 
185
323
  it 'instruments gauge metric' do
186
324
  expect(gauge.type).to eq(:gauge)
187
- expect(gauge.get({test_key: 'test_value', key: 'foo2'})).to eq(100)
325
+ expect(gauge.get(labels: {test_key: 'test_value', key: 'foo2'})).to eq(100)
188
326
  end
189
327
 
190
328
  it 'instruments summary metric' do
191
329
  expect(summary.type).to eq(:summary)
192
- expect(summary.get({test_key: 'test_value', key: 'foo3'})).to be_kind_of(Hash)
193
- expect(summary.get({test_key: 'test_value', key: 'foo3'})[0.99]).to eq(100)
194
- expect(summary_with_accessor.get({test_key: 'test_value', key: 'foo5'})[0.99]).to eq(100)
330
+ expect(summary.get(labels: {test_key: 'test_value', key: 'foo3'})).to be_kind_of(Hash)
331
+ expect(summary_with_accessor.get(labels: {test_key: 'test_value', key: 'foo5'})["sum"]).to eq(100)
195
332
  end
196
333
 
197
334
  it 'instruments histogram metric' do
@@ -200,8 +337,8 @@ shared_examples_for 'instruments record' do
200
337
  end
201
338
 
202
339
  expect(histogram.type).to eq(:histogram)
203
- expect(histogram.get({test_key: 'test_value', key: 'foo4'})).to be_kind_of(Hash)
204
- expect(histogram.get({test_key: 'test_value', key: 'foo4'})[10]).to eq(5) # 4 + `es` in before
340
+ expect(histogram.get(labels: {test_key: 'test_value', key: 'foo4'})).to be_kind_of(Hash)
341
+ expect(histogram.get(labels: {test_key: 'test_value', key: 'foo4'})["10"]).to eq(5) # 4 + `es` in before
205
342
  end
206
343
  end
207
344
 
@@ -231,7 +368,7 @@ shared_examples_for 'instruments record' do
231
368
  expect(counter).to be_kind_of(::Prometheus::Client::Metric)
232
369
  key, _ = counter.values.find {|k,v| v == 100 }
233
370
  expect(key).to be_kind_of(Hash)
234
- expect(key[:foo]).to eq(100)
371
+ expect(key[:foo]).to eq("100")
235
372
  end
236
373
  end
237
374
 
@@ -247,3 +384,66 @@ shared_examples_for 'instruments record' do
247
384
  end
248
385
  end
249
386
  end
387
+
388
+ shared_examples_for 'initalized metrics' do
389
+ before do
390
+ driver.run(default_tag: tag)
391
+ end
392
+
393
+ context 'full config' do
394
+ let(:config) { FULL_CONFIG }
395
+ let(:counter) { registry.get(:full_foo) }
396
+ let(:gauge) { registry.get(:full_bar) }
397
+ let(:summary) { registry.get(:full_baz) }
398
+ let(:histogram) { registry.get(:full_qux) }
399
+ let(:summary_with_accessor) { registry.get(:full_accessor1) }
400
+ let(:counter_with_accessor) { registry.get(:full_accessor2) }
401
+ let(:counter_with_two_accessors) { registry.get(:full_accessor3) }
402
+
403
+ it 'adds all metrics' do
404
+ expect(registry.metrics.map(&:name)).to eq(%i[full_foo full_bar full_baz full_qux full_accessor1 full_accessor2 full_accessor3])
405
+ expect(counter).to be_kind_of(::Prometheus::Client::Metric)
406
+ expect(gauge).to be_kind_of(::Prometheus::Client::Metric)
407
+ expect(summary).to be_kind_of(::Prometheus::Client::Metric)
408
+ expect(summary_with_accessor).to be_kind_of(::Prometheus::Client::Metric)
409
+ expect(counter_with_accessor).to be_kind_of(::Prometheus::Client::Metric)
410
+ expect(counter_with_two_accessors).to be_kind_of(::Prometheus::Client::Metric)
411
+ expect(histogram).to be_kind_of(::Prometheus::Client::Metric)
412
+ end
413
+
414
+ it 'tests uninitialized metrics' do
415
+ expect(counter.values).to eq({})
416
+ expect(summary_with_accessor.values).to eq({})
417
+ end
418
+
419
+ it 'tests initialized metrics' do
420
+ expect(gauge.values).to eq({{:key=>"foo2", :test_key=>"test_value"}=>0.0})
421
+ expect(summary.values).to eq({:key=>"foo3", :test_key=>"test_value"}=>{"count"=>0.0, "sum"=>0.0})
422
+ expect(histogram.values).to eq({:key=>"foo4", :test_key=>"test_value"} => {"+Inf"=>0.0, "0.1"=>0.0, "1"=>0.0, "10"=>0.0, "5"=>0.0, "sum"=>0.0})
423
+ expect(counter_with_accessor.values).to eq({{:key=>"foo6", :test_key=>"test_value"}=>0.0})
424
+ expect(counter_with_two_accessors.values).to eq({{:key=>"foo6", :key2=>"foo7", :key3=>"footix", :test_key=>"test_value"}=>0.0, {:key=>"foo8", :key2=>"foo9", :key3=>"footix", :test_key=>"test_value"}=>0.0})
425
+ end
426
+ end
427
+
428
+ context 'placeholder config' do
429
+ let(:config) { PLACEHOLDER_CONFIG }
430
+ let(:counter) { registry.get(:placeholder_foo) }
431
+
432
+ it 'expands placeholders with record values' do
433
+ expect(registry.metrics.map(&:name)).to eq([:placeholder_foo])
434
+ expect(counter).to be_kind_of(::Prometheus::Client::Metric)
435
+
436
+ key, _ = counter.values.find {|k,v| v == 0.0 }
437
+ expect(key).to be_kind_of(Hash)
438
+ expect(key[:foo]).to eq("foo")
439
+ expect(key[:foo2]).to eq("foo2")
440
+ expect(key[:hostname]).to be_kind_of(String)
441
+ expect(key[:hostname]).not_to eq("${hostname}")
442
+ expect(key[:hostname]).not_to be_empty
443
+ expect(key[:workerid]).to be_kind_of(String)
444
+ expect(key[:workerid]).not_to eq("${worker_id}")
445
+ expect(key[:workerid]).not_to be_empty
446
+ expect(key[:tag]).to eq("tag")
447
+ end
448
+ end
449
+ end
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.8.5
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Sano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-24 00:00:00.000000000 Z
11
+ date: 2023-06-15 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.10'
39
+ version: 2.1.0
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.10'
46
+ version: 2.1.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -107,9 +107,10 @@ executables: []
107
107
  extensions: []
108
108
  extra_rdoc_files: []
109
109
  files:
110
+ - ".github/dependabot.yml"
111
+ - ".github/workflows/linux.yml"
110
112
  - ".gitignore"
111
113
  - ".rspec"
112
- - ".travis.yml"
113
114
  - ChangeLog
114
115
  - Gemfile
115
116
  - LICENSE
@@ -158,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
159
  - !ruby/object:Gem::Version
159
160
  version: '0'
160
161
  requirements: []
161
- rubygems_version: 3.0.3
162
+ rubygems_version: 3.3.7
162
163
  signing_key:
163
164
  specification_version: 4
164
165
  summary: A fluent plugin that collects metrics and exposes for Prometheus.
data/.travis.yml DELETED
@@ -1,14 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - "2.4.7"
5
- - "2.5"
6
- - "2.6"
7
-
8
- gemfile:
9
- - Gemfile
10
-
11
- script:
12
- - bundle exec rake spec
13
-
14
- sudo: false