fluentd 0.12.8 → 0.12.9
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 +20 -0
- data/example/filter_stdout.conf +22 -0
- data/lib/fluent/formatter.rb +27 -0
- data/lib/fluent/load.rb +1 -0
- data/lib/fluent/parser.rb +21 -5
- data/lib/fluent/plugin/file_util.rb +24 -2
- data/lib/fluent/plugin/filter_stdout.rb +46 -0
- data/lib/fluent/plugin/in_tail.rb +10 -9
- data/lib/fluent/plugin/out_file.rb +1 -1
- data/lib/fluent/rpc.rb +94 -0
- data/lib/fluent/supervisor.rb +92 -34
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/test_file_util.rb +65 -8
- data/test/plugin/test_filter_stdout.rb +112 -0
- data/test/plugin/test_in_tail.rb +27 -2
- data/test/plugin/test_out_file.rb +9 -1
- data/test/test_parser.rb +127 -10
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 389d806680686ce59ee87528aeb56c6bb3eabe92
|
4
|
+
data.tar.gz: 7756ef8d28122b38f3ded3413a1735f664926117
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1e17c1773eb0e805352862b6bc76a051aaa6ff257c5acef32fa244de245d9d67e261824c939187a76b22146c87a288494c9b31e92df176a4f910008f6ef198c
|
7
|
+
data.tar.gz: 07c21ddcdbf4d140e6151d60187b951a909e9269a48f037e18e7f5f43dc5f45cc2a42ea55ac538af1d36d42d0a273acdeed01f4835342163cd6893d7922d1d3b
|
data/ChangeLog
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# v0.12
|
2
2
|
|
3
|
+
## Release 0.12.9 - 2015/05/19
|
4
|
+
|
5
|
+
### New features / Enhancement
|
6
|
+
|
7
|
+
* in_tail: Add read_lines_limit parameter to control chunk size
|
8
|
+
https://github.com/fluent/fluentd/pull/593
|
9
|
+
* filter: add filter_stdout plugin
|
10
|
+
https://github.com/fluent/fluentd/pull/586
|
11
|
+
* parser: add keep_time_key option
|
12
|
+
https://github.com/fluent/fluentd/pull/587
|
13
|
+
* parser: keys parameter accepts json array configuration
|
14
|
+
https://github.com/fluent/fluentd/pull/592
|
15
|
+
* Implement RPC server for better instance management
|
16
|
+
https://github.com/fluent/fluentd/pull/585
|
17
|
+
|
18
|
+
### Bug fixes
|
19
|
+
|
20
|
+
* out_file: Fix out_file can create directory recursively
|
21
|
+
https://github.com/fluent/fluentd/pull/595
|
22
|
+
|
3
23
|
## Release 0.12.8 - 2015/04/22
|
4
24
|
|
5
25
|
### New features / Enhancement
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<source>
|
2
|
+
type dummy
|
3
|
+
tag dummy
|
4
|
+
</source>
|
5
|
+
|
6
|
+
<filter **>
|
7
|
+
type stdout
|
8
|
+
</filter>
|
9
|
+
|
10
|
+
<filter **>
|
11
|
+
type stdout
|
12
|
+
output_type hash
|
13
|
+
</filter>
|
14
|
+
|
15
|
+
<filter **>
|
16
|
+
type stdout
|
17
|
+
format ltsv
|
18
|
+
</filter>
|
19
|
+
|
20
|
+
<match **>
|
21
|
+
type null
|
22
|
+
</match>
|
data/lib/fluent/formatter.rb
CHANGED
@@ -84,6 +84,22 @@ module Fluent
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
class StdoutFormatter < Formatter
|
88
|
+
config_param :output_type, :string, :default => 'json'
|
89
|
+
|
90
|
+
def configure(conf)
|
91
|
+
super
|
92
|
+
|
93
|
+
@formatter = Plugin.new_formatter(@output_type)
|
94
|
+
@formatter.configure(conf)
|
95
|
+
end
|
96
|
+
|
97
|
+
def format(tag, time, record)
|
98
|
+
header = "#{Time.now.localtime} #{tag}: "
|
99
|
+
"#{header}#{@formatter.format(tag, time, record)}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
87
103
|
module StructuredFormatMixin
|
88
104
|
def self.included(klass)
|
89
105
|
klass.instance_eval {
|
@@ -120,6 +136,15 @@ module Fluent
|
|
120
136
|
end
|
121
137
|
end
|
122
138
|
|
139
|
+
class HashFormatter < Formatter
|
140
|
+
include HandleTagAndTimeMixin
|
141
|
+
include StructuredFormatMixin
|
142
|
+
|
143
|
+
def format_record(record)
|
144
|
+
"#{record.to_s}\n"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
123
148
|
class MessagePackFormatter < Formatter
|
124
149
|
include HandleTagAndTimeMixin
|
125
150
|
include StructuredFormatMixin
|
@@ -203,7 +228,9 @@ module Fluent
|
|
203
228
|
TEMPLATE_REGISTRY = Registry.new(:formatter_type, 'fluent/plugin/formatter_')
|
204
229
|
{
|
205
230
|
'out_file' => Proc.new { OutFileFormatter.new },
|
231
|
+
'stdout' => Proc.new { StdoutFormatter.new },
|
206
232
|
'json' => Proc.new { JSONFormatter.new },
|
233
|
+
'hash' => Proc.new { HashFormatter.new },
|
207
234
|
'msgpack' => Proc.new { MessagePackFormatter.new },
|
208
235
|
'ltsv' => Proc.new { LabeledTSVFormatter.new },
|
209
236
|
'csv' => Proc.new { CsvFormatter.new },
|
data/lib/fluent/load.rb
CHANGED
data/lib/fluent/parser.rb
CHANGED
@@ -27,6 +27,8 @@ module Fluent
|
|
27
27
|
# 'configure()' may raise errors for unexpected configurations
|
28
28
|
attr_accessor :estimate_current_event
|
29
29
|
|
30
|
+
config_param :keep_time_key, :bool, :default => false
|
31
|
+
|
30
32
|
def initialize
|
31
33
|
super
|
32
34
|
@estimate_current_event = true
|
@@ -201,6 +203,13 @@ module Fluent
|
|
201
203
|
case name
|
202
204
|
when "time"
|
203
205
|
time = @mutex.synchronize { @time_parser.parse(value) }
|
206
|
+
if @keep_time_key
|
207
|
+
record[name] = if @type_converters.nil?
|
208
|
+
value
|
209
|
+
else
|
210
|
+
convert_type(name, value)
|
211
|
+
end
|
212
|
+
end
|
204
213
|
else
|
205
214
|
record[name] = if @type_converters.nil?
|
206
215
|
value
|
@@ -239,7 +248,8 @@ module Fluent
|
|
239
248
|
def parse(text)
|
240
249
|
record = Yajl.load(text)
|
241
250
|
|
242
|
-
|
251
|
+
value = @keep_time_key ? record[@time_key] : record.delete(@time_key)
|
252
|
+
if value
|
243
253
|
if @time_format
|
244
254
|
time = @mutex.synchronize { @time_parser.parse(value) }
|
245
255
|
else
|
@@ -274,15 +284,19 @@ module Fluent
|
|
274
284
|
class ValuesParser < Parser
|
275
285
|
include TypeConverter
|
276
286
|
|
277
|
-
config_param :keys, :
|
287
|
+
config_param :keys, :default => [] do |val|
|
288
|
+
if val.start_with?('[') # This check is enough because keys parameter is simple. No '[' started column name.
|
289
|
+
JSON.load(val)
|
290
|
+
else
|
291
|
+
val.split(",")
|
292
|
+
end
|
293
|
+
end
|
278
294
|
config_param :time_key, :string, :default => nil
|
279
295
|
config_param :time_format, :string, :default => nil
|
280
296
|
|
281
297
|
def configure(conf)
|
282
298
|
super
|
283
299
|
|
284
|
-
@keys = @keys.split(",")
|
285
|
-
|
286
300
|
if @time_key && !@keys.include?(@time_key) && @estimate_current_event
|
287
301
|
raise ConfigError, "time_key (#{@time_key.inspect}) is not included in keys (#{@keys.inspect})"
|
288
302
|
end
|
@@ -299,7 +313,7 @@ module Fluent
|
|
299
313
|
record = Hash[keys.zip(values)]
|
300
314
|
|
301
315
|
if @time_key
|
302
|
-
value = record.delete(@time_key)
|
316
|
+
value = @keep_time_key ? record[@time_key] : record.delete(@time_key)
|
303
317
|
time = if value.nil?
|
304
318
|
if @estimate_current_event
|
305
319
|
Engine.now
|
@@ -465,6 +479,7 @@ module Fluent
|
|
465
479
|
"referer" => referer,
|
466
480
|
"agent" => agent,
|
467
481
|
}
|
482
|
+
record["time"] = m['time'] if @keep_time_key
|
468
483
|
|
469
484
|
if block_given?
|
470
485
|
yield time, record
|
@@ -520,6 +535,7 @@ module Fluent
|
|
520
535
|
record['pri'] = value.to_i
|
521
536
|
when "time"
|
522
537
|
time = @mutex.synchronize { @time_parser.parse(value.gsub(/ +/, ' ')) }
|
538
|
+
record[name] = value if @keep_time_key
|
523
539
|
else
|
524
540
|
record[name] = value
|
525
541
|
end
|
@@ -22,9 +22,31 @@ module Fluent
|
|
22
22
|
# @param [String] path File path
|
23
23
|
# @return [Boolean] file is writable or not
|
24
24
|
def writable?(path)
|
25
|
-
|
26
|
-
File.writable?(path)
|
25
|
+
return false if File.directory?(path)
|
26
|
+
return File.writable?(path) if File.exist?(path)
|
27
|
+
|
28
|
+
dirname = File.dirname(path)
|
29
|
+
return false if !File.directory?(dirname)
|
30
|
+
File.writable?(dirname)
|
27
31
|
end
|
28
32
|
module_function :writable?
|
33
|
+
|
34
|
+
# Check file is writable in conjunction wtih mkdir_p(dirname(path))
|
35
|
+
#
|
36
|
+
# @param [String] path File path
|
37
|
+
# @return [Boolean] file writable or not
|
38
|
+
def writable_p?(path)
|
39
|
+
return false if File.directory?(path)
|
40
|
+
return File.writable?(path) if File.exist?(path)
|
41
|
+
|
42
|
+
dirname = File.dirname(path)
|
43
|
+
until File.exist?(dirname)
|
44
|
+
dirname = File.dirname(dirname)
|
45
|
+
end
|
46
|
+
|
47
|
+
return false if !File.directory?(dirname)
|
48
|
+
File.writable?(dirname)
|
49
|
+
end
|
50
|
+
module_function :writable_p?
|
29
51
|
end
|
30
52
|
end
|
@@ -0,0 +1,46 @@
|
|
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
|
+
|
17
|
+
module Fluent
|
18
|
+
class StdoutFilter < Filter
|
19
|
+
Plugin.register_filter('stdout', self)
|
20
|
+
|
21
|
+
# for tests
|
22
|
+
attr_reader :formatter
|
23
|
+
|
24
|
+
config_param :format, :string, :default => 'stdout'
|
25
|
+
# config_param :output_type, :string, :default => 'json' (StdoutFormatter defines this)
|
26
|
+
|
27
|
+
def configure(conf)
|
28
|
+
super
|
29
|
+
|
30
|
+
@formatter = Plugin.new_formatter(@format)
|
31
|
+
@formatter.configure(conf)
|
32
|
+
end
|
33
|
+
|
34
|
+
def filter_stream(tag, es)
|
35
|
+
es.each { |time, record|
|
36
|
+
begin
|
37
|
+
log.write @formatter.format(tag, time, record)
|
38
|
+
rescue => e
|
39
|
+
router.emit_error_event(tag, time, record, e)
|
40
|
+
end
|
41
|
+
}
|
42
|
+
log.flush
|
43
|
+
es
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -31,6 +31,7 @@ module Fluent
|
|
31
31
|
config_param :pos_file, :string, :default => nil
|
32
32
|
config_param :read_from_head, :bool, :default => false
|
33
33
|
config_param :refresh_interval, :time, :default => 60
|
34
|
+
config_param :read_lines_limit, :integer, :default => 1000
|
34
35
|
|
35
36
|
attr_reader :paths
|
36
37
|
|
@@ -131,7 +132,7 @@ module Fluent
|
|
131
132
|
end
|
132
133
|
|
133
134
|
def setup_watcher(path, pe)
|
134
|
-
tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, method(:update_watcher), &method(:receive_lines))
|
135
|
+
tw = TailWatcher.new(path, @rotate_wait, pe, log, @read_from_head, @read_lines_limit, method(:update_watcher), &method(:receive_lines))
|
135
136
|
tw.attach(@loop)
|
136
137
|
tw
|
137
138
|
end
|
@@ -292,11 +293,12 @@ module Fluent
|
|
292
293
|
end
|
293
294
|
|
294
295
|
class TailWatcher
|
295
|
-
def initialize(path, rotate_wait, pe, log, read_from_head, update_watcher, &receive_lines)
|
296
|
+
def initialize(path, rotate_wait, pe, log, read_from_head, read_lines_limit, update_watcher, &receive_lines)
|
296
297
|
@path = path
|
297
298
|
@rotate_wait = rotate_wait
|
298
299
|
@pe = pe || MemoryPositionEntry.new
|
299
300
|
@read_from_head = read_from_head
|
301
|
+
@read_lines_limit = read_lines_limit
|
300
302
|
@receive_lines = receive_lines
|
301
303
|
@update_watcher = update_watcher
|
302
304
|
|
@@ -377,7 +379,7 @@ module Fluent
|
|
377
379
|
end
|
378
380
|
io.seek(pos)
|
379
381
|
|
380
|
-
@io_handler = IOHandler.new(io, @pe, @log, &method(:wrap_receive_lines))
|
382
|
+
@io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
381
383
|
else
|
382
384
|
@io_handler = NullIOHandler.new
|
383
385
|
end
|
@@ -391,12 +393,12 @@ module Fluent
|
|
391
393
|
inode = stat.ino
|
392
394
|
if inode == @pe.read_inode # truncated
|
393
395
|
@pe.update_pos(stat.size)
|
394
|
-
io_handler = IOHandler.new(io, @pe, @log, &method(:wrap_receive_lines))
|
396
|
+
io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
395
397
|
@io_handler.close
|
396
398
|
@io_handler = io_handler
|
397
399
|
elsif @io_handler.io.nil? # There is no previous file. Reuse TailWatcher
|
398
400
|
@pe.update(inode, io.pos)
|
399
|
-
io_handler = IOHandler.new(io, @pe, @log, &method(:wrap_receive_lines))
|
401
|
+
io_handler = IOHandler.new(io, @pe, @log, @read_lines_limit, &method(:wrap_receive_lines))
|
400
402
|
@io_handler = io_handler
|
401
403
|
else # file is rotated and new file found
|
402
404
|
@update_watcher.call(@path, swap_state(@pe))
|
@@ -469,14 +471,13 @@ module Fluent
|
|
469
471
|
end
|
470
472
|
end
|
471
473
|
|
472
|
-
MAX_LINES_AT_ONCE = 1000
|
473
|
-
|
474
474
|
class IOHandler
|
475
|
-
def initialize(io, pe, log, first = true, &receive_lines)
|
475
|
+
def initialize(io, pe, log, read_lines_limit, first = true, &receive_lines)
|
476
476
|
@log = log
|
477
477
|
@log.info "following tail of #{io.path}" if first
|
478
478
|
@io = io
|
479
479
|
@pe = pe
|
480
|
+
@read_lines_limit = read_lines_limit
|
480
481
|
@receive_lines = receive_lines
|
481
482
|
@buffer = ''.force_encoding('ASCII-8BIT')
|
482
483
|
@iobuf = ''.force_encoding('ASCII-8BIT')
|
@@ -500,7 +501,7 @@ module Fluent
|
|
500
501
|
while line = @buffer.slice!(/.*?\n/m)
|
501
502
|
lines << line
|
502
503
|
end
|
503
|
-
if lines.size >=
|
504
|
+
if lines.size >= @read_lines_limit
|
504
505
|
# not to use too much memory in case the file is very large
|
505
506
|
read_more = true
|
506
507
|
break
|
@@ -61,7 +61,7 @@ module Fluent
|
|
61
61
|
end
|
62
62
|
|
63
63
|
test_path = generate_path(Time.now.strftime(@time_slice_format))
|
64
|
-
unless ::Fluent::FileUtil.
|
64
|
+
unless ::Fluent::FileUtil.writable_p?(test_path)
|
65
65
|
raise ConfigError, "out_file: `#{test_path}` is not writable"
|
66
66
|
end
|
67
67
|
|
data/lib/fluent/rpc.rb
ADDED
@@ -0,0 +1,94 @@
|
|
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
|
+
|
17
|
+
module Fluent
|
18
|
+
module RPC
|
19
|
+
require 'webrick'
|
20
|
+
|
21
|
+
class Server
|
22
|
+
def initialize(endpoint, log)
|
23
|
+
bind, port = endpoint.split(':')
|
24
|
+
@bind = bind
|
25
|
+
@port = port
|
26
|
+
@log = log
|
27
|
+
|
28
|
+
@server = WEBrick::HTTPServer.new(
|
29
|
+
:BindAddress => @bind,
|
30
|
+
:Port => @port,
|
31
|
+
:Logger => WEBrick::Log.new(STDERR, WEBrick::Log::FATAL),
|
32
|
+
:AccessLog => [],
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def mount(path, servlet, *args)
|
37
|
+
@server.mount(path, servlet, *args)
|
38
|
+
@log.debug "register #{path} RPC servlet"
|
39
|
+
end
|
40
|
+
|
41
|
+
def mount_proc(path, &block)
|
42
|
+
@server.mount_proc(path) { |req, res|
|
43
|
+
begin
|
44
|
+
code, header, body = block.call(req, res)
|
45
|
+
rescue => e
|
46
|
+
@log.warn "failed to handle RPC request", :path => path, :error => e.to_s
|
47
|
+
@log.warn_backtrace e.backtrace
|
48
|
+
|
49
|
+
code = 500
|
50
|
+
body = {
|
51
|
+
'message '=> 'Internal Server Error',
|
52
|
+
'error' => "#{e}",
|
53
|
+
'backtrace'=> e.backtrace,
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
code = 200 if code.nil?
|
58
|
+
header = {'Content-Type' => 'application/json'} if header.nil?
|
59
|
+
body = if body.nil?
|
60
|
+
'{"ok":true}'
|
61
|
+
else
|
62
|
+
body['ok'] = code == 200
|
63
|
+
body.to_json
|
64
|
+
end
|
65
|
+
|
66
|
+
res.status = code
|
67
|
+
header.each_pair { |k, v|
|
68
|
+
res[k] = v
|
69
|
+
}
|
70
|
+
res.body = body
|
71
|
+
}
|
72
|
+
@log.debug "register #{path} RPC handler"
|
73
|
+
end
|
74
|
+
|
75
|
+
def start
|
76
|
+
@log.debug "listening RPC http server on http://#{@bind}:#{@port}/"
|
77
|
+
@thread = Thread.new {
|
78
|
+
@server.start
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def shutdown
|
83
|
+
if @server
|
84
|
+
@server.shutdown
|
85
|
+
@server = nil
|
86
|
+
end
|
87
|
+
if @thread
|
88
|
+
@thread.join
|
89
|
+
@thread = nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -109,6 +109,7 @@ module Fluent
|
|
109
109
|
@plugin_dirs = opt[:plugin_dirs]
|
110
110
|
@chgroup = opt[:chgroup]
|
111
111
|
@chuser = opt[:chuser]
|
112
|
+
@rpc_server = nil
|
112
113
|
|
113
114
|
@log_level = opt[:log_level]
|
114
115
|
@suppress_interval = opt[:suppress_interval]
|
@@ -128,8 +129,10 @@ module Fluent
|
|
128
129
|
|
129
130
|
dry_run if @dry_run
|
130
131
|
start_daemonize if @daemonize
|
132
|
+
setup_rpc_server if @rpc_endpoint
|
131
133
|
if @supervise
|
132
134
|
install_supervisor_signal_handlers
|
135
|
+
run_rpc_server if @rpc_endpoint
|
133
136
|
until @finished
|
134
137
|
supervise do
|
135
138
|
change_privilege
|
@@ -144,6 +147,7 @@ module Fluent
|
|
144
147
|
end
|
145
148
|
else
|
146
149
|
$log.info "starting fluentd-#{Fluent::VERSION} without supervision"
|
150
|
+
run_rpc_server if @rpc_endpoint
|
147
151
|
main_process do
|
148
152
|
change_privilege
|
149
153
|
init_engine
|
@@ -154,6 +158,7 @@ module Fluent
|
|
154
158
|
exit 0
|
155
159
|
end
|
156
160
|
end
|
161
|
+
stop_rpc_server if @rpc_endpoint
|
157
162
|
end
|
158
163
|
|
159
164
|
def options
|
@@ -233,6 +238,41 @@ module Fluent
|
|
233
238
|
end
|
234
239
|
end
|
235
240
|
|
241
|
+
def setup_rpc_server
|
242
|
+
@rpc_server = RPC::Server.new(@rpc_endpoint, $log)
|
243
|
+
|
244
|
+
# built-in RPC for signals
|
245
|
+
@rpc_server.mount_proc('/api/processes.interruptWorkers') { |req, res|
|
246
|
+
$log.debug "fluentd RPC got /api/processes.interruptWorkers request"
|
247
|
+
supervisor_sigint_handler
|
248
|
+
nil
|
249
|
+
}
|
250
|
+
@rpc_server.mount_proc('/api/processes.killWorkers') { |req, res|
|
251
|
+
$log.debug "fluentd RPC got /api/processes.killWorkers request"
|
252
|
+
supervisor_sigterm_handler
|
253
|
+
nil
|
254
|
+
}
|
255
|
+
@rpc_server.mount_proc('/api/plugins.flushBuffers') { |req, res|
|
256
|
+
$log.debug "fluentd RPC got /api/plugins.flushBuffers request"
|
257
|
+
supervisor_sigusr1_handler
|
258
|
+
nil
|
259
|
+
}
|
260
|
+
@rpc_server.mount_proc('/api/config.reload') { |req, res|
|
261
|
+
$log.debug "fluentd RPC got /api/config.reload request"
|
262
|
+
$log.info "restarting"
|
263
|
+
supervisor_sighup_handler
|
264
|
+
nil
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
def run_rpc_server
|
269
|
+
@rpc_server.start
|
270
|
+
end
|
271
|
+
|
272
|
+
def stop_rpc_server
|
273
|
+
@rpc_server.shutdown
|
274
|
+
end
|
275
|
+
|
236
276
|
def supervise(&block)
|
237
277
|
start_time = Time.now
|
238
278
|
|
@@ -290,56 +330,72 @@ module Fluent
|
|
290
330
|
def install_supervisor_signal_handlers
|
291
331
|
trap :INT do
|
292
332
|
$log.debug "fluentd supervisor process get SIGINT"
|
293
|
-
|
294
|
-
if pid = @main_pid
|
295
|
-
# kill processes only still exists
|
296
|
-
unless Process.waitpid(pid, Process::WNOHANG)
|
297
|
-
begin
|
298
|
-
Process.kill(:INT, pid)
|
299
|
-
rescue Errno::ESRCH
|
300
|
-
# ignore processes already died
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
333
|
+
supervisor_sigint_handler
|
304
334
|
end
|
305
335
|
|
306
336
|
trap :TERM do
|
307
337
|
$log.debug "fluentd supervisor process get SIGTERM"
|
308
|
-
|
309
|
-
if pid = @main_pid
|
310
|
-
# kill processes only still exists
|
311
|
-
unless Process.waitpid(pid, Process::WNOHANG)
|
312
|
-
begin
|
313
|
-
Process.kill(:TERM, pid)
|
314
|
-
rescue Errno::ESRCH
|
315
|
-
# ignore processes already died
|
316
|
-
end
|
317
|
-
end
|
318
|
-
end
|
338
|
+
supervisor_sigterm_handler
|
319
339
|
end
|
320
340
|
|
321
341
|
trap :HUP do
|
322
342
|
$log.debug "fluentd supervisor process get SIGHUP"
|
323
343
|
$log.info "restarting"
|
324
|
-
|
325
|
-
# in main thread during trap context
|
326
|
-
Thread.new {
|
327
|
-
read_config
|
328
|
-
apply_system_config
|
329
|
-
if pid = @main_pid
|
330
|
-
Process.kill(:TERM, pid)
|
331
|
-
# don't resuce Erro::ESRSH here (invalid status)
|
332
|
-
end
|
333
|
-
}.run
|
344
|
+
supervisor_sighup_handler
|
334
345
|
end
|
335
346
|
|
336
347
|
trap :USR1 do
|
337
348
|
$log.debug "fluentd supervisor process get SIGUSR1"
|
338
|
-
|
349
|
+
supervisor_sigusr1_handler
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def supervisor_sigint_handler
|
354
|
+
@finished = true
|
355
|
+
if pid = @main_pid
|
356
|
+
# kill processes only still exists
|
357
|
+
unless Process.waitpid(pid, Process::WNOHANG)
|
358
|
+
begin
|
359
|
+
Process.kill(:INT, pid)
|
360
|
+
rescue Errno::ESRCH
|
361
|
+
# ignore processes already died
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def supervisor_sigterm_handler
|
368
|
+
@finished = true
|
369
|
+
if pid = @main_pid
|
370
|
+
# kill processes only still exists
|
371
|
+
unless Process.waitpid(pid, Process::WNOHANG)
|
372
|
+
begin
|
373
|
+
Process.kill(:TERM, pid)
|
374
|
+
rescue Errno::ESRCH
|
375
|
+
# ignore processes already died
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def supervisor_sighup_handler
|
382
|
+
# Creating new thread due to mutex can't lock
|
383
|
+
# in main thread during trap context
|
384
|
+
Thread.new {
|
385
|
+
read_config
|
386
|
+
apply_system_config
|
339
387
|
if pid = @main_pid
|
340
|
-
Process.kill(:
|
388
|
+
Process.kill(:TERM, pid)
|
341
389
|
# don't resuce Erro::ESRSH here (invalid status)
|
342
390
|
end
|
391
|
+
}.run
|
392
|
+
end
|
393
|
+
|
394
|
+
def supervisor_sigusr1_handler
|
395
|
+
@log.reopen!
|
396
|
+
if pid = @main_pid
|
397
|
+
Process.kill(:USR1, pid)
|
398
|
+
# don't resuce Erro::ESRSH here (invalid status)
|
343
399
|
end
|
344
400
|
end
|
345
401
|
|
@@ -366,6 +422,7 @@ module Fluent
|
|
366
422
|
config_param :emit_error_log_interval, :time, :default => nil
|
367
423
|
config_param :suppress_config_dump, :bool, :default => nil
|
368
424
|
config_param :without_source, :bool, :default => nil
|
425
|
+
config_param :rpc_endpoint, :string, :default => nil
|
369
426
|
|
370
427
|
def initialize(conf)
|
371
428
|
super()
|
@@ -380,6 +437,7 @@ module Fluent
|
|
380
437
|
@suppress_config_dump = system.suppress_config_dump unless system.suppress_config_dump.nil?
|
381
438
|
@suppress_repeated_stacktrace = system.suppress_repeated_stacktrace unless system.suppress_repeated_stacktrace.nil?
|
382
439
|
@without_source = system.without_source unless system.without_source.nil?
|
440
|
+
@rpc_endpoint = system.rpc_endpoint unless system.rpc_endpoint.nil?
|
383
441
|
}
|
384
442
|
end
|
385
443
|
end
|
data/lib/fluent/version.rb
CHANGED
@@ -22,18 +22,75 @@ class FileUtilTest < Test::Unit::TestCase
|
|
22
22
|
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
23
23
|
end
|
24
24
|
|
25
|
-
test '
|
26
|
-
|
25
|
+
test 'directory exists' do
|
26
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
27
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_dir")
|
27
28
|
end
|
28
29
|
|
29
|
-
test 'file does not exist and directory is
|
30
|
-
|
31
|
-
|
30
|
+
test 'file does not exist and parent directory is writable' do
|
31
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
32
|
+
assert_true Fluent::FileUtil.writable?("#{TEST_DIR}/test_dir/test_file")
|
32
33
|
end
|
33
34
|
|
34
|
-
test '
|
35
|
-
FileUtils.
|
36
|
-
|
35
|
+
test 'file does not exist and parent directory is not writable' do
|
36
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
37
|
+
File.chmod(0444, "#{TEST_DIR}/test_dir")
|
38
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_dir/test_file")
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'parent directory does not exist' do
|
42
|
+
FileUtils.rm_rf("#{TEST_DIR}/test_dir")
|
43
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_dir/test_file")
|
44
|
+
end
|
45
|
+
|
46
|
+
test 'parent file (not directory) exists' do
|
47
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
48
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_file/foo")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
sub_test_case 'writable_p?' do
|
53
|
+
test 'file exists and writable' do
|
54
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
55
|
+
assert_true Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_file")
|
56
|
+
end
|
57
|
+
|
58
|
+
test 'file exists and not writable' do
|
59
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
60
|
+
File.chmod(0444, "#{TEST_DIR}/test_file")
|
61
|
+
assert_false Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_file")
|
62
|
+
end
|
63
|
+
|
64
|
+
test 'directory exists' do
|
65
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
66
|
+
assert_false Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_dir")
|
67
|
+
end
|
68
|
+
|
69
|
+
test 'parent directory exists and writable' do
|
70
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
71
|
+
assert_true Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_dir/test_file")
|
72
|
+
end
|
73
|
+
|
74
|
+
test 'parent directory exists and not writable' do
|
75
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
76
|
+
File.chmod(0555, "#{TEST_DIR}/test_dir")
|
77
|
+
assert_false Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_dir/test_file")
|
78
|
+
end
|
79
|
+
|
80
|
+
test 'parent of parent (of parent ...) directory exists and writable' do
|
81
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
82
|
+
assert_true Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_dir/foo/bar/baz")
|
83
|
+
end
|
84
|
+
|
85
|
+
test 'parent of parent (of parent ...) directory exists and not writable' do
|
86
|
+
FileUtils.mkdir_p("#{TEST_DIR}/test_dir")
|
87
|
+
File.chmod(0555, "#{TEST_DIR}/test_dir")
|
88
|
+
assert_false Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_dir/foo/bar/baz")
|
89
|
+
end
|
90
|
+
|
91
|
+
test 'parent of parent (of parent ...) file (not directory) exists' do
|
92
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
93
|
+
assert_false Fluent::FileUtil.writable_p?("#{TEST_DIR}/test_file/foo/bar/baz")
|
37
94
|
end
|
38
95
|
end
|
39
96
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/plugin/filter_stdout'
|
3
|
+
require 'timecop'
|
4
|
+
require 'flexmock'
|
5
|
+
|
6
|
+
class StdoutFilterTest < Test::Unit::TestCase
|
7
|
+
include Fluent
|
8
|
+
include FlexMock::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
Fluent::Test.setup
|
12
|
+
Timecop.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
Timecop.return
|
17
|
+
end
|
18
|
+
|
19
|
+
CONFIG = %[
|
20
|
+
]
|
21
|
+
|
22
|
+
def create_driver(conf = CONFIG)
|
23
|
+
Test::FilterTestDriver.new(StdoutFilter, 'filter.test').configure(conf)
|
24
|
+
end
|
25
|
+
|
26
|
+
def emit(d, msg, time)
|
27
|
+
d.run {
|
28
|
+
d.emit(msg, time)
|
29
|
+
}.filtered_as_array[0][2]
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_through_record
|
33
|
+
d = create_driver
|
34
|
+
time = Time.now
|
35
|
+
filtered = emit(d, {'test' => 'test'}, time)
|
36
|
+
assert_equal({'test' => 'test'}, filtered)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_configure_default
|
40
|
+
d = create_driver
|
41
|
+
assert_equal 'json', d.instance.formatter.output_type
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_configure_output_type
|
45
|
+
d = create_driver(CONFIG + "\noutput_type json")
|
46
|
+
assert_equal 'json', d.instance.formatter.output_type
|
47
|
+
|
48
|
+
d = create_driver(CONFIG + "\noutput_type hash")
|
49
|
+
assert_equal 'hash', d.instance.formatter.output_type
|
50
|
+
|
51
|
+
d = create_driver(CONFIG + "\noutput_type ltsv")
|
52
|
+
assert_equal 'ltsv', d.instance.formatter.output_type
|
53
|
+
|
54
|
+
assert_raise(Fluent::ConfigError) do
|
55
|
+
d = create_driver(CONFIG + "\noutput_type foo")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_output_type_json
|
60
|
+
d = create_driver(CONFIG + "\noutput_type json")
|
61
|
+
time = Time.now
|
62
|
+
out = capture_log(d) { emit(d, {'test' => 'test'}, time) }
|
63
|
+
assert_equal "#{time.localtime} filter.test: {\"test\":\"test\"}\n", out
|
64
|
+
|
65
|
+
# NOTE: Float::NAN is not jsonable
|
66
|
+
d = create_driver(CONFIG + "\noutput_type json")
|
67
|
+
flexmock(d.instance.router).should_receive(:emit_error_event)
|
68
|
+
emit(d, {'test' => Float::NAN}, time)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_output_type_hash
|
72
|
+
d = create_driver(CONFIG + "\noutput_type hash")
|
73
|
+
time = Time.now
|
74
|
+
out = capture_log(d) { emit(d, {'test' => 'test'}, time) }
|
75
|
+
assert_equal "#{time.localtime} filter.test: {\"test\"=>\"test\"}\n", out
|
76
|
+
|
77
|
+
# NOTE: Float::NAN is not jsonable, but hash string can output it.
|
78
|
+
d = create_driver(CONFIG + "\noutput_type hash")
|
79
|
+
out = capture_log(d) { emit(d, {'test' => Float::NAN}, time) }
|
80
|
+
assert_equal "#{time.localtime} filter.test: {\"test\"=>NaN}\n", out
|
81
|
+
end
|
82
|
+
|
83
|
+
# Use include_time_key to output the message's time
|
84
|
+
def test_include_time_key
|
85
|
+
d = create_driver(CONFIG + "\noutput_type json\ninclude_time_key true\nutc")
|
86
|
+
time = Time.now
|
87
|
+
message_time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
88
|
+
out = capture_log(d) { emit(d, {'test' => 'test'}, message_time) }
|
89
|
+
assert_equal "#{time.localtime} filter.test: {\"test\":\"test\",\"time\":\"2011-01-02T13:14:15Z\"}\n", out
|
90
|
+
end
|
91
|
+
|
92
|
+
# out_stdout formatter itself can also be replaced
|
93
|
+
def test_format_json
|
94
|
+
d = create_driver(CONFIG + "\nformat json")
|
95
|
+
time = Time.now
|
96
|
+
out = capture_log(d) { emit(d, {'test' => 'test'}, time) }
|
97
|
+
assert_equal "{\"test\":\"test\"}\n", out
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Capture the log output of the block given
|
103
|
+
def capture_log(d, &block)
|
104
|
+
tmp = d.instance.log
|
105
|
+
d.instance.log = StringIO.new
|
106
|
+
yield
|
107
|
+
return d.instance.log.string
|
108
|
+
ensure
|
109
|
+
d.instance.log = tmp
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
data/test/plugin/test_in_tail.rb
CHANGED
@@ -40,6 +40,7 @@ class TailInputTest < Test::Unit::TestCase
|
|
40
40
|
assert_equal "t1", d.instance.tag
|
41
41
|
assert_equal 2, d.instance.rotate_wait
|
42
42
|
assert_equal "#{TMP_DIR}/tail.pos", d.instance.pos_file
|
43
|
+
assert_equal 1000, d.instance.read_lines_limit
|
43
44
|
end
|
44
45
|
|
45
46
|
# TODO: Should using more better approach instead of sleep wait
|
@@ -66,6 +67,30 @@ class TailInputTest < Test::Unit::TestCase
|
|
66
67
|
assert_equal(true, emits.length > 0)
|
67
68
|
assert_equal({"message" => "test3"}, emits[0][2])
|
68
69
|
assert_equal({"message" => "test4"}, emits[1][2])
|
70
|
+
assert_equal(1, d.emit_streams.size)
|
71
|
+
end
|
72
|
+
|
73
|
+
data('1' => [1, 2], '10' => [10, 1])
|
74
|
+
def test_emit_with_read_lines_limit(data)
|
75
|
+
limit, num_emits = data
|
76
|
+
d = create_driver(CONFIG_READ_FROM_HEAD + SINGLE_LINE_CONFIG + "read_lines_limit #{limit}")
|
77
|
+
msg = 'test' * 500 # in_tail reads 2048 bytes at once.
|
78
|
+
|
79
|
+
d.run do
|
80
|
+
sleep 1
|
81
|
+
|
82
|
+
File.open("#{TMP_DIR}/tail.txt", "a") {|f|
|
83
|
+
f.puts msg
|
84
|
+
f.puts msg
|
85
|
+
}
|
86
|
+
sleep 1
|
87
|
+
end
|
88
|
+
|
89
|
+
emits = d.emits
|
90
|
+
assert_equal(true, emits.length > 0)
|
91
|
+
assert_equal({"message" => msg}, emits[0][2])
|
92
|
+
assert_equal({"message" => msg}, emits[1][2])
|
93
|
+
assert_equal(num_emits, d.emit_streams.size)
|
69
94
|
end
|
70
95
|
|
71
96
|
def test_emit_with_read_from_head
|
@@ -409,7 +434,7 @@ class TailInputTest < Test::Unit::TestCase
|
|
409
434
|
|
410
435
|
flexstub(Fluent::NewTailInput::TailWatcher) do |watcherclass|
|
411
436
|
EX_PATHS.each do |path|
|
412
|
-
watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, any, any).once.and_return do
|
437
|
+
watcherclass.should_receive(:new).with(path, EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, 1000, any, any).once.and_return do
|
413
438
|
flexmock('TailWatcher') { |watcher|
|
414
439
|
watcher.should_receive(:attach).once
|
415
440
|
watcher.should_receive(:unwatched=).zero_or_more_times
|
@@ -425,7 +450,7 @@ class TailInputTest < Test::Unit::TestCase
|
|
425
450
|
end
|
426
451
|
|
427
452
|
flexstub(Fluent::NewTailInput::TailWatcher) do |watcherclass|
|
428
|
-
watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, any, any).once.and_return do
|
453
|
+
watcherclass.should_receive(:new).with('test/plugin/data/2010/01/20100102-030406.log', EX_RORATE_WAIT, Fluent::NewTailInput::FilePositionEntry, any, true, 1000, any, any).once.and_return do
|
429
454
|
flexmock('TailWatcher') do |watcher|
|
430
455
|
watcher.should_receive(:attach).once
|
431
456
|
watcher.should_receive(:unwatched=).zero_or_more_times
|
@@ -45,8 +45,16 @@ class FileOutputTest < Test::Unit::TestCase
|
|
45
45
|
create_driver %[path #{TMP_DIR}/test_path]
|
46
46
|
end
|
47
47
|
|
48
|
+
assert_nothing_raised do
|
49
|
+
FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
|
50
|
+
File.chmod(0777, "#{TMP_DIR}/test_dir")
|
51
|
+
create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
|
52
|
+
end
|
53
|
+
|
48
54
|
assert_raise(Fluent::ConfigError) do
|
49
|
-
|
55
|
+
FileUtils.mkdir_p("#{TMP_DIR}/test_dir")
|
56
|
+
File.chmod(0555, "#{TMP_DIR}/test_dir")
|
57
|
+
create_driver %[path #{TMP_DIR}/test_dir/foo/bar/baz]
|
50
58
|
end
|
51
59
|
end
|
52
60
|
|
data/test/test_parser.rb
CHANGED
@@ -131,6 +131,31 @@ module ParserTest
|
|
131
131
|
assert_nil time, "parser returns nil if configured so"
|
132
132
|
}
|
133
133
|
end
|
134
|
+
|
135
|
+
def test_parse_with_keep_time_key
|
136
|
+
parser = TextParser::RegexpParser.new(
|
137
|
+
Regexp.new(%q!(?<time>.*)!),
|
138
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
139
|
+
'keep_time_key'=>'true',
|
140
|
+
)
|
141
|
+
text = '28/Feb/2013:12:00:00 +0900'
|
142
|
+
parser.parse(text) do |time, record|
|
143
|
+
assert_equal text, record['time']
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_parse_with_keep_time_key_with_typecast
|
148
|
+
parser = TextParser::RegexpParser.new(
|
149
|
+
Regexp.new(%q!(?<time>.*)!),
|
150
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
151
|
+
'keep_time_key'=>'true',
|
152
|
+
'types'=>'time:time:%d/%b/%Y:%H:%M:%S %z',
|
153
|
+
)
|
154
|
+
text = '28/Feb/2013:12:00:00 +0900'
|
155
|
+
parser.parse(text) do |time, record|
|
156
|
+
assert_equal 1362020400, record['time']
|
157
|
+
end
|
158
|
+
end
|
134
159
|
end
|
135
160
|
|
136
161
|
class ApacheParserTest < ::Test::Unit::TestCase
|
@@ -155,6 +180,18 @@ module ParserTest
|
|
155
180
|
}, record)
|
156
181
|
}
|
157
182
|
end
|
183
|
+
|
184
|
+
def test_parse_with_keep_time_key
|
185
|
+
parser = TextParser::ApacheParser.new
|
186
|
+
parser.configure(
|
187
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
188
|
+
'keep_time_key'=>'true',
|
189
|
+
)
|
190
|
+
text = '192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777'
|
191
|
+
parser.parse(text) do |time, record|
|
192
|
+
assert_equal "28/Feb/2013:12:00:00 +0900", record['time']
|
193
|
+
end
|
194
|
+
end
|
158
195
|
end
|
159
196
|
|
160
197
|
class ApacheErrorParserTest < ::Test::Unit::TestCase
|
@@ -279,6 +316,17 @@ module ParserTest
|
|
279
316
|
assert_equal(TextParser::SyslogParser::REGEXP, @parser.patterns['format'])
|
280
317
|
assert_equal("%b %d %H:%M:%S", @parser.patterns['time_format'])
|
281
318
|
end
|
319
|
+
|
320
|
+
def test_parse_with_keep_time_key
|
321
|
+
@parser.configure(
|
322
|
+
'time_format' => '%b %d %M:%S:%H',
|
323
|
+
'keep_time_key'=>'true',
|
324
|
+
)
|
325
|
+
text = 'Feb 28 00:00:12 192.168.0.1 fluentd[11111]: [error] Syslog test'
|
326
|
+
@parser.parse(text) do |time, record|
|
327
|
+
assert_equal "Feb 28 00:00:12", record['time']
|
328
|
+
end
|
329
|
+
end
|
282
330
|
end
|
283
331
|
|
284
332
|
class JsonParserTest < ::Test::Unit::TestCase
|
@@ -329,6 +377,18 @@ module ParserTest
|
|
329
377
|
@parser.parse('{"time":[],"k":"v"}') { |time, record| }
|
330
378
|
end
|
331
379
|
end
|
380
|
+
|
381
|
+
def test_parse_with_keep_time_key
|
382
|
+
parser = TextParser::JSONParser.new
|
383
|
+
parser.configure(
|
384
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
385
|
+
'keep_time_key'=>'true',
|
386
|
+
)
|
387
|
+
text = "28/Feb/2013:12:00:00 +0900"
|
388
|
+
parser.parse("{\"time\":\"#{text}\"}") do |time, record|
|
389
|
+
assert_equal text, record['time']
|
390
|
+
end
|
391
|
+
end
|
332
392
|
end
|
333
393
|
|
334
394
|
class NginxParserTest < ::Test::Unit::TestCase
|
@@ -374,22 +434,25 @@ module ParserTest
|
|
374
434
|
class TSVParserTest < ::Test::Unit::TestCase
|
375
435
|
include ParserTest
|
376
436
|
|
377
|
-
|
437
|
+
data('array param' => '["a","b"]', 'string param' => 'a,b')
|
438
|
+
def test_config_params(param)
|
378
439
|
parser = TextParser::TSVParser.new
|
379
440
|
|
380
441
|
assert_equal "\t", parser.delimiter
|
381
442
|
|
382
443
|
parser.configure(
|
383
|
-
'keys' =>
|
444
|
+
'keys' => param,
|
384
445
|
'delimiter' => ',',
|
385
446
|
)
|
386
447
|
|
448
|
+
assert_equal ['a', 'b'], parser.keys
|
387
449
|
assert_equal ",", parser.delimiter
|
388
450
|
end
|
389
451
|
|
390
|
-
|
452
|
+
data('array param' => '["time","a","b"]', 'string param' => 'time,a,b')
|
453
|
+
def test_parse(param)
|
391
454
|
parser = TextParser::TSVParser.new
|
392
|
-
parser.configure('keys' =>
|
455
|
+
parser.configure('keys' => param, 'time_key' => 'time')
|
393
456
|
parser.parse("2013/02/28 12:00:00\t192.168.0.1\t111") { |time, record|
|
394
457
|
assert_equal(str2time('2013/02/28 12:00:00', '%Y/%m/%d %H:%M:%S'), time)
|
395
458
|
assert_equal({
|
@@ -440,14 +503,29 @@ module ParserTest
|
|
440
503
|
assert_equal(expected, record)
|
441
504
|
}
|
442
505
|
end
|
506
|
+
|
507
|
+
def test_parse_with_keep_time_key
|
508
|
+
parser = TextParser::TSVParser.new
|
509
|
+
parser.configure(
|
510
|
+
'keys'=>'time',
|
511
|
+
'time_key'=>'time',
|
512
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
513
|
+
'keep_time_key'=>'true',
|
514
|
+
)
|
515
|
+
text = '28/Feb/2013:12:00:00 +0900'
|
516
|
+
parser.parse(text) do |time, record|
|
517
|
+
assert_equal text, record['time']
|
518
|
+
end
|
519
|
+
end
|
443
520
|
end
|
444
521
|
|
445
522
|
class CSVParserTest < ::Test::Unit::TestCase
|
446
523
|
include ParserTest
|
447
524
|
|
448
|
-
|
525
|
+
data('array param' => '["time","c","d"]', 'string param' => 'time,c,d')
|
526
|
+
def test_parse(param)
|
449
527
|
parser = TextParser::CSVParser.new
|
450
|
-
parser.configure('keys' =>
|
528
|
+
parser.configure('keys' => param, 'time_key' => 'time')
|
451
529
|
parser.parse("2013/02/28 12:00:00,192.168.0.1,111") { |time, record|
|
452
530
|
assert_equal(str2time('2013/02/28 12:00:00', '%Y/%m/%d %H:%M:%S'), time)
|
453
531
|
assert_equal({
|
@@ -457,11 +535,12 @@ module ParserTest
|
|
457
535
|
}
|
458
536
|
end
|
459
537
|
|
460
|
-
|
538
|
+
data('array param' => '["c","d"]', 'string param' => 'c,d')
|
539
|
+
def test_parse_without_time(param)
|
461
540
|
time_at_start = Time.now.to_i
|
462
541
|
|
463
542
|
parser = TextParser::CSVParser.new
|
464
|
-
parser.configure('keys' =>
|
543
|
+
parser.configure('keys' => param)
|
465
544
|
parser.parse("192.168.0.1,111") { |time, record|
|
466
545
|
assert time && time >= time_at_start, "parser puts current time without time input"
|
467
546
|
assert_equal({
|
@@ -472,7 +551,7 @@ module ParserTest
|
|
472
551
|
|
473
552
|
parser = TextParser::CSVParser.new
|
474
553
|
parser.estimate_current_event = false
|
475
|
-
parser.configure('keys' =>
|
554
|
+
parser.configure('keys' => param, 'time_key' => 'time')
|
476
555
|
parser.parse("192.168.0.1,111") { |time, record|
|
477
556
|
assert_equal({
|
478
557
|
'c' => '192.168.0.1',
|
@@ -481,6 +560,20 @@ module ParserTest
|
|
481
560
|
assert_nil time, "parser returns nil w/o time and if configured so"
|
482
561
|
}
|
483
562
|
end
|
563
|
+
|
564
|
+
def test_parse_with_keep_time_key
|
565
|
+
parser = TextParser::CSVParser.new
|
566
|
+
parser.configure(
|
567
|
+
'keys'=>'time',
|
568
|
+
'time_key'=>'time',
|
569
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
570
|
+
'keep_time_key'=>'true',
|
571
|
+
)
|
572
|
+
text = '28/Feb/2013:12:00:00 +0900'
|
573
|
+
parser.parse(text) do |time, record|
|
574
|
+
assert_equal text, record['time']
|
575
|
+
end
|
576
|
+
end
|
484
577
|
end
|
485
578
|
|
486
579
|
class LabeledTSVParserTest < ::Test::Unit::TestCase
|
@@ -567,7 +660,19 @@ module ParserTest
|
|
567
660
|
assert_nil time, "parser returns nil w/o time and if configured so"
|
568
661
|
}
|
569
662
|
end
|
570
|
-
|
663
|
+
|
664
|
+
def test_parse_with_keep_time_key
|
665
|
+
parser = TextParser::LabeledTSVParser.new
|
666
|
+
parser.configure(
|
667
|
+
'time_format'=>"%d/%b/%Y:%H:%M:%S %z",
|
668
|
+
'keep_time_key'=>'true',
|
669
|
+
)
|
670
|
+
text = '28/Feb/2013:12:00:00 +0900'
|
671
|
+
parser.parse("time:#{text}") do |time, record|
|
672
|
+
assert_equal text, record['time']
|
673
|
+
end
|
674
|
+
end
|
675
|
+
end
|
571
676
|
|
572
677
|
class NoneParserTest < ::Test::Unit::TestCase
|
573
678
|
include ParserTest
|
@@ -700,6 +805,18 @@ EOS
|
|
700
805
|
}, record)
|
701
806
|
}
|
702
807
|
end
|
808
|
+
|
809
|
+
def test_parse_with_keep_time_key
|
810
|
+
parser = TextParser::MultilineParser.new
|
811
|
+
parser.configure(
|
812
|
+
'format1' => '/^(?<time>\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2})/',
|
813
|
+
'keep_time_key' => 'true'
|
814
|
+
)
|
815
|
+
text = '2013-3-03 14:27:33'
|
816
|
+
parser.parse(text) { |time, record|
|
817
|
+
assert_equal text, record['time']
|
818
|
+
}
|
819
|
+
end
|
703
820
|
end
|
704
821
|
|
705
822
|
class TextParserTest < ::Test::Unit::TestCase
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluentd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
4
|
+
version: 0.12.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -291,6 +291,7 @@ files:
|
|
291
291
|
- bin/fluent-debug
|
292
292
|
- bin/fluent-gem
|
293
293
|
- bin/fluentd
|
294
|
+
- example/filter_stdout.conf
|
294
295
|
- example/in_http.conf
|
295
296
|
- example/in_syslog.conf
|
296
297
|
- example/in_tail.conf
|
@@ -342,6 +343,7 @@ files:
|
|
342
343
|
- lib/fluent/plugin/file_util.rb
|
343
344
|
- lib/fluent/plugin/filter_grep.rb
|
344
345
|
- lib/fluent/plugin/filter_record_transformer.rb
|
346
|
+
- lib/fluent/plugin/filter_stdout.rb
|
345
347
|
- lib/fluent/plugin/in_debug_agent.rb
|
346
348
|
- lib/fluent/plugin/in_dummy.rb
|
347
349
|
- lib/fluent/plugin/in_exec.rb
|
@@ -370,6 +372,7 @@ files:
|
|
370
372
|
- lib/fluent/process.rb
|
371
373
|
- lib/fluent/registry.rb
|
372
374
|
- lib/fluent/root_agent.rb
|
375
|
+
- lib/fluent/rpc.rb
|
373
376
|
- lib/fluent/status.rb
|
374
377
|
- lib/fluent/supervisor.rb
|
375
378
|
- lib/fluent/test.rb
|
@@ -400,6 +403,7 @@ files:
|
|
400
403
|
- test/plugin/test_file_util.rb
|
401
404
|
- test/plugin/test_filter_grep.rb
|
402
405
|
- test/plugin/test_filter_record_transformer.rb
|
406
|
+
- test/plugin/test_filter_stdout.rb
|
403
407
|
- test/plugin/test_in_debug_agent.rb
|
404
408
|
- test/plugin/test_in_dummy.rb
|
405
409
|
- test/plugin/test_in_exec.rb
|
@@ -484,6 +488,7 @@ test_files:
|
|
484
488
|
- test/plugin/test_file_util.rb
|
485
489
|
- test/plugin/test_filter_grep.rb
|
486
490
|
- test/plugin/test_filter_record_transformer.rb
|
491
|
+
- test/plugin/test_filter_stdout.rb
|
487
492
|
- test/plugin/test_in_debug_agent.rb
|
488
493
|
- test/plugin/test_in_dummy.rb
|
489
494
|
- test/plugin/test_in_exec.rb
|