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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b7788505e84495465d06f359b75401362f04caaa
4
- data.tar.gz: 47a5ed1ef3ee3b55cabbcfdf65877374e66642b7
3
+ metadata.gz: 5a5677f5a62821f6d9300f21eddded90ff1f0e0a
4
+ data.tar.gz: 004b3bb49cd860b6132811b3cf1aa7ef89c4611f
5
5
  SHA512:
6
- metadata.gz: 7b174bf87334d6c06db915f3c244e39dbc637c7f21872fc227046f9b8c4e332809972e956013c0f0c4fea1ed187e89a45656723d698e733f8fcf78e85cf8f7d0
7
- data.tar.gz: 602154651def0f60ddc693b6ba1b1bd5c7d7ce0f2efbc7b4413ba16905929239fb7928e41fd00f93838f4a3ee909f5978737dded60b2fcf6c8fd035221cc381d
6
+ metadata.gz: 2aa4d2ef65d85eeb6df07245c6f9459b17d1a8d1546862ad7b96556d413a029dc3fff2514f49ffd1f3f307f11cf117816fcfcf28060f823525a01392c4e342fc
7
+ data.tar.gz: ff625fbf8a5b70866d413b6c5e5f1b8d4ebb52acf696ee1416de2cd249ff6ca14c223a312d633027f6c21e2e7a95b150abc3a43baa116ab54b55856543375432
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ pkg/*
19
19
  test/tmp/*
20
20
  test/config/tmp/*
21
21
  make_dist.sh
22
+ Gemfile.local
data/.travis.yml CHANGED
@@ -4,6 +4,7 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1
7
+ - 2.2
7
8
  - ruby-head
8
9
  - rbx-2
9
10
 
@@ -25,3 +26,4 @@ matrix:
25
26
  allow_failures:
26
27
  - rvm: ruby-head
27
28
  - rvm: rbx-2
29
+ - rvm: 2.2
data/ChangeLog CHANGED
@@ -1,6 +1,22 @@
1
1
  # v0.12
2
2
 
3
- ## Release 0.12.1 - 2014/12/14
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
- system("bundle install")
18
- unless $?.success?
19
- exit $?.exitstatus
20
- end
21
-
22
- cmdline = [
23
- 'bundle',
24
- 'exec',
25
- RbConfig.ruby,
26
- File.expand_path(File.join(File.dirname(__FILE__), 'fluentd.rb')),
27
- ] + ARGV
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
- exec *cmdline
30
- exit! 127
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
- enqueue_buffer
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
- @enqueue_buffer_proc.call
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>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
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>[^\"]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, {'time_format'=>"%d/%b/%Y:%H:%M:%S %z"}) },
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] = @placeholder_expander.expand(v) }
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
- begin
184
- eval "\"#{interpolated}\"", @placeholders.instance_eval { binding }
185
- rescue => e
186
- log.warn "failed to expand `#{str}`", :error_class => e.class, :error => e.message
187
- log.warn_backtrace
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 generate_path(chunk)
106
+ def suffix
101
107
  case @compress
102
108
  when nil
103
- suffix = ''
109
+ ''
104
110
  when :gz
105
- suffix = ".gz"
111
+ ".gz"
106
112
  end
113
+ end
107
114
 
115
+ def generate_path(time_string)
108
116
  if @append
109
- "#{@path_prefix}#{chunk.key}#{@path_suffix}#{suffix}"
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}#{chunk.key}_#{i}#{@path_suffix}#{suffix}"
122
+ path = "#{@path_prefix}#{time_string}_#{i}#{@path_suffix}#{suffix}"
115
123
  i += 1
116
124
  end while File.exist?(path)
117
125
  path
@@ -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)
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '0.12.2'
19
+ VERSION = '0.12.3'
20
20
 
21
21
  end
@@ -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.2
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: 2014-12-19 00:00:00.000000000 Z
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