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 +9 -0
- data/README +57 -0
- data/Rakefile +42 -3
- data/VERSION +1 -1
- data/bin/fluent-cat +6 -0
- data/bin/fluent-gem +10 -0
- data/bin/fluentd +6 -0
- data/lib/fluent/buffer.rb +10 -4
- data/lib/fluent/config.rb +2 -0
- data/lib/fluent/engine.rb +32 -16
- data/lib/fluent/load.rb +1 -0
- data/lib/fluent/output.rb +215 -156
- data/lib/fluent/plugin/buf_file.rb +2 -1
- data/lib/fluent/plugin/in_forward.rb +4 -2
- data/lib/fluent/plugin/in_http.rb +15 -8
- data/lib/fluent/plugin/in_stream.rb +2 -1
- data/lib/fluent/plugin/in_tail.rb +133 -9
- data/lib/fluent/plugin/out_forward.rb +15 -9
- data/lib/fluent/process.rb +469 -0
- data/lib/fluent/version.rb +1 -1
- metadata +33 -38
- data/.gitignore +0 -20
- data/.rvmrc +0 -1
- data/Gemfile +0 -3
- data/Makefile.am +0 -50
- data/autogen.sh +0 -67
- data/configure.in +0 -52
- data/fluentd.gemspec +0 -40
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
|
-
|
6
|
-
|
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.
|
1
|
+
0.10.7
|
data/bin/fluent-cat
ADDED
data/bin/fluent-gem
ADDED
data/bin/fluentd
ADDED
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
|
-
|
156
|
-
|
157
|
-
|
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 @
|
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
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
|
-
|
134
|
+
begin
|
135
|
+
start
|
133
136
|
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
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
|
-
|
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
|
-
@
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
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
|
-
|
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
|
121
|
+
@next_time = @output.try_flush
|
171
122
|
ensure
|
172
123
|
@mutex.lock
|
173
124
|
end
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
@
|
303
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
@
|
345
|
-
|
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
|
-
|
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
|
489
|
+
def enqueue_buffer
|
428
490
|
nowslice = @time_slicer.call(Engine.now.to_i - @time_slice_wait)
|
429
|
-
@buffer.
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
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)
|