fluentd 0.10.58 → 0.10.59

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: 3a62e7c7181c7f247c4a76b42638d0a6b4195d8a
4
- data.tar.gz: 12ccff6793701f59c0b67c17d66804a3d25ac818
3
+ metadata.gz: 434f3deef039256104205794d09c06668a82e8c4
4
+ data.tar.gz: 296981ab483797547447a60ce944987be7ff7431
5
5
  SHA512:
6
- metadata.gz: 81a1d182345423ecbb41a94351ccb6666f449126128718187345b9e62c187a3f8ed50fa56cfdee19cd5817ed37620471d10d871ca339cd345ef2111e1da76e29
7
- data.tar.gz: 7a61d7bd4b80b28373fb1ed7824add416a085d77a7ace5a229bb34ebb6965fadf7fbcbd5ff4d61ee89ce6166b05a17011b619d5e7d9e9e42ddade2d6828ab8f0
6
+ metadata.gz: 1f5cd6aef58345917d17e5ab6560d610954eba3f3043cad59d9a446f5b4aa4964e2d118e94a9b1be31610558fba1cccb74430fda55eb9e1e8744b6afee865af8
7
+ data.tar.gz: 2e6a4d27834b6cff8f8b7efe1cb1da480f935dd2e958f78aec5ded158e490216e311bcfc40437cf4caeba01248b88c4be4825dfd8f1ca36ba5673a4079f2209f
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/ChangeLog CHANGED
@@ -1,3 +1,15 @@
1
+ Release 0.10.59 - 2014/01/23
2
+
3
+ * in_tail: Support 64bit inode environment in in_tail
4
+ * parser: nginx and apache2 format can now parse access log without http-version
5
+ * buffer: Fix broken calc_retry_wait if Integer is used for retry_wait parameter
6
+ * buffer: Fix to flush a buffer by USR1 signal even on retrying
7
+ * buffer: Fix TimeSlicedOutput doesn't flush with SIGUSR1
8
+ * config: Support @ prefix build-in parameters
9
+ * engine:Fix ThreadError occuring on Signal.trap SIGHUP
10
+ * engine: Fix SIGHUP does not reload config
11
+ * Revert fluent.conf since @type, @id are not available with v0 config
12
+
1
13
  Release 0.10.58 - 2014/12/14
2
14
 
3
15
  * parser/formatter: Add base class and Plugin.new_xxx/Plugin.register_xxx APIs
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
@@ -4,20 +4,20 @@
4
4
  ## built-in TCP input
5
5
  ## $ echo <json> | fluent-cat <tag>
6
6
  <source>
7
- @type forward
8
- @id forward_input
7
+ type forward
8
+ id forward_input
9
9
  </source>
10
10
 
11
11
  ## built-in UNIX socket input
12
12
  #<source>
13
- # @type unix
13
+ # type unix
14
14
  #</source>
15
15
 
16
16
  # HTTP input
17
17
  # http://localhost:8888/<tag>?json=<json>
18
18
  <source>
19
- @type http
20
- @id http_input
19
+ type http
20
+ id http_input
21
21
 
22
22
  port 8888
23
23
  </source>
@@ -25,7 +25,7 @@
25
25
  ## File input
26
26
  ## read apache logs with tag=apache.access
27
27
  #<source>
28
- # @type tail
28
+ # type tail
29
29
  # format apache
30
30
  # path /var/log/httpd-access.log
31
31
  # tag apache.access
@@ -36,16 +36,16 @@
36
36
  # http://localhost:24220/api/plugins?type=TYPE
37
37
  # http://localhost:24220/api/plugins?tag=MYTAG
38
38
  <source>
39
- @type monitor_agent
40
- @id monitor_agent_input
39
+ type monitor_agent
40
+ id monitor_agent_input
41
41
 
42
42
  port 24220
43
43
  </source>
44
44
 
45
45
  # Listen DRb for debug
46
46
  <source>
47
- @type debug_agent
48
- @id debug_agent_input
47
+ type debug_agent
48
+ id debug_agent_input
49
49
 
50
50
  bind 127.0.0.1
51
51
  port 24230
@@ -53,20 +53,20 @@
53
53
 
54
54
  ## match tag=apache.access and write to file
55
55
  #<match apache.access>
56
- # @type file
56
+ # type file
57
57
  # path /var/log/fluent/access
58
58
  #</match>
59
59
 
60
60
  ## match tag=debug.** and dump to console
61
61
  <match debug.**>
62
- @type stdout
63
- @id stdout_output
62
+ type stdout
63
+ id stdout_output
64
64
  </match>
65
65
 
