fluentd 0.10.6 → 0.10.7

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.

data/ChangeLog CHANGED
@@ -1,4 +1,13 @@
1
1
 
2
+ Release 0.10.7 - 2011/11/16
3
+
4
+ * Supports multi-threaded on buffered output plugins ('num_threads')
5
+ * Supports multi-process on input plugins ('detach_process')
6
+ * Added ObjectBufferedOutput
7
+ * Ensure to call 'shutdown' method of started plugins
8
+ * in_tail supports 'pos_file' option that stores read position to a file
9
+
10
+
2
11
  Release 0.10.6 - 2011/11/11
3
12
 
4
13
  * Fixed --group NAME cmdline argument
data/README ADDED
@@ -0,0 +1,57 @@
1
+ = Fluent
2
+
3
+ Fluent is an event collector system. It's said that Fluent is generalized version of syslogd, which can deal with JSON object for the log message.
4
+
5
+ == Archtecture
6
+
7
+ Fluent collects events from various data sources and write them to files, database or other storages:
8
+
9
+
10
+ Web apps ---+ +--> file
11
+ | |
12
+ +--> ---+
13
+ /var/log ------> fluentd ------> mail
14
+ +--> ---+
15
+ | |
16
+ apache ---- +--> fluentd
17
+
18
+
19
+ Fluent also supports log transfer:
20
+
21
+
22
+ Web server
23
+ +---------+
24
+ | fluentd -------
25
+ +---------+| |
26
+ +---------+ |
27
+ |
28
+ Proxy server | Log server, Amazon S3, HDFS, ...
29
+ +---------+ +--> +---------+
30
+ | fluentd ----------> | fluentd ||
31
+ +---------+| +--> +---------+|
32
+ +---------+ | +---------+
33
+ |
34
+ Database server |
35
+ +---------+ |
36
+ | fluentd ---------> mail
37
+ +---------+|
38
+ +---------+
39
+
40
+
41
+ An event consists of *tag*, *time* and *record*. Tag is a string separated with '.' (e.g. myapp.access). It is used to categorize events. Time is a UNIX time when the event occurs. Record is a JSON object.
42
+
43
+
44
+ == Quick Start
45
+
46
+ $ gem install fluentd
47
+ $ fluentd &
48
+ $ echo '{"json":"message"}' | fluent-cat debug.test
49
+
50
+
51
+ Web site:: http://fluentd.org/
52
+ Documents:: http://fluentd.org/doc/
53
+ Source repository:: http://github.com/fluent
54
+ Author:: Sadayuki Furuhashi
55
+ Copyright:: (c) 2011 FURUHASHI Sadayuki
56
+ License:: Apache License, Version 2.0
57
+
data/Rakefile CHANGED
@@ -2,8 +2,32 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/clean'
4
4
 
5
- require 'bundler'
6
- Bundler::GemHelper.install_tasks
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "fluentd"
9
+ gemspec.summary = "Fluent event collector"
10
+ gemspec.author = "Sadayuki Furuhashi"
11
+ gemspec.email = "frsyuki@gmail.com"
12
+ gemspec.homepage = "http://fluentd.org/"
13
+ gemspec.has_rdoc = false
14
+ gemspec.require_paths = ["lib"]
15
+ gemspec.add_dependency "msgpack", "~> 0.4.4"
16
+ gemspec.add_dependency "json", ">= 1.4.3"
17
+ gemspec.add_dependency "yajl-ruby", "~> 1.0.0"
18
+ gemspec.add_dependency "cool.io", "~> 1.0.0"
19
+ gemspec.add_dependency "http_parser.rb", "~> 0.5.1"
20
+ gemspec.add_development_dependency "rake", ">= 0.9.2"
21
+ gemspec.test_files = Dir["test/**/*.rb"]
22
+ gemspec.files = Dir["bin/**/*", "lib/**/*", "test/**/*.rb"] +
23
+ %w[fluent.conf VERSION AUTHORS Rakefile COPYING fluentd.gemspec]
24
+ gemspec.executables = ['fluentd', 'fluent-cat', 'fluent-gem']
25
+ gemspec.required_ruby_version = '~> 1.9.2'
26
+ end
27
+ Jeweler::GemcutterTasks.new
28
+ rescue LoadError
29
+ puts "Jeweler not available. Install it with: gem install jeweler"
30
+ end
7
31
 
