fluent-plugin-record-modifier 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 3b44157dfddde7eefc70b4798674e499834fad6f52fafc8da7289696944ec9ca
4
- data.tar.gz: faa3f952b7bb41eaa46719494d3aed58bf8f59ed0d7fa476ff2491c700c5ff73
2
+ SHA1:
3
+ metadata.gz: 50fbb4b964b12f2b00fcb947614ba55e7d5df199
4
+ data.tar.gz: 1184b66de40e482d7cdc882b903b60656b8f323d
5
5
  SHA512:
6
- metadata.gz: 0e69f8b91068b1998c60a41449d97471aca548fd7eb002bf8e26048895b95af6cd64c17c771d4a85ef1bcecf63138d404bc1a56ed6a6c052047f5d85db57820f
7
- data.tar.gz: b47c9e3a4f054a663e96b7b7ac548ba2877edc941cfcc2e123a7d832239ae1be136baf8a330cdebed391523b92f2b737a68ceb6187211b79ec15e3f17843471f
6
+ metadata.gz: ca78925ef98fca002b037e3e4e250563eec3d1782e998ae3e8594ca72c363d1c510909a4ad44d7b56c3d6b1f274ef43c0438e515f2c984fcd9994ab9f6faa458
7
+ data.tar.gz: 03bc94a47d90ad10e3fa96931d9e2d315e3a3fdee6ccc901a7b233f5c927e7adf8d667580a333a9ec749adc1eb41885fb0b2529a0fd868a5e7663984ba070384
data/ChangeLog CHANGED
@@ -1,4 +1,9 @@
1
- Release 1.1.0 - 2017/06/01
1
+ Release 2.0.0 - 2019/01/23
2
+
3
+ * Update output plugin to follow filter plugin
4
+ * Remove backward compatibility check for 0.5.0 or earlier version
5
+
6
+ Release 1.1.0 - 2018/06/01
2
7
 
3
8
  * Add `replace` config sections
4
9
  https://github.com/repeatedly/fluent-plugin-record-modifier/pull/33
data/README.md CHANGED
@@ -9,6 +9,7 @@ In this case, you can use *record_modifier* to add "hostname" field to event rec
9
9
 
10
10
  | fluent-plugin-record-modifier | fluentd | ruby |
11
11
  |--------------------------------|---------|------|
12
+ | >= 2.0.0 | >= v1.0.0 | >= 2.1 |
12
13
  | >= 1.0.0 | >= v0.14.0 | >= 2.1 |
13
14
  | < 1.0.0 | >= v0.12.0 | >= 1.9 |
14
15
 
@@ -16,7 +17,7 @@ In this case, you can use *record_modifier* to add "hostname" field to event rec
16
17
 
17
18
  Use RubyGems:
18
19
 
19
- gem install fluent-plugin-record-modifier --no-document
20
+ fluent-gem install fluent-plugin-record-modifier --no-document
20
21
 
21
22
  ## Configuration
22
23
 
@@ -206,14 +207,16 @@ If you need own complex logic in filter, writing filter plugin is better. But if
206
207
 
207
208
  ### record_modifier output
208
209
 
209
- In v0.10, you can use `record_modifier` output to emulate filter. `record_modifier` output doesn't support `<record>` way.
210
+ Output plugin version of `record_modifier` filter. If you want to process events and change tag at the same time, this plugin is useful.
210
211
 
211
212
  <match pattern>
212
- type record_modifier
213
- tag foo.filtered
213
+ @type record_modifier
214
+ tag foo.${record["field1"]}
214
215
 
215
- gen_host "#{Socket.gethostname}"
216
- foo bar
216
+ <record>
217
+ gen_host "#{Socket.gethostname}"
218
+ foo bar
219
+ </record>
217
220
  </match>
218
221
 
219
222
  ## Copyright
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 2.0.0
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
17
  gem.require_paths = ['lib']
18
18
 
19
- gem.add_dependency "fluentd", [">= 0.14.0", "< 2"]
19
+ gem.add_dependency "fluentd", [">= 1.0", "< 2"]
20
20
  gem.add_development_dependency "rake", ">= 0.9.2"