66
66
  # match tag=system.** and forward to another fluent server
67
67
  <match system.**>
68
- @type forward
69
- @id forward_output
68
+ type forward
69
+ id forward_output
70
70
 
71
71
  <server>
72
72
  host 192.168.0.11
@@ -80,9 +80,9 @@
80
80
 
81
81
  ## match tag=myapp.** and forward and write to file
82
82
  #<match myapp.**>
83
- # @type copy
83
+ # type copy
84
84
  # <store>
85
- # @type forward
85
+ # type forward
86
86
  # buffer_type file
87
87
  # buffer_path /var/log/fluent/myapp-forward
88
88
  # retry_limit 50
@@ -92,19 +92,19 @@
92
92
  # </server>
93
93
  # </store>
94
94
  # <store>
95
- # @type file
95
+ # type file
96
96
  # path /var/log/fluent/myapp
97
97
  # </store>
98
98
  #</match>
99
99
 
100
100
  ## match fluent's internal events
101
101
  #<match fluent.**>
102
- # @type null
102
+ # type null
103
103
  #</match>
104
104
 
105
105
  ## match not matched logs and write to file
106
106
  #<match **>
107
- # @type file
107
+ # type file
108
108
  # path /var/log/fluent/else
109
109
  # compress gz
110
110
  #</match>
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
+
16
17
  module Fluent
17
18
  module Config
18
19
 
@@ -50,7 +51,7 @@ module Fluent
50
51
  end
51
52
 
52
53
  ELEM_SYMBOLS = ['match', 'source', 'filter', 'system']
53
- RESERVED_PARAMS = %W(@type @id @label)
54
+ RESERVED_PARAMS = %W(@type @id)
54
55
 
55
56
  def parse_element(root_element, elem_name, attrs = {}, elems = [])
56
57
  while true
@@ -108,6 +109,12 @@ module Fluent
108
109
  else
109
110
  if k == '@include'
110
111
  parse_include(attrs, elems)
112
+ elsif RESERVED_PARAMS.include?(k)
113
+ v = parse_literal
114
+ unless line_end
115
+ parse_error! "expected end of line"
116
+ end
117
+ attrs[k] = v
111
118
  else
112
119
  if k.start_with?('@')
113
120
  if root_element || ELEM_SYMBOLS.include?(elem_name)
