fluent-plugin-mutate_filter 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-