21
21
  gem.add_development_dependency("test-unit", ["~> 3.1.4"])
22
22
  end
@@ -50,10 +50,6 @@ DESC
50
50
  def configure(conf)
51
51
  super
52
52
 
53
- if conf.has_key?('include_tag_key')
54
- raise ConfigError, "include_tag_key and tag_key parameters are removed. Use 'tag ${tag}' in <record> section"
55
- end
56
-
57
53
  @map = {}
58
54
  @to_enc = nil
59
55
  if @char_encoding
@@ -75,7 +71,6 @@ DESC
75
71
  @has_tag_parts = false
76
72
  conf.elements.select { |element| element.name == 'record' }.each do |element|
77
73
  element.each_pair do |k, v|
78
- check_config_placeholders(k, v)
79
74
  element.has_key?(k) # to suppress unread configuration warning
80
75
  @has_tag_parts = true if v.include?('tag_parts')
81
76
  @map[k] = DynamicExpander.new(k, v, @prepare_value)
@@ -165,16 +160,6 @@ DESC
165
160
  end
166
161
  end
167
162
 
168
- HOSTNAME_PLACEHOLDERS = %W(__HOSTNAME__ ${hostname})
169
-
170
- def check_config_placeholders(k, v)
171
- HOSTNAME_PLACEHOLDERS.each { |ph|
172
- if v.include?(ph)
173
- raise ConfigError, %!#{ph} placeholder in #{k} is removed. Use "\#{Socket.gethostname}" instead.!
174
- end
175
- }
176
- end
177
-
178
163
  class DynamicExpander
179
164
  def initialize(param_key, param_value, prepare_value)
180
165
  if param_value.include?('${')
@@ -4,10 +4,16 @@ module Fluent
4
4
  class Plugin::RecordModifierOutput < Plugin::Output
5
5
  Fluent::Plugin.register_output('record_modifier', self)
6
6
 
7
- helpers :event_emitter, :compat_parameters, :inject
7
+ helpers :event_emitter
8
8
 
9
9
  config_param :tag, :string,
10
10
  desc: "The output record tag name."
11
+
12
+ config_param :prepare_value, :string, default: nil,
13
+ desc: <<-DESC
14
+ Prepare values for filtering in configure phase. Prepared values can be used in <record>.
15
+ You can write any ruby code.
16
+ DESC
11
17
  config_param :char_encoding, :string, default: nil,
12
18
  desc: <<-DESC
13
19
  Fluentd including some plugins treats the logs as a BINARY by default to forward.
@@ -16,7 +22,6 @@ e.g. handling char encoding correctly.
16
22
  In more detail, please refer this section:
17
23
  https://github.com/repeatedly/fluent-plugin-record-modifier#char_encoding.
18
24
  DESC
19
-
20
25
  config_param :remove_keys, :string, default: nil,
21
26
  desc: <<-DESC
22
27
  The logs include needless record keys in some cases.
@@ -31,21 +36,26 @@ Modified events will have only specified keys (if exist in original events).
31
36
  This option is exclusive with `remove_keys`.
32
37
  DESC
33
38
 
34
- BUILTIN_CONFIGURATIONS = %W(type @type log_level @log_level id @id tag include_tag_key tag_key char_encoding remove_keys whitelist_keys)
39
+ config_section :replace, param_name: :replaces, multi: true do
40
+ desc "The field name to which the regular expression is applied"
41
+ config_param :key, :string
42
+ desc "The regular expression"
43
+ config_param :expression do |value|
44
+ if value.start_with?("/") && value.end_with?("/")
45
+ Regexp.compile(value[1..-2])
46
+ else
47
+ $log.warn "You should use \"pattern /#{value}/\" instead of \"pattern #{value}\""
48
+ Regexp.compile(value)
49
+ end
50
+ end
51
+ desc "The replacement string"
52
+ config_param :replace, :string
53
+ end
35
54
 
36
55
  def configure(conf)
37
- compat_parameters_convert(conf, :buffer, :inject)
38
56
  super
39
57
 
40
58
  @map = {}