@@ -100,7 +100,7 @@ module Fluent
100
100
  conf.elements.select {|e|
101
101
  e.name == 'source'
102
102
  }.each {|e|
103
- type = e['type']
103
+ type = e['@type'] || e['type']
104
104
  unless type
105
105
  raise ConfigError, "Missing 'type' parameter on <source> directive"
106
106
  end
@@ -116,7 +116,7 @@ module Fluent
116
116
  conf.elements.select {|e|
117
117
  e.name == 'match'
118
118
  }.each {|e|
119
- type = e['type']
119
+ type = e['@type'] || e['type']
120
120
  pattern = e.arg
121
121
  unless type
122
122
  raise ConfigError, "Missing 'type' parameter on <match #{e.arg}> directive"
@@ -188,6 +188,7 @@ module Fluent
188
188
  def configure(conf)
189
189
  super
190
190
 
191
+ @retry_wait = @retry_wait.to_f # converted to Float for calc_retry_wait
191
192
  @buffer = Plugin.new_buffer(@buffer_type)
192
193
  @buffer.configure(conf)
193
194
 
@@ -267,7 +268,7 @@ module Fluent
267
268
  #def write(chunk)
268
269
  #end
269
270
 
270
- def enqueue_buffer
271
+ def enqueue_buffer(force = false)
271
272
  @buffer.keys.each {|key|
272
273
  @buffer.push(key)
273
274
  }
@@ -378,7 +379,10 @@ module Fluent
378
379
  end
379
380
 
380
381
  def force_flush
381
- enqueue_buffer
382
+ @num_errors_lock.synchronize do
383
+ @next_retry_time = Engine.now - 1
384
+ end
385
+ enqueue_buffer(true)
382
386
  submit_flush
383
387
  end
384
388
 
@@ -546,8 +550,14 @@ module Fluent
546
550
  }
547
551
  end
548
552
 
549
- def enqueue_buffer
550
- @enqueue_buffer_proc.call
553
+ def enqueue_buffer(force = false)
554
+ if force
555
+ @buffer.keys.each {|key|
556
+ @buffer.push(key)
557
+ }
558
+ else
559
+ @enqueue_buffer_proc.call
560
+ end
551
561
  end
552
562
 
553
563
  #def format(tag, event)
@@ -408,7 +408,7 @@ module Fluent
408
408
  end
409
409
 
410
410
  class ApacheParser < Parser
411
- REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
411
+ REGEXP = /^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
412
412
  TIME_FORMAT = "%d/%b/%Y:%H:%M:%S %z"
413
413
 
414
414
  def initialize
@@ -632,7 +632,7 @@ module Fluent
632
632
  'tsv' => Proc.new { TSVParser.new },
633
633
  'ltsv' => Proc.new { LabeledTSVParser.new },
634
634
  'csv' => Proc.new { CSVParser.new },
635
- '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
+ '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"}) },
636
636
  'none' => Proc.new { NoneParser.new },
637
637
  'multiline' => Proc.new { MultilineParser.new },
638
638
  }.each { |name, factory|
@@ -84,6 +84,7 @@ module Fluent
84
84
  end
85
85
 
86
86
  config_param :buffer_path, :string
87
+ config_param :flush_at_shutdown, :bool, :default => false
87
88
 
88
89
  attr_accessor :symlink_path
89
90
 
@@ -104,11 +105,6 @@ module Fluent
104
105
  @buffer_path_suffix = ".log"
105
106
  end
106
107
 
107
- if flush_at_shutdown = conf['flush_at_shutdown']
108
- @flush_at_shutdown = true
109
- else
110
- @flush_at_shutdown = false
111
- end
112
108
  end
113
109
 
114
110
  def start
@@ -598,7 +598,7 @@ module Fluent
598
598
  @file.write path
599
599
  @file.write "\t"
600
600
  seek = @file.pos
601
- @file.write "0000000000000000\t00000000\n"
601
+ @file.write "0000000000000000\t0000000000000000\n"
602
602
  @last_pos = @file.pos
603
603
 
604
604
  @map[path] = FilePositionEntry.new(@file, seek)
@@ -624,12 +624,15 @@ module Fluent
624
624
  # Clean up unwatched file entries
625
625
  def self.compact(file)
626
626
  file.pos = 0
627
- existent_entries = file.each_line.select { |line|
627
+ existent_entries = file.each_line.map { |line|
628
628
  m = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(line)
629
629
  next unless m
630
+ path = m[1]
630
631
  pos = m[2].to_i(16)
631
- pos == UNWATCHED_POSITION ? nil : line
632
- }
632
+ ino = m[3].to_i(16)
633
+ # 32bit inode converted to 64bit at this phase
634
+ pos == UNWATCHED_POSITION ? nil : ("%s\t%016x\t%016x\n" % [path, pos, ino])
635
+ }.compact
633
636
 
634
637
  file.pos = 0
635
638
  file.truncate(0)
@@ -638,13 +641,13 @@ module Fluent
638
641
  end
639
642
 
640
643
  # pos inode
641
- # ffffffffffffffff\tffffffff\n
644
+ # ffffffffffffffff\tffffffffffffffff\n
642
645
  class FilePositionEntry
643
646
  POS_SIZE = 16
644
647
  INO_OFFSET = 17
645
- INO_SIZE = 8
646
- LN_OFFSET = 25
647
- SIZE = 26
648
+ INO_SIZE = 16
649
+ LN_OFFSET = 33
650
+ SIZE = 34
648
651
 
649
652
  def initialize(file, seek)
650
653
  @file = file
@@ -653,7 +656,7 @@ module Fluent
653
656
 
654
657
  def update(ino, pos)
655
658
  @file.pos = @seek
656
- @file.write "%016x\t%08x" % [pos, ino]
659
+ @file.write "%016x\t%016x" % [pos, ino]
657
660
  end
658
661
 
659
662
  def update_pos(pos)
@@ -663,7 +666,7 @@ module Fluent
663
666
 
664
667
  def read_inode
665
668
  @file.pos = @seek + INO_OFFSET
666
- raw = @file.read(8)
669
+ raw = @file.read(16)
667
670
  raw ? raw.to_i(16) : 0
668
671
  end
669
672
 
@@ -323,10 +323,16 @@ module Fluent
323
323
  trap :HUP do
324
324
  $log.debug "fluentd supervisor process get SIGHUP"
325
325
  $log.info "restarting"
326
- if pid = @main_pid
327
- Process.kill(:TERM, pid)
328
- # don't resuce Erro::ESRSH here (invalid status)
329
- end
326
+ # Creating new thread due to mutex can't lock
327
+ # in main thread during trap context
328
+ Thread.new {
329
+ read_config
330
+ apply_system_config
331
+ if pid = @main_pid
332
+ Process.kill(:TERM, pid)
333
+ # don't resuce Erro::ESRSH here (invalid status)
334
+ end
335
+ }.run
330
336
  end
331
337
 
332
338
  trap :USR1 do
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
 
3
- VERSION = '0.10.58'
3
+ VERSION = '0.10.59'
4
4
 
5
5
  end
@@ -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
@@ -42,6 +45,10 @@ module FluentOutputTest
42
45
  # disable_retry_limit
43
46
  d = create_driver(CONFIG + %[disable_retry_limit true])
44
47
  assert_equal true, d.instance.disable_retry_limit
48
+
49
+ # retry_wait is converted to Float for calc_retry_wait
50
+ d = create_driver(CONFIG + %[retry_wait 1s])
51
+ assert_equal Float, d.instance.retry_wait.class
45
52
  end
46
53
 
47
54
  def test_calc_retry_wait
@@ -62,6 +69,14 @@ module FluentOutputTest
62
69
  assert_equal 4, d.instance.calc_retry_wait
63
70
  end
64
71
 
72
+ def test_calc_retry_wait_with_integer_retry_wait
73
+ d = create_driver(CONFIG + %[retry_wait 2s])
74
+ d.instance.retry_limit.times {
75
+ d.instance.instance_eval { @num_errors += 1 }
76
+ }
77
+ assert_equal true, d.instance.calc_retry_wait.finite?
78
+ end
79
+
65
80
  def test_large_num_retries
66
81
  # Test that everything works properly after a very large number of
67
82
  # retries and we hit the expected max_retry_wait.
@@ -146,5 +161,77 @@ module FluentOutputTest
146
161
  assert_true d.instance.respond_to?(:router=)
147
162
  assert_true d.instance.router.respond_to?(:emit)
148
163
  end
164
+
165
+ sub_test_case "test_force_flush" do
166
+ setup do
167
+ time = Time.parse("2011-01-02 13:14:15 UTC")
168
+ Timecop.freeze(time)
169
+ @time = time.to_i
170
+ end
171
+
172
+ teardown do
173
+ Timecop.return
174
+ end
175
+
176
+ test "force_flush works on retrying" do
177
+ d = create_driver(CONFIG)
178
+ d.instance.start
179
+ buffer = d.instance.instance_variable_get(:@buffer)
180
+ # imitate 10 failures
181
+ d.instance.instance_variable_set(:@num_errors, 10)
182
+ d.instance.instance_variable_set(:@next_retry_time, @time + d.instance.calc_retry_wait)
183
+ # buffer should be popped (flushed) immediately
184
+ flexmock(buffer).should_receive(:pop).once
185
+ # force_flush
186
+ buffer.emit("test", 'test', NullOutputChain.instance)
187
+ d.instance.force_flush
188
+ 10.times { sleep 0.05 }
189
+ end
190
+ end
191
+ end
192
+
193
+ class TimeSlicedOutputTest < ::Test::Unit::TestCase
194
+ include FluentOutputTest
195
+ include FlexMock::TestCase
196
+
197
+ def setup
198
+ Fluent::Test.setup
199
+ FileUtils.rm_rf(TMP_DIR)
200
+ FileUtils.mkdir_p(TMP_DIR)
201
+ end
202
+
203
+ TMP_DIR = File.expand_path(File.dirname(__FILE__) + "/../tmp/time_sliced_output")
204
+
205
+ CONFIG = %[]
206
+
207
+ def create_driver(conf=CONFIG)
208
+ Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::TimeSlicedOutput).configure(conf, true)
209
+ end
210
+
211
+ sub_test_case "test_force_flush" do
212
+ setup do
213
+ time = Time.parse("2011-01-02 13:14:15 UTC")
214
+ Timecop.freeze(time)
215
+ @es = OneEventStream.new(time.to_i, {"message" => "foo"})
216
+ end
217
+
218
+ teardown do
219
+ Timecop.return
220
+ end
221
+
222
+ test "force_flush immediately flushes" do
223
+ d = create_driver(CONFIG + %[
224
+ time_format %Y%m%d%H%M%S
225
+ buffer_path #{TMP_DIR}/foo
226
+ ])
227
+ d.instance.start
228
+ # buffer should be popped (flushed) immediately
229
+ flexmock(d.instance.instance_variable_get(:@buffer)).should_receive(:pop).once
230
+ # force_flush
231
+ d.instance.emit('test', @es, NullOutputChain.instance)
232
+ d.instance.force_flush
233
+ 10.times { sleep 0.05 }
234
+ end
235
+ end
149
236
  end
150
237
  end
@@ -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.10.58
4
+ version: 0.10.59
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-14 00:00:00.000000000 Z
11
+ date: 2015-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack