fluentd 0.12.0.pre.3 → 0.12.0
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/fluent.conf +26 -16
- data/lib/fluent/agent.rb +7 -5
- data/lib/fluent/config.rb +1 -8
- data/lib/fluent/config/v1_parser.rb +2 -1
- data/lib/fluent/event_router.rb +1 -8
- data/lib/fluent/filter.rb +16 -0
- data/lib/fluent/formatter.rb +19 -18
- data/lib/fluent/label.rb +16 -0
- data/lib/fluent/output.rb +2 -2
- data/lib/fluent/parser.rb +57 -65
- data/lib/fluent/plugin/buf_file.rb +2 -2
- data/lib/fluent/plugin/filter_grep.rb +16 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +16 -0
- data/lib/fluent/plugin/in_http.rb +17 -5
- data/lib/fluent/plugin/in_monitor_agent.rb +52 -19
- data/lib/fluent/plugin/in_syslog.rb +2 -2
- data/lib/fluent/plugin/in_tail.rb +3 -3
- data/lib/fluent/plugin/out_copy.rb +1 -1
- data/lib/fluent/plugin/out_roundrobin.rb +1 -1
- data/lib/fluent/plugin/socket_util.rb +1 -1
- data/lib/fluent/root_agent.rb +7 -12
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/test_in_http.rb +28 -8
- data/test/test_formatter.rb +12 -0
- data/test/test_output.rb +16 -0
- data/test/test_parser.rb +119 -75
- data/test/test_plugin_classes.rb +38 -0
- data/test/test_root_agent.rb +135 -0
- metadata +6 -5
- data/Gemfile.cool.io.1.1.1 +0 -4
@@ -93,9 +93,9 @@ module Fluent
|
|
93
93
|
super
|
94
94
|
|
95
95
|
if @@buffer_paths.has_key?(@buffer_path)
|
96
|
-
raise ConfigError, "Other '#{@@buffer_paths[@buffer_path]}' plugin already use same buffer_path: type = #{conf['type']}, buffer_path = #{@buffer_path}"
|
96
|
+
raise ConfigError, "Other '#{@@buffer_paths[@buffer_path]}' plugin already use same buffer_path: type = #{conf['@type'] || conf['type']}, buffer_path = #{@buffer_path}"
|
97
97
|
else
|
98
|
-
@@buffer_paths[@buffer_path] = conf['type']
|
98
|
+
@@buffer_paths[@buffer_path] = conf['@type'] || conf['type']
|
99
99
|
end
|
100
100
|
|
101
101
|
if pos = @buffer_path.index('*')
|
@@ -1,3 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Fluentd
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
1
17
|
module Fluent
|
2
18
|
class GrepFilter < Filter
|
3
19
|
Fluent::Plugin.register_filter('grep', self)
|
@@ -1,3 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Fluentd
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
1
17
|
require 'ostruct'
|
2
18
|
|
3
19
|
module Fluent
|
@@ -28,6 +28,8 @@ module Fluent
|
|
28
28
|
super
|
29
29
|
end
|
30
30
|
|
31
|
+
EMPTY_GIF_IMAGE = "GIF89a\u0001\u0000\u0001\u0000\x80\xFF\u0000\xFF\xFF\xFF\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;".force_encoding("UTF-8")
|
32
|
+
|
31
33
|
config_param :port, :integer, :default => 9880
|
32
34
|
config_param :bind, :string, :default => '0.0.0.0'
|
33
35
|
config_param :body_size_limit, :size, :default => 32*1024*1024 # TODO default
|
@@ -38,6 +40,7 @@ module Fluent
|
|
38
40
|
config_param :format, :string, :default => 'default'
|
39
41
|
config_param :blocking_timeout, :time, :default => 0.5
|
40
42
|
config_param :cors_allow_origins, :array, :default => nil
|
43
|
+
config_param :respond_with_empty_img, :bool, :default => false
|
41
44
|
|
42
45
|
def configure(conf)
|
43
46
|
super
|
@@ -121,7 +124,11 @@ module Fluent
|
|
121
124
|
|
122
125
|
# Skip nil record
|
123
126
|
if record.nil?
|
124
|
-
|
127
|
+
if @respond_with_empty_img
|
128
|
+
return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
|
129
|
+
else
|
130
|
+
return ["200 OK", {'Content-type'=>'text/plain'}, ""]
|
131
|
+
end
|
125
132
|
end
|
126
133
|
|
127
134
|
if @add_http_headers
|
@@ -163,7 +170,11 @@ module Fluent
|
|
163
170
|
return ["500 Internal Server Error", {'Content-type'=>'text/plain'}, "500 Internal Server Error\n#{$!}\n"]
|
164
171
|
end
|
165
172
|
|
166
|
-
|
173
|
+
if @respond_with_empty_img
|
174
|
+
return ["200 OK", {'Content-type'=>'image/gif; charset=utf-8'}, EMPTY_GIF_IMAGE]
|
175
|
+
else
|
176
|
+
return ["200 OK", {'Content-type'=>'text/plain'}, ""]
|
177
|
+
end
|
167
178
|
end
|
168
179
|
|
169
180
|
private
|
@@ -183,9 +194,10 @@ module Fluent
|
|
183
194
|
|
184
195
|
def parse_params_with_parser(params)
|
185
196
|
if content = params[EVENT_RECORD_PARAMETER]
|
186
|
-
|
187
|
-
|
188
|
-
|
197
|
+
@parser.parse(content) { |time, record|
|
198
|
+
raise "Received event is not #{@format}: #{content}" if record.nil?
|
199
|
+
return time, record
|
200
|
+
}
|
189
201
|
else
|
190
202
|
raise "'#{EVENT_RECORD_PARAMETER}' parameter is required"
|
191
203
|
end
|
@@ -76,7 +76,7 @@ module Fluent
|
|
76
76
|
opts[:pretty_json] = true
|
77
77
|
end
|
78
78
|
|
79
|
-
if
|
79
|
+
if tag = get_search_parameter(qs, 'tag'.freeze)
|
80
80
|
# ?tag= to search an output plugin by match pattern
|
81
81
|
if obj = @agent.plugin_info_by_tag(tag, opts)
|
82
82
|
list = [obj]
|
@@ -84,17 +84,29 @@ module Fluent
|
|
84
84
|
list = []
|
85
85
|
end
|
86
86
|
|
87
|
-
elsif
|
88
|
-
#
|
87
|
+
elsif plugin_id = get_search_parameter(qs, '@id'.freeze)
|
88
|
+
# ?@id= to search a plugin by 'id <plugin_id>' config param
|
89
89
|
if obj = @agent.plugin_info_by_id(plugin_id, opts)
|
90
90
|
list = [obj]
|
91
91
|
else
|
92
92
|
list = []
|
93
93
|
end
|
94
94
|
|
95
|
-
elsif
|
96
|
-
#
|
97
|
-
|
95
|
+
elsif plugin_id = get_search_parameter(qs, 'id'.freeze)
|
96
|
+
# Without @ version of ?@id= for backward compatibility
|
97
|
+
if obj = @agent.plugin_info_by_id(plugin_id, opts)
|
98
|
+
list = [obj]
|
99
|
+
else
|
100
|
+
list = []
|
101
|
+
end
|
102
|
+
|
103
|
+
elsif plugin_type = get_search_parameter(qs, '@type'.freeze)
|
104
|
+
# ?@type= to search plugins by 'type <type>' config param
|
105
|
+
list = @agent.plugins_info_by_type(plugin_type, opts)
|
106
|
+
|
107
|
+
elsif plugin_type = get_search_parameter(qs, 'type'.freeze)
|
108
|
+
# Without @ version of ?@type= for backward compatibility
|
109
|
+
list = @agent.plugins_info_by_type(plugin_type, opts)
|
98
110
|
|
99
111
|
else
|
100
112
|
# otherwise show all plugins
|
@@ -104,6 +116,11 @@ module Fluent
|
|
104
116
|
return list, opts
|
105
117
|
end
|
106
118
|
|
119
|
+
def get_search_parameter(qs, param_name)
|
120
|
+
return nil unless qs.has_key?(param_name)
|
121
|
+
qs[param_name].first
|
122
|
+
end
|
123
|
+
|
107
124
|
def render_json(obj, opts={})
|
108
125
|
render_json_error(200, obj, opts)
|
109
126
|
end
|
@@ -211,13 +228,10 @@ module Fluent
|
|
211
228
|
end
|
212
229
|
|
213
230
|
MONITOR_INFO = {
|
214
|
-
'plugin_id' => 'plugin_id',
|
215
|
-
'type' => 'config["type"]',
|
216
231
|
'output_plugin' => 'is_a?(::Fluent::Output)',
|
217
232
|
'buffer_queue_length' => '@buffer.queue_size',
|
218
233
|
'buffer_total_queued_size' => '@buffer.total_queued_chunk_size',
|
219
234
|
'retry_count' => '@num_errors',
|
220
|
-
'config' => 'config',
|
221
235
|
}
|
222
236
|
|
223
237
|
def all_plugins
|
@@ -252,16 +266,18 @@ module Fluent
|
|
252
266
|
array
|
253
267
|
end
|
254
268
|
|
255
|
-
# try to match the tag and get the info from the
|
256
|
-
#
|
269
|
+
# try to match the tag and get the info from the matched output plugin
|
270
|
+
# TODO: Support output in label
|
257
271
|
def plugin_info_by_tag(tag, opts={})
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
272
|
+
matches = Engine.root_agent.event_router.instance_variable_get(:@match_rules)
|
273
|
+
matches.each { |rule|
|
274
|
+
if rule.match?(tag)
|
275
|
+
if rule.collector.is_a?(Output)
|
276
|
+
return get_monitor_info(rule.collector, opts)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
}
|
280
|
+
nil
|
265
281
|
end
|
266
282
|
|
267
283
|
# search a plugin by plugin_id
|
@@ -280,7 +296,7 @@ module Fluent
|
|
280
296
|
# multiple plugins could have the same type
|
281
297
|
def plugins_info_by_type(type, opts={})
|
282
298
|
array = all_plugins.select {|pe|
|
283
|
-
pe.config['type'] == type rescue nil
|
299
|
+
(pe.config['@type'] == type || pe.config['type'] == type) rescue nil
|
284
300
|
}
|
285
301
|
array.map {|pe|
|
286
302
|
get_monitor_info(pe, opts)
|
@@ -297,6 +313,12 @@ module Fluent
|
|
297
313
|
def get_monitor_info(pe, opts={})
|
298
314
|
obj = {}
|
299
315
|
|
316
|
+
# Common plugin information
|
317
|
+
obj['plugin_id'] = pe.plugin_id
|
318
|
+
obj['plugin_category'] = plugin_category(pe)
|
319
|
+
obj['type'] = pe.config['@type'] || pe.config['type']
|
320
|
+
obj['config'] = pe.config
|
321
|
+
|
300
322
|
# run MONITOR_INFO in plugins' instance context and store the info to obj
|
301
323
|
MONITOR_INFO.each_pair {|key,code|
|
302
324
|
begin
|
@@ -320,6 +342,17 @@ module Fluent
|
|
320
342
|
obj
|
321
343
|
end
|
322
344
|
|
345
|
+
def plugin_category(pe)
|
346
|
+
case pe
|
347
|
+
when Fluent::Input
|
348
|
+
'input'.freeze
|
349
|
+
when Fluent::Output
|
350
|
+
'output'.freeze
|
351
|
+
else
|
352
|
+
'unknown'.freeze
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
323
356
|
def fluentd_opts
|
324
357
|
@fluentd_opts ||= get_fluentd_opts
|
325
358
|
end
|
@@ -133,7 +133,7 @@ module Fluent
|
|
133
133
|
pri = m[1].to_i
|
134
134
|
text = m[2]
|
135
135
|
|
136
|
-
@parser.
|
136
|
+
@parser.parse(text) { |time, record|
|
137
137
|
unless time && record
|
138
138
|
log.warn "pattern not match: #{text.inspect}"
|
139
139
|
return
|
@@ -148,7 +148,7 @@ module Fluent
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def receive_data(data, addr)
|
151
|
-
@parser.
|
151
|
+
@parser.parse(data) { |time, record|
|
152
152
|
unless time && record
|
153
153
|
log.warn "invalid syslog message", :data => data
|
154
154
|
return
|
@@ -192,7 +192,7 @@ module Fluent
|
|
192
192
|
def flush_buffer(tw)
|
193
193
|
if lb = tw.line_buffer
|
194
194
|
lb.chomp!
|
195
|
-
@parser.
|
195
|
+
@parser.parse(lb) { |time, record|
|
196
196
|
if time && record
|
197
197
|
tag = if @tag_prefix || @tag_suffix
|
198
198
|
@tag_prefix + tw.tag + @tag_suffix
|
@@ -233,7 +233,7 @@ module Fluent
|
|
233
233
|
def convert_line_to_event(line, es)
|
234
234
|
begin
|
235
235
|
line.chomp! # remove \n
|
236
|
-
@parser.
|
236
|
+
@parser.parse(line) { |time, record|
|
237
237
|
if time && record
|
238
238
|
es.add(time, record)
|
239
239
|
else
|
@@ -276,7 +276,7 @@ module Fluent
|
|
276
276
|
lb ||= ''
|
277
277
|
lines.each do |line|
|
278
278
|
lb << line
|
279
|
-
@parser.
|
279
|
+
@parser.parse(lb) { |time, record|
|
280
280
|
if time && record
|
281
281
|
convert_line_to_event(lb, es)
|
282
282
|
lb = ''
|
data/lib/fluent/root_agent.rb
CHANGED
@@ -34,14 +34,14 @@ module Fluent
|
|
34
34
|
# <filter> <match>
|
35
35
|
#
|
36
36
|
# Relation:
|
37
|
-
# * RootAgent has many <label>, <source> and <match>
|
37
|
+
# * RootAgent has many <label>, <source>, <filter> and <match>
|
38
38
|
# * <label> has many <match> and <filter>
|
39
39
|
#
|
40
40
|
# Next step: `fluentd/agent.rb`
|
41
41
|
# Next step: 'fluentd/label.rb'
|
42
42
|
#
|
43
43
|
class RootAgent < Agent
|
44
|
-
ERROR_LABEL = "@ERROR".freeze
|
44
|
+
ERROR_LABEL = "@ERROR".freeze # @ERROR is built-in error label
|
45
45
|
|
46
46
|
def initialize(opts = {})
|
47
47
|
super
|
@@ -60,11 +60,9 @@ module Fluent
|
|
60
60
|
attr_reader :labels
|
61
61
|
|
62
62
|
def configure(conf)
|
63
|
-
super
|
64
|
-
|
65
63
|
error_label_config = nil
|
66
64
|
|
67
|
-
# initialize <label> elements
|
65
|
+
# initialize <label> elements before configuring all plugins to avoid 'label not found' in input, filter and output.
|
68
66
|
label_configs = {}
|
69
67
|
conf.elements.select { |e| e.name == 'label' }.each { |e|
|
70
68
|
name = e.arg
|
@@ -79,23 +77,23 @@ module Fluent
|
|
79
77
|
}
|
80
78
|
# Call 'configure' here to avoid 'label not found'
|
81
79
|
label_configs.each { |name, e| @labels[name].configure(e) }
|
80
|
+
setup_error_label(error_label_config) if error_label_config
|
81
|
+
|
82
|
+
super
|
82
83
|
|
83
84
|
# initialize <source> elements
|
84
85
|
if @without_source
|
85
86
|
log.info "'--without-source' is applied. Ignore <source> sections"
|
86
87
|
else
|
87
88
|
conf.elements.select { |e| e.name == 'source' }.each { |e|
|
88
|
-
type = e['type']
|
89
|
+
type = e['@type'] || e['type']
|
89
90
|
raise ConfigError, "Missing 'type' parameter on <source> directive" unless type
|
90
91
|
add_source(type, e)
|
91
92
|
}
|
92
93
|
end
|
93
|
-
|
94
|
-
setup_error_label(error_label_config) if error_label_config
|
95
94
|
end
|
96
95
|
|
97
96
|
def setup_error_label(e)
|
98
|
-
# initialize built-in ERROR label
|
99
97
|
error_label = add_label(ERROR_LABEL)
|
100
98
|
error_label.configure(e)
|
101
99
|
error_label.root_agent = RootAgentProxyWithoutErrorCollector.new(self)
|
@@ -190,10 +188,7 @@ module Fluent
|
|
190
188
|
if @suppress_emit_error_log_interval.zero? || now > @next_emit_error_log_time
|
191
189
|
log.warn "emit transaction failed:", error_info
|
192
190
|
log.warn_backtrace
|
193
|
-
# log.debug "current next_emit_error_log_time: #{Time.at(@next_emit_error_log_time)}"
|
194
191
|
@next_emit_error_log_time = now + @suppress_emit_error_log_interval
|
195
|
-
# log.debug "next emit failure log suppressed"
|
196
|
-
# log.debug "next logged time is #{Time.at(@next_emit_error_log_time)}"
|
197
192
|
end
|
198
193
|
raise error
|
199
194
|
end
|
data/lib/fluent/version.rb
CHANGED
data/test/plugin/test_in_http.rb
CHANGED
@@ -13,6 +13,7 @@ class HttpInputTest < Test::Unit::TestCase
|
|
13
13
|
bind "127.0.0.1"
|
14
14
|
body_size_limit 10m
|
15
15
|
keepalive_timeout 5
|
16
|
+
respond_with_empty_img true
|
16
17
|
]
|
17
18
|
|
18
19
|
def create_driver(conf=CONFIG)
|
@@ -199,6 +200,25 @@ class HttpInputTest < Test::Unit::TestCase
|
|
199
200
|
end
|
200
201
|
end
|
201
202
|
|
203
|
+
def test_resonse_with_empty_img
|
204
|
+
d = create_driver(CONFIG + "respond_with_empty_img true")
|
205
|
+
assert_equal true, d.instance.respond_with_empty_img
|
206
|
+
|
207
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
208
|
+
|
209
|
+
d.expect_emit "tag1", time, {"a"=>1}
|
210
|
+
d.expect_emit "tag2", time, {"a"=>2}
|
211
|
+
|
212
|
+
d.run do
|
213
|
+
d.expected_emits.each {|tag,time,record|
|
214
|
+
res = post("/#{tag}", {"json"=>record.to_json, "time"=>time.to_s})
|
215
|
+
assert_equal "200", res.code
|
216
|
+
# Ruby returns ASCII-8 encoded string for GIF.
|
217
|
+
assert_equal Fluent::HttpInput::EMPTY_GIF_IMAGE, res.body.force_encoding("UTF-8")
|
218
|
+
}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
202
222
|
def test_if_content_type_is_initialized_properly
|
203
223
|
# This test is to check if Fluent::HttpInput::Handler's @content_type is initialized properly.
|
204
224
|
# Especially when in Keep-Alive and the second request has no 'Content-Type'.
|
@@ -214,17 +234,17 @@ class HttpInputTest < Test::Unit::TestCase
|
|
214
234
|
begin
|
215
235
|
# Create the extended Handler which can store @content_type per request
|
216
236
|
ext_handler = Class.new(Fluent::HttpInput::Handler) do
|
217
|
-
|
237
|
+
@@content_types = []
|
218
238
|
|
219
|
-
|
220
|
-
|
221
|
-
|
239
|
+
def self.content_types
|
240
|
+
@@content_types
|
241
|
+
end
|
222
242
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
end
|
243
|
+
def on_message_complete
|
244
|
+
@@content_types << @content_type
|
245
|
+
super
|
227
246
|
end
|
247
|
+
end
|
228
248
|
|
229
249
|
# Replace the original Handler temporally with the extended one
|
230
250
|
Fluent::HttpInput.module_eval do
|