fluent-plugin-mutate_filter 0.3.1 → 1.0.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.
@@ -1,514 +1,489 @@
1
- require 'socket'
2
- require 'fluent/filter'
3
- require 'fluent/plugin_mixin/mutate_event'
1
+ require "fluent/config/types"
2
+ require "fluent/plugin/filter"
3
+ require "fluent/plugin/mixin/mutate_event"
4
4
 
5
5
  module Fluent
6
- class MutateFilter < Filter
7
- Fluent::Plugin.register_filter('mutate', self)
8
-
9
- # Treat periods as nested field names
10
- config_param :expand_nesting, :bool, default: true
11
-
12
- # Remove any empty hashes or arrays
13
- config_param :prune_empty, :bool, default: true
14
-
15
- # Rename one or more fields
16
- # @example
17
- # rename {
18
- # "timestamp": "@timestamp"
19
- # }
20
- config_param :rename, :hash, default: Hash.new
21
-
22
- # Update an existing field with a new value.
23
- # - If the field does not exist then no action will be taken.
24
- # - If the new value contains a placeholder %{}, then the value will be
25
- # expanded to the related event record field.
26
- # - If the new value contains a placeholder %e{}, then the value will be
27
- # expanded to the relevant environment variable.
28
- # @example
29
- # update {
30
- # "message": "%e{HOSTNAME}: new message"
31
- # }
32
- config_param :update, :hash, default: Hash.new
33
-
34
- # Remove an existing field
35
- # @example
36
- # remove [
37
- # "dummy1", "placeholder1"
38
- # ]
39
- config_param :remove, :array, default: Array.new
40
-
41
- # Replace a field with a new value
42
- # - If the field does not exist, then it will be created.
43
- # - If the new value contains a placeholder %{}, then the value will be
44
- # expanded to the related event record field.
45
- # - If the new value contains a placeholder %e{}, then the value will be
46
- # expanded to the relevant environment variable.
47
- # @example
48
- # replace {
49
- # "new_message": "a new field"
50
- # }
51
- config_param :replace, :hash, default: Hash.new
52
-
53
- # Convert a field's value to a different type, like turning a string to an
54
- # integer.
55
- # - If the field value is an array, all members will be converted.
56
- # - If the field value is a hash, then no action will be taken.
57
- # - Valid conversion types are integer, float, string, boolean
58
- # @example
59
- # convert {
60
- # "id": "integer",
61
- # "message": "string"
62
- # }
63
- config_param :convert, :hash, default: Hash.new
64
-
65
- # Convert a string field by applying a regular expression and replacement.
66
- # - If the field is not a string, then no action will be taken.
67
- #
68
- # The configuration takes an array consisting of 3 elements per field/sub.
69
- #
70
- # @example
71
- # gsub [
72
- # "fieldname", "/", "_",
73
- # "fieldname2", "[\\?#-]", "."
74
- # ]
75
- config_param :gsub, :array, default: Array.new
76
-
77
- # Convert a string to it's uppercase equivalent
78
- # @example
79
- # uppercase [
80
- # "field1", "field2"
81
- # ]
82
- config_param :uppercase, :array, default: Array.new
83
-
84
- # Convert a string to it's lowercase equivalent
85
- # @example
86
- # lowercase [
87
- # "field1", "field2"
88
- # ]
89
- config_param :lowercase, :array, default: Array.new
90
-
91
- # Strip whitespace from field.
92
- # @example
93
- # strip [
94
- # "field1"
95
- # ]
96
- config_param :strip, :array, default: Array.new
97
-
98
- # Split a field to an array using a separator character
99
- # @example
100
- # split {
101
- # "field1": ","
102
- # }
103
- config_param :split, :hash, default: Hash.new
104
-
105
- # Join an array using a separator character
106
- # @example
107
- # join {
108
- # "field1": " "
109
- # }
110
- config_param :join, :hash, default: Hash.new
111
-
112
- # Merge two fields of arrays or hashes
113
- # @example
114
- # merge {
115
- # "dest_field": "added_field"
116
- # }
117
- config_param :merge, :hash, default: Hash.new
118
-
119
- # List of all possible mutate actions, in the order that we will apply
120
- # them. As it stands, this is the order in which Logstash would apply them.
121
- MUTATE_ACTIONS = %w(
122
- rename
123
- update
124
- replace
125
- convert
126
- gsub
127
- uppercase
128
- lowercase
129
- strip
130
- remove
131
- split
132
- join
133
- merge
134
- )
135
-
136
- # Convert valid types
137
- VALID_CONVERSIONS = %w(string integer float boolean datetime)
138
-
139
- # Convert helper method prefix
140
- CONVERT_PREFIX = "convert_".freeze
141
-
142
- # Convert boolean regex
143
- TRUE_REGEX = (/^(true|t|yes|y|1)$/i).freeze
144
- FALSE_REGEX = (/^(false|f|no|n|0)$/i).freeze
145
-
146
- # Placeholder regex
147
- ENVIRONMENT_TAG_REGEXP = /%e\{[^}]+\}/
148
-
149
- # Placeholder regex
150
- TEMPLATE_TAG_REGEXP = /%\{[^}]+\}/
151
-
152
- # Initialize attributes and parameters
153
- # @since 0.1.0
154
- # @return [NilClass]
155
- def configure(conf)
156
- super
157
-
158
- @convert.nil? or @convert.each do |field, type|
159
- if !VALID_CONVERSIONS.include?(type)
160
- raise ConfigError,
161
- "convert #{type} is not one of #{VALID_CONVERSIONS.join(',')}."
162
- end
163
- end
6
+ module Plugin
7
+ class MutateFilter < Fluent::Plugin::Filter
8
+ Fluent::Plugin.register_filter("mutate", self)
164
9
 