8
32
  Rake::TestTask.new(:test) do |t|
9
33
  t.test_files = Dir['test/*_test.rb']
@@ -11,6 +35,21 @@ Rake::TestTask.new(:test) do |t|
11
35
  t.ruby_opts << '-I.'
12
36
  end
13
37
 
38
+ VERSION_FILE = "lib/fluent/version.rb"
39
+
40
+ file VERSION_FILE => ["VERSION"] do |t|
41
+ version = File.read("VERSION").strip
42
+ File.open(VERSION_FILE, "w") {|f|
43
+ f.write <<EOF
44
+ module Fluent
45
+
46
+ VERSION = '#{version}'
47
+
48
+ end
49
+ EOF
50
+ }
51
+ end
52
+
14
53
  task :test => [:base_test]
15
54
 
16
55
  Rake::TestTask.new(:base_test) do |t|
@@ -20,5 +59,5 @@ Rake::TestTask.new(:base_test) do |t|
20
59
  #t.warning = true
21
60
  end
22
61
 
23
- task :default => [:build]
62
+ task :default => [VERSION_FILE, :build]
24
63
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.6
1
+ 0.10.7
data/bin/fluent-cat ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'rubygems' unless defined?(gem)
4
+ here = File.dirname(__FILE__)
5
+ $LOAD_PATH << File.expand_path(File.join(here, '..', 'lib'))
6
+ require 'fluent/command/cat'
data/bin/fluent-gem ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'rubygems' unless defined?(gem)
4
+ require 'rubygems/gem_runner'
5
+ require 'rubygems/exceptions'
6
+ begin
7
+ Gem::GemRunner.new.run ARGV
8
+ rescue Gem::SystemExitException => e
9
+ exit e.exit_code
10
+ end
data/bin/fluentd ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'rubygems' unless defined?(gem)
4
+ here = File.dirname(__FILE__)
5
+ $LOAD_PATH << File.expand_path(File.join(here, '..', 'lib'))
6
+ require 'fluent/command/fluentd'
data/lib/fluent/buffer.rb CHANGED
@@ -114,6 +114,11 @@ class BasicBuffer < Buffer
114
114
 
115
115
  def initialize
116
116
  super
117
+ @parallel_pop = true
118
+ end
119
+
120
+ def enable_parallel(b=true)
121
+ @parallel_pop = b
117
122
  end
118
123
 
119
124
  config_param :buffer_chunk_limit, :size, :default => 256*1024*1024
@@ -152,9 +157,10 @@ class BasicBuffer < Buffer
152
157
  top << data
153
158
  return false
154
159
 
155
- elsif data.bytesize > @buffer_chunk_limit
156
- # TODO
157
- raise BufferError, "received data too large"
160
+ ## FIXME
161
+ #elsif data.bytesize > @buffer_chunk_limit
162
+ # # TODO
163
+ # raise BufferError, "received data too large"
158
164
 
159
165
  elsif @queue.size >= @buffer_queue_limit
160
166
  # TODO
@@ -220,7 +226,7 @@ class BasicBuffer < Buffer
220
226
  def pop(out)
221
227
  chunk = nil
222
228
  @queue.synchronize do
223
- if @parallel
229
+ if @parallel_pop
224
230
  chunk = @queue.find {|c| c.try_mon_enter }
225
231
  return false unless chunk
226
232
  else
data/lib/fluent/config.rb CHANGED
@@ -283,6 +283,8 @@ module Configurable
283
283
  Proc.new {|val| val }
284
284
  when :integer
