fluentd 0.10.4 → 0.10.5

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 CHANGED
@@ -1,4 +1,11 @@
1
1
 
2
+ Release 0.10.5 - 2011/11/08
3
+
4
+ * Added out_forward, in_forward
5
+ * Added out_exec, in_exec
6
+ * Added out_exec_filter
7
+
8
+
2
9
  Release 0.10.4 - 2011/11/07
3
10
 
4
11
  * TestDriver uses klass.dup.module_eval instead of inheriting class to
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.4
1
+ 0.10.5
@@ -2,7 +2,7 @@
2
2
  ## built-in TCP input
3
3
  ## $ echo <json> | fluent-cat <tag>
4
4
  <source>
5
- type tcp
5
+ type forward
6
6
  </source>
7
7
 
8
8
  ## built-in UNIX socket input
@@ -40,7 +40,7 @@
40
40
 
41
41
  ## match tag=system.** and forward to another fluent server
42
42
  #<match system.**>
43
- # type tcp
43
+ # type forward
44
44
  # host 192.168.0.11
45
45
  # <secondary>
46
46
  # host 192.168.0.12
@@ -51,7 +51,7 @@
51
51
  #<match myapp.**>
52
52
  # type copy
53
53
  # <store>
54
- # type tcp
54
+ # type forward
55
55
  # host 192.168.0.13
56
56
  # buffer_type file
57
57
  # buffer_path /var/log/fluent/myapp-forward
@@ -0,0 +1,132 @@
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 ExecInput < Input
22
+ Plugin.register_input('exec', self)
23
+
24
+ def initialize
25
+ super
26
+ end
27
+
28
+ config_param :command, :string
29
+ config_param :keys, :string
30
+ config_param :tag, :string, :default => nil
31
+ config_param :tag_key, :string, :default => nil
32
+ config_param :time_key, :string, :default => nil
33
+ config_param :time_format, :string, :default => nil
34
+ config_param :run_interval, :time, :default => nil
35
+
36
+ def configure(conf)
37
+ super
38
+
39
+ if localtime = conf['localtime']
40
+ @localtime = true
41
+ elsif utc = conf['utc']
42
+ @localtime = false
43
+ end
44
+
45
+ if !@tag && !@tag_key
46
+ raise ConfigError, "'tag' or 'tag_key' option is required on exec input"
47
+ end
48
+
49
+ @keys = @keys.split(',')
50
+
51
+ if @time_key
52
+ if @time_format
53
+ f = @time_format
54
+ @time_parse_proc = Proc.new {|str| Time.strptime(str, f).to_i }
55
+ else
56
+ @time_parse_proc = Proc.new {|str| str.to_i }
57
+ end
58
+ end
59
+ end
60
+
61
+ def start
62
+ if @run_interval
63
+ @finished = false
64
+ @thread = Thread.new(&method(:run_periodic))
65
+ else
66
+ @io = IO.popen(@command, "r")
67
+ @pid = @io.pid
68
+ @thread = Thread.new(&method(:run))
69
+ end
70
+ end
71
+
72
+ def shutdown
73
+ if @run_interval
74
+ @finished = true
75
+ @thread.join
76
+ else
77
+ Process.kill(:TERM, @pid)
78
+ if @thread.join(60) # TODO wait time
79
+ return
80
+ end
81
+ Process.kill(:KILL, @pid)
82
+ @thread.join
83
+ end
84
+ end
85
+
86
+ def run
87
+ @io.each_line(&method(:each_line))
88
+ end
89
+
90
+ def run_periodic
91
+ until @finished
92
+ sleep @run_interval
93
+ io = IO.popen(@command, "r")
94
+ io.each_line(&method(:each_line))
95
+ Process.waitpid(io.pid)
96
+ end
97
+ end
98
+
99
+ private
100
+ def each_line(line)
101
+ begin
102
+ line.chomp!
103
+ vals = line.split("\t")
104
+
105
+ tag = @tag
106
+ time = nil
107
+ record = {}
108
+ for i in 0..@keys.length-1
109
+ key = @keys[i]
110
+ val = vals[i]
111
+ if key == @time_key
112
+ time = @time_parse_proc.call(val)
113
+ elsif key == @tag_key
114
+ tag = val
115
+ else
116
+ record[key] = val
117
+ end
118
+ end
119
+
120
+ if tag
121
+ time ||= Engine.now
122
+ Engine.emit(tag, time, record)
123
+ end
124
+ rescue
125
+ $log.error "exec failed to emit", :error=>$!, :line=>line
126
+ $log.warn_backtrace $!.backtrace
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+ end
@@ -0,0 +1,199 @@
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 ForwardInput < Input
22
+ Plugin.register_input('forward', self)
23
+
24
+ def initialize
25
+ require 'socket'
26
+ require 'yajl'
27
+ super
28
+ end
29
+
30
+ config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
31
+ config_param :bind, :string, :default => '0.0.0.0'
32
+
33
+ def configure(conf)
34
+ super
35
+ end
36
+
37
+ def start
38
+ @loop = Coolio::Loop.new
39
+
40
+ @lsock = listen
41
+ @loop.attach(@lsock)
42
+
43
+ @usock = UDPSocket.new
44
+ @usock.bind(@bind, @port)
45
+ @hbr = HeartbeatRequestHandler.new(@usock, method(:on_heartbeat_request))
46
+ @loop.attach(@hbr)
47
+
48
+ @thread = Thread.new(&method(:run))
49
+ @cached_unpacker = MessagePack::Unpacker.new
50
+ end
51
+
52
+ def shutdown
53
+ @lsock.close
54
+ @loop.stop
55
+ @thread.join
56
+ @usock.close
57
+ end
58
+
59
+ def listen
60
+ $log.info "listening fluent socket on #{@bind}:#{@port}"
61
+ Coolio::TCPServer.new(@bind, @port, Handler, method(:on_message))
62
+ end
63
+
64
+ #config_param :path, :string, :default => DEFAULT_SOCKET_PATH
65
+ #def listen
66
+ # if File.exist?(@path)
67
+ # File.unlink(@path)
68
+ # end
69
+ # FileUtils.mkdir_p File.dirname(@path)
70
+ # $log.debug "listening fluent socket on #{@path}"
71
+ # Coolio::UNIXServer.new(@path, Handler, method(:on_message))
72
+ #end
73
+
74
+ def run
75
+ @loop.run
76
+ rescue
77
+ $log.error "unexpected error", :error=>$!.to_s
78
+ $log.error_backtrace
79
+ end
80
+
81
+ protected
82
+ # message Entry {
83
+ # 1: long time
84
+ # 2: object record
85
+ # }
86
+ #
87
+ # message Forward {
88
+ # 1: string tag
89
+ # 2: list<Entry> entries
90
+ # }
91
+ #
92
+ # message PackedForward {
93
+ # 1: string tag
94
+ # 2: raw entries # msgpack stream of Entry
95
+ # }
96
+ #
97
+ # message Message {
98
+ # 1: string tag
99
+ # 2: long? time
100
+ # 3: object record
101
+ # }
102
+ def on_message(msg)
103
+ # TODO format error
104
+ tag = msg[0].to_s
105
+ entries = msg[1]
106
+
107
+ if entries.class == String
108
+ # PackedForward
109
+ es = MessagePackEventStream.new(entries, @cached_unpacker)
110
+ Engine.emit_stream(tag, es)
111
+
112
+ elsif entries.class == Array
113
+ # Forward
114
+ es = MultiEventStream.new
115
+ entries.each {|e|
116
+ time = e[0].to_i
117
+ time = (now ||= Engine.now) if time == 0
118
+ record = e[1]
119
+ es.add(time, record)
120
+ }
121
+ Engine.emit_stream(tag, es)
122
+
123
+ else
124
+ # Message
125
+ time = msg[1]
126
+ time = Engine.now if time == 0
127
+ record = msg[2]
128
+ Engine.emit(tag, time, record)
129
+ end
130
+ end
131
+
132
+ class Handler < Coolio::Socket
133
+ def initialize(io, on_message)
134
+ super(io)
135
+ if io.is_a?(TCPSocket)
136
+ opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
137
+ io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
138
+ end
139
+ $log.trace { "accepted fluent socket object_id=#{self.object_id}" }
140
+ @on_message = on_message
141
+ end
142
+
143
+ def on_connect
144
+ end
145
+
146
+ def on_read(data)
147
+ first = data[0]
148
+ if first == '{' || first == '['
149
+ m = method(:on_read_json)
150
+ @y = Yajl::Parser.new
151
+ @y.on_parse_complete = @on_message
152
+ else
153
+ m = method(:on_read_msgpack)
154
+ @u = MessagePack::Unpacker.new
155
+ end
156
+
157
+ (class<<self;self;end).module_eval do
158
+ define_method(:on_read, m)
159
+ end
160
+ m.call(data)
161
+ end
162
+
163
+ def on_read_json(data)
164
+ @y << data
165
+ end
166
+
167
+ def on_read_msgpack(data)
168
+ @u.feed_each(data, &@on_message)
169
+ end
170
+
171
+ def on_close
172
+ $log.trace { "closed fluent socket object_id=#{self.object_id}" }
173
+ end
174
+ end
175
+
176
+ class HeartbeatRequestHandler < Coolio::IO
177
+ def initialize(io, callback)
178
+ super(io)
179
+ @io = io
180
+ @callback = callback
181
+ end
182
+
183
+ def on_readable
184
+ msg, addr = @io.recvfrom(1024)
185
+ host = addr[3]
186
+ port = addr[1]
187
+ @callback.call(host, port, msg)
188
+ end
189
+ end
190
+
191
+ def on_heartbeat_request(host, port, msg)
192
+ $log.trace "heartbeat request from #{host}:#{port}"
193
+ @usock.send "", 0, host, port
194
+ end
195
+ end
196
+
197
+
198
+ end
199
+
@@ -18,6 +18,7 @@
18
18
  module Fluent