41
- conf.each_pair { |k, v|
42
- unless BUILTIN_CONFIGURATIONS.include?(k)
43
- check_config_placeholders(k, v)
44
- conf.has_key?(k)
45
- @map[k] = v
46
- end
47
- }
48
-
49
59
  @to_enc = nil
50
60
  if @char_encoding
51
61
  from, to = @char_encoding.split(':', 2)
@@ -63,6 +73,15 @@ DESC
63
73
  end
64
74
  end
65
75
 
76
+ @has_tag_parts = false
77
+ conf.elements.select { |element| element.name == 'record' }.each do |element|
78
+ element.each_pair do |k, v|
79
+ element.has_key?(k) # to suppress unread configuration warning
80
+ @has_tag_parts = true if v.include?('tag_parts')
81
+ @map[k] = DynamicExpander.new(k, v, @prepare_value)
82
+ end
83
+ end
84
+
66
85
  if @remove_keys and @whitelist_keys
67
86
  raise Fluent::ConfigError, "remove_keys and whitelist_keys are exclusive with each other."
68
87
  elsif @remove_keys
@@ -70,32 +89,42 @@ DESC
70
89
  elsif @whitelist_keys
71
90
  @whitelist_keys = @whitelist_keys.split(',').map(&:strip)
72
91
  end
92
+
93
+ @has_tag_parts = true if @tag.include?('tag_parts')
94
+ @tag_ex = DynamicExpander.new('tag', @tag, @prepare_value)
95
+
96
+ # Collect DynamicExpander related garbage instructions
97
+ GC.start
73
98
  end
74
99
 
75
100
  def process(tag, es)
76
- stream = MultiEventStream.new
77
- es.each { |time, record|
78
- record = inject_values_to_record(tag, time, record)
79
- stream.add(time, modify_record(record))
80
- }
81
- router.emit_stream(@tag, stream)
101
+ tag_parts = @has_tag_parts ? tag.split('.') : nil
102
+ if @tag_ex.param_value.nil?
103
+ result = {}
104
+ es.each { |time, record|
105
+ new_record = modify_record(tag, time, record, tag_parts)
106
+ new_tag = @tag_ex.expand(tag, time, new_record, tag_parts)
107
+ result[new_tag] ||= MultiEventStream.new
108
+ result[new_tag].add(time, new_record)
109
+ }
110
+ result.each { |tag, stream|
111
+ router.emit_stream(tag, stream)
112
+ }
113
+ else
114
+ stream = MultiEventStream.new
115
+ es.each { |time, record|
116
+ new_record = modify_record(tag, time, record, tag_parts)
117
+ stream.add(time, new_record)
118
+ }
119
+ router.emit_stream(@tag, stream)
120
+ end
82
121
  end
83
122
 
84
123
  private
85
124
 
86
- HOSTNAME_PLACEHOLDERS = %W(__HOSTNAME__ ${hostname})
87
-
88
- def check_config_placeholders(k, v)
89
- HOSTNAME_PLACEHOLDERS.each { |ph|
90
- if v.include?(ph)
91
- raise ConfigError, %!#{ph} placeholder in #{k} is removed. Use "\#{Socket.gethostname}" instead.!
92
- end
93
- }
94
- end
95
-
96
- def modify_record(record)
125
+ def modify_record(tag, time, record, tag_parts)
97
126
  @map.each_pair { |k, v|
98
- record[k] = v
127
+ record[k] = v.expand(tag, time, record, tag_parts)
99
128
  }
100
129
 
101
130
  if @remove_keys
@@ -110,25 +139,110 @@ DESC
110
139
  record = modified
111
140
  end
112
141
 
142
+ unless @replaces.empty?
143
+ @replaces.each { |replace|
144
+ target_key = replace.key
145
+ if record.include?(target_key) && replace.expression.match(record[target_key])
146
+ record[target_key] = record[target_key].gsub(replace.expression, replace.replace)
147
+ end
148
+ }
149
+ end
150
+
113
151
  record = change_encoding(record) if @char_encoding
114
152
  record
115
153
  end
116
154
 