285
285
  Proc.new {|val| val.to_i }
286
+ when :float
287
+ Proc.new {|val| val.to_f }
286
288
  when :size
287
289
  Proc.new {|val| Config.size_value(val) }
288
290
  when :bool
data/lib/fluent/engine.rb CHANGED
@@ -23,6 +23,8 @@ class EngineClass
23
23
  @matches = []
24
24
  @sources = []
25
25
  @match_cache = {}
26
+ @started = []
27
+ @default_loop = nil
26
28
  end
27
29
 
28
30
  def init
@@ -129,23 +131,33 @@ class EngineClass
129
131
  end
130
132
 
131
133
  def run
132
- start
134
+ begin
135
+ start
133
136
 
134
- if match?($log.tag)
135
- $log.enable_event
136
- end
137
-
138
- # for empty loop
139
- Coolio::Loop.default.attach Coolio::TimerWatcher.new(1, true)
140
- # TODO attach async watch for thread pool
141
- Coolio::Loop.default.run
137
+ if match?($log.tag)
138
+ $log.enable_event
139
+ end
142
140
 
143
- shutdown
141
+ # for empty loop
142
+ @default_loop = Coolio::Loop.default
143
+ @default_loop.attach Coolio::TimerWatcher.new(1, true)
144
+ # TODO attach async watch for thread pool
145
+ @default_loop.run
146
+
147
+ rescue
148
+ $log.error "unexpected error", :error=>$!.to_s
149
+ $log.error_backtrace
150
+ ensure
151
+ shutdown
152
+ end
144
153
  end
145
154
 
146
155
  def stop
147
156
  $log.info "shutting down fluentd"
148
- Coolio::Loop.default.stop
157
+ if @default_loop
158
+ @default_loop.stop
159
+ @default_loop = nil
160
+ end
149
161
  nil
150
162
  end
151
163
 
@@ -153,18 +165,22 @@ class EngineClass
153
165
  def start
154
166
  @matches.each {|m|
155
167
  m.start
168
+ @started << m
156
169
  }
157
170
  @sources.each {|s|
158
171
  s.start
172
+ @started << s
159
173
  }
160
174
  end
161
175
 
162
176
  def shutdown
