fluent-plugin-prometheus 1.8.5 → 2.1.0

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