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.

Files changed (54) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +14 -0
  3. data/ChangeLog +178 -0
  4. data/README.rdoc +57 -0
  5. data/Rakefile +62 -0
  6. data/VERSION +1 -0
  7. data/bin/fluent-cat +6 -0
  8. data/bin/fluent-gem +10 -0
  9. data/bin/fluentd +6 -0
  10. data/fluent.conf +78 -0
  11. data/fluentd.gemspec +116 -0
  12. data/lib/fluent/buffer.rb +274 -0
  13. data/lib/fluent/command/cat.rb +299 -0
  14. data/lib/fluent/command/fluentd.rb +245 -0
  15. data/lib/fluent/config.rb +304 -0
  16. data/lib/fluent/engine.rb +224 -0
  17. data/lib/fluent/env.rb +6 -0
  18. data/lib/fluent/event.rb +159 -0
  19. data/lib/fluent/input.rb +41 -0
  20. data/lib/fluent/load.rb +23 -0
  21. data/lib/fluent/log.rb +277 -0
  22. data/lib/fluent/match.rb +189 -0
  23. data/lib/fluent/mixin.rb +170 -0
  24. data/lib/fluent/output.rb +466 -0
  25. data/lib/fluent/parser.rb +115 -0
  26. data/lib/fluent/plugin.rb +145 -0
  27. data/lib/fluent/plugin/buf_file.rb +181 -0
  28. data/lib/fluent/plugin/buf_memory.rb +97 -0
  29. data/lib/fluent/plugin/buf_zfile.rb +84 -0
  30. data/lib/fluent/plugin/in_http.rb +282 -0
  31. data/lib/fluent/plugin/in_stream.rb +187 -0
  32. data/lib/fluent/plugin/in_syslog.rb +174 -0
  33. data/lib/fluent/plugin/in_tail.rb +150 -0
  34. data/lib/fluent/plugin/out_copy.rb +72 -0
  35. data/lib/fluent/plugin/out_file.rb +111 -0
  36. data/lib/fluent/plugin/out_null.rb +44 -0
  37. data/lib/fluent/plugin/out_roundrobin.rb +72 -0
  38. data/lib/fluent/plugin/out_stdout.rb +34 -0
  39. data/lib/fluent/plugin/out_stream.rb +128 -0
  40. data/lib/fluent/plugin/out_test.rb +68 -0
  41. data/lib/fluent/test.rb +8 -0
  42. data/lib/fluent/test/base.rb +63 -0
  43. data/lib/fluent/test/input_test.rb +89 -0
  44. data/lib/fluent/test/output_test.rb +93 -0
  45. data/lib/fluent/version.rb +5 -0
  46. data/test/helper.rb +6 -0
  47. data/test/match.rb +115 -0
  48. data/test/plugin/in_http.rb +84 -0
  49. data/test/plugin/in_stream.rb +136 -0
  50. data/test/plugin/out_copy.rb +55 -0
  51. data/test/plugin/out_file.rb +82 -0
  52. data/test/plugin/out_roundrobin.rb +65 -0
  53. data/test/plugin/out_stream.rb +74 -0
  54. metadata +224 -0
@@ -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
+