fluentd 0.12.2 → 0.12.3
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/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/ChangeLog +17 -1
- data/Gemfile +6 -0
- data/lib/fluent/command/bundler_injection.rb +25 -13
- data/lib/fluent/output.rb +14 -4
- data/lib/fluent/parser.rb +2 -2
- data/lib/fluent/plugin/buf_file.rb +1 -5
- data/lib/fluent/plugin/file_util.rb +30 -0
- data/lib/fluent/plugin/filter_record_transformer.rb +36 -13
- data/lib/fluent/plugin/in_debug_agent.rb +6 -0
- data/lib/fluent/plugin/out_file.rb +14 -6
- data/lib/fluent/supervisor.rb +2 -0
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/test_file_util.rb +39 -0
- data/test/plugin/test_filter_record_transformer.rb +83 -0
- data/test/plugin/test_in_debug_agent.rb +26 -0
- data/test/plugin/test_out_file.rb +10 -0
- data/test/test_output.rb +87 -0
- data/test/test_parser.rb +25 -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: 5a5677f5a62821f6d9300f21eddded90ff1f0e0a
|
4
|
+
data.tar.gz: 004b3bb49cd860b6132811b3cf1aa7ef89c4611f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aa4d2ef65d85eeb6df07245c6f9459b17d1a8d1546862ad7b96556d413a029dc3fff2514f49ffd1f3f307f11cf117816fcfcf28060f823525a01392c4e342fc
|
7
|
+
data.tar.gz: ff625fbf8a5b70866d413b6c5e5f1b8d4ebb52acf696ee1416de2cd249ff6ca14c223a312d633027f6c21e2e7a95b150abc3a43baa116ab54b55856543375432
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# v0.12
|
2
2
|
|
3
|
-
## Release 0.12.
|
3
|
+
## Release 0.12.3 - 2015/01/16
|
4
|
+
|
5
|
+
### New features / Enhancement
|
6
|
+
|
7
|
+
* parser: nginx and apache2 format can now parse access log without http-version
|
8
|
+
* filter_record_transformer: Allow non-string values like array / hash
|
9
|
+
* plugin: Add writable check for path related parameter
|
10
|
+
* command: -g and -G now works in embedded Ruby environment like Treasure Agent
|
11
|
+
|
12
|
+
### Bug fixes
|
13
|
+
|
14
|
+
* buffer: Fix to flush a buffer by USR1 signal even on retrying
|
15
|
+
* buffer: Fix TimeSlicedOutput doesn't flush with SIGUSR1
|
16
|
+
* buffer: Fix broken calc_retry_wait if Integer is used for retry_wait parameter
|
17
|
+
* engine: Fix SIGHUP does not reload config
|
18
|
+
|
19
|
+
## Release 0.12.2 - 2014/12/20
|
4
20
|
|
5
21
|
### New Features
|
6
22
|
|
data/Gemfile
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
source 'https://rubygems.org/'
|
2
2
|
|
3
3
|
gemspec
|
4
|
+
|
5
|
+
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
|
6
|
+
if File.exist?(local_gemfile)
|
7
|
+
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
|
8
|
+
instance_eval File.read(local_gemfile)
|
9
|
+
end
|
@@ -14,18 +14,30 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
exit
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
if ENV['BUNDLE_BIN_PATH']
|
18
|
+
puts 'error: You seem to use `bundle exec` already.'
|
19
|
+
exit 1
|
20
|
+
else
|
21
|
+
begin
|
22
|
+
bundle_bin = Gem::Specification.find_by_name('bundler').bin_file('bundle')
|
23
|
+
rescue Gem::LoadError => e
|
24
|
+
puts "error: #{e}"
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
ruby_bin = RbConfig.ruby
|
28
|
+
system("#{ruby_bin} #{bundle_bin} install")
|
29
|
+
unless $?.success?
|
30
|
+
exit $?.exitstatus
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
33
|
+
cmdline = [
|
34
|
+
ruby_bin,
|
35
|
+
bundle_bin,
|
36
|
+
'exec',
|
37
|
+
ruby_bin,
|
38
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'fluentd.rb')),
|
39
|
+
] + ARGV
|
31
40
|
|
41
|
+
exec *cmdline
|
42
|
+
exit! 127
|
43
|
+
end
|
data/lib/fluent/output.rb
CHANGED
@@ -192,6 +192,7 @@ module Fluent
|
|
192
192
|
def configure(conf)
|
193
193
|
super
|
194
194
|
|
195
|
+
@retry_wait = @retry_wait.to_f # converted to Float for calc_retry_wait
|
195
196
|
@buffer = Plugin.new_buffer(@buffer_type)
|
196
197
|
@buffer.configure(conf)
|
197
198
|
|
@@ -272,7 +273,7 @@ module Fluent
|
|
272
273
|
#def write(chunk)
|
273
274
|
#end
|
274
275
|
|
275
|
-
def enqueue_buffer
|
276
|
+
def enqueue_buffer(force = false)
|
276
277
|
@buffer.keys.each {|key|
|
277
278
|
@buffer.push(key)
|
278
279
|
}
|
@@ -383,7 +384,10 @@ module Fluent
|
|
383
384
|
end
|
384
385
|
|
385
386
|
def force_flush
|
386
|
-
|
387
|
+
@num_errors_lock.synchronize do
|
388
|
+
@next_retry_time = Engine.now - 1
|
389
|
+
end
|
390
|
+
enqueue_buffer(true)
|
387
391
|
submit_flush
|
388
392
|
end
|
389
393
|
|
@@ -551,8 +555,14 @@ module Fluent
|
|
551
555
|
}
|
552
556
|
end
|
553
557
|
|
554
|
-
def enqueue_buffer
|
555
|
-
|
558
|
+
def enqueue_buffer(force = false)
|
559
|
+
if force
|
560
|
+
@buffer.keys.each {|key|
|
561
|
+
@buffer.push(key)
|
562
|
+
}
|
563
|
+
else
|
564
|
+
@enqueue_buffer_proc.call
|
565
|
+
end
|
556
566
|
end
|
557
567
|
|
558
568
|
#def format(tag, event)
|
data/lib/fluent/parser.rb
CHANGED
@@ -407,7 +407,7 @@ module Fluent
|
|
407
407
|
end
|
408
408
|
|
409
409
|
class ApacheParser < Parser
|
410
|
-
REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[
|
410
|
+
REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
|
411
411
|
TIME_FORMAT = "%d/%b/%Y:%H:%M:%S %z"
|
412
412
|
|
413
413
|
def initialize
|
@@ -631,7 +631,7 @@ module Fluent
|
|
631
631
|
'tsv' => Proc.new { TSVParser.new },
|
632
632
|
'ltsv' => Proc.new { LabeledTSVParser.new },
|
633
633
|
'csv' => Proc.new { CSVParser.new },
|
634
|
-
'nginx' => Proc.new { RegexpParser.new(/^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]
|
634
|
+
'nginx' => Proc.new { RegexpParser.new(/^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
|
635
635
|
'none' => Proc.new { NoneParser.new },
|
636
636
|
'multiline' => Proc.new { MultilineParser.new },
|
637
637
|
}.each { |name, factory|
|
@@ -83,6 +83,7 @@ module Fluent
|
|
83
83
|
end
|
84
84
|
|
85
85
|
config_param :buffer_path, :string
|
86
|
+
config_param :flush_at_shutdown, :bool, :default => false
|
86
87
|
|
87
88
|
# 'symlink_path' is currently only for out_file.
|
88
89
|
# That is the reason why this is not config_param, but attr_accessor.
|
@@ -106,11 +107,6 @@ module Fluent
|
|
106
107
|
@buffer_path_suffix = ".log"
|
107
108
|
end
|
108
109
|
|
109
|
-
if flush_at_shutdown = conf['flush_at_shutdown']
|
110
|
-
@flush_at_shutdown = true
|
111
|
-
else
|
112
|
-
@flush_at_shutdown = false
|
113
|
-
end
|
114
110
|
end
|
115
111
|
|
116
112
|
def start
|
@@ -0,0 +1,30 @@
|
|
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 FileUtil
|
19
|
+
# Check file is writable if file exists
|
20
|
+
# Check directory is writable if file does not exist
|
21
|
+
#
|
22
|
+
# @param [String] path File path
|
23
|
+
# @return [Boolean] file is writable or not
|
24
|
+
def writable?(path)
|
25
|
+
path = File.exist?(path) ? path : File.dirname(path)
|
26
|
+
File.writable?(path)
|
27
|
+
end
|
28
|
+
module_function :writable?
|
29
|
+
end
|
30
|
+
end
|
@@ -38,7 +38,7 @@ module Fluent
|
|
38
38
|
conf.elements.select { |element| element.name == 'record' }.each do |element|
|
39
39
|
element.each_pair do |k, v|
|
40
40
|
element.has_key?(k) # to suppress unread configuration warning
|
41
|
-
@map[k] = v
|
41
|
+
@map[k] = parse_value(v)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -92,17 +92,45 @@ module Fluent
|
|
92
92
|
|
93
93
|
private
|
94
94
|
|
95
|
+
def parse_value(value_str)
|
96
|
+
if value_str.start_with?('{', '[')
|
97
|
+
JSON.parse(value_str)
|
98
|
+
else
|
99
|
+
value_str
|
100
|
+
end
|
101
|
+
rescue => e
|
102
|
+
log.warn "failed to parse #{value_str} as json. Assuming #{value_str} is a string", :error_class => e.class, :error => e.message
|
103
|
+
value_str # emit as string
|
104
|
+
end
|
105
|
+
|
95
106
|
def reform(time, record, opts)
|
96
107
|
@placeholder_expander.prepare_placeholders(time, record, opts)
|
97
108
|
|
98
109
|
new_record = @renew_record ? {} : record.dup
|
99
110
|
@keep_keys.each {|k| new_record[k] = record[k]} if @keep_keys and @renew_record
|
100
|
-
@map.each_pair {|k, v| new_record[k] =
|
111
|
+
@map.each_pair {|k, v| new_record[k] = interpolate(v)}
|
101
112
|
@remove_keys.each {|k| new_record.delete(k) } if @remove_keys
|
102
113
|
|
103
114
|
new_record
|
104
115
|
end
|
105
116
|
|
117
|
+
def interpolate(value)
|
118
|
+
if value.is_a?(String)
|
119
|
+
value = @placeholder_expander.expand(value)
|
120
|
+
elsif value.is_a?(Hash)
|
121
|
+
new_value = {}
|
122
|
+
value.each_pair do |k, v|
|
123
|
+
new_value[@placeholder_expander.expand(k)] = interpolate(v)
|
124
|
+
end
|
125
|
+
value = new_value
|
126
|
+
elsif value.is_a?(Array)
|
127
|
+
value.each_with_index do |v, i|
|
128
|
+
value[i] = interpolate(v)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
value
|
132
|
+
end
|
133
|
+
|
106
134
|
def tag_prefix(tag_parts)
|
107
135
|
return [] if tag_parts.empty?
|
108
136
|
tag_prefix = [tag_parts.first]
|
@@ -175,18 +203,13 @@ module Fluent
|
|
175
203
|
@placeholders = struct
|
176
204
|
end
|
177
205
|
|
178
|
-
# Replace placeholders in a string
|
179
|
-
#
|
180
|
-
# @param [String] str the string to be replaced
|
181
206
|
def expand(str)
|
182
|
-
interpolated = str.gsub(/\$\{([^}]+)\}/, '#{\1}') # ${..} => #{..}
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
nil
|
189
|
-
end
|
207
|
+
interpolated = str.gsub(/\$\{([^}]+)\}/, '#{\1}') # ${..} => #{..}
|
208
|
+
eval "\"#{interpolated}\"", @placeholders.instance_eval { binding }
|
209
|
+
rescue => e
|
210
|
+
log.warn "failed to expand `#{str}`", :error_class => e.class, :error => e.message
|
211
|
+
log.warn_backtrace
|
212
|
+
nil
|
190
213
|
end
|
191
214
|
|
192
215
|
class UndefOpenStruct < OpenStruct
|
@@ -20,6 +20,7 @@ module Fluent
|
|
20
20
|
|
21
21
|
def initialize
|
22
22
|
require 'drb/drb'
|
23
|
+
require 'fluent/plugin/file_util'
|
23
24
|
super
|
24
25
|
end
|
25
26
|
|
@@ -31,6 +32,11 @@ module Fluent
|
|
31
32
|
|
32
33
|
def configure(conf)
|
33
34
|
super
|
35
|
+
if @unix_path
|
36
|
+
unless ::Fluent::FileUtil.writable?(@unix_path)
|
37
|
+
raise ConfigError, "in_debug_agent: `#{@unix_path}` is not writable"
|
38
|
+
end
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
def start
|
@@ -38,6 +38,7 @@ module Fluent
|
|
38
38
|
def initialize
|
39
39
|
require 'zlib'
|
40
40
|
require 'time'
|
41
|
+
require 'fluent/plugin/file_util'
|
41
42
|
super
|
42
43
|
end
|
43
44
|
|
@@ -59,6 +60,11 @@ module Fluent
|
|
59
60
|
conf['buffer_path'] ||= "#{@path}.*"
|
60
61
|
end
|
61
62
|
|
63
|
+
test_path = generate_path(Time.now.strftime(@time_slice_format))
|
64
|
+
unless ::Fluent::FileUtil.writable?(test_path)
|
65
|
+
raise ConfigError, "out_file: `#{test_path}` is not writable"
|
66
|
+
end
|
67
|
+
|
62
68
|
super
|
63
69
|
|
64
70
|
@formatter = Plugin.new_formatter(@format)
|
@@ -72,7 +78,7 @@ module Fluent
|
|
72
78
|
end
|
73
79
|
|
74
80
|
def write(chunk)
|
75
|
-
path = generate_path(chunk)
|
81
|
+
path = generate_path(chunk.key)
|
76
82
|
FileUtils.mkdir_p File.dirname(path), :mode => DEFAULT_DIR_PERMISSION
|
77
83
|
|
78
84
|
case @compress
|
@@ -97,21 +103,23 @@ module Fluent
|
|
97
103
|
|
98
104
|
private
|
99
105
|
|
100
|
-
def
|
106
|
+
def suffix
|
101
107
|
case @compress
|
102
108
|
when nil
|
103
|
-
|
109
|
+
''
|
104
110
|
when :gz
|
105
|
-
|
111
|
+
".gz"
|
106
112
|
end
|
113
|
+
end
|
107
114
|
|
115
|
+
def generate_path(time_string)
|
108
116
|
if @append
|
109
|
-
"#{@path_prefix}#{
|
117
|
+
"#{@path_prefix}#{time_string}#{@path_suffix}#{suffix}"
|
110
118
|
else
|
111
119
|
path = nil
|
112
120
|
i = 0
|
113
121
|
begin
|
114
|
-
path = "#{@path_prefix}#{
|
122
|
+
path = "#{@path_prefix}#{time_string}_#{i}#{@path_suffix}#{suffix}"
|
115
123
|
i += 1
|
116
124
|
end while File.exist?(path)
|
117
125
|
path
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -321,6 +321,8 @@ module Fluent
|
|
321
321
|
trap :HUP do
|
322
322
|
$log.debug "fluentd supervisor process get SIGHUP"
|
323
323
|
$log.info "restarting"
|
324
|
+
read_config
|
325
|
+
apply_system_config
|
324
326
|
if pid = @main_pid
|
325
327
|
Process.kill(:TERM, pid)
|
326
328
|
# don't resuce Erro::ESRSH here (invalid status)
|
data/lib/fluent/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/plugin/file_util'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class FileUtilTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
FileUtils.rm_rf(TEST_DIR)
|
8
|
+
FileUtils.mkdir_p(TEST_DIR)
|
9
|
+
end
|
10
|
+
|
11
|
+
TEST_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/file_util")
|
12
|
+
|
13
|
+
sub_test_case 'writable?' do
|
14
|
+
test 'file exists and writable' do
|
15
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
16
|
+
assert_true Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
17
|
+
end
|
18
|
+
|
19
|
+
test 'file exists and not writable' do
|
20
|
+
FileUtils.touch("#{TEST_DIR}/test_file")
|
21
|
+
File.chmod(0444, "#{TEST_DIR}/test_file")
|
22
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'file does not exist and directory is writable' do
|
26
|
+
assert_true Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
27
|
+
end
|
28
|
+
|
29
|
+
test 'file does not exist and directory is not writable' do
|
30
|
+
File.chmod(0444, TEST_DIR)
|
31
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
32
|
+
end
|
33
|
+
|
34
|
+
test 'directory does not exist' do
|
35
|
+
FileUtils.rm_rf(TEST_DIR)
|
36
|
+
assert_false Fluent::FileUtil.writable?("#{TEST_DIR}/test_file")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -115,6 +115,45 @@ class RecordTransformerFilterTest < Test::Unit::TestCase
|
|
115
115
|
assert_equal("#{@hostname} #{@tag_parts[-1]} #{msgs[i]}", r['message'])
|
116
116
|
end
|
117
117
|
end
|
118
|
+
|
119
|
+
test 'hash_value' do
|
120
|
+
config = %[
|
121
|
+
<record>
|
122
|
+
hash_field {"k1":100, "k2":"foobar"}
|
123
|
+
</record>
|
124
|
+
%]
|
125
|
+
msgs = ['1', '2']
|
126
|
+
es = emit(config, msgs)
|
127
|
+
es.each_with_index do |(t, r), i|
|
128
|
+
assert_equal({"k1"=>100, "k2"=>"foobar"}, r['hash_field'])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
test 'array_value' do
|
133
|
+
config = %[
|
134
|
+
<record>
|
135
|
+
array_field [1, 2, 3]
|
136
|
+
</record>
|
137
|
+
%]
|
138
|
+
msgs = ['1', '2']
|
139
|
+
es = emit(config, msgs)
|
140
|
+
es.each_with_index do |(t, r), i|
|
141
|
+
assert_equal([1,2,3], r['array_field'])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
test 'array_hash_mixed' do
|
146
|
+
config = %[
|
147
|
+
<record>
|
148
|
+
mixed_field {"hello":[1,2,3], "world":{"foo":"bar"}}
|
149
|
+
</record>
|
150
|
+
%]
|
151
|
+
msgs = ['1', '2']
|
152
|
+
es = emit(config, msgs)
|
153
|
+
es.each_with_index do |(t, r), i|
|
154
|
+
assert_equal({"hello"=>[1,2,3], "world"=>{"foo"=>"bar"}}, r['mixed_field'])
|
155
|
+
end
|
156
|
+
end
|
118
157
|
end
|
119
158
|
|
120
159
|
sub_test_case 'test placeholders' do
|
@@ -214,6 +253,50 @@ class RecordTransformerFilterTest < Test::Unit::TestCase
|
|
214
253
|
assert_equal("bar #{msgs[i]}", r['message'])
|
215
254
|
end
|
216
255
|
end
|
256
|
+
|
257
|
+
test "hash values with placeholders with enable_ruby #{enable_ruby}" do
|
258
|
+
config = %[
|
259
|
+
enable_ruby #{enable_ruby}
|
260
|
+
<record>
|
261
|
+
hash_field {
|
262
|
+
"hostname":"${hostname}",
|
263
|
+
"tag":"${tag}",
|
264
|
+
"${tag}":100
|
265
|
+
}
|
266
|
+
</record>
|
267
|
+
]
|
268
|
+
msgs = ['1', '2']
|
269
|
+
es = emit(config, msgs)
|
270
|
+
es.each_with_index do |(t, r), i|
|
271
|
+
assert_equal({"hostname" => @hostname, "tag" => @tag, "#{@tag}" => 100}, r['hash_field'])
|
272
|
+
end
|
273
|
+
end
|
274
|
+
test "array values with placeholders with enable_ruby #{enable_ruby}" do
|
275
|
+
config = %[
|
276
|
+
enable_ruby #{enable_ruby}
|
277
|
+
<record>
|
278
|
+
array_field ["${hostname}", "${tag}"]
|
279
|
+
</record>
|
280
|
+
]
|
281
|
+
msgs = ['1', '2']
|
282
|
+
es = emit(config, msgs)
|
283
|
+
es.each_with_index do |(t, r), i|
|
284
|
+
assert_equal([@hostname, @tag], r['array_field'])
|
285
|
+
end
|
286
|
+
end
|
287
|
+
test "array and hash values with placeholders with enable_ruby #{enable_ruby}" do
|
288
|
+
config = %[
|
289
|
+
enable_ruby #{enable_ruby}
|
290
|
+
<record>
|
291
|
+
mixed_field [{"tag":"${tag}"}]
|
292
|
+
</record>
|
293
|
+
]
|
294
|
+
msgs = ['1', '2']
|
295
|
+
es = emit(config, msgs)
|
296
|
+
es.each_with_index do |(t, r), i|
|
297
|
+
assert_equal([{"tag" => @tag}], r['mixed_field'])
|
298
|
+
end
|
299
|
+
end
|
217
300
|
end
|
218
301
|
|
219
302
|
test 'unknown placeholder (enable_ruby no)' do
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class DebugAgentInputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
FileUtils.rm_rf(TMP_DIR)
|
8
|
+
FileUtils.mkdir_p(TMP_DIR)
|
9
|
+
end
|
10
|
+
|
11
|
+
TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/in_debug_agent")
|
12
|
+
|
13
|
+
def create_driver(conf = '')
|
14
|
+
Fluent::Test::InputTestDriver.new(Fluent::DebugAgentInput).configure(conf)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_unix_path_writable
|
18
|
+
assert_nothing_raised do
|
19
|
+
create_driver %[unix_path #{TMP_DIR}/test_path]
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_raise(Fluent::ConfigError) do
|
23
|
+
create_driver %[unix_path #{TMP_DIR}/does_not_exist/test_path]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -40,6 +40,16 @@ class FileOutputTest < Test::Unit::TestCase
|
|
40
40
|
assert_equal :gz, d.instance.compress
|
41
41
|
end
|
42
42
|
|
43
|
+
def test_path_writable
|
44
|
+
assert_nothing_raised do
|
45
|
+
create_driver %[path #{TMP_DIR}/test_path]
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_raise(Fluent::ConfigError) do
|
49
|
+
create_driver %[path #{TMP_DIR}/does_not_exist/test_path]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
43
53
|
def test_default_localtime
|
44
54
|
d = create_driver(%[path #{TMP_DIR}/out_file_test])
|
45
55
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
data/test/test_output.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require_relative 'helper'
|
2
2
|
require 'fluent/test'
|
3
3
|
require 'fluent/output'
|
4
|
+
require 'timecop'
|
5
|
+
require 'flexmock'
|
4
6
|
|
5
7
|
module FluentOutputTest
|
6
8
|
include Fluent
|
9
|
+
include FlexMock::TestCase
|
7
10
|
|
8
11
|
class BufferedOutputTest < ::Test::Unit::TestCase
|
9
12
|
include FluentOutputTest
|
@@ -53,6 +56,10 @@ module FluentOutputTest
|
|
53
56
|
# disable_retry_limit
|
54
57
|
d = create_driver(CONFIG + %[disable_retry_limit true])
|
55
58
|
assert_equal true, d.instance.disable_retry_limit
|
59
|
+
|
60
|
+
# retry_wait is converted to Float for calc_retry_wait
|
61
|
+
d = create_driver(CONFIG + %[retry_wait 1s])
|
62
|
+
assert_equal Float, d.instance.retry_wait.class
|
56
63
|
end
|
57
64
|
|
58
65
|
def test_calc_retry_wait
|
@@ -73,6 +80,14 @@ module FluentOutputTest
|
|
73
80
|
assert_equal 4, d.instance.calc_retry_wait
|
74
81
|
end
|
75
82
|
|
83
|
+
def test_calc_retry_wait_with_integer_retry_wait
|
84
|
+
d = create_driver(CONFIG + %[retry_wait 2s])
|
85
|
+
d.instance.retry_limit.times {
|
86
|
+
d.instance.instance_eval { @num_errors += 1 }
|
87
|
+
}
|
88
|
+
assert_equal true, d.instance.calc_retry_wait.finite?
|
89
|
+
end
|
90
|
+
|
76
91
|
def test_large_num_retries
|
77
92
|
# Test that everything works properly after a very large number of
|
78
93
|
# retries and we hit the expected max_retry_wait.
|
@@ -159,5 +174,77 @@ module FluentOutputTest
|
|
159
174
|
])
|
160
175
|
assert_not_nil d.instance.instance_variable_get(:@secondary).router
|
161
176
|
end
|
177
|
+
|
178
|
+
sub_test_case "test_force_flush" do
|
179
|
+
setup do
|
180
|
+
time = Time.parse("2011-01-02 13:14:15 UTC")
|
181
|
+
Timecop.freeze(time)
|
182
|
+
@time = time.to_i
|
183
|
+
end
|
184
|
+
|
185
|
+
teardown do
|
186
|
+
Timecop.return
|
187
|
+
end
|
188
|
+
|
189
|
+
test "force_flush works on retrying" do
|
190
|
+
d = create_driver(CONFIG)
|
191
|
+
d.instance.start
|
192
|
+
buffer = d.instance.instance_variable_get(:@buffer)
|
193
|
+
# imitate 10 failures
|
194
|
+
d.instance.instance_variable_set(:@num_errors, 10)
|
195
|
+
d.instance.instance_variable_set(:@next_retry_time, @time + d.instance.calc_retry_wait)
|
196
|
+
# buffer should be popped (flushed) immediately
|
197
|
+
flexmock(buffer).should_receive(:pop).once
|
198
|
+
# force_flush
|
199
|
+
buffer.emit("test", 'test', NullOutputChain.instance)
|
200
|
+
d.instance.force_flush
|
201
|
+
10.times { sleep 0.05 }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class TimeSlicedOutputTest < ::Test::Unit::TestCase
|
207
|
+
include FluentOutputTest
|
208
|
+
include FlexMock::TestCase
|
209
|
+
|
210
|
+
def setup
|
211
|
+
Fluent::Test.setup
|
212
|
+
FileUtils.rm_rf(TMP_DIR)
|
213
|
+
FileUtils.mkdir_p(TMP_DIR)
|
214
|
+
end
|
215
|
+
|
216
|
+
TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/time_sliced_output")
|
217
|
+
|
218
|
+
CONFIG = %[]
|
219
|
+
|
220
|
+
def create_driver(conf=CONFIG)
|
221
|
+
Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::TimeSlicedOutput).configure(conf, true)
|
222
|
+
end
|
223
|
+
|
224
|
+
sub_test_case "test_force_flush" do
|
225
|
+
setup do
|
226
|
+
time = Time.parse("2011-01-02 13:14:15 UTC")
|
227
|
+
Timecop.freeze(time)
|
228
|
+
@es = OneEventStream.new(time.to_i, {"message" => "foo"})
|
229
|
+
end
|
230
|
+
|
231
|
+
teardown do
|
232
|
+
Timecop.return
|
233
|
+
end
|
234
|
+
|
235
|
+
test "force_flush immediately flushes" do
|
236
|
+
d = create_driver(CONFIG + %[
|
237
|
+
time_format %Y%m%d%H%M%S
|
238
|
+
buffer_path #{TMP_DIR}/foo
|
239
|
+
])
|
240
|
+
d.instance.start
|
241
|
+
# buffer should be popped (flushed) immediately
|
242
|
+
flexmock(d.instance.instance_variable_get(:@buffer)).should_receive(:pop).once
|
243
|
+
# force_flush
|
244
|
+
d.instance.emit('test', @es, NullOutputChain.instance)
|
245
|
+
d.instance.force_flush
|
246
|
+
10.times { sleep 0.05 }
|
247
|
+
end
|
248
|
+
end
|
162
249
|
end
|
163
250
|
end
|
data/test/test_parser.rb
CHANGED
@@ -199,25 +199,33 @@ module ParserTest
|
|
199
199
|
|
200
200
|
def setup
|
201
201
|
@parser = TextParser::ApacheParser.new
|
202
|
+
@expected = {
|
203
|
+
'user' => nil,
|
204
|
+
'method' => 'GET',
|
205
|
+
'code' => 200,
|
206
|
+
'size' => 777,
|
207
|
+
'host' => '192.168.0.1',
|
208
|
+
'path' => '/',
|
209
|
+
'referer' => nil,
|
210
|
+
'agent' => 'Opera/12.0'
|
211
|
+
}
|
202
212
|
end
|
203
213
|
|
204
214
|
def test_parse
|
205
215
|
@parser.parse('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0"') { |time, record|
|
206
216
|
assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
|
207
|
-
assert_equal(
|
208
|
-
'user' => nil,
|
209
|
-
'method' => 'GET',
|
210
|
-
'code' => 200,
|
211
|
-
'size' => 777,
|
212
|
-
'host' => '192.168.0.1',
|
213
|
-
'path' => '/',
|
214
|
-
'referer' => nil,
|
215
|
-
'agent' => 'Opera/12.0'
|
216
|
-
}, record)
|
217
|
+
assert_equal(@expected, record)
|
217
218
|
}
|
218
219
|
assert_equal(TextParser::ApacheParser::REGEXP, @parser.patterns['format'])
|
219
220
|
assert_equal(TextParser::ApacheParser::TIME_FORMAT, @parser.patterns['time_format'])
|
220
221
|
end
|
222
|
+
|
223
|
+
def test_parse_without_http_version
|
224
|
+
@parser.parse('192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET /" 200 777 "-" "Opera/12.0"') { |time, record|
|
225
|
+
assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
|
226
|
+
assert_equal(@expected, record)
|
227
|
+
}
|
228
|
+
end
|
221
229
|
end
|
222
230
|
|
223
231
|
class SyslogParserTest < ::Test::Unit::TestCase
|
@@ -354,6 +362,13 @@ module ParserTest
|
|
354
362
|
assert_equal(@expected.merge('path' => '/a[ ]b'), record)
|
355
363
|
}
|
356
364
|
end
|
365
|
+
|
366
|
+
def test_parse_without_http_version
|
367
|
+
@parser.parse('127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET /" 200 777 "-" "Opera/12.0"') { |time, record|
|
368
|
+
assert_equal(str2time('28/Feb/2013:12:00:00 +0900', '%d/%b/%Y:%H:%M:%S %z'), time)
|
369
|
+
assert_equal(@expected, record)
|
370
|
+
}
|
371
|
+
end
|
357
372
|
end
|
358
373
|
|
359
374
|
class TSVParserTest < ::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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msgpack
|
@@ -339,6 +339,7 @@ files:
|
|
339
339
|
- lib/fluent/plugin/buf_file.rb
|
340
340
|
- lib/fluent/plugin/buf_memory.rb
|
341
341
|
- lib/fluent/plugin/exec_util.rb
|
342
|
+
- lib/fluent/plugin/file_util.rb
|
342
343
|
- lib/fluent/plugin/filter_grep.rb
|
343
344
|
- lib/fluent/plugin/filter_record_transformer.rb
|
344
345
|
- lib/fluent/plugin/in_debug_agent.rb
|
@@ -396,8 +397,10 @@ files:
|
|
396
397
|
- test/plugin/data/log/test.log
|
397
398
|
- test/plugin/test_buf_file.rb
|
398
399
|
- test/plugin/test_buf_memory.rb
|
400
|
+
- test/plugin/test_file_util.rb
|
399
401
|
- test/plugin/test_filter_grep.rb
|
400
402
|
- test/plugin/test_filter_record_transformer.rb
|
403
|
+
- test/plugin/test_in_debug_agent.rb
|
401
404
|
- test/plugin/test_in_dummy.rb
|
402
405
|
- test/plugin/test_in_exec.rb
|
403
406
|
- test/plugin/test_in_forward.rb
|
@@ -477,8 +480,10 @@ test_files:
|
|
477
480
|
- test/plugin/data/log/test.log
|
478
481
|
- test/plugin/test_buf_file.rb
|
479
482
|
- test/plugin/test_buf_memory.rb
|
483
|
+
- test/plugin/test_file_util.rb
|
480
484
|
- test/plugin/test_filter_grep.rb
|
481
485
|
- test/plugin/test_filter_record_transformer.rb
|
486
|
+
- test/plugin/test_in_debug_agent.rb
|
482
487
|
- test/plugin/test_in_dummy.rb
|
483
488
|
- test/plugin/test_in_exec.rb
|
484
489
|
- test/plugin/test_in_forward.rb
|