165
- @gsub_parsed = []
166
- @gsub.nil? or
167
- @gsub.each_slice(3) do |field, needle, replacement|
168
- if [field, needle, replacement].any? {|n| n.nil?}
169
- raise ConfigError,
170
- "gsub #{[field,needle,replacement]} requires 3 elements."
171
- end
10
+ # Treat periods as nested field names
11
+ config_param :expand_nesting, :bool, default: true
172
12
 
173
- @gsub_parsed << {
174
- field: field,
175
- needle: (needle.index("%{").nil?? Regexp.new(needle): needle),
176
- replacement: replacement
177
- }
13
+ # Remove any empty hashes or arrays
14
+ config_param :prune_empty, :bool, default: true
15
+
16
+ # Define mutators
17
+ config_section :mutate, param_name: :mutators, multi: true do
18
+ config_param :@type, :string, default: nil
178
19
  end
179
- end
180
20
 
181
- # Filter action which will manipulate records
182
- # @since 0.1.0
183
- # @return [Hash] the modified event record
184
- def filter(tag, time, record)
185
- # In order to more easily navigate the record, we wrap the record in a
186
- # delegator. We additionally pass the `expand_nesting` option which
187
- # determines whether we should treat periods as field separators.
188
- result = Fluent::PluginMixin::MutateEvent.
189
- new(record, expand_nesting: @expand_nesting)
190
- result.event_time = time.to_i
191
- result.event_tag = tag
192
-
193
- MUTATE_ACTIONS.each do |action|
194
- begin
195
- send(action.to_sym, result) if instance_variable_get("@#{action}")
196
- rescue => e
197
- log.warn "failed to mutate #{action} action", error: e
198
- log.warn_backtrace
199
- end
21
+ def initialize
22
+ super
23
+
24
+ @actions = []
200
25
  end
201
26
 
202
- result.prune if @prune_empty
203
- result.to_record
204
- end
27
+ # Initialize attributes and parameters
28
+ # @since 0.1.0
29
+ # @return [NilClass]
30
+ def configure(conf)
31
+ super
205
32
 