117
- def set_encoding(record)
118
- record.each_pair { |k, v|
119
- if v.is_a?(String)
120
- v.force_encoding(@from_enc)
121
- end
122
- }
155
+ def set_encoding(value)
156
+ if value.is_a?(String)
157
+ value.force_encoding(@from_enc)
158
+ elsif value.is_a?(Hash)
159
+ value.each_pair { |k, v|
160
+ if v.frozen? && v.is_a?(String)
161
+ value[k] = set_encoding(v.dup)
162
+ else
163
+ set_encoding(v)
164
+ end
165
+ }
166
+ elsif value.is_a?(Array)
167
+ value.each { |v| set_encoding(v) }
168
+ else
169
+ value
170
+ end
123
171
  end
124
172
 
125
- def convert_encoding(record)
126
- record.each_pair { |k, v|
127
- if v.is_a?(String)
128
- v.force_encoding(@from_enc) if v.encoding == Encoding::BINARY
129
- v.encode!(@to_enc, @from_enc, :invalid => :replace, :undef => :replace)
173
+ def convert_encoding(value)
174
+ if value.is_a?(String)
175
+ value.force_encoding(@from_enc) if value.encoding == Encoding::BINARY
176
+ value.encode!(@to_enc, @from_enc, :invalid => :replace, :undef => :replace)
177
+ elsif value.is_a?(Hash)
178
+ value.each_pair { |k, v|
179
+ if v.frozen? && v.is_a?(String)
180
+ value[k] = convert_encoding(v.dup)
181
+ else
182
+ convert_encoding(v)
183
+ end
184
+ }
185
+ elsif value.is_a?(Array)
186
+ value.each { |v| convert_encoding(v) }
187
+ else
188
+ value
189
+ end
190
+ end
191
+
192
+ class DynamicExpander
193
+ attr_reader :param_value
194
+
195
+ def initialize(param_key, param_value, prepare_value)
196
+ if param_value.include?('${')
197
+ __str_eval_code__ = parse_parameter(param_value)
198
+
199
+ # Use class_eval with string instead of define_method for performance.
200
+ # It can't share instructions but this is 2x+ faster than define_method in filter case.
201
+ # Refer: http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html
202
+ (class << self; self; end).class_eval <<-EORUBY, __FILE__, __LINE__ + 1
203
+ def expand(tag, time, record, tag_parts)
204
+ #{__str_eval_code__}
205
+ end
206
+ EORUBY
207
+ else
208
+ @param_value = param_value
130
209
  end
131
- }
210
+
211
+ begin
212
+ eval prepare_value if prepare_value
213
+ rescue SyntaxError
214
+ raise ConfigError, "Pass invalid syntax parameter : key = prepare_value, value = #{prepare_value}"
215
+ end
216
+
217
+ begin
218
+ # check eval genarates wrong code or not
219
+ expand(nil, nil, nil, nil)
220
+ rescue SyntaxError
221
+ raise ConfigError, "Pass invalid syntax parameter : key = #{param_key}, value = #{param_value}"
222
+ rescue
223
+ # Ignore other runtime errors
224
+ end
225
+ end
226
+
227
+ # Default implementation for fixed value. This is overwritten when parameter contains '${xxx}' placeholder
228
+ def expand(tag, time, record, tag_parts)
229
+ @param_value
230
+ end
231
+
232
+ private
233
+
234
+ def parse_parameter(value)
235
+ num_placeholders = value.scan('${').size
236
+ if num_placeholders == 1
237
+ if value.start_with?('${') && value.end_with?('}')
238
+ return value[2..-2]
239
+ else
240
+ "\"#{value.gsub('${', '#{')}\""
241
+ end
242
+ else
243
+ "\"#{value.gsub('${', '#{')}\""
244
+ end
245
+ end
132
246
  end
133
247
  end
134
248
  end
@@ -8,13 +8,11 @@ class RecordModifierOutputTest < Test::Unit::TestCase
8
8
  end
9
9
 
10
10
  CONFIG = %q!
11
- type record_modifier
12
11
  tag foo.filtered