163
- @matches.each {|m|
164
- m.shutdown rescue nil
165
- }
166
- @sources.each {|s|
167
- s.shutdown rescue nil
177
+ @started.reverse_each {|s|
178
+ begin
179
+ s.shutdown
180
+ rescue
181
+ $log.warn "unexpected error while shutting down", :error=>$!.to_s
182
+ $log.warn_backtrace
183
+ end
168
184
  }
169
185
  end
170
186
 
data/lib/fluent/load.rb CHANGED
@@ -15,6 +15,7 @@ require 'fluent/log'
15
15
  require 'fluent/config'
16
16
  require 'fluent/engine'
17
17
  require 'fluent/mixin'
18
+ require 'fluent/process'
18
19
  require 'fluent/plugin'
19
20
  require 'fluent/parser'
20
21
  require 'fluent/event'
data/lib/fluent/output.rb CHANGED
@@ -77,63 +77,18 @@ end
77
77
  class OutputThread
78
78
  def initialize(output)
79
79
  @output = output
80
- @error_history = []
81
80
  @flush_now = false
82
81
  @finish = false
83
-
84
- @flush_interval = 60 # TODO default
85
- @retry_limit = 17 # TODO default
86
- @retry_wait = 1.0 # TODO default
87
-
88
- @secondary = nil
89
- @secondary_limit = 8 # TODO default
82
+ @next_time = Engine.now + 1.0
90
83
  end
91
84
 
92
- attr_accessor :flush_interval, :retry_limit, :retry_wait, :secondary_limit
93
-
94
85
  def configure(conf)
95
- if flush_interval = conf['flush_interval']
96
- @flush_interval = Config.time_value(flush_interval).to_i
97
- end
98
-
99
- if retry_limit = conf['retry_limit']
100
- @retry_limit = retry_limit.to_i
101
- if @retry_limit < 0
102
- raise ConfigError, "invalid parameter 'retry_limit #{retry_limit}'"
103
- end
104
- end
105
-
106
- if retry_wait = conf['retry_wait']
107
- @retry_wait = Config.time_value(retry_wait)
108
- end
109
-
110
- # TODO configure retry_pattern
111
-
112
- if econf = conf.elements.select {|e| e.name == 'secondary' }.first
113
- type = econf['type'] || conf['type']
114
- @secondary = Plugin.new_output(type)
115
- @secondary.configure(econf)
116
-
117
- if secondary_limit = conf['secondary_limit']
118
- @secondary_limit = secondary_limit.to_i
119
- if @secondary_limit < 0
120
- raise ConfigError, "invalid parameter 'secondary_limit #{secondary_limit}'"
121
- end
122
- end
123
-
124
- @secondary.secondary_init(@output)
125
- end
126
86
  end
127
87
 
128
88
  def start
129
89
  @mutex = Mutex.new
130
90
  @cond = ConditionVariable.new
131
- @last_try = Engine.now
132
- #calc_next_time()
133
- # TODO
134
- @next_time = @last_try
135
91
  @thread = Thread.new(&method(:run))
136
- @secondary.start if @secondary
137
92
  end
138
93
 
139
94
  def shutdown
@@ -141,7 +96,6 @@ class OutputThread
141
96
  @mutex.synchronize {
142
97
  @cond.signal
143
98
  }
144
- @secondary.shutdown if @secondary
145
99
  Thread.pass
146
100
  @thread.join
147
101
  end
@@ -159,33 +113,25 @@ class OutputThread
159
113
  @mutex.lock
160
114
  begin
161
115
  until @finish
116
+ time = Engine.now
162
117
 
163
- now = Engine.now
164
-
165
- if @next_time <= now ||
166
- (@flush_now && @error_history.empty?)
167
-
118
+ if @flush_now || @next_time <= time
168
119
  @mutex.unlock
169
120
  begin
170
- try_flush(now)
121
+ @next_time = @output.try_flush
171
122
  ensure
172
123
  @mutex.lock
173
124
  end
174
-
175
- now = @last_try = Engine.now
176
- @flush_now = false
177
- end
178
-
179
- next_wait = calc_next_time() - now
180
- if next_wait > 0
181
- cond_wait(next_wait)
125
+ next_wait = @next_time - Engine.now
126
+ else
127
+ next_wait = @next_time - time
182
128
  end
183
129
 
130
+ cond_wait(next_wait) if next_wait > 0
184
131
  end
185
132
  ensure
186
133
  @mutex.unlock
187
134
  end
188
-
189
135
  rescue
190
136
  $log.error "error on output thread", :error=>$!.to_s
191
137
  $log.error_backtrace
@@ -196,17 +142,6 @@ class OutputThread
196
142
  }
197
143
  end
198
144
 
199
- def calc_next_time
200
- # TODO retry pattern
201
- if @error_history.empty?
202
- @next_time = @last_try + @flush_interval
203
- elsif @error_history.size <= @retry_limit
204
- @next_time = @last_try + @retry_wait * (2 ** (@error_history.size-1))
205
- else
206
- @next_time = @last_try + @retry_wait * (2 ** (@error_history.size-2-@retry_limit))
207
- end
208
- end
209
-
210
145
  if ConditionVariable.new.method(:wait).arity == 1
211
146
  $log.warn "WARNING: Running on Ruby 1.8. Ruby 1.9 is recommended."
212
147
  require 'timeout'
@@ -221,77 +156,25 @@ class OutputThread
221
156
  @cond.wait(@mutex, sec)
222
157
  end
223
158
  end
