fluent-plugin-time-cutoff 0.3.0 → 0.4.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 +4 -4
- data/README.md +93 -14
- data/lib/fluent/plugin/filter_time_cutoff.rb +154 -43
- metadata +21 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a52993553c1cdc40bb6800791120e5248662a1f84546bf13b686c90c16f5c35d
|
|
4
|
+
data.tar.gz: c58051186b9056ee4fe0b6aa25850e11d1013de01038cca983323e525e551bdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b2c8c998860001c4142850878506fa29978fd996049019babdc7331dc74b6889c90a1caa55344075c4d399e883cdf384e9e7f2f3f9b8f3c7ba07c38c181a45c
|
|
7
|
+
data.tar.gz: 1be548ff25ac8d3e9db3314a3c01a36404c7c77cae535f1857366deb87ce2b62199913b84ffd3ffbf88c2de86a421c0cb5f4f936b722f8835d13cbda2064982b
|
data/README.md
CHANGED
|
@@ -104,20 +104,22 @@ $ bundle
|
|
|
104
104
|
|
|
105
105
|
old_cutoff 24h
|
|
106
106
|
old_action pass
|
|
107
|
-
old_log
|
|
107
|
+
old_log full
|
|
108
108
|
|
|
109
109
|
new_cutoff 24h
|
|
110
110
|
new_action pass
|
|
111
|
-
new_log
|
|
111
|
+
new_log full
|
|
112
112
|
|
|
113
113
|
source_time_key source_time
|
|
114
114
|
source_time_format iso8601
|
|
115
115
|
|
|
116
116
|
bad_time_action replace_timestamp
|
|
117
|
-
bad_time_log
|
|
117
|
+
bad_time_log full
|
|
118
118
|
bad_time_key source_time_raw
|
|
119
119
|
|
|
120
120
|
time_key_conflict skip
|
|
121
|
+
|
|
122
|
+
metrics false
|
|
121
123
|
</filter>
|
|
122
124
|
|
|
123
125
|
```
|
|
@@ -138,11 +140,15 @@ Available values: pass, replace_timestamp, drop
|
|
|
138
140
|
|
|
139
141
|
Default value: `pass`.
|
|
140
142
|
|
|
141
|
-
### old_log (
|
|
143
|
+
### old_log (enum) (optional)
|
|
144
|
+
|
|
145
|
+
Log the "old" messages to Fluentd's own log. Defaults to "full".
|
|
146
|
+
|
|
147
|
+
Available values: none (or false/no), metadata, full (or true/yes)
|
|
142
148
|
|
|
143
|
-
|
|
149
|
+
See [Logging](#logging) for what each mode logs.
|
|
144
150
|
|
|
145
|
-
Default value: `
|
|
151
|
+
Default value: `full`.
|
|
146
152
|
|
|
147
153
|
### new_cutoff (time) (optional)
|
|
148
154
|
|
|
@@ -160,11 +166,15 @@ Available values: pass, replace_timestamp, drop
|
|
|
160
166
|
|
|
161
167
|
Default value: `pass`.
|
|
162
168
|
|
|
163
|
-
### new_log (
|
|
169
|
+
### new_log (enum) (optional)
|
|
170
|
+
|
|
171
|
+
Log the "new" messages to Fluentd's own log. Defaults to "full".
|
|
172
|
+
|
|
173
|
+
Available values: none (or false/no), metadata, full (or true/yes)
|
|
164
174
|
|
|
165
|
-
|
|
175
|
+
See [Logging](#logging) for what each mode logs.
|
|
166
176
|
|
|
167
|
-
Default value: `
|
|
177
|
+
Default value: `full`.
|
|
168
178
|
|
|
169
179
|
### source_time_key (string) (optional)
|
|
170
180
|
|
|
@@ -186,17 +196,21 @@ Default value: `iso8601`.
|
|
|
186
196
|
|
|
187
197
|
The action that will be performed to all messages with an unparseable time. Defaults to replacing it with current time.
|
|
188
198
|
|
|
189
|
-
Event times that aren't a `Fluent::EventTime` are parsed before the age check. Integers, floats, numeric strings and ISO 8601 datetime strings are accepted. ISO 8601 strings may use a space instead of `T` and may have a space before the timezone (e.g. `2025-04-08 14:43:14 +0300` or `2025-04-08 11:43:14 UTC`, as produced by Ruby's `Time#to_s`). Strings without a timezone are treated as local time. Any other time is deemed unparseable and is handled according to this action (and logged, unless `bad_time_log` is
|
|
199
|
+
Event times that aren't a `Fluent::EventTime` are parsed before the age check. Integers, floats, numeric strings and ISO 8601 datetime strings are accepted. ISO 8601 strings may use a space instead of `T` and may have a space before the timezone (e.g. `2025-04-08 14:43:14 +0300` or `2025-04-08 11:43:14 UTC`, as produced by Ruby's `Time#to_s`). Strings without a timezone are treated as local time. Any other time is deemed unparseable and is handled according to this action (and logged, unless `bad_time_log` is `none`).
|
|
190
200
|
|
|
191
201
|
Available values: replace_timestamp, drop
|
|
192
202
|
|
|
193
203
|
Default value: `replace_timestamp`.
|
|
194
204
|
|
|
195
|
-
### bad_time_log (
|
|
205
|
+
### bad_time_log (enum) (optional)
|
|
196
206
|
|
|
197
|
-
Log the messages with an unparseable time to Fluentd's own log. Defaults to "
|
|
207
|
+
Log the messages with an unparseable time to Fluentd's own log. Defaults to "full".
|
|
198
208
|
|
|
199
|
-
|
|
209
|
+
Available values: none (or false/no), metadata, full (or true/yes)
|
|
210
|
+
|
|
211
|
+
See [Logging](#logging) for what each mode logs.
|
|
212
|
+
|
|
213
|
+
Default value: `full`.
|
|
200
214
|
|
|
201
215
|
### bad_time_key (string) (optional)
|
|
202
216
|
|
|
@@ -215,13 +229,78 @@ The action that will be performed when the record already has source_time_key or
|
|
|
215
229
|
This happens e.g. when a record passes through more than one `time_cutoff` filter, in which case the existing value is the true original time. The event time itself is replaced with current time in any case.
|
|
216
230
|
|
|
217
231
|
* `skip`: keep the existing value.
|
|
218
|
-
* `warn`: keep the existing value and log a warning to Fluentd's own log.
|
|
232
|
+
* `warn`: keep the existing value and log a warning to Fluentd's own log. The warning is logged even if the record's log mode is `none`, but includes the record only in `full` mode.
|
|
219
233
|
* `overwrite`: replace the existing value.
|
|
220
234
|
|
|
221
235
|
Available values: skip, warn, overwrite
|
|
222
236
|
|
|
223
237
|
Default value: `skip`.
|
|
224
238
|
|
|
239
|
+
### metrics (bool) (optional)
|
|
240
|
+
|
|
241
|
+
Count caught records in a Prometheus counter (needs the prometheus-client gem). Defaults to "false".
|
|
242
|
+
|
|
243
|
+
See [Metrics](#metrics) for details.
|
|
244
|
+
|
|
245
|
+
Default value: `false`.
|
|
246
|
+
|
|
247
|
+
## Logging
|
|
248
|
+
|
|
249
|
+
Each caught record is logged as a warning to Fluentd's own log, according to the `old_log`, `new_log` or `bad_time_log` mode:
|
|
250
|
+
|
|
251
|
+
* `none` (or `false`/`no`): nothing is logged.
|
|
252
|
+
* `metadata`: only the tag, the event time and its age (or the raw unparseable time) are logged, but not the record itself. Use it for records that may hold sensitive data, e.g. audit logs.
|
|
253
|
+
* `full` (or `true`/`yes`): the whole record is logged as well.
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
# metadata
|
|
257
|
+
[warn]: [time_cutoff] Record caught [old, drop]: "vault.audit: 2025-04-07T14:43:24+03:00 (age 1d 0h 0m 0s)".
|
|
258
|
+
[warn]: [time_cutoff] Record caught [new, pass]: "vault.audit: 2025-04-08T16:43:29+03:00 (age -2h 0m 5s)".
|
|
259
|
+
[warn]: [time_cutoff] Record caught [bad_time, drop]: "vault.audit: "not-a-time"".
|
|
260
|
+
|
|
261
|
+
# full
|
|
262
|
+
[warn]: [time_cutoff] Record caught [old, drop]: "vault.audit: 2025-04-07T14:43:24+03:00 {"message":"hello"}".
|
|
263
|
+
[warn]: [time_cutoff] Record caught [bad_time, drop]: "vault.audit: "not-a-time" {"message":"hello"}".
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Metrics
|
|
267
|
+
|
|
268
|
+
With `metrics true`, caught records are counted in a Prometheus counter:
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
fluentd_time_cutoff_records_total{plugin_id, worker_id, hostname, tag, reason, action}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
* `plugin_id`: the filter's `@id`. Give each `time_cutoff` filter a stable `@id` to tell them apart.
|
|
275
|
+
* `worker_id`, `hostname`: the Fluentd worker and host, so that series from multiple workers (e.g. on fluent-plugin-prometheus's `/aggregated_metrics` endpoint) and hosts don't collide.
|
|
276
|
+
* `tag`: the record's tag.
|
|
277
|
+
* `reason`: why the record was caught: `old`, `new` or `bad_time`.
|
|
278
|
+
* `action`: what was done to it: `pass`, `replace_timestamp` or `drop`.
|
|
279
|
+
|
|
280
|
+
Records inside the time window aren't counted. Records are counted regardless of their log mode, so the counter can replace per-record logging.
|
|
281
|
+
|
|
282
|
+
The counter is registered in the default registry of the [prometheus-client](https://github.com/prometheus/client_ruby) gem (version 2.1 or newer), which is what [fluent-plugin-prometheus](https://github.com/fluent/fluent-plugin-prometheus) serves, so adding its `prometheus` input is enough to expose it:
|
|
283
|
+
|
|
284
|
+
``` xml
|
|
285
|
+
<source>
|
|
286
|
+
@type prometheus
|
|
287
|
+
</source>
|
|
288
|
+
|
|
289
|
+
<filter **>
|
|
290
|
+
@type time_cutoff
|
|
291
|
+
@id cutoff_main
|
|
292
|
+
metrics true
|
|
293
|
+
</filter>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The prometheus-client gem is an optional dependency of this plugin. If it's not installed, enabling `metrics` only logs a warning on startup. Errors while updating the counter are logged once and never affect how records are processed.
|
|
297
|
+
|
|
298
|
+
Example query, records dropped per second for being too old, by tag:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
sum by (tag) (rate(fluentd_time_cutoff_records_total{reason="old", action="drop"}[5m]))
|
|
302
|
+
```
|
|
303
|
+
|
|
225
304
|
## Copyright
|
|
226
305
|
|
|
227
306
|
* Copyright(c) 2025-2026 Qrator Labs and Serge Tkatchouk
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'fluent/plugin/filter'
|
|
4
4
|
require 'json'
|
|
5
|
+
require 'socket'
|
|
5
6
|
require 'time'
|
|
6
7
|
|
|
7
8
|
module Fluent
|
|
@@ -50,6 +51,29 @@ module Fluent
|
|
|
50
51
|
:overwrite
|
|
51
52
|
].freeze
|
|
52
53
|
|
|
54
|
+
# Available log modes for caught records.
|
|
55
|
+
LOG_MODES = [
|
|
56
|
+
# Do not log:
|
|
57
|
+
:none,
|
|
58
|
+
# Log tag, time and age (or the raw unparseable time) only:
|
|
59
|
+
:metadata,
|
|
60
|
+
# Log the whole record:
|
|
61
|
+
:full,
|
|
62
|
+
# Aliases for :none and :full, kept for compatibility with the
|
|
63
|
+
# boolean *_log options of earlier versions:
|
|
64
|
+
:false,
|
|
65
|
+
:no,
|
|
66
|
+
:true,
|
|
67
|
+
:yes
|
|
68
|
+
].freeze
|
|
69
|
+
|
|
70
|
+
# Log modes the boolean aliases stand for.
|
|
71
|
+
LOG_MODE_ALIASES = { false: :none, no: :none, true: :full, yes: :full }.freeze
|
|
72
|
+
|
|
73
|
+
# Name and label names of the Prometheus counter for caught records.
|
|
74
|
+
METRIC_NAME = :fluentd_time_cutoff_records_total
|
|
75
|
+
METRIC_LABELS = %i[plugin_id worker_id hostname tag reason action].freeze
|
|
76
|
+
|
|
53
77
|
# strftime() format string for :iso8601 time format.
|
|
54
78
|
TIME_ISO8601 = '%FT%T%:z'
|
|
55
79
|
|
|
@@ -72,26 +96,28 @@ module Fluent
|
|
|
72
96
|
config_param :old_cutoff, :time, default: 86_400 # 24 hours
|
|
73
97
|
desc 'The action that will be performed to all "old" messages. Defaults to passthrough.'
|
|
74
98
|
config_param :old_action, :enum, list: CUTOFF_ACTIONS, default: :pass
|
|
75
|
-
desc 'Log the "old" messages to Fluentd\'s own log. Defaults to "
|
|
76
|
-
config_param :old_log, :
|
|
99
|
+
desc 'Log the "old" messages to Fluentd\'s own log: none (false/no), metadata or full (true/yes). Defaults to "full".'
|
|
100
|
+
config_param :old_log, :enum, list: LOG_MODES, default: :full
|
|
77
101
|
desc 'Records newer than this amount of time are deemed "new". Defaults to 24 hours.'
|
|
78
102
|
config_param :new_cutoff, :time, default: 86_400 # 24 hours
|
|
79
103
|
desc 'The action that will be performed to all "new" messages. Defaults to passthrough.'
|
|
80
104
|
config_param :new_action, :enum, list: CUTOFF_ACTIONS, default: :pass
|
|
81
|
-
desc 'Log the "new" messages to Fluentd\'s own log. Defaults to "
|
|
82
|
-
config_param :new_log, :
|
|
105
|
+
desc 'Log the "new" messages to Fluentd\'s own log: none (false/no), metadata or full (true/yes). Defaults to "full".'
|
|
106
|
+
config_param :new_log, :enum, list: LOG_MODES, default: :full
|
|
83
107
|
desc 'The key that will hold the original time (for :replace_timestamp action). Defaults to "source_time".'
|
|
84
108
|
config_param :source_time_key, :string, default: 'source_time'
|
|
85
109
|
desc 'Time format for the original timestamp field. Defaults to ISO8601-formatted string.'
|
|
86
110
|
config_param :source_time_format, :enum, list: TIME_FORMATS, default: :iso8601
|
|
87
111
|
desc 'The action that will be performed to all messages with an unparseable time. Defaults to replacing it with current time.'
|
|
88
112
|
config_param :bad_time_action, :enum, list: BAD_TIME_ACTIONS, default: :replace_timestamp
|
|
89
|
-
desc 'Log the messages with an unparseable time to Fluentd\'s own log. Defaults to "
|
|
90
|
-
config_param :bad_time_log, :
|
|
113
|
+
desc 'Log the messages with an unparseable time to Fluentd\'s own log: none (false/no), metadata or full (true/yes). Defaults to "full".'
|
|
114
|
+
config_param :bad_time_log, :enum, list: LOG_MODES, default: :full
|
|
91
115
|
desc 'The key that will hold the original unparseable time (for :replace_timestamp action). Defaults to "source_time_raw".'
|
|
92
116
|
config_param :bad_time_key, :string, default: 'source_time_raw'
|
|
93
117
|
desc 'The action that will be performed when the record already has source_time_key or bad_time_key. Defaults to keeping the existing value.'
|
|
94
118
|
config_param :time_key_conflict, :enum, list: TIME_KEY_CONFLICT_ACTIONS, default: :skip
|
|
119
|
+
desc 'Count caught records in a Prometheus counter (needs the prometheus-client gem). Defaults to "false".'
|
|
120
|
+
config_param :metrics, :bool, default: false
|
|
95
121
|
|
|
96
122
|
def configure(conf)
|
|
97
123
|
super
|
|
@@ -107,9 +133,16 @@ module Fluent
|
|
|
107
133
|
%i[source_time_key bad_time_key].each do |param|
|
|
108
134
|
raise Fluent::ConfigError, "#{param} must not be empty" if instance_variable_get("@#{param}").empty?
|
|
109
135
|
end
|
|
110
|
-
|
|
136
|
+
if @source_time_key == @bad_time_key
|
|
137
|
+
raise Fluent::ConfigError, "source_time_key and bad_time_key must differ, both are \"#{@source_time_key}\""
|
|
138
|
+
end
|
|
111
139
|
|
|
112
|
-
|
|
140
|
+
%i[old_log new_log bad_time_log].each do |param|
|
|
141
|
+
mode = instance_variable_get("@#{param}")
|
|
142
|
+
instance_variable_set("@#{param}", LOG_MODE_ALIASES.fetch(mode, mode))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
setup_metrics
|
|
113
146
|
end
|
|
114
147
|
|
|
115
148
|
def filter_with_time(tag, time, record)
|
|
@@ -139,6 +172,67 @@ module Fluent
|
|
|
139
172
|
|
|
140
173
|
private
|
|
141
174
|
|
|
175
|
+
# Set up the Prometheus counter for caught records, if enabled. The
|
|
176
|
+
# counter is shared by all time_cutoff filters in the process.
|
|
177
|
+
def setup_metrics
|
|
178
|
+
return unless @metrics
|
|
179
|
+
|
|
180
|
+
unless load_prometheus_client
|
|
181
|
+
log.warn 'Metrics are enabled, but the prometheus-client gem is not installed, so caught records will not be counted.'
|
|
182
|
+
return
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
registry = prometheus_registry
|
|
186
|
+
@records_counter = registry.get(METRIC_NAME) || registry.counter(
|
|
187
|
+
METRIC_NAME,
|
|
188
|
+
docstring: 'Number of records caught by the time_cutoff filter.',
|
|
189
|
+
labels: METRIC_LABELS
|
|
190
|
+
)
|
|
191
|
+
@metric_labels = { plugin_id: plugin_id, worker_id: worker_id.to_s, hostname: Socket.gethostname }
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Load the prometheus-client gem, which is an optional dependency.
|
|
195
|
+
# @return [Boolean] whether the gem could be loaded
|
|
196
|
+
def load_prometheus_client
|
|
197
|
+
require 'prometheus/client'
|
|
198
|
+
true
|
|
199
|
+
rescue LoadError
|
|
200
|
+
false
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# The Prometheus registry to register the counter in. This is the one
|
|
204
|
+
# fluent-plugin-prometheus serves on its /metrics endpoints.
|
|
205
|
+
def prometheus_registry
|
|
206
|
+
::Prometheus::Client.registry
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# ID of the Fluentd worker this plugin runs in.
|
|
210
|
+
# NOTE: Plugin::Base#fluentd_worker_id isn't available in the oldest
|
|
211
|
+
# Fluentd releases declared in the gemspec, so fall back to the env var
|
|
212
|
+
# it reads.
|
|
213
|
+
# @return [Integer] worker ID
|
|
214
|
+
def worker_id
|
|
215
|
+
return fluentd_worker_id if respond_to?(:fluentd_worker_id, true)
|
|
216
|
+
|
|
217
|
+
(ENV['SERVERENGINE_WORKER_ID'] || 0).to_i
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Count a caught record. Errors are logged once and never affect how
|
|
221
|
+
# the record is processed.
|
|
222
|
+
# @param tag [String] log/stream's tag name
|
|
223
|
+
# @param reason [Symbol] why the record was caught (:old, :new or :bad_time)
|
|
224
|
+
# @param action [Symbol] determined action for this record
|
|
225
|
+
def count_record(tag, reason, action)
|
|
226
|
+
return unless @records_counter
|
|
227
|
+
|
|
228
|
+
@records_counter.increment(labels: @metric_labels.merge(tag: tag, reason: reason.to_s, action: action.to_s))
|
|
229
|
+
rescue StandardError => e
|
|
230
|
+
return if @metrics_error_logged
|
|
231
|
+
|
|
232
|
+
@metrics_error_logged = true
|
|
233
|
+
log.warn "Failed to update metrics, further errors will not be logged: #{e.class}: #{e.message}"
|
|
234
|
+
end
|
|
235
|
+
|
|
142
236
|
# Current UNIX time, used as the reference point for the age check.
|
|
143
237
|
# Kept as a separate method so that tests can freeze the clock.
|
|
144
238
|
# @return [Integer] current time in seconds
|
|
@@ -176,10 +270,11 @@ module Fluent
|
|
|
176
270
|
# @param tag [String] log/stream's tag name
|
|
177
271
|
# @param time [Fluent::EventTime] the original event time
|
|
178
272
|
# @param record [Hash] the original record contents
|
|
273
|
+
# @param log_mode [Symbol] log mode for this record
|
|
179
274
|
# @param @source_time_key [String] name of the field to put the old timestamp to.
|
|
180
275
|
# @return [Array] new event time and a modified record.
|
|
181
|
-
def do_replace_timestamp(tag, time, record)
|
|
182
|
-
[Fluent::EventTime.now, put_time_key(tag, record, @source_time_key, format_time(time))]
|
|
276
|
+
def do_replace_timestamp(tag, time, record, log_mode)
|
|
277
|
+
[Fluent::EventTime.now, put_time_key(tag, record, @source_time_key, format_time(time), log_mode)]
|
|
183
278
|
end
|
|
184
279
|
|
|
185
280
|
# Put the original time into the given field of a copy of the record,
|
|
@@ -189,10 +284,11 @@ module Fluent
|
|
|
189
284
|
# @param record [Hash] the original record contents
|
|
190
285
|
# @param key [String] name of the field to put the time to
|
|
191
286
|
# @param value the time value to put into the field
|
|
287
|
+
# @param log_mode [Symbol] log mode for this record
|
|
192
288
|
# @return [Hash] the modified copy of the record, or the original record if kept as is
|
|
193
|
-
def put_time_key(tag, record, key, value)
|
|
289
|
+
def put_time_key(tag, record, key, value, log_mode) # rubocop:disable Metrics/ParameterLists
|
|
194
290
|
if record.key?(key) && @time_key_conflict != :overwrite
|
|
195
|
-
log_time_key_conflict(tag, record, key, value) if @time_key_conflict == :warn
|
|
291
|
+
log_time_key_conflict(tag, record, key, value, log_mode) if @time_key_conflict == :warn
|
|
196
292
|
return record
|
|
197
293
|
end
|
|
198
294
|
|
|
@@ -201,44 +297,57 @@ module Fluent
|
|
|
201
297
|
new_record
|
|
202
298
|
end
|
|
203
299
|
|
|
204
|
-
# Log a time key conflict to Fluentd's log.
|
|
300
|
+
# Log a time key conflict to Fluentd's log. It is logged even if the
|
|
301
|
+
# record's log mode is :none, since it was asked for explicitly, but the
|
|
302
|
+
# record itself is only included in :full mode.
|
|
205
303
|
# @param tag [String] log/stream's tag name
|
|
206
304
|
# @param record [Hash] the original record contents
|
|
207
305
|
# @param key [String] name of the conflicting field
|
|
208
306
|
# @param value the time value that was not put into the field
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
307
|
+
# @param log_mode [Symbol] log mode for this record
|
|
308
|
+
def log_time_key_conflict(tag, record, key, value, log_mode) # rubocop:disable Metrics/ParameterLists
|
|
309
|
+
subject = log_mode == :full ? "#{tag}: #{dump_record(record)}" : tag
|
|
310
|
+
log.warn format(
|
|
311
|
+
'Record already has the "%<key>s" key, keeping its existing value instead of %<val>s: "%<subject>s".',
|
|
312
|
+
key: key, val: value.inspect, subject: subject
|
|
213
313
|
)
|
|
214
|
-
log.warn log_line
|
|
215
314
|
end
|
|
216
315
|
|
|
217
316
|
# Log the caught event to Fluentd's log.
|
|
218
317
|
# @param tag [String] log/stream's tag name
|
|
219
|
-
# @param time [Fluent::EventTime] the original event time
|
|
318
|
+
# @param time [Fluent::EventTime] the original event time, or the raw
|
|
319
|
+
# time for :bad_time records
|
|
220
320
|
# @param record [Hash] the original record contents
|
|
221
321
|
# @param action [Symbol] determined action for this record
|
|
222
|
-
# @param
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
322
|
+
# @param reason [Symbol] why the record was caught (:old, :new or :bad_time)
|
|
323
|
+
# @param log_mode [Symbol] :none, :metadata (no record contents) or :full
|
|
324
|
+
def log_record(tag, time, record, action, reason, log_mode) # rubocop:disable Metrics/ParameterLists
|
|
325
|
+
return if log_mode == :none
|
|
326
|
+
|
|
327
|
+
details = reason == :bad_time ? time.inspect : iso8601(time)
|
|
328
|
+
if log_mode == :full
|
|
329
|
+
details = "#{details} #{dump_record(record)}"
|
|
330
|
+
elsif reason != :bad_time
|
|
331
|
+
details = "#{details} (age #{format_age(current_time - time.to_i)})"
|
|
332
|
+
end
|
|
333
|
+
log.warn format(
|
|
334
|
+
'Record caught [%<reason>s, %<act>s]: "%<tag>s: %<details>s".',
|
|
335
|
+
reason: reason, act: action, tag: tag, details: details
|
|
227
336
|
)
|
|
228
|
-
log.warn log_line
|
|
229
337
|
end
|
|
230
338
|
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
# @param
|
|
234
|
-
# @
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
339
|
+
# Format an age in seconds as a compact human-readable string, e.g.
|
|
340
|
+
# "1d 2h 3m 4s", "5m 0s" or "-2h 0m 5s" for times in the future.
|
|
341
|
+
# @param seconds [Integer] the age in seconds
|
|
342
|
+
# @return [String] the formatted age
|
|
343
|
+
def format_age(seconds)
|
|
344
|
+
rest = seconds.abs
|
|
345
|
+
parts = [[86_400, 'd'], [3600, 'h'], [60, 'm'], [1, 's']].map do |size, unit|
|
|
346
|
+
value, rest = rest.divmod(size)
|
|
347
|
+
"#{value}#{unit}"
|
|
348
|
+
end
|
|
349
|
+
parts.shift while parts.size > 1 && parts.first.start_with?('0')
|
|
350
|
+
"#{'-' if seconds.negative?}#{parts.join(' ')}"
|
|
242
351
|
end
|
|
243
352
|
|
|
244
353
|
# Serialize the record for logging. Falls back to #inspect when the
|
|
@@ -304,17 +413,18 @@ module Fluent
|
|
|
304
413
|
# @param tag [String] log/stream's tag name
|
|
305
414
|
# @param time [Fluent::EventTime] the original event time
|
|
306
415
|
# @param record [Hash] the original record contents
|
|
307
|
-
# @param
|
|
416
|
+
# @param log_mode [Symbol] log mode for this record
|
|
308
417
|
# @param action [Symbol] determined action for this record
|
|
309
|
-
# @param
|
|
310
|
-
def process_record(tag, time, record,
|
|
311
|
-
|
|
418
|
+
# @param reason [Symbol] why the record was caught (:old or :new)
|
|
419
|
+
def process_record(tag, time, record, log_mode, action, reason) # rubocop:disable Metrics/ParameterLists
|
|
420
|
+
count_record(tag, reason, action)
|
|
421
|
+
log_record(tag, time, record, action, reason, log_mode)
|
|
312
422
|
|
|
313
423
|
case action
|
|
314
424
|
when :pass
|
|
315
425
|
[time, record]
|
|
316
426
|
when :replace_timestamp
|
|
317
|
-
do_replace_timestamp(tag, time, record)
|
|
427
|
+
do_replace_timestamp(tag, time, record, log_mode)
|
|
318
428
|
when :drop
|
|
319
429
|
nil
|
|
320
430
|
end
|
|
@@ -326,7 +436,8 @@ module Fluent
|
|
|
326
436
|
# @param record [Hash] the original record contents
|
|
327
437
|
# @return [Array,nil] current time and the record, or nil if dropped.
|
|
328
438
|
def process_bad_time_record(tag, time, record)
|
|
329
|
-
|
|
439
|
+
count_record(tag, :bad_time, @bad_time_action)
|
|
440
|
+
log_record(tag, time, record, @bad_time_action, :bad_time, @bad_time_log)
|
|
330
441
|
|
|
331
442
|
case @bad_time_action
|
|
332
443
|
when :replace_timestamp
|
|
@@ -347,7 +458,7 @@ module Fluent
|
|
|
347
458
|
# @return [Array] new event time and a modified record.
|
|
348
459
|
def do_replace_bad_timestamp(tag, time, record)
|
|
349
460
|
raw_time = time.is_a?(String) ? time : time.inspect
|
|
350
|
-
[Fluent::EventTime.now, put_time_key(tag, record, @bad_time_key, raw_time)]
|
|
461
|
+
[Fluent::EventTime.now, put_time_key(tag, record, @bad_time_key, raw_time, @bad_time_log)]
|
|
351
462
|
end
|
|
352
463
|
|
|
353
464
|
# Process the "old" record according to the configuration
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fluent-plugin-time-cutoff
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Qrator Labs
|
|
@@ -30,6 +30,26 @@ dependencies:
|
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '2'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: prometheus-client
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.1'
|
|
40
|
+
- - "<"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '6'
|
|
43
|
+
type: :development
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '2.1'
|
|
50
|
+
- - "<"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '6'
|
|
33
53
|
- !ruby/object:Gem::Dependency
|
|
34
54
|
name: rake
|
|
35
55
|
requirement: !ruby/object:Gem::Requirement
|