fluentd 0.12.28 → 0.12.29

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

data/Rakefile CHANGED
@@ -15,7 +15,7 @@ Rake::TestTask.new(:base_test) do |t|
15
15
  t.libs << "test"
16
16
  t.test_files = Dir["test/**/test_*.rb"].sort
17
17
  t.verbose = true
18
- #t.warning = true
18
+ t.warning = false
19
19
  end
20
20
 
21
21
  task :parallel_test do
@@ -57,6 +57,7 @@ module Fluent
57
57
  conf.elements.select { |e| e.name == 'filter' || e.name == 'match' }.each { |e|
58
58
  pattern = e.arg.empty? ? '**' : e.arg
59
59
  type = e['@type'] || e['type']
60
+ raise ConfigError, "Missing '@type' parameter on <#{e.name}> directive" unless type
60
61
  if e.name == 'filter'
61
62
  add_filter(type, pattern, e)
62
63
  else
@@ -129,6 +129,12 @@ module Fluent
129
129
  merged
130
130
  end
131
131
 
132
+ def option_value_type!(name, opts, key, klass)
133
+ if opts.has_key?(key) && !opts[key].is_a?(klass)
134
+ raise ArgumentError, "#{name}: #{key} must be a #{klass}, but #{opts[key].class}"
135
+ end
136
+ end
137
+
132
138
  def parameter_configuration(name, *args, &block)
133
139
  name = name.to_sym
134
140
 
@@ -145,7 +151,7 @@ module Fluent
145
151
 
146
152
  type = opts[:type]
147
153
  if block && type
148
- raise ArgumentError, "#{self.name}: both of block and type cannot be specified"
154
+ raise ArgumentError, "#{name}: both of block and type cannot be specified"
149
155
  end
150
156
 
151
157
  begin
@@ -153,8 +159,19 @@ module Fluent
153
159
  block ||= Configurable.lookup_type(type)
154
160
  rescue ConfigError
155
161
  # override error message
156
- raise ArgumentError, "#{self.name}: unknown config_argument type `#{type}'"
162
+ raise ArgumentError, "#{name}: unknown config_argument type `#{type}'"
163
+ end
164
+
165
+ option_value_type!(name, opts, :desc, String)
166
+ option_value_type!(name, opts, :alias, Symbol)
167
+ option_value_type!(name, opts, :deprecated, String)
168
+ option_value_type!(name, opts, :obsoleted, String)
169
+ if type == :enum
170
+ if !opts.has_key?(:list) || !opts[:list].all?{|v| v.is_a?(Symbol) }
171
+ raise ArgumentError, "#{name}: enum parameter requires :list of Symbols"
172
+ end
157
173
  end
174
+ option_value_type!(name, opts, :value_type, Symbol) # hash, array
158
175
 
159
176
  if opts.has_key?(:default)
160
177
  config_set_default(name, opts[:default])
@@ -164,6 +181,10 @@ module Fluent
164
181
  config_set_desc(name, opts[:desc])
165
182
  end
166
183
 
184
+ if opts[:deprecated] && opts[:obsoleted]
185
+ raise ArgumentError, "#{name}: both of deprecated and obsoleted cannot be specified at once"
186
+ end
187
+
167
188
  [name, block, opts]
168
189
  end
169
190
 
@@ -218,7 +239,7 @@ module Fluent
218
239
 
219
240
  def config_section(name, *args, &block)
220
241
  unless block_given?
221
- raise ArgumentError, "#{self.name}: config_section requires block parameter"
242
+ raise ArgumentError, "#{name}: config_section requires block parameter"
222
243
  end
223
244
  name = name.to_sym
224
245
 
@@ -20,4 +20,7 @@ module Fluent
20
20
 
21
21
  class ConfigParseError < ConfigError
22
22
  end
23
+
24
+ class ObsoletedParameterError < ConfigError
25
+ end
23
26
  end
@@ -100,6 +100,7 @@ module Fluent
100
100
  logger.error "config error in:\n#{conf}"
101
101
  raise ConfigError, "'<#{proxy.name} ARG>' section requires argument" + section_stack
102
102
  end
103
+ # argument should NOT be deprecated... (argument always has a value: '')
103
104
  end
104
105
 
105
106
  proxy.params.each_pair do |name, defval|
@@ -112,6 +113,20 @@ module Fluent
112
113
  conf[opts[:alias].to_s]
113
114
  end
114
115
  section_params[varname] = self.instance_exec(val, opts, name, &block)
116
+
117
+ # Source of definitions of deprecated/obsoleted:
118
+ # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features
119
+ #
120
+ # Deprecated: These deprecated features can still be used, but should be used with caution
121
+ # because they are expected to be removed entirely sometime in the future.
122
+ # Obsoleted: These obsolete features have been entirely removed from JavaScript and can no longer be used.
123
+ if opts[:deprecated]
124
+ logger.warn "'#{name}' parameter is deprecated: #{opts[:deprecated]}"
125
+ end
126
+ if opts[:obsoleted]
127
+ logger.error "config error in:\n#{conf}" if logger
128
+ raise ObsoletedParameterError, "'#{name}' parameter is already removed: #{opts[:obsoleted]}" + section_stack
129
+ end
115
130
  end
116
131
  unless section_params.has_key?(varname)
117
132
  logger.error "config error in:\n#{conf}"
@@ -0,0 +1,107 @@
1
+ require 'time'
2
+ require 'fluent/parser'
3
+
4
+ class Fluent::ParserFilter < Fluent::Filter
5
+ Fluent::Plugin.register_filter('parser', self)
6
+
7
+ config_param :key_name, :string
8
+ config_param :reserve_data, :bool, default: false
9
+ config_param :inject_key_prefix, :string, default: nil
10
+ config_param :replace_invalid_sequence, :bool, default: false
11
+ config_param :hash_value_field, :string, default: nil
12
+ config_param :suppress_parse_error_log, :bool, default: false
13
+ config_param :time_parse, :bool, default: true
14
+ config_param :ignore_key_not_exist, :bool, default: false
15
+
16
+ attr_reader :parser
17
+
18
+ def configure(conf)
19
+ super
20
+
21
+ @parser = Fluent::TextParser.new
22
+ @parser.estimate_current_event = false
23
+ @parser.configure(conf)
24
+ if !@time_parse && @parser.parser.respond_to?("time_key=".to_sym)
25
+ # disable parse time
26
+ @parser.parser.time_key = nil
27
+ end
28
+
29
+ self
30
+ end
31
+
32
+ def filter_stream(tag, es)
33
+ new_es = Fluent::MultiEventStream.new
34
+ es.each do |time,record|
35
+ raw_value = record[@key_name]
36
+ if raw_value.nil?
37
+ log.warn "#{@key_name} does not exist" unless @ignore_key_not_exist
38
+ new_es.add(time, handle_parsed(tag, record, time, {})) if @reserve_data
39
+ next
40
+ end
41
+ begin
42
+ @parser.parse(raw_value) do |t,values|
43
+ if values
44
+ t ||= time
45
+ r = handle_parsed(tag, record, t, values)
46
+ new_es.add(t, r)
47
+ else
48
+ log.warn "pattern not match with data '#{raw_value}'" unless @suppress_parse_error_log
49
+ if @reserve_data
50
+ t = time
51
+ r = handle_parsed(tag, record, time, {})
52
+ new_es.add(t, r)
53
+ end
54
+ end
55
+ end
56
+ rescue Fluent::TextParser::ParserError => e
57
+ log.warn e.message unless @suppress_parse_error_log
58
+ rescue ArgumentError => e
59
+ if @replace_invalid_sequence
60
+ unless e.message.index("invalid byte sequence in") == 0
61
+ raise
62
+ end
63
+ replaced_string = replace_invalid_byte(raw_value)
64
+ @parser.parse(replaced_string) do |t,values|
65
+ if values
66
+ t ||= time
67
+ r = handle_parsed(tag, record, t, values)
68
+ new_es.add(t, r)
69
+ else
70
+ log.warn "pattern not match with data '#{raw_value}'" unless @suppress_parse_error_log
71
+ if @reserve_data
72
+ t = time
73
+ r = handle_parsed(tag, record, time, {})
74
+ new_es.add(t, r)
75
+ end
76
+ end
77
+ end
78
+ else
79
+ raise
80
+ end
81
+ rescue => e
82
+ log.warn "parse failed #{e.message}" unless @suppress_parse_error_log
83
+ end
84
+ end
85
+ new_es
86
+ end
87
+
88
+ private
89
+
90
+ def handle_parsed(tag, record, t, values)
91
+ if values && @inject_key_prefix
92
+ values = Hash[values.map{|k,v| [ @inject_key_prefix + k, v ]}]
93
+ end
94
+ r = @hash_value_field ? {@hash_value_field => values} : values
95
+ if @reserve_data
96
+ r = r ? record.merge(r) : record
97
+ end
98
+ r
99
+ end
100
+
101
+ def replace_invalid_byte(string)
102
+ replace_options = { invalid: :replace, undef: :replace, replace: '?' }
103
+ original_encoding = string.encoding
104
+ temporal_encoding = (original_encoding == Encoding::UTF_8 ? Encoding::UTF_16BE : Encoding::UTF_8)
105
+ string.encode(temporal_encoding, original_encoding, replace_options).encode(original_encoding)
106
+ end
107
+ end
@@ -204,7 +204,9 @@ module Fluent
204
204
  end
205
205
  elsif value.kind_of?(Hash) # record, etc
206
206
  value.each do |k, v|
207
- placeholders.store("${#{k}}", v) # foo
207
+ unless placeholder_values.has_key?(k) # prevent overwriting reserved keys such as tag
208
+ placeholders.store("${#{k}}", v) # foo
209
+ end
208
210
  placeholders.store(%Q[${#{key}["#{k}"]}], v) # record["foo"]
209
211
  end
210
212
  else # string, interger, float, and others?
@@ -50,14 +50,10 @@ module Fluent
50
50
  config_param :multiline_flush_interval, :time, default: nil
51
51
  desc 'Enable the additional watch timer.'
52
52
  config_param :enable_watch_timer, :bool, default: true
53
+ desc 'The encoding after conversion of the input.'
54
+ config_param :encoding, :string, default: nil
53
55
  desc 'The encoding of the input.'
54
- config_param :encoding, default: nil do |encoding_name|
55
- begin
56
- Encoding.find(encoding_name)
57
- rescue ArgumentError => e
58
- raise ConfigError, e.message
59
- end
60
- end
56
+ config_param :from_encoding, :string, default: nil
61
57
  desc 'Add the log path being tailed to records. Specify the field name to be used.'
62
58
  config_param :path_key, :string, default: nil
63
59
 
@@ -78,6 +74,7 @@ module Fluent
78
74
 
79
75
  configure_parser(conf)
80
76
  configure_tag
77
+ configure_encoding
81
78
 
82
79
  @multiline_mode = conf['format'] =~ /multiline/
83
80
  @receive_handler = if @multiline_mode
@@ -102,6 +99,25 @@ module Fluent
102
99
  end
103
100
  end
104
101
 
102
+ def configure_encoding
103
+ unless @encoding
104
+ if @from_encoding
105
+ raise ConfigError, "tail: 'from_encoding' parameter must be specified with 'encoding' parameter."
106
+ end
107
+ end
108
+
109
+ @encoding = parse_encoding_param(@encoding) if @encoding
110
+ @from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
111
+ end
112
+
113
+ def parse_encoding_param(encoding_name)
114
+ begin
115
+ Encoding.find(encoding_name) if encoding_name
116
+ rescue ArgumentError => e
117
+ raise ConfigError, e.message
118
+ end
119
+ end
120
+
105
121
  def start
106
122
  if @pos_file
107
123
  @pf_file = File.open(@pos_file, File::RDWR|File::CREAT, DEFAULT_FILE_PERMISSION)
@@ -232,7 +248,13 @@ module Fluent
232
248
  def flush_buffer(tw)
233
249
  if lb = tw.line_buffer
234
250
  lb.chomp!
235
- lb.force_encoding(@encoding) if @encoding
251
+ if @encoding
252
+ if @from_encoding
253
+ lb.encode!(@encoding, @from_encoding)
254
+ else
255
+ lb.force_encoding(@encoding)
256
+ end
257
+ end
236
258
  @parser.parse(lb) { |time, record|
237
259
  if time && record
238
260
  tag = if @tag_prefix || @tag_suffix
@@ -281,7 +303,13 @@ module Fluent
281
303
  def convert_line_to_event(line, es, tail_watcher)
282
304
  begin
283
305
  line.chomp! # remove \n
284
- line.force_encoding(@encoding) if @encoding
306
+ if @encoding
307
+ if @from_encoding
308
+ line.encode!(@encoding, @from_encoding)
309
+ else
310
+ line.force_encoding(@encoding)
311
+ end
312
+ end
285
313
  @parser.parse(line) { |time, record|
286
314
  if time && record
287
315
  record[@path_key] ||= tail_watcher.path unless @path_key.nil?
@@ -86,9 +86,8 @@ module Fluent
86
86
 
87
87
  attr_reader :nodes
88
88
 
89
- # backward compatibility
90
- config_param :port, :integer, default: DEFAULT_LISTEN_PORT
91
- config_param :host, :string, default: nil
89
+ config_param :port, :integer, default: DEFAULT_LISTEN_PORT, deprecated: "User <server> host xxx </server> instead."
90
+ config_param :host, :string, default: nil, deprecated: "Use <server> port xxx </server> instead."
92
91
 
93
92
  attr_accessor :extend_internal_protocol
94
93
 
@@ -97,7 +96,6 @@ module Fluent
97
96
 
98
97
  # backward compatibility
99
98
  if host = conf['host']
100
- log.warn "'host' option in forward output is obsoleted. Use '<server> host xxx </server>' instead."
101
99
  port = conf['port']
102
100
  port = port ? port.to_i : DEFAULT_LISTEN_PORT
103
101
  e = conf.add_element('server')
@@ -91,7 +91,7 @@ module Fluent
91
91
  else
92
92
  conf.elements.select { |e| e.name == 'source' }.each { |e|
93
93
  type = e['@type'] || e['type']
94
- raise ConfigError, "Missing 'type' parameter on <source> directive" unless type
94
+ raise ConfigError, "Missing '@type' parameter on <source> directive" unless type
95
95
  add_source(type, e)
96
96
  }
97
97
  end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.28'
19
+ VERSION = '0.12.29'
20
20
 
21
21
  end
@@ -158,6 +158,13 @@ module ConfigurableSpec
158
158
  config_param :secret_param2, :string, secret: true
159
159
  end
160
160
  end
161
+
162
+ class UnRecommended
163
+ include Fluent::Configurable
164
+ attr_accessor :log
165
+ config_param :key1, :string, default: 'deprecated', deprecated: "key1 will be removed."
166
+ config_param :key2, :string, default: 'obsoleted', obsoleted: "key2 has been removed."
167
+ end
161
168
  end
162
169
 
163
170
  module Fluent::Config
@@ -740,5 +747,87 @@ module Fluent::Config
740
747
  end
741
748
  end
742
749
  end
750
+ sub_test_case 'non-required options for config_param' do
751
+ test 'desc must be a string if specified' do
752
+ assert_raise ArgumentError.new("key: desc must be a String, but Symbol") do
753
+ class InvalidDescClass
754
+ include Fluent::Configurable
755
+ config_param :key, :string, default: '', desc: :invalid_description
756
+ end
757
+ end
758
+ end
759
+ test 'alias must be a symbol if specified' do
760
+ assert_raise ArgumentError.new("key: alias must be a Symbol, but String") do
761
+ class InvalidAliasClass
762
+ include Fluent::Configurable
763
+ config_param :key, :string, default: '', alias: 'yay'
764
+ end
765
+ end
766
+ end
767
+ test 'deprecated must be a string if specified' do
768
+ assert_raise ArgumentError.new("key: deprecated must be a String, but TrueClass") do
769
+ class InvalidDeprecatedClass
770
+ include Fluent::Configurable
771
+ config_param :key, :string, default: '', deprecated: true
772
+ end
773
+ end
774
+ end
775
+ test 'obsoleted must be a string if specified' do
776
+ assert_raise ArgumentError.new("key: obsoleted must be a String, but TrueClass") do
777
+ class InvalidObsoletedClass
778
+ include Fluent::Configurable
779
+ config_param :key, :string, default: '', obsoleted: true
780
+ end
781
+ end
782
+ end
783
+ test 'value_type for hash must be a symbol' do
784
+ assert_raise ArgumentError.new("key: value_type must be a Symbol, but String") do
785
+ class InvalidValueTypeOfHashClass
786
+ include Fluent::Configurable
787
+ config_param :key, :hash, value_type: 'yay'
788
+ end
789
+ end
790
+ end
791
+ test 'value_type for array must be a symbol' do
792
+ assert_raise ArgumentError.new("key: value_type must be a Symbol, but String") do
793
+ class InvalidValueTypeOfArrayClass
794
+ include Fluent::Configurable
795
+ config_param :key, :array, value_type: 'yay'
796
+ end
797
+ end
798
+ end
799
+ end
800
+ sub_test_case 'enum parameters' do
801
+ test 'list must be specified as an array of symbols'
802
+ end
803
+ sub_test_case 'deprecated/obsoleted parameters' do
804
+ test 'both cannot be specified at once' do
805
+ assert_raise ArgumentError.new("param1: both of deprecated and obsoleted cannot be specified at once") do
806
+ class Buggy1
807
+ include Fluent::Configurable
808
+ config_param :param1, :string, default: '', deprecated: 'yay', obsoleted: 'foo!'
809
+ end
810
+ end
811
+ end
812
+
813
+ test 'warned if deprecated parameter is configured' do
814
+ obj = ConfigurableSpec::UnRecommended.new
815
+ obj.log = Fluent::Test::TestLogger.new
816
+ obj.configure(Fluent::Config::Element.new('ROOT', '', {'key1' => 'yay'}, []))
817
+
818
+ assert_equal 'yay', obj.key1
819
+ first_log = obj.log.logs.first
820
+ assert{ first_log && first_log.include?("[warn]") && first_log.include?("'key1' parameter is deprecated: key1 will be removed.") }
821
+ end
822
+
823
+ test 'error raised if obsoleted parameter is configured' do
824
+ obj = ConfigurableSpec::UnRecommended.new
825
+ obj.log = Fluent::Test::TestLogger.new
826
+
827
+ assert_raise Fluent::ObsoletedParameterError.new("'key2' parameter is already removed: key2 has been removed.") do
828
+ obj.configure(Fluent::Config::Element.new('ROOT', '', {'key2' => 'yay'}, []))
829
+ end
830
+ end
831
+ end
743
832
  end
744
833
  end