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,84 @@
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 ZFileBufferChunk < FileBufferChunk
22
+ def initialize(path)
23
+ super(path)
24
+ @z = Zlib::Deflate.new
25
+ end
26
+
27
+ def <<(data)
28
+ zdata = @z.deflate(data, Z_NO_FLUSH)
29
+ unless zdata.empty?
30
+ super(zdata)
31
+ end
32
+ end
33
+
34
+ def close_write
35
+ zdata = @z.flush
36
+ unless zdata.empty?
37
+ @file.write(zdata)
38
+ @size += zdata.bytesize
39
+ end
40
+ super
41
+ end
42
+
43
+ def close
44
+ @z.close
45
+ super
46
+ end
47
+
48
+ def purge
49
+ @z.close
50
+ super
51
+ end
52
+
53
+ # TODO
54
+ #def open(&block)
55
+ #end
56
+ end
57
+
58
+
59
+ class ZFileBuffer < FileBuffer
60
+ # TODO
61
+ #Plugin.register_buffer('zfile', self)
62
+
63
+ def initialize
64
+ require 'zlib'
65
+ super
66
+ end
67
+
68
+ def resume_queue
69
+ queue = resume_queue_paths.map {|path|
70
+ ZFileBufferChunk.new(path)
71
+ }
72
+ top = new_chunk
73
+ return queue, top
74
+ end
75
+
76
+ def new_chunk
77
+ path = new_chunk_path
78
+ ZFileBufferChunk.new(path)
79
+ end
80
+ end
81
+
82
+
83
+ end
84
+
@@ -0,0 +1,282 @@
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 HttpInput < Input
22
+ Plugin.register_input('http', self)
23
+
24
+ require 'http/parser'
25
+
26
+ def initialize
27
+ require 'webrick/httputils'
28
+ super
29
+ end
30
+
31
+ config_param :port, :integer, :default => 9880
32
+ config_param :bind, :string, :default => '0.0.0.0'
33
+ config_param :body_size_limit, :size, :default => 32*1024*1024 # TODO default
34
+ config_param :keepalive_timeout, :time, :default => 10 # TODO default
35
+
36
+ def configure(conf)
37
+ super
38
+ end
39
+
40
+ class KeepaliveManager < Coolio::TimerWatcher
41
+ class TimerValue
42
+ def initialize
43
+ @value = 0
44
+ end
45
+ attr_accessor :value
46
+ end
47
+
48
+ def initialize(timeout)
49
+ super(1, true)
50
+ @cons = {}
51
+ @timeout = timeout.to_i
52
+ end
53
+
54
+ def add(sock)
55
+ @cons[sock] = sock
56
+ end
57
+
58
+ def delete(sock)
59
+ @cons.delete(sock)
60
+ end
61
+
62
+ def on_timer
63
+ @cons.each_pair {|sock,val|
64
+ if sock.step_idle > @timeout
65
+ sock.close
66
+ end
67
+ }
68
+ end
69
+ end
70
+
71
+ # TODO multithreading
72
+ def start
73
+ $log.debug "listening http on #{@bind}:#{@port}"
74
+
75
+ @km = KeepaliveManager.new(@keepalive_timeout)
76
+ @lsock = Coolio::TCPServer.new(@bind, @port, Handler, @km, method(:on_request), @body_size_limit)
77
+
78
+ @loop = Coolio::Loop.new
79
+ @loop.attach(@km)
80
+ @loop.attach(@lsock)
81
+
82
+ @thread = Thread.new(&method(:run))
83
+ end
84
+
85
+ def shutdown
86
+ @lsock.close
87
+ @loop.stop
88
+ @thread.join
89
+ end
90
+
91
+ def run
92
+ @loop.run
93
+ rescue
94
+ $log.error "unexpected error", :error=>$!.to_s
95
+ $log.error_backtrace
96
+ end
97
+
98
+ def on_request(path_info, params)
99
+ begin
100
+ path = path_info[1..-1] # remove /
101
+ tag = path.split('/').join('.')
102
+
103
+ if msgpack = params['msgpack']
104
+ record = MessagePack.unpack(msgpack)
105
+
106
+ elsif js = params['json']
107
+ record = JSON.parse(js)
108
+
109
+ else
110
+ raise "'json' or 'msgpack' parameter is required"
111
+ end
112
+
113
+ time = params['time']
114
+ time = time.to_i
115
+ if time == 0
116
+ time = Engine.now
117
+ end
118
+
119
+ rescue
120
+ return ["400 Bad Request", {'Content-type'=>'text/plain'}, "400 Bad Request\n#{$!}\n"]
121
+ end
122
+
123
+ # TODO server error
124
+ begin
125
+ Engine.emit(tag, time, record)
126
+ rescue
127
+ return ["500 Internal Server Error", {'Content-type'=>'text/plain'}, "500 Internal Server Error\n#{$!}\n"]
128
+ end
129
+
130
+ return ["200 OK", {'Content-type'=>'text/plain'}, ""]
131
+ end
132
+
133
+ class Handler < Coolio::Socket
134
+ def initialize(io, km, callback, body_size_limit)
135
+ super(io)
136
+ @km = km
137
+ @callback = callback
138
+ @body_size_limit = body_size_limit
139
+ @content_type = ""
140
+ @next_close = false
141
+
142
+ @idle = 0
143
+ @km.add(self)
144
+ end
145
+
146
+ def step_idle
147
+ @idle += 1
148
+ end
149
+
150
+ def on_close
151
+ @km.delete(self)
152
+ end
153
+
154
+ def on_connect
155
+ @parser = Http::Parser.new(self)
156
+ end
157
+
158
+ def on_read(data)
159
+ @idle = 0
160
+ @parser << data
161
+ rescue
162
+ $log.warn "unexpected error", :error=>$!.to_s
163
+ $log.warn_backtrace
164
+ close
165
+ end
166
+
167
+ def on_message_begin
168
+ @body = ''
169
+ end
170
+
171
+ def on_headers_complete(headers)
172
+ expect = nil
173
+ size = nil
174
+ if @parser.http_version == [1, 1]
175
+ @keep_alive = true
176
+ else
177
+ @keep_alive = false
178
+ end
179
+ headers.each_pair {|k,v|
180
+ case k
181
+ when /Expect/i
182
+ expect = v
183
+ when /Content-Length/i
184
+ size = v.to_i
185
+ when /Content-Type/i
186
+ @content_type = v
187
+ when /Connection/i
188
+ if v =~ /close/i
189
+ @keep_alive = false
190
+ elsif v =~ /Keep-alive/i
191
+ @keep_alive = true
192
+ end
193
+ end
194
+ }
195
+ if expect
196
+ if expect == '100-continue'
197
+ if !size || size < @body_size_limit
198
+ send_response_nobody("100 Continue", {})
199
+ else
200
+ send_response_and_close("413 Request Entity Too Large", {}, "Too large")
201
+ end
202
+ else
203
+ send_response_and_close("417 Expectation Failed", {}, "")
204
+ end
205
+ end
206
+ end
207
+
208
+ def on_body(chunk)
209
+ if @body.bytesize + chunk.bytesize > @body_size_limit
210
+ unless closing?
211
+ send_response_and_close("413 Request Entity Too Large", {}, "Too large")
212
+ end
213
+ return
214
+ end
215
+ @body << chunk
216
+ end
217
+
218
+ def on_message_complete
219
+ return if closing?
220
+
221
+ params = WEBrick::HTTPUtils.parse_query(@parser.query_string)
222
+
223
+ if @content_type =~ /^application\/x-www-form-urlencoded/
224
+ params.update WEBrick::HTTPUtils.parse_query(@body)
225
+ elsif @content_type =~ /^multipart\/form-data; boundary=(.+)/
226
+ boundary = WEBrick::HTTPUtils.dequote($1)
227
+ params.update WEBrick::HTTPUtils.parse_form_data(@body, boundary)
228
+ end
229
+ path_info = @parser.request_path
230
+
231
+ code, header, body = *@callback.call(path_info, params)
232
+ body = body.to_s
233
+
234
+ if @keep_alive
235
+ header['Connection'] = 'Keep-Alive'
236
+ send_response(code, header, body)
237
+ else
238
+ send_response_and_close(code, header, body)
239
+ end
240
+ end
241
+
242
+ def on_write_complete
243
+ close if @next_close
244
+ end
245
+
246
+ def send_response_and_close(code, header, body)
247
+ send_response(code, header, body)
248
+ @next_close = true
249
+ end
250
+
251
+ def closing?
252
+ @next_close
253
+ end
254
+
255
+ def send_response(code, header, body)
256
+ header['Content-length'] ||= body.bytesize
257
+ header['Content-type'] ||= 'text/plain'
258
+
259
+ data = %[HTTP/1.1 #{code}\r\n]
260
+ header.each_pair {|k,v|
261
+ data << "#{k}: #{v}\r\n"
262
+ }
263
+ data << "\r\n"
264
+ write data
265
+
266
+ write body
267
+ end
268
+
269
+ def send_response_nobody(code, header)
270
+ data = %[HTTP/1.1 #{code}\r\n]
271
+ header.each_pair {|k,v|
272
+ data << "#{k}: #{v}\r\n"
273
+ }
274
+ data << "\r\n"
275
+ write data
276
+ end
277
+ end
278
+ end
279
+
280
+
281
+ end
282
+
@@ -0,0 +1,187 @@
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 StreamInput < Input
22
+ def initialize
23
+ require 'socket'
24
+ require 'yajl'
25
+ super
26
+ end
27
+
28
+ def start
29
+ @loop = Coolio::Loop.new
30
+ @lsock = listen
31
+ @loop.attach(@lsock)
32
+ @thread = Thread.new(&method(:run))
33
+ @cached_unpacker = MessagePack::Unpacker.new
34
+ end
35
+
36
+ def shutdown
37
+ @lsock.close
38
+ @loop.stop
39
+ @thread.join
40
+ end
41
+
42
+ #def listen
43
+ #end
44
+
45
+ def run
46
+ @loop.run
47
+ rescue
48
+ $log.error "unexpected error", :error=>$!.to_s
49
+ $log.error_backtrace
50
+ end
51
+
52
+ protected
53
+ # message Entry {
54
+ # 1: long time
55
+ # 2: object record
56
+ # }
57
+ #
58
+ # message Forward {
59
+ # 1: string tag
60
+ # 2: list<Entry> entries
61
+ # }
62
+ #
63
+ # message PackedForward {
64
+ # 1: string tag
65
+ # 2: raw entries # msgpack stream of Entry
66
+ # }
67
+ #
68
+ # message Message {
69
+ # 1: string tag
70
+ # 2: long? time
71
+ # 3: object record
72
+ # }
73
+ def on_message(msg)
74
+ # TODO format error
75
+ tag = msg[0].to_s
76
+ entries = msg[1]
77
+
78
+ if entries.class == String
79
+ # PackedForward
80
+ es = MessagePackEventStream.new(entries, @cached_unpacker)
81
+ Engine.emit_stream(tag, es)
82
+
83
+ elsif entries.class == Array
84
+ # Forward
85
+ es = MultiEventStream.new
86
+ entries.each {|e|
87
+ time = e[0].to_i
88
+ time = (now ||= Engine.now) if time == 0
89
+ record = e[1]
90
+ es.add(time, record)
91
+ }
92
+ Engine.emit_stream(tag, es)
93
+
94
+ else
95
+ # Message
96
+ time = msg[1]
97
+ time = Engine.now if time == 0
98
+ record = msg[2]
99
+ Engine.emit(tag, time, record)
100
+ end
101
+ end
102
+
103
+ class Handler < Coolio::Socket
104
+ def initialize(io, on_message)
105
+ super(io)
106
+ if io.is_a?(TCPSocket)
107
+ opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
108
+ io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
109
+ end
110
+ $log.trace { "accepted fluent socket object_id=#{self.object_id}" }
111
+ @on_message = on_message
112
+ end
113
+
114
+ def on_connect
115
+ end
116
+
117
+ def on_read(data)
118
+ first = data[0]
119
+ if first == '{' || first == '['
120
+ m = method(:on_read_json)
121
+ @y = Yajl::Parser.new
122
+ @y.on_parse_complete = @on_message
123
+ else
124
+ m = method(:on_read_msgpack)
125
+ @u = MessagePack::Unpacker.new
126
+ end
127
+
128
+ (class<<self;self;end).module_eval do
129
+ define_method(:on_read, m)
130
+ end
131
+ m.call(data)
132
+ end
133
+
134
+ def on_read_json(data)
135
+ @y << data
136
+ end
137
+
138
+ def on_read_msgpack(data)
139
+ @u.feed_each(data, &@on_message)
140
+ end
141
+
142
+ def on_close
143
+ $log.trace { "closed fluent socket object_id=#{self.object_id}" }
144
+ end
145
+ end
146
+ end
147
+
148
+
149
+ class TcpInput < StreamInput
150
+ Plugin.register_input('tcp', self)
151
+
152
+ config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
153
+ config_param :bind, :string, :default => '0.0.0.0'
154
+
155
+ def configure(conf)
156
+ super
157
+ end
158
+
159
+ def listen
160
+ $log.debug "listening fluent socket on #{@bind}:#{@port}"
161
+ Coolio::TCPServer.new(@bind, @port, Handler, method(:on_message))
162
+ end
163
+ end
164
+
165
+
166
+ class UnixInput < StreamInput
167
+ Plugin.register_input('unix', self)
168
+
169
+ config_param :path, :string, :default => DEFAULT_SOCKET_PATH
170
+
171
+ def configure(conf)
172
+ super
173
+ end
174
+
175
+ def listen
176
+ if File.exist?(@path)
177
+ File.unlink(@path)
178
+ end
179
+ FileUtils.mkdir_p File.dirname(@path)
180
+ $log.debug "listening fluent socket on #{@path}"
181
+ Coolio::UNIXServer.new(@path, Handler, method(:on_message))
182
+ end
183
+ end
184
+
185
+
186
+ end
187
+