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 +7 -0
- data/VERSION +1 -1
- data/fluent.conf +3 -3
- data/lib/fluent/plugin/in_exec.rb +132 -0
- data/lib/fluent/plugin/in_forward.rb +199 -0
- data/lib/fluent/plugin/in_stream.rb +25 -10
- data/lib/fluent/plugin/out_exec.rb +91 -0
- data/lib/fluent/plugin/out_exec_filter.rb +143 -0
- data/lib/fluent/plugin/out_forward.rb +356 -0
- data/lib/fluent/plugin/out_stream.rb +7 -0
- data/lib/fluent/plugin/out_test.rb +15 -5
- data/lib/fluent/test/input_test.rb +16 -6
- data/lib/fluent/version.rb +1 -1
- data/test/plugin/in_exec.rb +44 -0
- data/test/plugin/in_forward.rb +123 -0
- data/test/plugin/out_exec.rb +69 -0
- data/test/plugin/out_exec_filter.rb +74 -0
- data/test/plugin/out_forward.rb +35 -0
- metadata +29 -17
data/ChangeLog
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.10.
|
1
|
+
0.10.5
|
data/fluent.conf
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
## built-in TCP input
|
3
3
|
## $ echo <json> | fluent-cat <tag>
|
4
4
|
<source>
|
5
|
-
type
|
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
|
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
|
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
|
-
|
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
|
-
|
153
|
-
config_param :bind, :string, :default => '0.0.0.0'
|
154
|
-
|
155
|
-
def configure(conf)
|
170
|
+
def initialize
|
156
171
|
super
|
157
|
-
|
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
|
+
|