206
- protected
33
+ @mutators.each do |mutator|
34
+ section = mutator.corresponding_config_element
207
35
 
208
- # Expand replacable patterns on the event
209
- # @since 0.3.0
210
- # @return [String] the modified string
211
- def expand_patterns(event, string)
212
- string = expand_references(event, string)
213
- string = expand_environment(event, string)
214
- string
215
- end
36
+ type = section["@type"]
37
+ data = section.to_h.tap { |h| h.delete("@type") }
216
38
 
217
- # Expand %{} strings to the related event fields.
218
- # @since 0.1.0
219
- # @return [String] the modified string
220
- def expand_references(event, string)
221
- new_string = ''
222
-
223
- position = 0
224
- matches = string.scan(TEMPLATE_TAG_REGEXP).map{|m| $~}
225
-
226
- matches.each do |match|
227
- reference_tag = match[0][2..-2]
228
- reference_value = case reference_tag
229
- when "event_time" then event.event_time.to_s
230
- when "event_tag" then event.event_tag
231
- else event.get(reference_tag.downcase).to_s
232
- end
233
- if reference_value.nil?
234
- @log.error "failed to replace tag", field: reference_tag.downcase
235
- reference_value = match.to_s
39
+ unless type
40
+ raise Fluent::ConfigError, "Missing '@type' parameter in <mutator>"
41
+ end
42
+
43
+ unless self.respond_to?(type.to_sym, :include_private)
44
+ raise Fluent::ConfigError, "Invalid mutator #{type}"
45
+ end
46
+
47
+ # Iterate over section keys to remove access warnings, we'll be
48
+ # iterating over the data which has been dumped to array later
49
+ data.keys.each do |key|
50
+ section.has_key?(key)
51
+ end
52
+
53
+ # Validate config section types
54
+ case type
55
+ when "convert"
56
+ data.each do |key, value|
57
+ unless VALID_CONVERSIONS.include?(value)
58
+ raise Fluent::ConfigError, "mutate #{type} action " +
59
+ "received an invalid type for #{key}, should be one " +
60
+ "of #{VALID_CONVERSIONS.join(', ')}."
61
+ end
62
+ end
63
+ when "parse"
64
+ data.each do |key, value|
65
+ unless VALID_PARSERS.include?(value)
66
+ raise Fluent::ConfigError, "mutate #{type} action " +
67
+ "received an invalid type for #{key}, should be one " +
68
+ "of #{VALID_PARSERS.join(', ')}."
69
+ end
70
+ end
71
+ when "lowercase", "uppercase", "remove", "strip"
72
+ data.each do |key, value|
73
+ v = Fluent::Config.bool_value(value)
74
+ if v.nil?
75
+ raise Fluent::ConfigError, "mutate #{type} action " +
76
+ "requires boolean values, received '#{value}' " +
77
+ "for #{key}."
78
+ end
79
+ data[key] = v
80
+ end
81
+ when "gsub"
82
+ data.each do |key, value|
83
+ v = Fluent::Config::ARRAY_TYPE.call(value, {value_type: :string})
84
+ if not v.is_a?(Array) or not v.length == 2
85
+ raise Fluent::ConfigError, "mutate #{type} action " +
86
+ "requires array values, representing " +
87
+ "[pattern, replacement] for #{key}, " +
88
+ "received '#{value}'"
89
+ end
90
+
91
+ pattern = v[0]
92
+ replacement = v[1]
93
+
94
+ data[key] = {
95
+ pattern: (
96
+ pattern.index("%{").nil?? Regexp.new(pattern): pattern\
97
+ ),
98
+ replacement: replacement
99
+ }
100
+ end
101
+ end
102
+
103
+ @actions << {
104
+ "@type": type,
105
+ "data": data
106
+ }
236
107
  end
108
+ end
237
109
 
