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,274 @@
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 BufferError < StandardError
22
+ end
23
+
24
+
25
+ class Buffer
26
+ include Configurable
27
+
28
+ def initialize
29
+ super
30
+ end
31
+
32
+ def configure(conf)
33
+ super
34
+ end
35
+
36
+ def start
37
+ end
38
+
39
+ def shutdown
40
+ end
41
+
42
+ def before_shutdown(out)
43
+ end
44
+
45
+ #def emit(key, data, chain)
46
+ #end
47
+
48
+ #def keys
49
+ #end
50
+
51
+ #def push(key)
52
+ #end
53
+
54
+ #def pop(out)
55
+ #end
56
+
57
+ #def clear!
58
+ #end
59
+ end
60
+
61
+
62
+ class BufferChunk
63
+ include MonitorMixin
64
+
65
+ def initialize(key)
66
+ super()
67
+ @key = key
68
+ end
69
+
70
+ attr_reader :key
71
+
72
+ #def <<(data)
73
+ #end
74
+
75
+ #def size
76
+ #end
77
+
78
+ def empty?
79
+ size == 0
80
+ end
81
+
82
+ #def close
83
+ #end
84
+
85
+ #def purge
86
+ #end
87
+
88
+ #def read
89
+ #end
90
+
91
+ #def open
92
+ #end
93
+
94
+ def write_to(io)
95
+ open {|i|
96
+ FileUtils.copy_stream(i, io)
97
+ }
98
+ end
99
+
100
+ def msgpack_each(&block)
101
+ open {|io|
102
+ u = MessagePack::Unpacker.new(io)
103
+ begin
104
+ u.each(&block)
105
+ rescue EOFError
106
+ end
107
+ }
108
+ end
109
+ end
110
+
111
+
112
+ class BasicBuffer < Buffer
113
+ include MonitorMixin
114
+
115
+ def initialize
116
+ super()
117
+ @chunk_limit = 16*1024*1024 # TODO default
118
+ @queue_limit = 64 # TODO default
119
+ @parallel = false
120
+ end
121
+
122
+ attr_accessor :queue_limit, :chunk_limit
123
+
124
+ def configure(conf)
125
+ super
126
+
127
+ if chunk_limit = conf['buffer_chunk_limit']
128
+ @chunk_limit = Config.size_value(chunk_limit)
129
+ end
130
+
131
+ if queue_limit = conf['buffer_queue_limit']
132
+ @queue_limit = queue_limit.to_i
133
+ end
134
+ end
135
+
136
+ def start
137
+ @queue, @map = resume
138
+ @queue.extend(MonitorMixin)
139
+ end
140
+
141
+ def shutdown
142
+ synchronize do
143
+ @queue.synchronize do
144
+ until @queue.empty?
145
+ @queue.shift.close
146
+ end
147
+ end
148
+ @map.each_pair {|key,chunk|
149
+ chunk.close
150
+ }
151
+ end
152
+ end
153
+
154
+ def emit(key, data, chain)
155
+ key = key.to_s
156
+
157
+ synchronize do
158
+ top = (@map[key] ||= new_chunk(key)) # TODO generate unique chunk id
159
+
160
+ if top.size + data.bytesize <= @chunk_limit
161
+ chain.next
162
+ top << data
163
+ return false
164
+
165
+ elsif data.bytesize > @chunk_limit
166
+ # TODO
167
+ raise BufferError, "received data too large"
168
+
169
+ elsif @queue.size >= @queue_limit
170
+ # TODO
171
+ raise BufferError, "queue size exceeds limit"
172
+ end
173
+
174
+ nc = new_chunk(key) # TODO generate unique chunk id
175
+ ok = false
176
+
177
+ begin
178
+ nc << data
179
+ chain.next
180
+
181
+ @queue.synchronize {
182
+ enqueue(top)
183
+ @queue << top
184
+ @map[key] = nc
185
+ }
186
+
187
+ ok = true
188
+ return @queue.size == 1
189
+ ensure
190
+ nc.purge unless ok
191
+ end
192
+
193
+ end # synchronize
194
+ end
195
+
196
+ def keys
197
+ @map.keys
198
+ end
199
+
200
+ def queue_size
201
+ @queue.size
202
+ end
203
+
204
+ #def new_chunk(key)
205
+ #end
206
+
207
+ #def resume
208
+ #end
209
+
210
+ #def enqueue(chunk)
211
+ #end
212
+
213
+ def push(key)
214
+ synchronize do
215
+ top = @map[key]
216
+ if !top || top.empty?
217
+ return false
218
+ end
219
+
220
+ @queue.synchronize do
221
+ enqueue(top)
222
+ @queue << top
223
+ @map.delete(key)
224
+ end
225
+
226
+ return true
227
+ end # synchronize
228
+ end
229
+
230
+ def pop(out)
231
+ chunk = nil
232
+ @queue.synchronize do
233
+ if @parallel
234
+ chunk = @queue.find {|c| c.try_mon_enter }
235
+ return false unless chunk
236
+ else
237
+ chunk = @queue.first
238
+ return false unless chunk
239
+ return false unless chunk.try_mon_enter
240
+ end
241
+ end
242
+
243
+ begin
244
+ if !chunk.empty?
245
+ write_chunk(chunk, out)
246
+ end
247
+
248
+ @queue.delete_if {|c|
249
+ c.object_id == chunk.object_id
250
+ }
251
+
252
+ chunk.purge
253
+
254
+ return !@queue.empty?
255
+ ensure
256
+ chunk.mon_exit
257
+ end
258
+ end
259
+
260
+ def write_chunk(chunk, out)
261
+ out.write(chunk)
262
+ end
263
+
264
+ def clear!
265
+ @queue.delete_if {|chunk|
266
+ chunk.purge
267
+ true
268
+ }
269
+ end
270
+ end
271
+
272
+
273
+ end
274
+
@@ -0,0 +1,299 @@
1
+ #
2
+ # Fluent cat
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
+
19
+ require 'optparse'
20
+ require 'fluent/env'
21
+
22
+ op = OptionParser.new
23
+
24
+ op.banner += " <tag>"
25
+
26
+ port = Fluent::DEFAULT_LISTEN_PORT
27
+ host = '127.0.0.1'
28
+ unix = false
29
+ socket_path = Fluent::DEFAULT_SOCKET_PATH
30
+
31
+ config_path = Fluent::DEFAULT_CONFIG_PATH
32
+ format = 'json'
33
+
34
+ op.on('-p', '--port PORT', "fluent tcp port (default: #{port})", Integer) {|i|
35
+ port = i
36
+ }
37
+
38
+ op.on('-h', '--host HOST', "fluent host (default: #{host})") {|s|
39
+ host = s
40
+ }
41
+
42
+ op.on('-u', '--unix', "use unix socket instead of tcp", TrueClass) {|b|
43
+ unix = b
44
+ }
45
+
46
+ op.on('-s', '--socket PATH', "unix socket path (default: #{socket_path})") {|s|
47
+ socket_path = s
48
+ }
49
+
50
+ op.on('-f', '--format FORMAT', "input format (default: #{format})") {|s|
51
+ format = s
52
+ }
53
+
54
+ op.on('--json', "same as: -f json", TrueClass) {|b|
55
+ format = 'json'
56
+ }
57
+
58
+ op.on('--msgpack', "same as: -f msgpack", TrueClass) {|b|
59
+ format = 'msgpack'
60
+ }
61
+
62
+ (class<<self;self;end).module_eval do
63
+ define_method(:usage) do |msg|
64
+ puts op.to_s
65
+ puts "error: #{msg}" if msg
66
+ exit 1
67
+ end
68
+ end
69
+
70
+ begin
71
+ op.parse!(ARGV)
72
+
73
+ if ARGV.length != 1
74
+ usage nil
75
+ end
76
+
77
+ tag = ARGV.shift
78
+
79
+ rescue
80
+ usage $!.to_s
81
+ end
82
+
83
+
84
+ require 'thread'
85
+ require 'monitor'
86
+ require 'socket'
87
+ require 'json'
88
+ require 'msgpack'
89
+
90
+
91
+ class Writer
92
+ include MonitorMixin
93
+
94
+ class TimerThread
95
+ def initialize(writer)
96
+ @writer = writer
97
+ end
98
+
99
+ def start
100
+ @finish = false
101
+ @thread = Thread.new(&method(:run))
102
+ end
103
+
104
+ def shutdown
105
+ @finish = true
106
+ @thread.join
107
+ end
108
+
109
+ def run
110
+ until @finish
111
+ sleep 1
112
+ @writer.on_timer
113
+ end
114
+ end
115
+ end
116
+
117
+ def initialize(tag, connector)
118
+ @tag = tag
119
+ @connector = connector
120
+ @socket = false
121
+
122
+ @socket_time = Time.now.to_i
123
+ @socket_ttl = 10 # TODO
124
+ @error_history = []
125
+
126
+ @pending = []
127
+ @pending_limit = 1024 # TODO
128
+ @retry_wait = 1
129
+ @retry_limit = 5 # TODO
130
+
131
+ super()
132
+ end
133
+
134
+ def write(record)
135
+ if record.class != Hash
136
+ raise ArgumentError, "Input must be a map (got #{record.class})"
137
+ end
138
+
139
+ entry = [Time.now.to_i, record]
140
+ synchronize {
141
+ unless write_impl([entry])
142
+ # write failed
143
+ @pending.push(entry)
144
+
145
+ while @pending.size > @pending_limit
146
+ # exceeds pending limit; trash oldest record
147
+ time, record = @pending.shift
148
+ abort_message(time, record)
149
+ end
150
+ end
151
+ }
152
+ end
153
+
154
+ def on_timer
155
+ now = Time.now.to_i
156
+
157
+ synchronize {
158
+ unless @pending.empty?
159
+ # flush pending records
160
+ if write_impl(@pending)
161
+ # write succeeded
162
+ @pending.clear
163
+ end
164
+ end
165
+
166
+ if @socket && @socket_time + @socket_ttl < now
167
+ # socket is not used @socket_ttl seconds
168
+ close
169
+ end
170
+ }
171
+ end
172
+
173
+ def close
174
+ @socket.close
175
+ @socket = nil
176
+ end
177
+
178
+ def start
179
+ @timer = TimerThread.new(self)
180
+ @timer.start
181
+ self
182
+ end
183
+
184
+ def shutdown
185
+ @timer.shutdown
186
+ end
187
+
188
+ private
189
+ def write_impl(array)
190
+ socket = get_socket
191
+ unless socket
192
+ return false
193
+ end
194
+
195
+ begin
196
+ socket.write [@tag, array].to_msgpack
197
+ socket.flush
198
+ rescue
199
+ $stderr.puts "write failed: #{$!}"
200
+ close
201
+ return false
202
+ end
203
+
204
+ return true
205
+ end
206
+
207
+ def get_socket
208
+ unless @socket
209
+ unless try_connect
210
+ return nil
211
+ end
212
+ end
213
+
214
+ @socket_time = Time.now.to_i
215
+ return @socket
216
+ end
217
+
218
+ def try_connect
219
+ now = Time.now.to_i
220
+
221
+ unless @error_history.empty?
222
+ # wait before re-connecting
223
+ wait = @retry_wait * (2 ** (@error_history.size-1))
224
+ if now <= @socket_time + wait
225
+ return false
226
+ end
227
+ end
228
+
229
+ begin
230
+ @socket = @connector.call
231
+ @error_history.clear
232
+ return true
233
+
234
+ rescue
235
+ $stderr.puts "connect failed: #{$!}"
236
+ @error_history << $!
237
+ @socket_time = now
238
+
239
+ if @retry_limit < @error_history.size
240
+ # abort all pending records
241
+ @pending.each {|(time, record)|
242
+ abort_message(time, record)
243
+ }
244
+ @pending.clear
245
+ @error_history.clear
246
+ end
247
+
248
+ return false
249
+ end
250
+ end
251
+
252
+ def abort_message(time, record)
253
+ $stdout.puts "!#{time}:#{record.to_json}"
254
+ end
255
+ end
256
+
257
+
258
+ if unix
259
+ connector = Proc.new {
260
+ UNIXSocket.open(socket_path)
261
+ }
262
+ else
263
+ connector = Proc.new {
264
+ TCPSocket.new(host, port)
265
+ }
266
+ end
267
+
268
+ w = Writer.new(tag, connector)
269
+ w.start
270
+
271
+ case format
272
+ when 'json'
273
+ begin
274
+ while line = $stdin.gets
275
+ record = JSON.parse(line)
276
+ w.write(record)
277
+ end
278
+ rescue
279
+ $stderr.puts $!
280
+ exit 1
281
+ end
282
+
283
+ when 'msgpack'
284
+ begin
285
+ u = MessagePack::Unpacker.new($stdin)
286
+ u.each {|record|
287
+ w.write(record)
288
+ }
289
+ rescue EOFError
290
+ rescue
291
+ $stderr.puts $!
292
+ exit 1
293
+ end
294
+
295
+ else
296
+ $stderr.puts "Unknown format '#{format}'"
297
+ exit 1
298
+ end
299
+