13
-
14
- gen_host "#{Socket.gethostname}"
15
- foo bar
16
- include_tag_key
17
- tag_key included_tag
12
+ <record>
13
+ gen_host "#{Socket.gethostname}"
14
+ foo bar
15
+ </record>
18
16
  remove_keys hoge
19
17
  !
20
18
 
@@ -31,8 +29,8 @@ class RecordModifierOutputTest < Test::Unit::TestCase
31
29
  d = create_driver
32
30
  map = d.instance.instance_variable_get(:@map)
33
31
 
34
- assert_equal get_hostname, map['gen_host']
35
- assert_equal 'bar', map['foo']
32
+ assert_equal get_hostname, map['gen_host'].param_value
33
+ assert_equal 'bar', map['foo'].param_value
36
34
  end
37
35
 
38
36
  def test_format
@@ -43,17 +41,39 @@ class RecordModifierOutputTest < Test::Unit::TestCase
43
41
  d.feed({"a" => 2})
44
42
  end
45
43
 
46
- mapped = {'gen_host' => get_hostname, 'foo' => 'bar', 'included_tag' => 'test_tag'}
44
+ mapped = {'gen_host' => get_hostname, 'foo' => 'bar'}
47
45
  assert_equal [
48
46
  {"a" => 1}.merge(mapped),
49
47
  {"a" => 2}.merge(mapped),
50
48
  ], d.events.map { |e| e.last }
51
49
  end
52
50
 
53
- def test_set_char_encoding
51
+ def test_dynamic_tag_with_tag
54
52
  d = create_driver %[
55
- type record_modifier
53
+ tag foo.${tag}
54
+ ]
55
+
56
+ d.run(default_tag: 'test_tag') do
57
+ d.feed({"k" => 'v'})
58
+ end
59
+
60
+ assert_equal 'foo.test_tag', d.events.first.first
61
+ end
62
+
63
+ def test_dynamic_tag_with_record_field
64
+ d = create_driver %[
65
+ tag foo.${record["k"]}
66
+ ]
56
67
 
68
+ d.run(default_tag: 'test_tag') do
69
+ d.feed({"k" => 'v'})
70
+ end
71
+
72
+ assert_equal 'foo.v', d.events.first.first
73
+ end
74
+
75
+ def test_set_char_encoding
76
+ d = create_driver %[
57
77
  tag foo.filtered
58
78
  char_encoding utf-8
59
79
  ]
@@ -67,8 +87,6 @@ class RecordModifierOutputTest < Test::Unit::TestCase
67
87
 
68
88
  def test_convert_char_encoding
69
89
  d = create_driver %[
70
- type record_modifier
71
-
72
90
  tag foo.filtered
73
91
  char_encoding utf-8:cp932
74
92
  ]
@@ -82,8 +100,6 @@ class RecordModifierOutputTest < Test::Unit::TestCase
82
100
 
83
101
  def test_remove_one_key
84
102
  d = create_driver %[
85
- type record_modifier
86
-
87
103
  tag foo.filtered
88
104
  remove_keys k1
89
105
  ]
@@ -97,8 +113,6 @@ class RecordModifierOutputTest < Test::Unit::TestCase
97
113
 
98
114
  def test_remove_multiple_keys
99
115
  d = create_driver %[
100
- type record_modifier
101
-
102
116
  tag foo.filtered
103
117
  remove_keys k1, k2, k3
104
118
  ]
@@ -112,8 +126,6 @@ class RecordModifierOutputTest < Test::Unit::TestCase
112
126
 
113
127
  def test_remove_non_whitelist_keys
114
128
  d = create_driver %[
115
- type record_modifier
116
-
117
129
  tag foo.filtered
118
130
  whitelist_keys k1, k2, k3
119
131
  ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-record-modifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Nakagawa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-01 00:00:00.000000000 Z
11
+ date: 2019-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.14.0
19
+ version: '1.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '2'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.14.0
29
+ version: '1.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2'
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  version: '0'
96
96
  requirements: []
97
97
  rubyforge_project:
98
- rubygems_version: 2.7.6
98
+ rubygems_version: 2.6.14.1
99
99
  signing_key:
100
100
  specification_version: 4
101
101
  summary: Filter plugin for modifying event record