238
- start = match.offset(0).first
239
- new_string << string[position..(start-1)] if start > 0
240
- new_string << reference_value
241
- position = match.offset(0).last
110
+ # Convert valid types
111
+ VALID_CONVERSIONS = %w(string integer float boolean datetime)
112
+
113
+ # Parser valid types
114
+ VALID_PARSERS = %w(json)
115
+
116
+ # Convert helper method prefix
117
+ CONVERT_PREFIX = "convert_".freeze
118
+
119
+ # Convert boolean regex
120
+ TRUE_REGEX = (/^(true|t|yes|y|1)$/i).freeze
121
+ FALSE_REGEX = (/^(false|f|no|n|0)$/i).freeze
122
+
123
+ # Placeholder regex
124
+ ENVIRONMENT_TAG_REGEXP = /%e\{[^}]+\}/
125
+
126
+ # Placeholder regex
127
+ TEMPLATE_TAG_REGEXP = /%\{[^}]+\}/
128
+
129
+ # Filter action which will manipulate records
130
+ # @since 0.1.0
131
+ # @return [Hash] the modified event record
132
+ def filter(tag, time, record)
133
+ # In order to more easily navigate the record, we wrap the record in a
134
+ # delegator. We additionally pass the `expand_nesting` option which
135
+ # determines whether we should treat periods as field separators.
136
+ result = Fluent::Plugin::Mixin::MutateEvent.
137
+ new(record, expand_nesting: @expand_nesting)
138
+ result.event_time = time.to_i
139
+ result.event_tag = tag
140
+
141
+ @actions.each do |action|
142
+ type = action[:@type]
143
+ data = action[:data]
144
+
145
+ begin
146
+ send(type.to_sym, data, result)
147
+ rescue => e
148
+ log.warn "failed to mutate #{action} action", error: e
149
+ log.warn_backtrace
150
+ end
151
+ end
152
+
153
+ result.prune if @prune_empty
154
+ result.to_record
242
155
  end
243
156
 
244
- if position < string.size
245
- new_string << string[position..-1]
157
+ protected
158
+
159
+ # Expand replacable patterns on the event
160
+ # @since 0.3.0
161
+ # @return [String] the modified string
162
+ def expand_patterns(event, string)
163
+ string = expand_references(event, string)
164
+ string = expand_environment(event, string)
165
+ string
246
166
  end
247
167
 
248
- new_string
249
- end
168
+ # Expand %{} strings to the related event fields.
169
+ # @since 0.1.0
170
+ # @return [String] the modified string
171
+ def expand_references(event, string)
172
+ new_string = ''
173
+
174
+ position = 0
175
+ matches = string.scan(TEMPLATE_TAG_REGEXP).map{|m| $~}
176
+
177
+ matches.each do |match|
178
+ reference_tag = match[0][2..-2]
179
+ reference_value = case reference_tag
180
+ when "event_time" then event.event_time.to_s
181
+ when "event_tag" then event.event_tag
182
+ else event.get(reference_tag.downcase).to_s
183
+ end
184
+ if reference_value.nil?
185
+ @log.error "failed to replace tag", field: reference_tag.downcase
186
+ reference_value = match.to_s
187
+ end
250
188
 
251
- # Expand %e{} strings to the related environment variables.
252
- # @since 0.3.0
253
- # @return [String] the modified string
254
- def expand_environment(event, string)
255
- new_string = ''
256
-
257
- position = 0
258
- matches = string.scan(ENVIRONMENT_TAG_REGEXP).map{|m| $~}
259
-
260
- matches.each do |match|
261
- reference_tag = match[0][3..-2]
262
- reference_value = case reference_tag
263
- when "hostname" then Socket.gethostname
264
- else ENV[reference_tag]
265
- end
266
- if reference_value.nil?
267
- @log.error "failed to replace tag", field: reference_tag
268
- reference_value = match.to_s
189
+ start = match.offset(0).first
190
+ new_string << string[position..(start-1)] if start > 0
191
+ new_string << reference_value
192
+ position = match.offset(0).last
269
193
  end
