fluent-plugin-record-modifier 1.1.0 → 2.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.
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