224
-
225
- def try_flush(time)
226
- while true
227
-
228
- begin
229
- if @error_history.size > @retry_limit
230
- has_next = @output.flush_secondary(@secondary)
231
- else
232
- has_next = @output.try_flush
233
- end
234
-
235
- @error_history.clear
236
- next if has_next
237
-
238
- rescue
239
- e = $!
240
- error_count = @error_history.size
241
- @error_history << time
242
-
243
- if error_count < @retry_limit
244
- $log.warn "failed to flush the buffer, retrying.", :error=>e.to_s
245
- $log.warn_backtrace e.backtrace
246
-
247
- elsif @secondary
248
- if error_count == @retry_limit
249
- $log.warn "failed to flush the buffer.", :error=>e.to_s
250
- $log.warn "retry count exceededs limit. falling back to secondary output."
251
- $log.warn_backtrace e.backtrace
252
- next # retry immediately
253
- elsif error_count <= @retry_limit + @secondary_limit
254
- $log.warn "failed to flush the buffer, retrying secondary.", :error=>e.to_s
255
- $log.warn_backtrace e.backtrace
256
- else
257
- $log.warn "failed to flush the buffer.", :error=>e.to_s
258
- $log.warn "secondary retry count exceededs limit."
259
- $log.warn_backtrace e.backtrace
260
- write_abort
261
- end
262
-
263
- else
264
- $log.warn "failed to flush the buffer.", :error=>e.to_s
265
- $log.warn "retry count exceededs limit."
266
- $log.warn_backtrace e.backtrace
267
- write_abort
268
- end
269
-
270
- end
271
-
272
- break
273
- end
274
- end
275
-
276
- def write_abort
277
- $log.error "throwing away old logs."
278
- begin
279
- @output.write_abort
280
- rescue
281
- $log.error "unexpected error while aborting", :error=>$!.to_s
282
- $log.error_backtrace
283
- end
284
- @error_history.clear
285
- end
286
159
  end
287
160
 
288
161
 
289
162
  class BufferedOutput < Output
290
163
  def initialize
291
164
  super
165
+ @next_flush_time = 0
166
+ @last_retry_time = 0
167
+ @next_retry_time = 0
168
+ @error_history = []
169
+ @error_history.extend(MonitorMixin)
170
+ @secondary_limit = 8
292
171
  end
293
172
 
294
173
  config_param :buffer_type, :string, :default => 'memory'
174
+ config_param :flush_interval, :time, :default => 60
175
+ config_param :retry_limit, :integer, :default => 17
176
+ config_param :retry_wait, :float, :default => 1.0
177
+ config_param :num_threads, :integer, :default => 1
295
178
 
296
179
  def configure(conf)
297
180
  super
@@ -299,17 +182,46 @@ class BufferedOutput < Output
299
182
  @buffer = Plugin.new_buffer(@buffer_type)
300
183
  @buffer.configure(conf)
301
184
 
302
- @writer = OutputThread.new(self)
303
- @writer.configure(conf)
185
+ if @buffer.respond_to?(:enable_parallel)
186
+ if @num_threads == 1
187
+ @buffer.enable_parallel(false)
188
+ else
189
+ @buffer.enable_parallel(true)
190
+ end
191
+ end
192
+
193
+ @writers = (1..@num_threads).map {
194
+ writer = OutputThread.new(self)
195
+ writer.configure(conf)
196
+ writer
197
+ }
198
+
199
+ if sconf = conf.elements.select {|e| e.name == 'secondary' }.first
200
+ type = sconf['type'] || conf['type']
201
+ @secondary = Plugin.new_output(type)
202
+ @secondary.configure(sconf)
203
+
204
+ if secondary_limit = conf['secondary_limit']
205
+ @secondary_limit = secondary_limit.to_i
206
+ if @secondary_limit < 0
207
+ raise ConfigError, "invalid parameter 'secondary_limit #{secondary_limit}'"
208
+ end
209
+ end
210
+
211
+ @secondary.secondary_init(self)
212
+ end
304
213
  end
305
214
 
306
215
  def start