270
194
 
271
- start = match.offset(0).first
272
- new_string << string[position..(start-1)] if start > 0
273
- new_string << reference_value
274
- position = match.offset(0).last
275
- end
195
+ if position < string.size
196
+ new_string << string[position..-1]
197
+ end
276
198
 
277
- if position < string.size
278
- new_string << string[position..-1]
199
+ new_string
279
200
  end
280
201
 
281
- new_string
282
- end
202
+ # Expand %e{} strings to the related environment variables.
203
+ # @since 0.3.0
204
+ # @return [String] the modified string
205
+ def expand_environment(event, string)
206
+ new_string = ''
207
+
208
+ position = 0
209
+ matches = string.scan(ENVIRONMENT_TAG_REGEXP).map{|m| $~}
210
+
211
+ matches.each do |match|
212
+ reference_tag = match[0][3..-2]
213
+ reference_value = case reference_tag
214
+ when "hostname" then Socket.gethostname
215
+ else ENV[reference_tag]
216
+ end
217
+ if reference_value.nil?
218
+ @log.error "failed to replace tag", field: reference_tag
219
+ reference_value = match.to_s
220
+ end
283
221
 
284
- # Remove fields from the event hash
285
- # @since 0.1.0
286
- def remove(event)
287
- @remove.each do |field|
288
- event.remove(field)
222
+ start = match.offset(0).first
223
+ new_string << string[position..(start-1)] if start > 0
224
+ new_string << reference_value
225
+ position = match.offset(0).last
226
+ end
227
+
228
+ if position < string.size
229
+ new_string << string[position..-1]
230
+ end
231
+
232
+ new_string
289
233
  end
290
- end
291
234
 
292
- # Rename fields in the event hash
293
- # @since 0.1.0
294
- def rename(event)
295
- @rename.each do |old, new|
296
- item = event.get(old)
297
- next if item.nil?
298
- event.set(new, item)
299
- event.remove(old)
235
+ # Remove fields from the event hash
236
+ # @since 0.1.0
237
+ def remove(params, event)
238
+ params.each do |field, bool|
239
+ next unless bool
240
+ event.remove(field)
241
+ end
300
242
  end
301
- end
302
243
 
303
- # Update (existing) fields in the event hash
304
- # @since 0.1.0
305
- def update(event)
306
- @update.each do |field, newvalue|
307
- newvalue = expand_patterns(event, newvalue)
308
- next unless event.include?(field)
309
- event.set(field, newvalue)
244
+ # Rename fields in the event hash
245
+ # @since 0.1.0
246
+ def rename(params, event)
247
+ params.each do |old, new|
248
+ item = event.get(old)
249
+ next if item.nil?
250
+ event.set(new, item)
251
+ event.remove(old)
252
+ end
310
253
  end
311
- end
312
254
 
313
- # Replace fields in the event hash
314
- # @since 0.1.0
315
- def replace(event)
316
- @replace.each do |field, newvalue|
317
- newvalue = expand_patterns(event, newvalue)
318
- event.set(field, newvalue)
255
+ # Update (existing) fields in the event hash
256
+ # @since 0.1.0
257
+ def update(params, event)
258
+ params.each do |field, newvalue|
259
+ newvalue = expand_patterns(event, newvalue)
260
+ next unless event.include?(field)
261
+ event.set(field, newvalue)
262
+ end
319
263
  end
320
- end
321
264
 
322
- # Convert fields to given types in the record hash
323
- # @since 0.1.0
324
- def convert(event)
325
- @convert.each do |field, type|
326
- converter = method(CONVERT_PREFIX + type)
327
-
328
- case original = event.get(field)
329
- when NilClass
330
- next
331
- when Hash
332
- @log.error("cannot convert hash", field: field, value: original)
333
- when Array
334
- event.set(field, original.map{|v| converter.call(v)})
335
- else
336
- event.set(field, converter.call(original))
265
+ # Replace fields in the event hash
266
+ # @since 0.1.0
267
+ def replace(params, event)
268
+ params.each do |field, newvalue|
269
+ newvalue = expand_patterns(event, newvalue)
270
+ event.set(field, newvalue)
337
271
  end
