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,174 @@
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 SyslogInput < Input
22
+ Plugin.register_input('syslog', self)
23
+
24
+ SYSLOG_REGEXP = /^\<([0-9]+)\>(.*)/
25
+ SYSLOG_ALL_REGEXP = /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
26
+ TIME_FORMAT = "%b %d %H:%M:%S"
27
+
28
+ FACILITY_MAP = {
29
+ 0 => 'kern',
30
+ 1 => 'user',
31
+ 2 => 'mail',
32
+ 3 => 'daemon',
33
+ 4 => 'auth',
34
+ 5 => 'syslog',
35
+ 6 => 'lpr',
36
+ 7 => 'news',
37
+ 8 => 'uucp',
38
+ 9 => 'cron',
39
+ 10 => 'authpriv',
40
+ 11 => 'ftp',
41
+ 12 => 'ntp',
42
+ 13 => 'audit',
43
+ 14 => 'alert',
44
+ 15 => 'at',
45
+ 16 => 'local0',
46
+ 17 => 'local1',
47
+ 18 => 'local2',
48
+ 19 => 'local3',
49
+ 20 => 'local4',
50
+ 21 => 'local5',
51
+ 22 => 'local6',
52
+ 23 => 'local7'
53
+ }
54
+
55
+ PRIORITY_MAP = {
56
+ 0 => 'emerg',
57
+ 1 => 'alert',
58
+ 2 => 'crit',
59
+ 3 => 'err',
60
+ 4 => 'warn',
61
+ 5 => 'notice',
62
+ 6 => 'info',
63
+ 7 => 'debug'
64
+ }
65
+
66
+ def initialize
67
+ super
68
+ end
69
+
70
+ config_param :port, :integer, :default => 5140
71
+ config_param :bind, :string, :default => '0.0.0.0'
72
+ config_param :tag, :string
73
+
74
+ def configure(conf)
75
+ super
76
+
77
+ parser = TextParser.new
78
+ if parser.configure(conf, false)
79
+ parser.use_template('syslog')
80
+ @parser = parser
81
+ else
82
+ @parser = nil
83
+ end
84
+ end
85
+
86
+ def start
87
+ if @parser
88
+ callback = method(:receive_data_parser)
89
+ else
90
+ callback = method(:receive_data)
91
+ end
92
+ EventMachine.open_datagram_socket(@bind, @port, UdpHandler, callback)
93
+ end
94
+
95
+ def receive_data_parser(data)
96
+ m = SYSLOG_REGEXP.match(data)
97
+ unless m
98
+ $log.debug "invalid syslog message: #{data.dump}"
99
+ return
100
+ end
101
+ pri = m[1].to_i
102
+ text = m[2]
103
+
104
+ time, record = @parser.parse(text)
105
+ unless time && record
106
+ return
107
+ end
108
+
109
+ emit(pri, time, record)
110
+
111
+ rescue
112
+ $log.warn data.dump, :error=>$!.to_s
113
+ $log.debug_backtrace
114
+ end
115
+
116
+ def receive_data(data)
117
+ m = SYSLOG_ALL_REGEXP.match(data)
118
+ unless m
119
+ $log.debug "invalid syslog message", :data=>data
120
+ return
121
+ end
122
+
123
+ pri = nil
124
+ time = nil
125
+ record = {}
126
+
127
+ m.names.each {|name|
128
+ if value = m[name]
129
+ case name
130
+ when "pri"
131
+ pri = value.to_i
132
+ when "time"
133
+ time = Time.strptime(value, TIME_FORMAT).to_i
134
+ else
135
+ record[name] = value
136
+ end
137
+ end
138
+ }
139
+
140
+ time ||= Engine.now
141
+
142
+ emit(pri, time, record)
143
+
144
+ rescue
145
+ $log.warn data.dump, :error=>$!.to_s
146
+ $log.debug_backtrace
147
+ end
148
+
149
+ private
150
+ def emit(pri, time, record)
151
+ facility = FACILITY_MAP[pri >> 3]
152
+ priority = PRIORITY_MAP[pri & 0b111]
153
+
154
+ tag = "#{@tag}.#{facility}.#{priority}"
155
+
156
+ Engine.emit(tag, time, record)
157
+ end
158
+
159
+ class UdpHandler < EventMachine::Connection
160
+ def initialize(callback)
161
+ super()
162
+ @callback = callback
163
+ end
164
+
165
+ def receive_data(data)
166
+ EventMachine.defer {
167
+ @callback.call(data)
168
+ }
169
+ end
170
+ end
171
+ end
172
+
173
+
174
+ end
@@ -0,0 +1,150 @@
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 TailInput < Input
22
+ Plugin.register_input('tail', self)
23
+
24
+ def initialize
25
+ super
26
+ @paths = []
27
+ end
28
+
29
+ config_param :path, :string
30
+ config_param :tag, :string
31
+
32
+ def configure(conf)
33
+ super
34
+
35
+ @paths = @path.split(',').map {|path| path.strip }
36
+ if @paths.empty?
37
+ raise ConfigError, "tail: 'path' parameter is required on tail input"
38
+ end
39
+
40
+ configure_parser(conf)
41
+ end
42
+
43
+ def configure_parser(conf)
44
+ @parser = TextParser.new
45
+ @parser.configure(conf)
46
+ end
47
+
48
+ def start
49
+ @loop = Coolio::Loop.new
50
+ @paths.each {|path|
51
+ $log.debug "following tail of #{path}"
52
+ @loop.attach Handler.new(path, method(:receive_lines))
53
+ }
54
+ @thread = Thread.new(&method(:run))
55
+ end
56
+
57
+ def shutdown
58
+ @loop.stop
59
+ @thread.join
60
+ end
61
+
62
+ def run
63
+ @loop.run
64
+ rescue
65
+ $log.error "unexpected error", :error=>$!.to_s
66
+ $log.error_backtrace
67
+ end
68
+
69
+ def receive_lines(lines)
70
+ es = MultiEventStream.new
71
+ lines.each {|line|
72
+ begin
73
+ line.rstrip! # remove \n
74
+ time, record = parse_line(line)
75
+ if time && record
76
+ es.add(time, record)
77
+ end
78
+ rescue
79
+ $log.warn line.dump, :error=>$!.to_s
80
+ $log.debug_backtrace
81
+ end
82
+ }
83
+
84
+ unless es.empty?
85
+ Engine.emit_stream(@tag, es)
86
+ end
87
+ end
88
+
89
+ def parse_line(line)
90
+ return @parser.parse(line)
91
+ end
92
+
93
+ # seek to the end of file first.
94
+ # logs never duplicate but may be lost if fluent is down.
95
+ class Handler < Coolio::StatWatcher
96
+ def initialize(path, callback)
97
+ @pos = File.stat(path).size
98
+ @buffer = ''
99
+ @callback = callback
100
+ super(path)
101
+ end
102
+
103
+ def on_change
104
+ lines = []
105
+
106
+ File.open(path) {|f|
107
+ if f.lstat.size < @pos
108
+ # moved or deleted
109
+ @pos = 0
110
+ else
111
+ f.seek(@pos)
112
+ end
113
+
114
+ line = f.gets
115
+ unless line
116
+ return
117
+ end
118
+
119
+ @buffer << line
120
+ unless line[line.length-1] == ?\n
121
+ @pos = f.pos
122
+ return
123
+ end
124
+
125
+ lines << @buffer
126
+ @buffer = ''
127
+
128
+ while line = f.gets
129
+ unless line[line.length-1] == ?\n
130
+ @buffer = line
131
+ break
132
+ end
133
+ lines << line
134
+ end
135
+
136
+ @pos = f.pos
137
+ }
138
+
139
+ @callback.call(lines)
140
+
141
+ rescue Errno::ENOENT
142
+ # moved or deleted
143
+ @pos = 0
144
+ end
145
+ end
146
+ end
147
+
148
+
149
+ end
150
+
@@ -0,0 +1,72 @@
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 CopyOutput < MultiOutput
22
+ Plugin.register_output('copy', self)
23
+
24
+ def initialize
25
+ @outputs = []
26
+ end
27
+
28
+ attr_reader :outputs
29
+
30
+ def configure(conf)
31
+ conf.elements.select {|e|
32
+ e.name == 'store'
33
+ }.each {|e|
34
+ type = e['type']
35
+ unless type
36
+ raise ConfigError, "Missing 'type' parameter on <store> directive"
37
+ end
38
+ $log.debug "adding store type=#{type.dump}"
39
+
40
+ output = Plugin.new_output(type)
41
+ output.configure(e)
42
+ @outputs << output
43
+ }
44
+ end
45
+
46
+ def start
47
+ @outputs.each {|o|
48
+ o.start
49
+ }
50
+ end
51
+
52
+ def shutdown
53
+ @outputs.each {|o|
54
+ o.shutdown
55
+ }
56
+ end
57
+
58
+ def emit(tag, es, chain)
59
+ unless es.repeatable?
60
+ es = MultiEventStream.new
61
+ array = es.map {|time,record|
62
+ es.add(time, record)
63
+ }
64
+ end
65
+ chain = OutputChain.new(@outputs, tag, es, chain)
66
+ chain.next
67
+ end
68
+ end
69
+
70
+
71
+ end
72
+
@@ -0,0 +1,111 @@
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 FileOutput < TimeSlicedOutput
22
+ Plugin.register_output('file', self)
23
+
24
+ SUPPORTED_COMPRESS = {
25
+ :gz => :gz,
26
+ :gzip => :gz,
27
+ }
28
+
29
+ config_param :path, :string
30
+
31
+ config_param :time_format, :string, :default => nil
32
+
33
+ config_param :compress, :default => nil do |val|
34
+ c = SUPPORTED_COMPRESS[val.to_sym]
35
+ unless c
36
+ raise ConfigError, "Unsupported compression algorithm '#{compress}'"
37
+ end
38
+ c
39
+ end
40
+
41
+ def initialize
42
+ require 'zlib'
43
+ require 'time'
44
+ super
45
+ end
46
+
47
+ def configure(conf)
48
+ if path = conf['path']
49
+ @path = path
50
+ end
51
+ unless @path
52
+ raise ConfigError, "'path' parameter is required on file output"
53
+ end
54
+
55
+ if pos = @path.index('*')
56
+ @path_prefix = @path[0,pos]
57
+ @path_suffix = @path[pos+1..-1]
58
+ conf['buffer_path'] ||= "#{@path}"
59
+ else
60
+ @path_prefix = @path+"."
61
+ @path_suffix = ".log"
62
+ conf['buffer_path'] ||= "#{@path}.*"
63
+ end
64
+
65
+ super
66
+
67
+ @timef = TimeFormatter.new(@time_format, @localtime)
68
+ end
69
+
70
+ def format(tag, time, record)
71
+ time_str = @timef.format(time)
72
+ "#{time_str}\t#{tag}\t#{record.to_json}\n"
73
+ end
74
+
75
+ def write(chunk)
76
+ case @compress
77
+ when nil
78
+ suffix = ''
79
+ when :gz
80
+ suffix = ".gz"
81
+ end
82
+
83
+ i = 0
84
+ begin
85
+ path = "#{@path_prefix}#{chunk.key}_#{i}#{@path_suffix}#{suffix}"
86
+ i += 1
87
+ end while File.exist?(path)
88
+ FileUtils.mkdir_p File.dirname(path)
89
+
90
+ case @compress
91
+ when nil
92
+ File.open(path, "a") {|f|
93
+ chunk.write_to(f)
94
+ }
95
+ when :gz
96
+ Zlib::GzipWriter.open(path) {|f|
97
+ chunk.write_to(f)
98
+ }
99
+ end
100
+
101
+ return path # for test
102
+ end
103
+
104
+ def secondary_init(primary)
105
+ # don't warn even if primary.class is not FileOutput
106
+ end
107
+ end
108
+
109
+
110
+ end
111
+