216
+ @next_flush_time = Engine.now + @flush_interval
307
217
  @buffer.start
308
- @writer.start
218
+ @secondary.start if @secondary
219
+ @writers.each {|writer| writer.start }
309
220
  end
310
221
 
311
222
  def shutdown
312
- @writer.shutdown
223
+ @writers.each {|writer| writer.shutdown }
224
+ @secondary.shutdown if @secondary
313
225
  @buffer.shutdown
314
226
  end
315
227
 
@@ -321,7 +233,8 @@ class BufferedOutput < Output
321
233
  end
322
234
 
323
235
  def submit_flush
324
- @writer.submit_flush
236
+ # TODO roundrobin?
237
+ @writers.first.submit_flush
325
238
  end
326
239
 
327
240
  def format_stream(tag, es)
@@ -338,15 +251,113 @@ class BufferedOutput < Output
338
251
  #def write(chunk)
339
252
  #end
340
253
 
254
+ def enqueue_buffer
255
+ @buffer.keys.each {|key|
256
+ @buffer.push(key)
257
+ }
258
+ end
259
+
341
260
  def try_flush
342
- if @buffer.queue_size == 0
261
+ time = Engine.now
262
+
263
+ empty = @buffer.queue_size == 0
264
+ if empty && @next_flush_time < (now = Engine.now)
343
265
  @buffer.synchronize do
344
- @buffer.keys.each {|key|
345
- @buffer.push(key)
346
- }
266
+ if @next_flush_time < now
267
+ enqueue_buffer
268
+ @next_flush_time = now + @flush_interval
269
+ empty = @buffer.queue_size == 0
270
+ end
271
+ end
272
+ end
273
+ if empty
274
+ return time + 1 # TODO 1
275
+ end
276
+
277
+ begin
278
+ retrying = !@error_history.empty?
279
+
280
+ if retrying
281
+ @error_history.synchronize do
282
+ if retrying = !@error_history.empty? # re-check in synchronize
283
+ if @next_retry_time >= time
284
+ # allow retrying for only one thread
285
+ return time + 1 # TODO 1
286
+ end
287
+ # assume next retry failes and
288
+ # clear them if when it suceeds
289
+ @last_retry_time = time
290
+ @error_history << time
291
+ @next_retry_time += calc_retry_wait
292
+ end
293
+ end
294
+ end
295
+
296
+ if @secondary && @error_history.size > @retry_limit
297
+ has_next = flush_secondary(@secondary)
298
+ else
299
+ has_next = @buffer.pop(self)
300
+ end
301
+
302
+ # success
303
+ if retrying
304
+ @error_history.clear
305
+ # Note: don't notify to other threads to prevent
306
+ # burst to recovered server
307
+ end
308
+
309
+ if has_next
310
+ return time # call try_flush soon
311
+ else
312
+ return time + 1 # TODO 1
313
+ end
314
+
315
+ rescue => e
316
+ if retrying
317
+ error_count = @error_history.size
318
+ else
319
+ # first error
320
+ error_count = 0
321
+ @error_history.synchronize do
322
+ if @error_history.empty?
323
+ @last_retry_time = time
324
+ @error_history << time
325
+ @next_retry_time = time + calc_retry_wait
326
+ end
327
+ end
328
+ end
329
+
330
+ if error_count < @retry_limit
331
+ $log.warn "failed to flush the buffer, retrying.", :error=>e.to_s
332
+ $log.warn_backtrace e.backtrace
333
+
334
+ elsif @secondary
335
+ if error_count == @retry_limit
336
+ $log.warn "failed to flush the buffer.", :error=>e.to_s
337
+ $log.warn "retry count exceededs limit. falling back to secondary output."
338
+ $log.warn_backtrace e.backtrace
339
+ retry # retry immediately
340
+ elsif error_count <= @retry_limit + @secondary_limit
341
+ $log.warn "failed to flush the buffer, retrying secondary.", :error=>e.to_s
342
+ $log.warn_backtrace e.backtrace
343
+ else
344
+ $log.warn "failed to flush the buffer.", :error=>e.to_s
345
+ $log.warn "secondary retry count exceededs limit."
346
+ $log.warn_backtrace e.backtrace
347
+ write_abort
348
+ @error_history.clear
349
+ end
350
+
351
+ else
352
+ $log.warn "failed to flush the buffer.", :error=>e.to_s
353
+ $log.warn "retry count exceededs limit."
354
+ $log.warn_backtrace e.backtrace
355
+ write_abort
356
+ @error_history.clear
347
357
  end