338
272
  end
339
- end
340
273
 
341
- def convert_string(value)
342
- value.to_s.force_encoding(Encoding::UTF_8)
343
- end
274
+ # Convert fields to given types in the record hash
275
+ # @since 0.1.0
276
+ def convert(params, event)
277
+ params.each do |field, type|
278
+ converter = method(CONVERT_PREFIX + type)
344
279
 
345
- def convert_integer(value)
346
- value.to_i
347
- end
280
+ case original = event.get(field)
281
+ when NilClass
282
+ next
283
+ when Hash
284
+ @log.error("cannot convert hash", field: field, value: original)
285
+ when Array
286
+ event.set(field, original.map{|v| converter.call(v)})
287
+ else
288
+ event.set(field, converter.call(original))
289
+ end
290
+ end
291
+ end
348
292
 
349
- def convert_float(value)
350
- value.to_f
351
- end
293
+ def convert_string(value)
294
+ value.to_s.force_encoding(Encoding::UTF_8)
295
+ end
352
296
 
353
- def convert_datetime(value)
354
- value = convert_integer(value) if value.is_a?(String)
355
- Time.at(value).to_datetime.to_s
356
- end
297
+ def convert_integer(value)
298
+ value.to_i
299
+ end
357
300
 
358
- def convert_boolean(value)
359
- return true if value =~ TRUE_REGEX
360
- return false if value.empty? || value =~ FALSE_REGEX
361
- @log.error("failed to convert to boolean", value: value)
362
- end
301
+ def convert_float(value)
302
+ value.to_f
303
+ end
363
304
 
364
- # Convert field values to uppercase in the record hash
365
- # @since 0.1.0
366
- def uppercase(event)
367
- @uppercase.each do |field|
368
- original = event.get(field)
369
- result = case original
370
- when Array
371
- original.map do |elem|
372
- (elem.is_a?(String) ? elemen.upcase : elem)
305
+ def convert_datetime(value)
306
+ value = convert_integer(value) if value.is_a?(String)
307
+ Time.at(value).to_datetime.to_s
308
+ end
309
+
310
+ def convert_boolean(value)
311
+ return true if value =~ TRUE_REGEX
312
+ return false if value.empty? || value =~ FALSE_REGEX
313
+ @log.error("failed to convert to boolean", value: value)
314
+ end
315
+
316
+ # Convert field values to uppercase in the record hash
317
+ # @since 0.1.0
318
+ def uppercase(params, event)
319
+ params.each do |field, bool|
320
+ next unless bool
321
+
322
+ original = event.get(field)
323
+ result = case original
324
+ when Array
325
+ original.map do |elem|
326
+ (elem.is_a?(String) ? elemen.upcase : elem)
327
+ end
328
+ when String
329
+ original.upcase! || original
330
+ else
331
+ @log.error("can't uppercase field",
332
+ field: field,
333
+ value: original)
334
+ original
373
335
  end
374
- when String
375
- original.upcase! || original
376
- else
377
- @log.error("can't uppercase field",
378
- field: field,
379
- value: original)
380
- original
381
- end
382
- event.set(field, result)
336
+ event.set(field, result)
337
+ end
383
338
  end
384
- end
385
339
 
