fluent-plugin-mutate_filter 0.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 +7 -0
- data/.gitignore +10 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/README.md +33 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fluent-plugin-mutate_filter.gemspec +18 -0
- data/lib/fluent/plugin/filter_mutate.rb +453 -0
- data/lib/fluent/plugin_mixin/mutate_event.rb +78 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 289f196651f835bc0e7ea5dab50346802c25c1ed
|
4
|
+
data.tar.gz: 3dda295dc953b5ff46804564e689618f29c75d59
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ffa1c0787fc79a87f7d39cff9af9064b0a7d4c35d5c9b21057ee54147da95169c00f8231d7399a41866ffde2db5b60979627880e9e897cc94a9b7cfb25accfdb
|
7
|
+
data.tar.gz: 9f4231a628362dbb30d6c60a3d639bd5141ddb69c6f744965dad045b5e5c87109090d90c1965f6f98d784c29cfe391862e70671def104d141bcb4d839360dc8e
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Fluent::MutateFilter
|
2
|
+
|
3
|
+
This gem provides the `mutate` filter for Fluentd which is designed to replicate the way `mutate` works in Logstash as much as possible.
|
4
|
+
|
5
|
+
To be honest, this is a translation of [logstash-filter-mutate](https://github.com/logstash-plugins/logstash-filter-mutate) bordering on copy-paste.
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
|
9
|
+
* Fluentd v0.12+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
```bash
|
14
|
+
gem install fluent-plugin-mutate_filter
|
15
|
+
```
|
16
|
+
|
17
|
+
## Configuration Options
|
18
|
+
|
19
|
+
All of the documentation and potential options are documented in the config_params section of the filter module. Below, only a subset of the options are displayed.
|
20
|
+
|
21
|
+
```
|
22
|
+
<filter *>
|
23
|
+
@type mutate
|
24
|
+
rename {
|
25
|
+
"old_field_name": "new_field_name",
|
26
|
+
"old_nest.field_name": "new_nest.field_name"
|
27
|
+
}
|
28
|
+
replace {
|
29
|
+
"new_nest.field_name": "%{old_nest.field_name}"
|
30
|
+
}
|
31
|
+
</filter>
|
32
|
+
```
|
33
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "fluent/plugin/mutate_filter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
Gem::Specification.new do |spec|
|
3
|
+
spec.name = "fluent-plugin-mutate_filter"
|
4
|
+
spec.version = "0.1.0"
|
5
|
+
spec.authors = ["Jonathan Serafini"]
|
6
|
+
spec.email = ["jonathan@serafini.ca"]
|
7
|
+
spec.summary = %q{A mutate filter for Fluent which functions like Logstash.}
|
8
|
+
spec.description = spec.description
|
9
|
+
spec.homepage = "http://fuck.off"
|
10
|
+
|
11
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
12
|
+
spec.executables = []
|
13
|
+
spec.require_paths = ["lib"]
|
14
|
+
|
15
|
+
spec.add_runtime_dependency "fluentd", [">= 0.12.0", "< 0.15.0"]
|
16
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
17
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
18
|
+
end
|
@@ -0,0 +1,453 @@
|
|
1
|
+
require 'fluent/filter'
|
2
|
+
require 'fluent/plugin_mixin/mutate_event'
|
3
|
+
|
4
|
+
module Fluent
|
5
|
+
class MutateFilter < Filter
|
6
|
+
Fluent::Plugin.register_filter('mutate', self)
|
7
|
+
|
8
|
+
# Treat periods as nested field names
|
9
|
+
config_param :expand_nesting, :bool, default: true
|
10
|
+
|
11
|
+
# Remove any empty hashes or arrays
|
12
|
+
config_param :prune_empty, :bool, default: true
|
13
|
+
|
14
|
+
# Rename one or more fields
|
15
|
+
# @example
|
16
|
+
# rename {
|
17
|
+
# "timestamp": "@timestamp"
|
18
|
+
# }
|
19
|
+
config_param :rename, :hash, default: Hash.new
|
20
|
+
|
21
|
+
# Update an existing field with a new value.
|
22
|
+
# - If the field does not exist then no action will be taken.
|
23
|
+
# - If the new value contains a placeholder %{}, then the value will be
|
24
|
+
# expanded to the related event record field.
|
25
|
+
# @example
|
26
|
+
# update {
|
27
|
+
# "message": "%{hostname}: new message"
|
28
|
+
# }
|
29
|
+
config_param :update, :hash, default: Hash.new
|
30
|
+
|
31
|
+
# Remove an existing field
|
32
|
+
# @example
|
33
|
+
# remove [
|
34
|
+
# "dummy1", "placeholder1"
|
35
|
+
# ]
|
36
|
+
config_param :remove, :array, default: Array.new
|
37
|
+
|
38
|
+
# Replace a field with a new value
|
39
|
+
# - If the field does not exist, then it will be created.
|
40
|
+
# - If the new value contains a placeholder %{}, then the value will be
|
41
|
+
# expanded to the related event record field.
|
42
|
+
# @example
|
43
|
+
# replace {
|
44
|
+
# "new_message": "a new field"
|
45
|
+
# }
|
46
|
+
config_param :replace, :hash, default: Hash.new
|
47
|
+
|
48
|
+
# Convert a field's value to a different type, like turning a string to an
|
49
|
+
# integer.
|
50
|
+
# - If the field value is an array, all members will be converted.
|
51
|
+
# - If the field value is a hash, then no action will be taken.
|
52
|
+
# - Valid conversion types are integer, float, string, boolean
|
53
|
+
# @example
|
54
|
+
# convert {
|
55
|
+
# "id": "integer",
|
56
|
+
# "message": "string"
|
57
|
+
# }
|
58
|
+
config_param :convert, :hash, default: Hash.new
|
59
|
+
|
60
|
+
# Convert a string field by applying a regular expression and replacement.
|
61
|
+
# - If the field is not a string, then no action will be taken.
|
62
|
+
#
|
63
|
+
# The configuration takes an array consisting of 3 elements per field/sub.
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# gsub [
|
67
|
+
# "fieldname", "/", "_",
|
68
|
+
# "fieldname2", "[\\?#-]", "."
|
69
|
+
# ]
|
70
|
+
config_param :gsub, :hash, default: Array.new
|
71
|
+
|
72
|
+
# Convert a string to it's uppercase equivalent
|
73
|
+
# @example
|
74
|
+
# uppercase [
|
75
|
+
# "field1", "field2"
|
76
|
+
# ]
|
77
|
+
config_param :uppercase, :array, default: Array.new
|
78
|
+
|
79
|
+
# Convert a string to it's lowercase equivalent
|
80
|
+
# @example
|
81
|
+
# lowercase [
|
82
|
+
# "field1", "field2"
|
83
|
+
# ]
|
84
|
+
config_param :lowercase, :array, default: Array.new
|
85
|
+
|
86
|
+
# Strip whitespace from field.
|
87
|
+
# @example
|
88
|
+
# strip [
|
89
|
+
# "field1"
|
90
|
+
# ]
|
91
|
+
config_param :strip, :array, default: Array.new
|
92
|
+
|
93
|
+
# Split a field to an array using a separator character
|
94
|
+
# @example
|
95
|
+
# split {
|
96
|
+
# "field1": ","
|
97
|
+
# }
|
98
|
+
config_param :split, :hash, default: Hash.new
|
99
|
+
|
100
|
+
# Join an array using a separator character
|
101
|
+
# @example
|
102
|
+
# join {
|
103
|
+
# "field1": " "
|
104
|
+
# }
|
105
|
+
config_param :join, :hash, default: Hash.new
|
106
|
+
|
107
|
+
# Merge two fields of arrays or hashes
|
108
|
+
# @example
|
109
|
+
# merge {
|
110
|
+
# "dest_field": "added_field"
|
111
|
+
# }
|
112
|
+
config_param :merge, :hash, default: Hash.new
|
113
|
+
|
114
|
+
# List of all possible mutate actions, in the order that we will apply
|
115
|
+
# them. As it stands, this is the order in which Logstash would apply them.
|
116
|
+
MUTATE_ACTIONS = %w(
|
117
|
+
rename
|
118
|
+
update
|
119
|
+
replace
|
120
|
+
convert
|
121
|
+
gsub
|
122
|
+
uppercase
|
123
|
+
lowercase
|
124
|
+
strip
|
125
|
+
remove
|
126
|
+
split
|
127
|
+
join
|
128
|
+
merge
|
129
|
+
)
|
130
|
+
|
131
|
+
# Convert valid types
|
132
|
+
VALID_CONVERSIONS = %w(string integer float boolean)
|
133
|
+
|
134
|
+
# Convert helper method prefix
|
135
|
+
CONVERT_PREFIX = "convert_".freeze
|
136
|
+
|
137
|
+
# Convert boolean regex
|
138
|
+
TRUE_REGEX = (/^(true|t|yes|y|1)$/i).freeze
|
139
|
+
FALSE_REGEX = (/^(false|f|no|n|0)$/i).freeze
|
140
|
+
|
141
|
+
# Placeholder regex
|
142
|
+
TEMPLATE_TAG_REGEXP = /%\{[^}]+\}/
|
143
|
+
|
144
|
+
# Initialize attributes and parameters
|
145
|
+
# @since 0.1.0
|
146
|
+
# @return [NilClass]
|
147
|
+
def configure(conf)
|
148
|
+
super
|
149
|
+
|
150
|
+
@convert.nil? or @convert.each do |field, type|
|
151
|
+
if !VALID_CONVERSIONS.include?(type)
|
152
|
+
raise ConfigError,
|
153
|
+
"convert #{type} is not one of #{VALID_CONVERSIONS.join(',')}."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@gsub_parsed = []
|
158
|
+
@gsub.nil? or
|
159
|
+
@gsub.each_slice(3) do |field, needle, replacement|
|
160
|
+
if [field, needle, replacement].any? {|n| n.nil?}
|
161
|
+
raise ConfigError,
|
162
|
+
"gsub #{[field,needle,replacement]} requires 3 elements."
|
163
|
+
end
|
164
|
+
|
165
|
+
@gsub_parsed << {
|
166
|
+
field: field,
|
167
|
+
needle: (needle.index("%{").nil?? Regexp.new(needle): needle),
|
168
|
+
replacement: replacement
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Filter action which will manipulate records
|
174
|
+
# @since 0.1.0
|
175
|
+
# @return [Hash] the modified event record
|
176
|
+
def filter(tag, time, record)
|
177
|
+
# In order to more easily navigate the record, we wrap the record in a
|
178
|
+
# delegator. We additionally pass the `expand_nesting` option which
|
179
|
+
# determines whether we should treat periods as field separators.
|
180
|
+
result = Fluent::PluginMixin::MutateEvent.
|
181
|
+
new(record, expand_nesting: @expand_nesting)
|
182
|
+
|
183
|
+
MUTATE_ACTIONS.each do |action|
|
184
|
+
begin
|
185
|
+
send(action.to_sym, result) if instance_variable_get("@#{action}")
|
186
|
+
rescue => e
|
187
|
+
log.warn "failed to mutate #{action} action", error: e
|
188
|
+
log.warn_backtrace
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
result.prune if @prune_empty
|
193
|
+
result.to_record
|
194
|
+
end
|
195
|
+
|
196
|
+
protected
|
197
|
+
|
198
|
+
# Expand %{} strings to the related event fields.
|
199
|
+
# @since 0.1.0
|
200
|
+
# @return [String] the modified string
|
201
|
+
def expand_references(event, string)
|
202
|
+
new_string = ''
|
203
|
+
|
204
|
+
position = 0
|
205
|
+
matches = string.scan(TEMPLATE_TAG_REGEXP).map{|m| $~}
|
206
|
+
|
207
|
+
matches.each do |match|
|
208
|
+
reference_tag = match[0][2..-2]
|
209
|
+
reference_value = event.get(reference_tag.downcase).to_s
|
210
|
+
if reference_value.nil?
|
211
|
+
@log.error "failed to replace tag", field: reference_tag.downcase
|
212
|
+
reference_value = match.to_s
|
213
|
+
end
|
214
|
+
|
215
|
+
start = match.offset(0).first
|
216
|
+
new_string << string[position..(start-1)] if start > 0
|
217
|
+
new_string << reference_value
|
218
|
+
position = match.offset(0).last
|
219
|
+
end
|
220
|
+
|
221
|
+
if position < string.size
|
222
|
+
new_string << string[position..-1]
|
223
|
+
end
|
224
|
+
|
225
|
+
new_string
|
226
|
+
end
|
227
|
+
|
228
|
+
# Remove fields from the event hash
|
229
|
+
# @since 0.1.0
|
230
|
+
def remove(event)
|
231
|
+
@remove.each do |field|
|
232
|
+
event.remove(field)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Rename fields in the event hash
|
237
|
+
# @since 0.1.0
|
238
|
+
def rename(event)
|
239
|
+
@rename.each do |old, new|
|
240
|
+
item = event.get(old)
|
241
|
+
next if item.nil?
|
242
|
+
event.set(new, item)
|
243
|
+
event.remove(old)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Update (existing) fields in the event hash
|
248
|
+
# @since 0.1.0
|
249
|
+
def update(event)
|
250
|
+
@update.each do |field, newvalue|
|
251
|
+
newvalue = expand_references(event, newvalue)
|
252
|
+
next unless event.include?(field)
|
253
|
+
event.set(field, newvalue)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Replace fields in the event hash
|
258
|
+
# @since 0.1.0
|
259
|
+
def replace(event)
|
260
|
+
@replace.each do |field, newvalue|
|
261
|
+
newvalue = expand_references(event, newvalue)
|
262
|
+
event.set(field, newvalue)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Convert fields to given types in the record hash
|
267
|
+
# @since 0.1.0
|
268
|
+
def convert(event)
|
269
|
+
@convert.each do |field, type|
|
270
|
+
converter = method(CONVERT_PREFIX + type)
|
271
|
+
|
272
|
+
case original = event.get(field)
|
273
|
+
when NilClass
|
274
|
+
next
|
275
|
+
when Hash
|
276
|
+
@log.error("cannot convert hash", field: field, value: original)
|
277
|
+
when Array
|
278
|
+
event.set(field, original.map{|v| converter.call(v)})
|
279
|
+
else
|
280
|
+
event.set(field, converter.call(original))
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def convert_string(value)
|
286
|
+
value.to_s.force_encoding(Encoding::UTF_8)
|
287
|
+
end
|
288
|
+
|
289
|
+
def convert_integer(value)
|
290
|
+
value.to_i
|
291
|
+
end
|
292
|
+
|
293
|
+
def convert_float(value)
|
294
|
+
value.to_f
|
295
|
+
end
|
296
|
+
|
297
|
+
def convert_boolean(value)
|
298
|
+
return true if value =~ TRUE_REGEX
|
299
|
+
return false if value.empty? || value =~ FALSE_REGEX
|
300
|
+
@log.error("failed to convert to boolean", value: value)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Convert field values to uppercase in the record hash
|
304
|
+
# @since 0.1.0
|
305
|
+
def uppercase(event)
|
306
|
+
@uppercase.each do |field|
|
307
|
+
original = event.get(field)
|
308
|
+
result = case original
|
309
|
+
when Array
|
310
|
+
original.map do |elem|
|
311
|
+
(elem.is_a?(String) ? elemen.upcase : elem)
|
312
|
+
end
|
313
|
+
when String
|
314
|
+
original.upcase! || original
|
315
|
+
else
|
316
|
+
@log.error("can't uppercase field",
|
317
|
+
field: field,
|
318
|
+
value: original)
|
319
|
+
original
|
320
|
+
end
|
321
|
+
event.set(field, result)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Convert field values to lowercase in the record hash
|
326
|
+
# @since 0.1.0
|
327
|
+
def lowercase(event)
|
328
|
+
@lowercase.each do |field|
|
329
|
+
original = event.get(field)
|
330
|
+
result = case original
|
331
|
+
when Array
|
332
|
+
original.map do |elem|
|
333
|
+
(elem.is_a?(String) ? elemen.downcase : elem)
|
334
|
+
end
|
335
|
+
when String
|
336
|
+
original.downcase! || original
|
337
|
+
else
|
338
|
+
@log.error("can't lowercase field",
|
339
|
+
field: field,
|
340
|
+
value: original)
|
341
|
+
original
|
342
|
+
end
|
343
|
+
event.set(field, result)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Split fields based on delimiters in the record hash
|
348
|
+
# @since 0.1.0
|
349
|
+
def split(event)
|
350
|
+
@split.each do |field, separator|
|
351
|
+
value = event.get(field)
|
352
|
+
if value.is_a?(String)
|
353
|
+
event.set(field, value.split(separator))
|
354
|
+
else
|
355
|
+
@loger.error("can't split field",
|
356
|
+
field: field,
|
357
|
+
value: value)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Join fields based on delimiters in the record hash
|
363
|
+
# @since 0.1.0
|
364
|
+
def join(event)
|
365
|
+
@join.each do |field, separator|
|
366
|
+
value = event.get(field)
|
367
|
+
if value.is_a?(Array)
|
368
|
+
event.set(field, value.join(separator))
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Strip whitespace surrounding fields in the record hash
|
374
|
+
# @since 0.1.0
|
375
|
+
def strip(event)
|
376
|
+
@strip.each do |field|
|
377
|
+
value = event.get(field)
|
378
|
+
case value
|
379
|
+
when Array
|
380
|
+
event.set(field, value.map{|s| s.strip})
|
381
|
+
when String
|
382
|
+
event.set(field, value.strip)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Merge hashes and arrays in the record hash
|
388
|
+
# @since 0.1.0
|
389
|
+
def merge(event)
|
390
|
+
@merge.each do |dest_field, added_fields|
|
391
|
+
dest_field_value = event.get(dest_field)
|
392
|
+
|
393
|
+
Array(added_fields).each do |added_field|
|
394
|
+
added_field_value = event.get(added_field)
|
395
|
+
|
396
|
+
if dest_field_value.is_a?(Hash) ^ added_field_value.is_a?(Hash)
|
397
|
+
@log.error('cannot merge an array and hash',
|
398
|
+
dest_field: dest_field,
|
399
|
+
added_field: added_field)
|
400
|
+
next
|
401
|
+
end
|
402
|
+
|
403
|
+
if dest_field_value.is_a?(Hash)
|
404
|
+
event.set(dest_field, dest_field_value.update(added_field_value))
|
405
|
+
else
|
406
|
+
event.set(dest_field, Array(dest_field_value).
|
407
|
+
concat(Array(added_field_value)))
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Perform regular expression substitutions in the record hahs
|
414
|
+
# @since 0.1.0
|
415
|
+
def gsub(event)
|
416
|
+
@gsub_parsed.each do |config|
|
417
|
+
field = config[:field]
|
418
|
+
needle = config[:needle]
|
419
|
+
replacement = config[:replacement]
|
420
|
+
|
421
|
+
value = event.get(field)
|
422
|
+
case value
|
423
|
+
when Array
|
424
|
+
result = value.map do |v|
|
425
|
+
if v.is_a?(String)
|
426
|
+
gsub_dynamic_fields(event, v, needle, replacement)
|
427
|
+
else
|
428
|
+
@log.error('cannot gsub non Strings',
|
429
|
+
field: field,
|
430
|
+
value: v)
|
431
|
+
end
|
432
|
+
event.set(field, result)
|
433
|
+
end
|
434
|
+
when String
|
435
|
+
v = gsub_dynamic_fields(event, value, needle, replacement)
|
436
|
+
event.set(field, v)
|
437
|
+
else
|
438
|
+
@log.error('cannot gsub non Strings', field: field, value: value)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
def gsub_dynamic_fields(event, original, needle, replacement)
|
444
|
+
replacement = expand_references(event, replacement)
|
445
|
+
if needle.is_a?(Regexp)
|
446
|
+
original.gsub(needle, replacement)
|
447
|
+
else
|
448
|
+
original.gsub(Regexp.new(needle), replacement)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Fluent
|
2
|
+
module PluginMixin
|
3
|
+
class MutateEvent < SimpleDelegator
|
4
|
+
def initialize(record, expand_nesting = true)
|
5
|
+
super(record)
|
6
|
+
@record = record
|
7
|
+
@expand_nesting = expand_nesting
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_record
|
11
|
+
@record
|
12
|
+
end
|
13
|
+
|
14
|
+
def dig(*keys)
|
15
|
+
item = @record
|
16
|
+
|
17
|
+
keys.each do |key|
|
18
|
+
break if item.nil?
|
19
|
+
item = item.is_a?(Hash) ? item[key] : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
item
|
23
|
+
end
|
24
|
+
|
25
|
+
def prune
|
26
|
+
delete_proc = proc do |_,v|
|
27
|
+
v.delete_if(&delete_proc) if v.respond_to?(:delete_if)
|
28
|
+
v.nil? || v.respond_to?(:empty?) && v.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
@record.delete_if(&delete_proc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(key_or_path, &block)
|
35
|
+
item = dig(*expand_key(key_or_path))
|
36
|
+
block_given? ? yield(item) : item
|
37
|
+
end
|
38
|
+
|
39
|
+
def parent(key_or_path, &block)
|
40
|
+
path = expand_key(key_or_path)
|
41
|
+
child = path.pop
|
42
|
+
|
43
|
+
item = dig(*path)
|
44
|
+
block_given? ? yield(item, child) : item
|
45
|
+
end
|
46
|
+
|
47
|
+
def set(key_or_path, value)
|
48
|
+
path = expand_key(key_or_path)
|
49
|
+
child = path.pop
|
50
|
+
|
51
|
+
item = @record
|
52
|
+
path.each do |key|
|
53
|
+
item = item[key] ||= {}
|
54
|
+
end
|
55
|
+
item[child] = value
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove(key_or_path)
|
59
|
+
path = expand_key(key_or_path)
|
60
|
+
child = path.pop
|
61
|
+
|
62
|
+
parent(key_or_path) do |item, child|
|
63
|
+
item.delete(child) unless item.nil?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def include?(key_or_path)
|
68
|
+
!get(key_or_path).nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def expand_key(key_or_path)
|
74
|
+
@expand_nesting ? key_or_path.split(".") : [key_or_path]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-mutate_filter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Serafini
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.12.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.15.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.12.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.15.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.11'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.11'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
description: ''
|
62
|
+
email:
|
63
|
+
- jonathan@serafini.ca
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- ".gitignore"
|
69
|
+
- CHANGELOG.md
|
70
|
+
- Gemfile
|
71
|
+
- README.md
|
72
|
+
- Rakefile
|
73
|
+
- bin/console
|
74
|
+
- bin/setup
|
75
|
+
- fluent-plugin-mutate_filter.gemspec
|
76
|
+
- lib/fluent/plugin/filter_mutate.rb
|
77
|
+
- lib/fluent/plugin_mixin/mutate_event.rb
|
78
|
+
homepage: http://fuck.off
|
79
|
+
licenses: []
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.5.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: A mutate filter for Fluent which functions like Logstash.
|
101
|
+
test_files: []
|
102
|
+
has_rdoc:
|