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.
- checksums.yaml +4 -4
- data/ChangeLog +19 -708
- data/Rakefile +1 -1
- data/lib/fluent/agent.rb +1 -0
- data/lib/fluent/config/configure_proxy.rb +24 -3
- data/lib/fluent/config/error.rb +3 -0
- data/lib/fluent/config/section.rb +15 -0
- data/lib/fluent/plugin/filter_parser.rb +107 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +3 -1
- data/lib/fluent/plugin/in_tail.rb +37 -9
- data/lib/fluent/plugin/out_forward.rb +2 -4
- data/lib/fluent/root_agent.rb +1 -1
- data/lib/fluent/version.rb +1 -1
- data/test/config/test_configurable.rb +89 -0
- data/test/config/test_types.rb +7 -3
- data/test/plugin/test_filter_parser.rb +719 -0
- data/test/plugin/test_filter_record_transformer.rb +18 -4
- data/test/plugin/test_in_tail.rb +73 -0
- data/test/test_root_agent.rb +42 -0
- metadata +5 -2
data/Rakefile
CHANGED
data/lib/fluent/agent.rb
CHANGED
@@ -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, "#{
|
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, "#{
|
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, "#{
|
242
|
+
raise ArgumentError, "#{name}: config_section requires block parameter"
|
222
243
|
end
|
223
244
|
name = name.to_sym
|
224
245
|
|
data/lib/fluent/config/error.rb
CHANGED
@@ -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
|
-
|
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 :
|
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
|
-
|
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
|
-
|
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
|
-
|
90
|
-
config_param :
|
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')
|
data/lib/fluent/root_agent.rb
CHANGED
@@ -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
|
data/lib/fluent/version.rb
CHANGED
@@ -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
|