386
- # Convert field values to lowercase in the record hash
387
- # @since 0.1.0
388
- def lowercase(event)
389
- @lowercase.each do |field|
390
- original = event.get(field)
391
- result = case original
392
- when Array
393
- original.map do |elem|
394
- (elem.is_a?(String) ? elemen.downcase : elem)
340
+ # Convert field values to lowercase in the record hash
341
+ # @since 0.1.0
342
+ def lowercase(params, event)
343
+ params.each do |field, bool|
344
+ next unless bool
345
+ original = event.get(field)
346
+ result = case original
347
+ when Array
348
+ original.map do |elem|
349
+ (elem.is_a?(String) ? elemen.downcase : elem)
350
+ end
351
+ when String
352
+ original.downcase! || original
353
+ else
354
+ @log.error("can't lowercase field",
355
+ field: field,
356
+ value: original)
357
+ original
395
358
  end
396
- when String
397
- original.downcase! || original
398
- else
399
- @log.error("can't lowercase field",
400
- field: field,
401
- value: original)
402
- original
403
- end
404
- event.set(field, result)
359
+ event.set(field, result)
360
+ end
405
361
  end
406
- end
407
362
 
408
- # Split fields based on delimiters in the record hash
409
- # @since 0.1.0
410
- def split(event)
411
- @split.each do |field, separator|
412
- value = event.get(field)
413
- if value.is_a?(String)
414
- event.set(field, value.split(separator))
415
- else
416
- @log.error("can't split field",
417
- field: field,
418
- value: value)
363
+ # Split fields based on delimiters in the record hash
364
+ # @since 0.1.0
365
+ def split(params, event)
366
+ params.each do |field, separator|
367
+ value = event.get(field)
368
+ if value.is_a?(String)
369
+ event.set(field, value.split(separator))
370
+ else
371
+ @log.error("can't split field",
372
+ field: field,
373
+ value: value)
374
+ end
419
375
  end
420
376
  end
421
- end
422
377
 
423
- # Join fields based on delimiters in the record hash
424
- # @since 0.1.0
425
- def join(event)
426
- @join.each do |field, separator|
427
- value = event.get(field)
428
- if value.is_a?(Array)
429
- event.set(field, value.join(separator))
378
+ # Join fields based on delimiters in the record hash
379
+ # @since 0.1.0
380
+ def join(params, event)
381
+ params.each do |field, separator|
382
+ value = event.get(field)
383
+ if value.is_a?(Array)
384
+ event.set(field, value.join(separator))
385
+ end
430
386
  end
431
387
  end
432
- end
433
388
 
434
- # Strip whitespace surrounding fields in the record hash
435
- # @since 0.1.0
436
- def strip(event)
437
- @strip.each do |field|
438
- value = event.get(field)
439
- case value
440
- when Array
441
- event.set(field, value.map{|s| s.strip})
442
- when String
443
- event.set(field, value.strip)
389
+ # Strip whitespace surrounding fields in the record hash
390
+ # @since 0.1.0
391
+ def strip(params, event)
392
+ params.each do |field, bool|
393
+ next unless bool
394
+ value = event.get(field)
395
+ case value
396
+ when Array
397
+ event.set(field, value.map{|s| s.strip})
398
+ when String
399
+ event.set(field, value.strip)
400
+ end
444
401
  end
445
402
  end
446
- end
447
403
 
448
- # Merge hashes and arrays in the record hash
449
- # @since 0.1.0
450
- def merge(event)
451
- @merge.each do |dest_field, added_fields|
452
- dest_field_value = event.get(dest_field)
404
+ # Merge hashes and arrays in the record hash
405
+ # @since 0.1.0
406
+ def merge(params, event)
407
+ params.each do |dest_field, added_fields|
408
+ dest_field_value = event.get(dest_field)
409
+
410
+ Array(added_fields).each do |added_field|
411
+ added_field_value = event.get(added_field)
412
+
413
+ if dest_field_value.is_a?(Hash) ^ added_field_value.is_a?(Hash)
414
+ @log.error('cannot merge an array and hash',
415
+ dest_field: dest_field,
416
+ added_field: added_field)
417
+ next
418
+ end
419
+
420
+ if dest_field_value.is_a?(Hash)
421
+ event.set(dest_field, dest_field_value.update(added_field_value))
422
+ else
423
+ event.set(dest_field, Array(dest_field_value).
424
+ concat(Array(added_field_value)))
425
+ end
426
+ end
427
+ end
428
+ end
453
429
 