19
19
 
20
20
 
21
+ # obsolete
21
22
  class StreamInput < Input
22
23
  def initialize
23
24
  require 'socket'
@@ -146,23 +147,35 @@ class StreamInput < Input
146
147
  end
147
148
 
148
149
 
149
- class TcpInput < StreamInput
150
+ # obsolete
151
+ # ForwardInput is backward compatible with TcpInput
152
+ #class TcpInput < StreamInput
153
+ # Plugin.register_input('tcp', self)
154
+ #
155
+ # config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
156
+ # config_param :bind, :string, :default => '0.0.0.0'
157
+ #
158
+ # def configure(conf)
159
+ # super
160
+ # end
161
+ #
162
+ # def listen
163
+ # $log.debug "listening fluent socket on #{@bind}:#{@port}"
164
+ # Coolio::TCPServer.new(@bind, @port, Handler, method(:on_message))
165
+ # end
166
+ #end
167
+ class TcpInput < ForwardInput
150
168
  Plugin.register_input('tcp', self)
151
169
 
152
- config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
153
- config_param :bind, :string, :default => '0.0.0.0'
154
-
155
- def configure(conf)
170
+ def initialize
156
171
  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))
172
+ ## TODO
173
+ #$log.warn "'tcp' input is obsoleted and will be removed. Use 'forward' instead."
162
174
  end
163
175
  end
164
176
 
165
177
 
178
+ # obsolete
166
179
  class UnixInput < StreamInput
167
180
  Plugin.register_input('unix', self)
168
181
 
@@ -170,6 +183,8 @@ class UnixInput < StreamInput
170
183
 
171
184
  def configure(conf)
172
185
  super
186
+ ## TODO
187
+ #$log.warn "'unix' input is obsoleted and will be removed. Use 'forward' instead."
173
188
  end
174
189
 
175
190
  def listen
@@ -0,0 +1,91 @@
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 ExecOutput < TimeSlicedOutput
22
+ Plugin.register_output('exec', self)
23
+
24
+ def initialize
25
+ super
26
+ require 'tempfile'
27
+ @localtime = false
28
+ end
29
+
30
+ config_param :command, :string
31
+ config_param :keys, :string
32
+ config_param :tag_key, :string, :default => nil
33
+ config_param :time_key, :string, :default => nil
34
+ config_param :time_format, :string, :default => nil
35
+
36
+ def configure(conf)
37
+ super
38
+
39
+ @keys = @keys.split(',')
40
+
41
+ if @time_key
42
+ if @time_format
43
+ tf = TimeFormatter.new(@time_format, @localtime)
44
+ @time_format_proc = tf.method(:format)
45
+ else
46
+ @time_format_proc = Proc.new {|time| time.to_s }
47
+ end
48
+ end
49
+ end
50
+
51
+ def format(tag, time, record)
52
+ out = ''
53
+ last = @keys.length-1
54
+ for i in 0..last
55
+ key = @keys[i]
56
+ if key == @time_key
57
+ out << @time_format_proc.call(time)
58
+ elsif key == @tag_key
59
+ out << tag
60
+ else
61
+ out << record[key].to_s
62
+ end
63
+ out << "\t" if i != last
64
+ end
65
+ out << "\n"
66
+ out
67
+ end
68
+
69
+ def write(chunk)
70
+ if chunk.respond_to?(:path)
71
+ prog = "#{@command} #{chunk.path}"
72
+ else
73
+ tmpfile = Tempfile.new("fluent-plugin-exec-")
74
+ chunk.write_to(tmpfile)
75
+ tmpfile.close
76
+ prog = "#{@command} #{tmpfile.path}"
77
+ end
78
+
79
+ system(prog)
80
+ ecode = $?.to_i
81
+ tmpfile.delete if tmpfile
82
+
83
+ if ecode != 0
84
+ raise "command returns #{ecode}: #{prog}"
85
+ end
86
+ end
87
+ end
88
+
89
+
90
+ end
91
+