358
+
359
+ return @next_retry_time
348
360
  end
349
- @buffer.pop(self)
350
361
  end
351
362
 
352
363
  def before_shutdown
@@ -358,8 +369,24 @@ class BufferedOutput < Output
358
369
  end
359
370
  end
360
371
 
372
+ def calc_retry_wait
373
+ # TODO retry pattern
374
+ if @error_history.size <= @retry_limit
375
+ @retry_wait * (2 ** (@error_history.size-1))
376
+ else
377
+ # secondary retry
378
+ @retry_wait * (2 ** (@error_history.size-2-@retry_limit))
379
+ end
380
+ end
381
+
361
382
  def write_abort
362
- @buffer.clear!
383
+ $log.error "throwing away old logs."
384
+ begin
385
+ @buffer.clear!
386
+ rescue
387
+ $log.error "unexpected error while aborting", :error=>$!.to_s
388
+ $log.error_backtrace
389
+ end
363
390
  end
364
391
 
365
392
  def flush_secondary(secondary)
@@ -368,6 +395,42 @@ class BufferedOutput < Output
368
395
  end
369
396
 
370
397
 
398
+ class ObjectBufferedOutput < BufferedOutput
399
+ def initialize
400
+ super
401
+ end
402
+
403
+ def emit(tag, es, chain)
404
+ data = es.to_msgpack_stream
405
+ key = tag
406
+ if @buffer.emit(key, data, chain)
407
+ submit_flush
408
+ end
409
+ end
410
+
411
+ module BufferedEventStreamMixin
412
+ include Enumerable
413
+
414
+ def repeatable?
415
+ true
416
+ end
417
+
418
+ def each(&block)
419
+ msgpack_each(&block)
420
+ end
421
+
422
+ def to_msgpack_stream
423
+ read
424
+ end
425
+ end
426
+
427
+ def write(chunk)
428
+ chunk.extend(BufferedEventStreamMixin)
429
+ write_objects(chunk.key, chunk)
430
+ end
431
+ end
432
+
433
+
371
434
  class TimeSlicedOutput < BufferedOutput
372
435
  def initialize
373
436
  super
@@ -378,7 +441,6 @@ class TimeSlicedOutput < BufferedOutput
378
441
  config_param :time_slice_format, :string, :default => '%Y%m%d'
379
442
  config_param :time_slice_wait, :time, :default => 10*60
380
443
  config_set_default :buffer_type, 'file' # overwrite default buffer_type
381
- # config_set_default :flush_interval, 60 # TODO overwrite default flush_interval
382
444
 
383
445
  attr_accessor :localtime
384
446
 
@@ -424,16 +486,13 @@ class TimeSlicedOutput < BufferedOutput
424
486
  }
425
487
  end
426
488
 
427
- def try_flush
489
+ def enqueue_buffer
428
490
  nowslice = @time_slicer.call(Engine.now.to_i - @time_slice_wait)
429
- @buffer.synchronize do
430
- @buffer.keys.each {|key|
431
- if key < nowslice
432
- @buffer.push(key)
433
- end
434
- }
435
- end
436
- @buffer.pop(self)
491
+ @buffer.keys.each {|key|
492
+ if key < nowslice
493
+ @buffer.push(key)
494
+ end
495
+ }
437
496
  end
438
497
 
439
498
  #def format(tag, event)