fluentd 0.10.50 → 0.10.51
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/CONTRIBUTING.md +34 -0
- data/ChangeLog +11 -0
- data/README.md +3 -3
- data/Rakefile +3 -0
- data/fluent.conf +1 -0
- data/lib/fluent/config.rb +10 -15
- data/lib/fluent/config/parser.rb +0 -7
- data/lib/fluent/config/v1_parser.rb +0 -6
- data/lib/fluent/engine.rb +10 -14
- data/lib/fluent/formatter.rb +1 -2
- data/lib/fluent/log.rb +13 -9
- data/lib/fluent/parser.rb +130 -9
- data/lib/fluent/plugin/in_stream.rb +0 -28
- data/lib/fluent/plugin/in_syslog.rb +28 -100
- data/lib/fluent/plugin/in_tail.rb +6 -5
- data/lib/fluent/plugin/in_tcp.rb +15 -0
- data/lib/fluent/plugin/in_udp.rb +17 -0
- data/lib/fluent/plugin/socket_util.rb +119 -0
- data/lib/fluent/supervisor.rb +52 -10
- data/lib/fluent/test/base.rb +2 -2
- data/lib/fluent/version.rb +1 -1
- data/spec/config/config_parser_spec.rb +7 -1
- data/test/plugin/test_in_http.rb +3 -3
- data/test/plugin/test_in_stream.rb +0 -24
- data/test/plugin/test_in_syslog.rb +22 -1
- data/test/plugin/test_in_tail.rb +60 -19
- data/test/plugin/test_in_tcp.rb +88 -0
- data/test/plugin/test_in_udp.rb +104 -0
- data/test/plugin/test_out_exec.rb +4 -4
- data/test/plugin/test_out_file.rb +19 -0
- data/test/test_config.rb +9 -2
- data/test/test_formatter.rb +8 -4
- data/test/test_parser.rb +175 -2
- metadata +9 -2
@@ -160,34 +160,6 @@ module Fluent
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
-
|
164
|
-
# obsolete
|
165
|
-
# ForwardInput is backward compatible with TcpInput
|
166
|
-
#class TcpInput < StreamInput
|
167
|
-
# Plugin.register_input('tcp', self)
|
168
|
-
#
|
169
|
-
# config_param :port, :integer, :default => DEFAULT_LISTEN_PORT
|
170
|
-
# config_param :bind, :string, :default => '0.0.0.0'
|
171
|
-
#
|
172
|
-
# def configure(conf)
|
173
|
-
# super
|
174
|
-
# end
|
175
|
-
#
|
176
|
-
# def listen
|
177
|
-
# log.debug "listening fluent socket on #{@bind}:#{@port}"
|
178
|
-
# Coolio::TCPServer.new(@bind, @port, Handler, method(:on_message))
|
179
|
-
# end
|
180
|
-
#end
|
181
|
-
class TcpInput < ForwardInput
|
182
|
-
Plugin.register_input('tcp', self)
|
183
|
-
|
184
|
-
def initialize
|
185
|
-
super
|
186
|
-
$log.warn "'tcp' input is obsoleted and will be removed soon. Use 'forward' instead."
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
|
191
163
|
class UnixInput < StreamInput
|
192
164
|
Plugin.register_input('unix', self)
|
193
165
|
|
@@ -20,8 +20,6 @@ module Fluent
|
|
20
20
|
Plugin.register_input('syslog', self)
|
21
21
|
|
22
22
|
SYSLOG_REGEXP = /^\<([0-9]+)\>(.*)/
|
23
|
-
SYSLOG_ALL_REGEXP = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
|
24
|
-
TIME_FORMAT = "%b %d %H:%M:%S"
|
25
23
|
|
26
24
|
FACILITY_MAP = {
|
27
25
|
0 => 'kern',
|
@@ -88,17 +86,19 @@ module Fluent
|
|
88
86
|
if parser.configure(conf, false)
|
89
87
|
@parser = parser
|
90
88
|
else
|
91
|
-
|
92
|
-
@
|
89
|
+
conf['with_priority'] = true
|
90
|
+
@parser = TextParser::SyslogParser.new
|
91
|
+
@parser.configure(conf)
|
92
|
+
@use_default = true
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
96
|
def start
|
97
|
-
if @
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
97
|
+
callback = if @use_default
|
98
|
+
method(:receive_data)
|
99
|
+
else
|
100
|
+
method(:receive_data_parser)
|
101
|
+
end
|
102
102
|
|
103
103
|
@loop = Coolio::Loop.new
|
104
104
|
@handler = listen(callback)
|
@@ -122,10 +122,10 @@ module Fluent
|
|
122
122
|
end
|
123
123
|
|
124
124
|
protected
|
125
|
-
def receive_data_parser(data)
|
125
|
+
def receive_data_parser(data, addr)
|
126
126
|
m = SYSLOG_REGEXP.match(data)
|
127
127
|
unless m
|
128
|
-
log.
|
128
|
+
log.warn "invalid syslog message: #{data.dump}"
|
129
129
|
return
|
130
130
|
end
|
131
131
|
pri = m[1].to_i
|
@@ -139,42 +139,24 @@ module Fluent
|
|
139
139
|
|
140
140
|
emit(pri, time, record)
|
141
141
|
}
|
142
|
-
rescue
|
143
|
-
log.
|
144
|
-
log.
|
142
|
+
rescue => e
|
143
|
+
log.error data.dump, :error => e.to_s
|
144
|
+
log.error_backtrace
|
145
145
|
end
|
146
146
|
|
147
|
-
def receive_data(data)
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
pri = nil
|
155
|
-
time = nil
|
156
|
-
record = {}
|
157
|
-
|
158
|
-
m.names.each {|name|
|
159
|
-
if value = m[name]
|
160
|
-
case name
|
161
|
-
when "pri"
|
162
|
-
pri = value.to_i
|
163
|
-
when "time"
|
164
|
-
time = @time_parser.parse(value.gsub(/ +/, ' '))
|
165
|
-
else
|
166
|
-
record[name] = value
|
167
|
-
end
|
147
|
+
def receive_data(data, addr)
|
148
|
+
@parser.call(data) { |time, record|
|
149
|
+
unless time && record
|
150
|
+
log.warn "invalid syslog message", :data => data
|
151
|
+
return
|
168
152
|
end
|
169
|
-
}
|
170
|
-
|
171
|
-
time ||= Engine.now
|
172
|
-
|
173
|
-
emit(pri, time, record)
|
174
153
|
|
175
|
-
|
176
|
-
|
177
|
-
|
154
|
+
pri = record.delete('pri')
|
155
|
+
emit(pri, time, record)
|
156
|
+
}
|
157
|
+
rescue => e
|
158
|
+
log.error data.dump, :error => e.to_s
|
159
|
+
log.error_backtrace
|
178
160
|
end
|
179
161
|
|
180
162
|
private
|
@@ -184,9 +166,10 @@ module Fluent
|
|
184
166
|
if @protocol_type == :udp
|
185
167
|
@usock = SocketUtil.create_udp_socket(@bind)
|
186
168
|
@usock.bind(@bind, @port)
|
187
|
-
UdpHandler.new(@usock, callback)
|
169
|
+
SocketUtil::UdpHandler.new(@usock, log, 2048, callback)
|
188
170
|
else
|
189
|
-
|
171
|
+
# syslog family add "\n" to each message and this seems only way to split messages in tcp stream
|
172
|
+
Coolio::TCPServer.new(@bind, @port, SocketUtil::TcpHandler, log, "\n", callback)
|
190
173
|
end
|
191
174
|
end
|
192
175
|
|
@@ -200,60 +183,5 @@ module Fluent
|
|
200
183
|
rescue => e
|
201
184
|
log.error "syslog failed to emit", :error => e.to_s, :error_class => e.class.to_s, :tag => tag, :record => Yajl.dump(record)
|
202
185
|
end
|
203
|
-
|
204
|
-
class UdpHandler < Coolio::IO
|
205
|
-
def initialize(io, callback)
|
206
|
-
super(io)
|
207
|
-
@io = io
|
208
|
-
@callback = callback
|
209
|
-
end
|
210
|
-
|
211
|
-
def on_readable
|
212
|
-
msg, addr = @io.recvfrom_nonblock(2048)
|
213
|
-
#host = addr[3]
|
214
|
-
#port = addr[1]
|
215
|
-
#@callback.call(host, port, msg)
|
216
|
-
@callback.call(msg)
|
217
|
-
rescue
|
218
|
-
# TODO log?
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
class TcpHandler < Coolio::Socket
|
223
|
-
def initialize(io, log, on_message)
|
224
|
-
super(io)
|
225
|
-
if io.is_a?(TCPSocket)
|
226
|
-
opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
|
227
|
-
io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
|
228
|
-
end
|
229
|
-
@on_message = on_message
|
230
|
-
@log = log
|
231
|
-
@log.trace { "accepted fluent socket object_id=#{self.object_id}" }
|
232
|
-
@buffer = "".force_encoding('ASCII-8BIT')
|
233
|
-
end
|
234
|
-
|
235
|
-
def on_connect
|
236
|
-
end
|
237
|
-
|
238
|
-
def on_read(data)
|
239
|
-
@buffer << data
|
240
|
-
pos = 0
|
241
|
-
|
242
|
-
# syslog family add "\n" to each message and this seems only way to split messages in tcp stream
|
243
|
-
while i = @buffer.index("\n", pos)
|
244
|
-
msg = @buffer[pos..i]
|
245
|
-
@on_message.call(msg)
|
246
|
-
pos = i + 1
|
247
|
-
end
|
248
|
-
@buffer.slice!(0, pos) if pos > 0
|
249
|
-
rescue => e
|
250
|
-
@log.error "syslog error", :error => e, :error_class => e.class
|
251
|
-
close
|
252
|
-
end
|
253
|
-
|
254
|
-
def on_close
|
255
|
-
@log.trace { "closed fluent socket object_id=#{self.object_id}" }
|
256
|
-
end
|
257
|
-
end
|
258
186
|
end
|
259
187
|
end
|
@@ -338,7 +338,7 @@ module Fluent
|
|
338
338
|
end
|
339
339
|
|
340
340
|
def on_notify
|
341
|
-
@rotate_handler.on_notify
|
341
|
+
@rotate_handler.on_notify if @rotate_handler
|
342
342
|
return unless @io_handler
|
343
343
|
@io_handler.on_notify
|
344
344
|
end
|
@@ -396,12 +396,13 @@ module Fluent
|
|
396
396
|
@pe.update(inode, io.pos)
|
397
397
|
io_handler = IOHandler.new(io, @pe, @log, &method(:wrap_receive_lines))
|
398
398
|
@io_handler = io_handler
|
399
|
-
else
|
399
|
+
else # file is rotated and new file found
|
400
400
|
@update_watcher.call(@path, swap_state(@pe))
|
401
401
|
end
|
402
|
-
else
|
403
|
-
|
404
|
-
@
|
402
|
+
else # file is rotated and new file not found
|
403
|
+
# Clear RotateHandler to avoid duplicated file watch in same path.
|
404
|
+
@rotate_handler = nil
|
405
|
+
@update_watcher.call(@path, swap_state(@pe))
|
405
406
|
end
|
406
407
|
end
|
407
408
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fluent/plugin/socket_util'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
class TcpInput < SocketUtil::BaseInput
|
5
|
+
Plugin.register_input('tcp', self)
|
6
|
+
|
7
|
+
config_set_default :port, 5170
|
8
|
+
config_param :delimiter, :string, :default => "\n" # syslog family add "\n" to each message and this seems only way to split messages in tcp stream
|
9
|
+
|
10
|
+
def listen(callback)
|
11
|
+
log.debug "listening tcp socket on #{@bind}:#{@port}"
|
12
|
+
Coolio::TCPServer.new(@bind, @port, SocketUtil::TcpHandler, log, @delimiter, callback)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fluent/plugin/socket_util'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
class UdpInput < SocketUtil::BaseInput
|
5
|
+
Plugin.register_input('udp', self)
|
6
|
+
|
7
|
+
config_set_default :port, 5160
|
8
|
+
config_param :body_size_limit, :size, :default => 4096
|
9
|
+
|
10
|
+
def listen(callback)
|
11
|
+
log.debug "listening udp socket on #{@bind}:#{@port}"
|
12
|
+
@usock = SocketUtil.create_udp_socket(@bind)
|
13
|
+
@usock.bind(@bind, @port)
|
14
|
+
SocketUtil::UdpHandler.new(@usock, log, @body_size_limit, callback)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'cool.io'
|
2
|
+
|
1
3
|
module Fluent
|
2
4
|
module SocketUtil
|
3
5
|
def create_udp_socket(host)
|
@@ -10,5 +12,122 @@ module Fluent
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
module_function :create_udp_socket
|
15
|
+
|
16
|
+
class UdpHandler < Coolio::IO
|
17
|
+
def initialize(io, log, body_size_limit, callback)
|
18
|
+
super(io)
|
19
|
+
@io = io
|
20
|
+
@log = log
|
21
|
+
@body_size_limit = body_size_limit
|
22
|
+
@callback = callback
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_readable
|
26
|
+
msg, addr = @io.recvfrom_nonblock(@body_size_limit)
|
27
|
+
msg.chomp!
|
28
|
+
@callback.call(msg, addr)
|
29
|
+
rescue => e
|
30
|
+
@log.error "unexpected error", :error => e, :error_class => e.class
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TcpHandler < Coolio::Socket
|
35
|
+
PEERADDR_FAILED = ["?", "?", "name resolusion failed", "?"]
|
36
|
+
|
37
|
+
def initialize(io, log, delimiter, callback)
|
38
|
+
super(io)
|
39
|
+
if io.is_a?(TCPSocket)
|
40
|
+
@addr = (io.peeraddr rescue PEERADDR_FAILED)
|
41
|
+
|
42
|
+
opt = [1, @timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
|
43
|
+
io.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
|
44
|
+
end
|
45
|
+
@delimiter = delimiter
|
46
|
+
@callback = callback
|
47
|
+
@log = log
|
48
|
+
@log.trace { "accepted fluent socket object_id=#{self.object_id}" }
|
49
|
+
@buffer = "".force_encoding('ASCII-8BIT')
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_connect
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_read(data)
|
56
|
+
@buffer << data
|
57
|
+
pos = 0
|
58
|
+
|
59
|
+
while i = @buffer.index(@delimiter, pos)
|
60
|
+
msg = @buffer[pos...i]
|
61
|
+
@callback.call(msg, @addr)
|
62
|
+
pos = i + @delimiter.length
|
63
|
+
end
|
64
|
+
@buffer.slice!(0, pos) if pos > 0
|
65
|
+
rescue => e
|
66
|
+
@log.error "unexpected error", :error => e, :error_class => e.class
|
67
|
+
close
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_close
|
71
|
+
@log.trace { "closed fluent socket object_id=#{self.object_id}" }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class BaseInput < Fluent::Input
|
76
|
+
def initialize
|
77
|
+
super
|
78
|
+
require 'fluent/parser'
|
79
|
+
end
|
80
|
+
|
81
|
+
config_param :tag, :string
|
82
|
+
config_param :format, :string
|
83
|
+
config_param :port, :integer, :default => 5150
|
84
|
+
config_param :bind, :string, :default => '0.0.0.0'
|
85
|
+
config_param :source_host_key, :string, :default => nil
|
86
|
+
|
87
|
+
def configure(conf)
|
88
|
+
super
|
89
|
+
|
90
|
+
@parser = TextParser.new
|
91
|
+
@parser.configure(conf)
|
92
|
+
end
|
93
|
+
|
94
|
+
def start
|
95
|
+
@loop = Coolio::Loop.new
|
96
|
+
@handler = listen(method(:on_message))
|
97
|
+
@loop.attach(@handler)
|
98
|
+
@thread = Thread.new(&method(:run))
|
99
|
+
end
|
100
|
+
|
101
|
+
def shutdown
|
102
|
+
@loop.watchers.each { |w| w.detach }
|
103
|
+
@loop.stop
|
104
|
+
@handler.close
|
105
|
+
@thread.join
|
106
|
+
end
|
107
|
+
|
108
|
+
def run
|
109
|
+
@loop.run
|
110
|
+
rescue => e
|
111
|
+
log.error "unexpected error", :error => e, :error_class => e.class
|
112
|
+
log.error_backtrace
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def on_message(msg, addr)
|
118
|
+
@parser.parse(msg) { |time, record|
|
119
|
+
unless time && record
|
120
|
+
log.warn "pattern not match: #{msg.inspect}"
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
record[@source_host_key] = addr[3] if @source_host_key
|
125
|
+
Engine.emit(@tag, time, record)
|
126
|
+
}
|
127
|
+
rescue => e
|
128
|
+
log.error msg.dump, :error => e, :error_class => e.class, :host => addr[3]
|
129
|
+
log.error_backtrace
|
130
|
+
end
|
131
|
+
end
|
13
132
|
end
|
14
133
|
end
|
data/lib/fluent/supervisor.rb
CHANGED
@@ -18,6 +18,7 @@
|
|
18
18
|
|
19
19
|
require 'fluent/env'
|
20
20
|
require 'fluent/log'
|
21
|
+
require 'fluent/config'
|
21
22
|
require 'etc'
|
22
23
|
|
23
24
|
module Fluent
|
@@ -95,19 +96,22 @@ module Fluent
|
|
95
96
|
end
|
96
97
|
|
97
98
|
def initialize(opt)
|
99
|
+
@daemonize = opt[:daemonize]
|
98
100
|
@config_path = opt[:config_path]
|
101
|
+
@inline_config = opt[:inline_config]
|
102
|
+
@use_v1_config = opt[:use_v1_config]
|
99
103
|
@log_path = opt[:log_path]
|
100
|
-
@
|
101
|
-
@daemonize = opt[:daemonize]
|
102
|
-
@chgroup = opt[:chgroup]
|
103
|
-
@chuser = opt[:chuser]
|
104
|
+
@dry_run = opt[:dry_run]
|
104
105
|
@libs = opt[:libs]
|
105
106
|
@plugin_dirs = opt[:plugin_dirs]
|
106
|
-
@
|
107
|
+
@chgroup = opt[:chgroup]
|
108
|
+
@chuser = opt[:chuser]
|
109
|
+
|
110
|
+
apply_system_config(opt)
|
111
|
+
|
112
|
+
@log_level = opt[:log_level]
|
107
113
|
@suppress_interval = opt[:suppress_interval]
|
108
|
-
@dry_run = opt[:dry_run]
|
109
114
|
@suppress_config_dump = opt[:suppress_config_dump]
|
110
|
-
@use_v1_config = opt[:use_v1_config]
|
111
115
|
|
112
116
|
log_opts = {:suppress_repeated_stacktrace => opt[:suppress_repeated_stacktrace]}
|
113
117
|
@log = LoggerInitializer.new(@log_path, @log_level, @chuser, @chgroup, log_opts)
|
@@ -317,8 +321,9 @@ module Fluent
|
|
317
321
|
end
|
318
322
|
end
|
319
323
|
|
320
|
-
|
321
|
-
|
324
|
+
# with_log is for disabling logging before Log#init is called
|
325
|
+
def read_config(with_log = true)
|
326
|
+
$log.info "reading config file", :path => @config_path if with_log
|
322
327
|
@config_fname = File.basename(@config_path)
|
323
328
|
@config_basedir = File.dirname(@config_path)
|
324
329
|
@config_data = File.read(@config_path)
|
@@ -329,8 +334,45 @@ module Fluent
|
|
329
334
|
end
|
330
335
|
end
|
331
336
|
|
337
|
+
class SystemConfig
|
338
|
+
include Configurable
|
339
|
+
|
340
|
+
config_param :log_level, :default => nil do |level|
|
341
|
+
Log.str_to_level(level)
|
342
|
+
end
|
343
|
+
config_param :suppress_repeated_stacktrace, :bool, :default => nil
|
344
|
+
config_param :emit_error_log_interval, :time, :default => nil
|
345
|
+
config_param :suppress_config_dump, :bool, :default => nil
|
346
|
+
|
347
|
+
def initialize(conf)
|
348
|
+
super()
|
349
|
+
configure(conf)
|
350
|
+
end
|
351
|
+
|
352
|
+
def to_opt
|
353
|
+
opt = {}
|
354
|
+
opt[:log_level] = @log_level unless @log_level.nil?
|
355
|
+
opt[:suppress_interval] = @emit_error_log_interval unless @emit_error_log_interval.nil?
|
356
|
+
opt[:suppress_config_dump] = @suppress_config_dump unless @suppress_config_dump.nil?
|
357
|
+
opt[:suppress_repeated_stacktrace] = @suppress_repeated_stacktrace unless @suppress_repeated_stacktrace.nil?
|
358
|
+
opt
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def apply_system_config(opt)
|
363
|
+
read_config(false)
|
364
|
+
systems = Fluent::Config.parse(@config_data, @config_fname, @config_basedir, @use_v1_config).elements.select { |e|
|
365
|
+
e.name == 'system'
|
366
|
+
}
|
367
|
+
return if systems.empty?
|
368
|
+
raise ConfigError, "<system> is duplicated. <system> should be only one" if systems.size > 1
|
369
|
+
|
370
|
+
opt.merge!(SystemConfig.new(systems.first).to_opt)
|
371
|
+
end
|
372
|
+
|
332
373
|
def run_configure
|
333
|
-
Fluent::
|
374
|
+
conf = Fluent::Config.parse(@config_data, @config_fname, @config_basedir, @use_v1_config)
|
375
|
+
Fluent::Engine.run_configure(conf)
|
334
376
|
end
|
335
377
|
|
336
378
|
def change_privilege
|