454
- Array(added_fields).each do |added_field|
455
- added_field_value = event.get(added_field)
430
+ # Parse the value of a field
431
+ # Lazily just support json for now
432
+ # @since 1.0.0
433
+ def parse(params, event)
434
+ params.each do |field, parser|
435
+ value = event.get(field)
456
436
 
457
- if dest_field_value.is_a?(Hash) ^ added_field_value.is_a?(Hash)
458
- @log.error('cannot merge an array and hash',
459
- dest_field: dest_field,
460
- added_field: added_field)
437
+ unless value.is_a?(String)
438
+ @log.warn("field value cannot be parsed by #{parser}")
461
439
  next
462
440
  end
463
441
 
464
- if dest_field_value.is_a?(Hash)
465
- event.set(dest_field, dest_field_value.update(added_field_value))
466
- else
467
- event.set(dest_field, Array(dest_field_value).
468
- concat(Array(added_field_value)))
442
+ if value.start_with?('{') and value.ends_with?('}') \
443
+ or value.start_with?('[') and value.ends_with?(']')
444
+ value = JSON.load(value)
445
+ event.set(field, value)
469
446
  end
470
447
  end
471
448
  end
472
- end
473
449
 
474
- # Perform regular expression substitutions in the record hahs
475
- # @since 0.1.0
476
- def gsub(event)
477
- @gsub_parsed.each do |config|
478
- field = config[:field]
479
- needle = config[:needle]
480
- replacement = config[:replacement]
481
-
482
- value = event.get(field)
483
- case value
484
- when Array
485
- result = value.map do |v|
486
- if v.is_a?(String)
487
- gsub_dynamic_fields(event, v, needle, replacement)
488
- else
489
- @log.error('cannot gsub non Strings',
490
- field: field,
491
- value: v)
450
+ # Perform regular expression substitutions in the record hahs
451
+ # @since 0.1.0
452
+ def gsub(params, event)
453
+ params.each do |key, config|
454
+ pattern = config[:pattern]
455
+ replacement = config[:replacement]
456
+
457
+ value = event.get(key)
458
+ case value
459
+ when Array
460
+ result = value.map do |v|
461
+ if v.is_a?(String)
462
+ gsub_dynamic_fields(event, v, pattern, replacement)
463
+ else
464
+ @log.error('cannot gsub non Strings',
465
+ field: key,
466
+ value: v)
467
+ end
468
+ event.set(key, result)
492
469
  end
493
- event.set(field, result)
470
+ when String
471
+ v = gsub_dynamic_fields(event, value, pattern, replacement)
472
+ event.set(key, v)
473
+ else
474
+ @log.error('cannot gsub non Strings', field: key, value: value)
494
475
  end
495
- when String
496
- v = gsub_dynamic_fields(event, value, needle, replacement)
497
- event.set(field, v)
498
- else
499
- @log.error('cannot gsub non Strings', field: field, value: value)
500
476
  end
501
477
  end
502
- end
503
478
 
504
- def gsub_dynamic_fields(event, original, needle, replacement)
505
- replacement = expand_patterns(event, replacement)
506
- if needle.is_a?(Regexp)
507
- original.gsub(needle, replacement)
508
- else
509
- original.gsub(Regexp.new(needle), replacement)
479
+ def gsub_dynamic_fields(event, original, pattern, replacement)
480
+ replacement = expand_patterns(event, replacement)
481
+ if pattern.is_a?(Regexp)
482
+ original.gsub(pattern, replacement)
483
+ else
484
+ original.gsub(Regexp.new(pattern), replacement)
485
+ end
510
486
  end
511
487
  end
512
488
  end
513
489
  end
514
-