fluentd 0.10.0
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/AUTHORS +1 -0
- data/COPYING +14 -0
- data/ChangeLog +178 -0
- data/README.rdoc +57 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/bin/fluent-cat +6 -0
- data/bin/fluent-gem +10 -0
- data/bin/fluentd +6 -0
- data/fluent.conf +78 -0
- data/fluentd.gemspec +116 -0
- data/lib/fluent/buffer.rb +274 -0
- data/lib/fluent/command/cat.rb +299 -0
- data/lib/fluent/command/fluentd.rb +245 -0
- data/lib/fluent/config.rb +304 -0
- data/lib/fluent/engine.rb +224 -0
- data/lib/fluent/env.rb +6 -0
- data/lib/fluent/event.rb +159 -0
- data/lib/fluent/input.rb +41 -0
- data/lib/fluent/load.rb +23 -0
- data/lib/fluent/log.rb +277 -0
- data/lib/fluent/match.rb +189 -0
- data/lib/fluent/mixin.rb +170 -0
- data/lib/fluent/output.rb +466 -0
- data/lib/fluent/parser.rb +115 -0
- data/lib/fluent/plugin.rb +145 -0
- data/lib/fluent/plugin/buf_file.rb +181 -0
- data/lib/fluent/plugin/buf_memory.rb +97 -0
- data/lib/fluent/plugin/buf_zfile.rb +84 -0
- data/lib/fluent/plugin/in_http.rb +282 -0
- data/lib/fluent/plugin/in_stream.rb +187 -0
- data/lib/fluent/plugin/in_syslog.rb +174 -0
- data/lib/fluent/plugin/in_tail.rb +150 -0
- data/lib/fluent/plugin/out_copy.rb +72 -0
- data/lib/fluent/plugin/out_file.rb +111 -0
- data/lib/fluent/plugin/out_null.rb +44 -0
- data/lib/fluent/plugin/out_roundrobin.rb +72 -0
- data/lib/fluent/plugin/out_stdout.rb +34 -0
- data/lib/fluent/plugin/out_stream.rb +128 -0
- data/lib/fluent/plugin/out_test.rb +68 -0
- data/lib/fluent/test.rb +8 -0
- data/lib/fluent/test/base.rb +63 -0
- data/lib/fluent/test/input_test.rb +89 -0
- data/lib/fluent/test/output_test.rb +93 -0
- data/lib/fluent/version.rb +5 -0
- data/test/helper.rb +6 -0
- data/test/match.rb +115 -0
- data/test/plugin/in_http.rb +84 -0
- data/test/plugin/in_stream.rb +136 -0
- data/test/plugin/out_copy.rb +55 -0
- data/test/plugin/out_file.rb +82 -0
- data/test/plugin/out_roundrobin.rb +65 -0
- data/test/plugin/out_stream.rb +74 -0
- metadata +224 -0
data/lib/fluent/mixin.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
|
20
|
+
|
21
|
+
class TimeFormatter
|
22
|
+
def initialize(format, localtime)
|
23
|
+
@tc1 = 0
|
24
|
+
@tc1_str = nil
|
25
|
+
@tc2 = 0
|
26
|
+
@tc2_str = nil
|
27
|
+
|
28
|
+
if format
|
29
|
+
if localtime
|
30
|
+
define_singleton_method(:format_nocache) {|time|
|
31
|
+
Time.at(time).strftime(format)
|
32
|
+
}
|
33
|
+
else
|
34
|
+
define_singleton_method(:format_nocache) {|time|
|
35
|
+
Time.at(time).utc.strftime(format)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
else
|
39
|
+
if localtime
|
40
|
+
define_singleton_method(:format_nocache) {|time|
|
41
|
+
Time.at(time).iso8601
|
42
|
+
}
|
43
|
+
else
|
44
|
+
define_singleton_method(:format_nocache) {|time|
|
45
|
+
Time.at(time).utc.iso8601
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def format(time)
|
52
|
+
if @tc1 == time
|
53
|
+
return @tc1_str
|
54
|
+
elsif @tc2 == time
|
55
|
+
return @tc2_str
|
56
|
+
else
|
57
|
+
str = format_nocache(time)
|
58
|
+
if @tc1 < @tc2
|
59
|
+
@tc1 = time
|
60
|
+
@tc1_str = str
|
61
|
+
else
|
62
|
+
@tc2 = time
|
63
|
+
@tc2_str = str
|
64
|
+
end
|
65
|
+
return str
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_nocache(time)
|
70
|
+
# will be overridden in initialize
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.configure(conf)
|
74
|
+
if time_format = conf['time_format']
|
75
|
+
@time_format = time_format
|
76
|
+
end
|
77
|
+
|
78
|
+
if localtime = conf['localtime']
|
79
|
+
@localtime = true
|
80
|
+
elsif utc = conf['utc']
|
81
|
+
@localtime = false
|
82
|
+
end
|
83
|
+
|
84
|
+
@timef = new(@time_format, @localtime)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
module RecordFilterMixin
|
90
|
+
def filter_record(tag, time, record)
|
91
|
+
end
|
92
|
+
|
93
|
+
def format_stream(tag, es)
|
94
|
+
out = ''
|
95
|
+
es.each {|time,record|
|
96
|
+
filter_record(tag, time, record)
|
97
|
+
out << format(tag, time, record)
|
98
|
+
}
|
99
|
+
out
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
module SetTimeKeyMixin
|
105
|
+
include RecordFilterMixin
|
106
|
+
|
107
|
+
def initialize
|
108
|
+
super
|
109
|
+
@time_key = "time"
|
110
|
+
@localtime = true
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_accessor :time_key, :localtime
|
114
|
+
|
115
|
+
def configure(conf)
|
116
|
+
super
|
117
|
+
|
118
|
+
if time_key = conf['time_key']
|
119
|
+
@time_key = time_key
|
120
|
+
end
|
121
|
+
unless @time_key
|
122
|
+
@time_key = 'time'
|
123
|
+
end
|
124
|
+
|
125
|
+
if time_format = conf['time_format']
|
126
|
+
@time_format = time_format
|
127
|
+
end
|
128
|
+
|
129
|
+
if localtime = conf['localtime']
|
130
|
+
@localtime = true
|
131
|
+
elsif utc = conf['utc']
|
132
|
+
@localtime = false
|
133
|
+
end
|
134
|
+
|
135
|
+
@timef = TimeFormatter.new(@time_format, @localtime)
|
136
|
+
end
|
137
|
+
|
138
|
+
def filter_record(tag, time, record)
|
139
|
+
super
|
140
|
+
record[@time_key] = @timef.format(time)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
module SetTagKeyMixin
|
146
|
+
include RecordFilterMixin
|
147
|
+
|
148
|
+
def initialize
|
149
|
+
super
|
150
|
+
@tag_key = "tag"
|
151
|
+
end
|
152
|
+
|
153
|
+
attr_accessor :tag_key
|
154
|
+
|
155
|
+
def configure(conf)
|
156
|
+
super
|
157
|
+
|
158
|
+
if tag_key = conf['tag_key']
|
159
|
+
@tag_key = tag_key
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def filter_record(tag, time, record)
|
164
|
+
super
|
165
|
+
record[@time_key] = @timef.format(time)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
|
20
|
+
|
21
|
+
class OutputChain
|
22
|
+
def initialize(array, tag, es, chain=NullOutputChain.instance)
|
23
|
+
@array = array
|
24
|
+
@tag = tag
|
25
|
+
@es = es
|
26
|
+
@offset = 0
|
27
|
+
@chain = chain
|
28
|
+
end
|
29
|
+
|
30
|
+
def next
|
31
|
+
if @array.length <= @offset
|
32
|
+
return @chain.next
|
33
|
+
end
|
34
|
+
@offset += 1
|
35
|
+
result = @array[@offset-1].emit(@tag, @es, self)
|
36
|
+
result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class NullOutputChain
|
41
|
+
require 'singleton'
|
42
|
+
include Singleton
|
43
|
+
|
44
|
+
def next
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
class Output
|
50
|
+
include Configurable
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure(conf)
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def start
|
61
|
+
end
|
62
|
+
|
63
|
+
def shutdown
|
64
|
+
end
|
65
|
+
|
66
|
+
#def emit(tag, es, chain)
|
67
|
+
#end
|
68
|
+
|
69
|
+
def secondary_init(primary)
|
70
|
+
if primary.class != self.class
|
71
|
+
$log.warn "type of secondary output should be same as primary output", :primary=>primary.class.to_s, :secondary=>self.class.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
class OutputThread
|
78
|
+
def initialize(output)
|
79
|
+
@output = output
|
80
|
+
@error_history = []
|
81
|
+
@flush_now = false
|
82
|
+
@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
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_accessor :flush_interval, :retry_limit, :retry_wait, :secondary_limit
|
93
|
+
|
94
|
+
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
|
+
end
|
127
|
+
|
128
|
+
def start
|
129
|
+
@mutex = Mutex.new
|
130
|
+
@cond = ConditionVariable.new
|
131
|
+
@last_try = Engine.now
|
132
|
+
#calc_next_time()
|
133
|
+
# TODO
|
134
|
+
@next_time = @last_try
|
135
|
+
@thread = Thread.new(&method(:run))
|
136
|
+
@secondary.start if @secondary
|
137
|
+
end
|
138
|
+
|
139
|
+
def shutdown
|
140
|
+
@finish = true
|
141
|
+
@mutex.synchronize {
|
142
|
+
@cond.signal
|
143
|
+
}
|
144
|
+
@secondary.shutdown if @secondary
|
145
|
+
Thread.pass
|
146
|
+
@thread.join
|
147
|
+
end
|
148
|
+
|
149
|
+
def submit_flush
|
150
|
+
@mutex.synchronize {
|
151
|
+
@flush_now = true
|
152
|
+
@cond.signal
|
153
|
+
}
|
154
|
+
Thread.pass
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
def run
|
159
|
+
@mutex.lock
|
160
|
+
begin
|
161
|
+
until @finish
|
162
|
+
|
163
|
+
now = Engine.now
|
164
|
+
|
165
|
+
if @next_time <= now ||
|
166
|
+
(@flush_now && @error_history.empty?)
|
167
|
+
|
168
|
+
@mutex.unlock
|
169
|
+
begin
|
170
|
+
try_flush(now)
|
171
|
+
ensure
|
172
|
+
@mutex.lock
|
173
|
+
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)
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
ensure
|
186
|
+
@mutex.unlock
|
187
|
+
end
|
188
|
+
|
189
|
+
rescue
|
190
|
+
$log.error "error on output thread", :error=>$!.to_s
|
191
|
+
$log.error_backtrace
|
192
|
+
raise
|
193
|
+
ensure
|
194
|
+
@mutex.synchronize {
|
195
|
+
@output.before_shutdown
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
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
|
+
if ConditionVariable.new.method(:wait).arity == 1
|
211
|
+
$log.warn "WARNING: Running on Ruby 1.8. Ruby 1.9 is recommended."
|
212
|
+
require 'timeout'
|
213
|
+
def cond_wait(sec)
|
214
|
+
Timeout.timeout(sec) {
|
215
|
+
@cond.wait(@mutex)
|
216
|
+
}
|
217
|
+
rescue Timeout::Error
|
218
|
+
end
|
219
|
+
else
|
220
|
+
def cond_wait(sec)
|
221
|
+
@cond.wait(@mutex, sec)
|
222
|
+
end
|
223
|
+
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
|
+
$log.warn "failed to flush the buffer", :error=>$!.to_s
|
240
|
+
$log.warn_backtrace
|
241
|
+
|
242
|
+
error_count = @error_history.size
|
243
|
+
@error_history << time
|
244
|
+
|
245
|
+
if error_count < @retry_limit
|
246
|
+
$log.info "retrying."
|
247
|
+
|
248
|
+
elsif @secondary
|
249
|
+
if error_count == @retry_limit
|
250
|
+
$log.warn "retry count exceededs limit. falling back to secondary output."
|
251
|
+
next # retry immediately
|
252
|
+
elsif error_count <= @retry_limit + @secondary_limit
|
253
|
+
$log.info "retrying secondary."
|
254
|
+
else
|
255
|
+
$log.warn "secondary retry count exceededs limit."
|
256
|
+
write_abort
|
257
|
+
end
|
258
|
+
|
259
|
+
else
|
260
|
+
$log.warn "retry count exceededs limit."
|
261
|
+
write_abort
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
break
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def write_abort
|
271
|
+
$log.error "throwing away old logs."
|
272
|
+
begin
|
273
|
+
@output.write_abort
|
274
|
+
rescue
|
275
|
+
$log.error "unexpected error while aborting", :error=>$!.to_s
|
276
|
+
$log.error_backtrace
|
277
|
+
end
|
278
|
+
@error_history.clear
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
class BufferedOutput < Output
|
284
|
+
def initialize
|
285
|
+
super
|
286
|
+
end
|
287
|
+
|
288
|
+
config_param :buffer_type, :string, :default => 'memory'
|
289
|
+
|
290
|
+
def configure(conf)
|
291
|
+
super
|
292
|
+
|
293
|
+
@buffer = Plugin.new_buffer(@buffer_type)
|
294
|
+
@buffer.configure(conf)
|
295
|
+
|
296
|
+
@writer = OutputThread.new(self)
|
297
|
+
@writer.configure(conf)
|
298
|
+
end
|
299
|
+
|
300
|
+
def start
|
301
|
+
@buffer.start
|
302
|
+
@writer.start
|
303
|
+
end
|
304
|
+
|
305
|
+
def shutdown
|
306
|
+
@writer.shutdown
|
307
|
+
@buffer.shutdown
|
308
|
+
end
|
309
|
+
|
310
|
+
def emit(tag, es, chain, key="")
|
311
|
+
data = format_stream(tag, es)
|
312
|
+
if @buffer.emit(key, data, chain)
|
313
|
+
submit_flush
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def submit_flush
|
318
|
+
@writer.submit_flush
|
319
|
+
end
|
320
|
+
|
321
|
+
def format_stream(tag, es)
|
322
|
+
out = ''
|
323
|
+
es.each {|time,record|
|
324
|
+
out << format(tag, time, record)
|
325
|
+
}
|
326
|
+
out
|
327
|
+
end
|
328
|
+
|
329
|
+
#def format(tag, time, record)
|
330
|
+
#end
|
331
|
+
|
332
|
+
#def write(chunk)
|
333
|
+
#end
|
334
|
+
|
335
|
+
def try_flush
|
336
|
+
if @buffer.queue_size == 0
|
337
|
+
@buffer.synchronize do
|
338
|
+
@buffer.keys.each {|key|
|
339
|
+
@buffer.push(key)
|
340
|
+
}
|
341
|
+
end
|
342
|
+
end
|
343
|
+
@buffer.pop(self)
|
344
|
+
end
|
345
|
+
|
346
|
+
def before_shutdown
|
347
|
+
begin
|
348
|
+
@buffer.before_shutdown(self)
|
349
|
+
rescue
|
350
|
+
$log.warn "before_shutdown failed", :error=>$!.to_s
|
351
|
+
$log.warn_backtrace
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def write_abort
|
356
|
+
@buffer.clear!
|
357
|
+
end
|
358
|
+
|
359
|
+
def flush_secondary(secondary)
|
360
|
+
@buffer.pop(secondary)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
class TimeSlicedOutput < BufferedOutput
|
366
|
+
def initialize
|
367
|
+
super
|
368
|
+
@localtime = true
|
369
|
+
#@ignore_old = false # TODO
|
370
|
+
# TODO @flush_interval = 60 # overwrite default flush_interval
|
371
|
+
end
|
372
|
+
|
373
|
+
config_param :time_slice_format, :string, :default => '%Y%m%d'
|
374
|
+
config_param :time_slice_wait, :time, :default => 10*60 # TODO default value
|
375
|
+
config_set_default :buffer_type, 'file' # overwrite default buffer_type
|
376
|
+
|
377
|
+
attr_accessor :localtime
|
378
|
+
|
379
|
+
def configure(conf)
|
380
|
+
super
|
381
|
+
|
382
|
+
# TODO timezone
|
383
|
+
if conf['utc']
|
384
|
+
@localtime = false
|
385
|
+
elsif conf['localtime']
|
386
|
+
@localtime = true
|
387
|
+
end
|
388
|
+
|
389
|
+
if time_slice_format = conf['time_slice_format']
|
390
|
+
@time_slice_format = time_slice_format
|
391
|
+
end
|
392
|
+
|
393
|
+
if time_slice_wait = conf['time_slice_wait']
|
394
|
+
@time_slice_wait = Config.time_value(time_slice_wait)
|
395
|
+
end
|
396
|
+
|
397
|
+
if @localtime
|
398
|
+
@time_slicer = Proc.new {|time|
|
399
|
+
Time.at(time).strftime(@time_slice_format)
|
400
|
+
}
|
401
|
+
else
|
402
|
+
@time_slicer = Proc.new {|time|
|
403
|
+
Time.at(time).utc.strftime(@time_slice_format)
|
404
|
+
}
|
405
|
+
end
|
406
|
+
|
407
|
+
@time_slice_cache_interval = time_slice_cache_interval
|
408
|
+
@before_tc = nil
|
409
|
+
@before_key = nil
|
410
|
+
end
|
411
|
+
|
412
|
+
def emit(tag, es, chain)
|
413
|
+
es.each {|time,record|
|
414
|
+
tc = time / @time_slice_cache_interval
|
415
|
+
if @before_tc == tc
|
416
|
+
key = @before_key
|
417
|
+
else
|
418
|
+
@before_tc = tc
|
419
|
+
key = @time_slicer.call(time)
|
420
|
+
@before_key = key
|
421
|
+
end
|
422
|
+
data = format(tag, time, record)
|
423
|
+
if @buffer.emit(key, data, chain)
|
424
|
+
submit_flush
|
425
|
+
end
|
426
|
+
}
|
427
|
+
end
|
428
|
+
|
429
|
+
def try_flush
|
430
|
+
nowslice = @time_slicer.call(Engine.now.to_i - @time_slice_wait)
|
431
|
+
@buffer.synchronize do
|
432
|
+
@buffer.keys.each {|key|
|
433
|
+
if key < nowslice
|
434
|
+
@buffer.push(key)
|
435
|
+
end
|
436
|
+
}
|
437
|
+
end
|
438
|
+
@buffer.pop(self)
|
439
|
+
end
|
440
|
+
|
441
|
+
#def format(tag, event)
|
442
|
+
#end
|
443
|
+
|
444
|
+
private
|
445
|
+
def time_slice_cache_interval
|
446
|
+
if @time_slicer.call(0) != @time_slicer.call(60-1)
|
447
|
+
return 1
|
448
|
+
elsif @time_slicer.call(0) != @time_slicer.call(60*60-1)
|
449
|
+
return 30
|
450
|
+
elsif @time_slicer.call(0) != @time_slicer.call(24*60*60-1)
|
451
|
+
return 60*30
|
452
|
+
else
|
453
|
+
return 24*60*30
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
class MultiOutput < Output
|
460
|
+
#def outputs
|
461
|
+
#end
|
462
|
+
end
|
463
|
+
|
464
|
+
|
465
|
+
end
|
466
|
+
|