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,115 @@
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 TextParser
22
+ TEMPLATES = {
23
+ 'apache' => [/^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/, "%d/%b/%Y:%H:%M:%S %z"],
24
+ 'syslog' => [/^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, "%b %d %H:%M:%S"],
25
+ }
26
+
27
+ def self.register_template(name, regexp, time_format=nil)
28
+ TEMPLATES[name] = [regexp, time_format]
29
+ end
30
+
31
+ def self.get_template(name)
32
+ return *TEMPLATES[name]
33
+ end
34
+
35
+ def initialize
36
+ require 'time' # Time.strptime, Time.parse
37
+ @regexp = nil
38
+ @time_format = nil
39
+ end
40
+
41
+ attr_accessor :regexp, :time_format
42
+
43
+ def use_template(name)
44
+ @regexp, @time_format = TextParser.get_template(name)
45
+ unless @regexp
46
+ raise ConfigError, "Unknown format template '#{name}'"
47
+ end
48
+ end
49
+
50
+ def configure(conf, required=true)
51
+ if format = conf['format']
52
+ if format[0] == ?/ && format[format.length-1] == ?/
53
+ # regexp
54
+ begin
55
+ @regexp = Regexp.new(format[1..-2])
56
+ if @regexp.named_captures.empty?
57
+ raise "No named captures"
58
+ end
59
+ rescue
60
+ raise ConfigError, "Invalid regexp '#{format[1..-2]}': #{$!}"
61
+ end
62
+
63
+ else
64
+ # template
65
+ use_template(format)
66
+ end
67
+ else
68
+ return nil if !required
69
+ raise ConfigError, "'format' parameter is required"
70
+ end
71
+
72
+ if time_format = conf['time_format']
73
+ unless @regexp.names.include?('time')
74
+ raise ConfigError, "'time_format' parameter is invalid when format doesn't have 'time' capture"
75
+ end
76
+ @time_format = time_format
77
+ end
78
+
79
+ return true
80
+ end
81
+
82
+ def parse(text)
83
+ m = @regexp.match(text)
84
+ unless m
85
+ $log.debug "pattern not match: #{text}"
86
+ # TODO?
87
+ return nil, nil
88
+ end
89
+
90
+ time = nil
91
+ record = {}
92
+
93
+ m.names.each {|name|
94
+ if value = m[name]
95
+ case name
96
+ when "time"
97
+ if @time_format
98
+ time = Time.strptime(value, @time_format).to_i
99
+ else
100
+ time = Time.parse(value).to_i
101
+ end
102
+ else
103
+ record[name] = value
104
+ end
105
+ end
106
+ }
107
+
108
+ time ||= Engine.now
109
+
110
+ return time, record
111
+ end
112
+ end
113
+
114
+
115
+ end
@@ -0,0 +1,145 @@
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 PluginClass
22
+ def initialize
23
+ @input = {}
24
+ @output = {}
25
+ @buffer = {}
26
+ end
27
+
28
+ def register_input(type, klass)
29
+ register_impl('input', @input, type, klass)
30
+ end
31
+
32
+ def register_output(type, klass)
33
+ register_impl('output', @output, type, klass)
34
+ end
35
+
36
+ def register_buffer(type, klass)
37
+ register_impl('buffer', @buffer, type, klass)
38
+ end
39
+
40
+ def new_input(type)
41
+ new_impl('input', @input, type)
42
+ end
43
+
44
+ def new_output(type)
45
+ new_impl('output', @output, type)
46
+ end
47
+
48
+ def new_buffer(type)
49
+ new_impl('buffer', @buffer, type)
50
+ end
51
+
52
+ def load_plugins
53
+ dir = File.join(File.dirname(__FILE__), "plugin")
54
+ load_plugin_dir(dir)
55
+ load_gem_plugins
56
+ end
57
+
58
+ def load_plugin_dir(dir)
59
+ dir = File.expand_path(dir)
60
+ Dir.entries(dir).sort.each {|fname|
61
+ if fname =~ /\.rb$/
62
+ require File.join(dir, fname)
63
+ end
64
+ }
65
+ nil
66
+ end
67
+
68
+ private
69
+ def load_gem_plugins
70
+ return unless defined? Gem
71
+ plugins = Gem.find_files('fluent_plugin')
72
+
73
+ plugins.each {|plugin|
74
+ begin
75
+ load plugin
76
+ rescue ::Exception => e
77
+ msg = "#{plugin.inspect}: #{e.message} (#{e.class})"
78
+ $log.warn "Error loading Fluent plugin #{msg}"
79
+ end
80
+ }
81
+ end
82
+
83
+ def register_impl(name, map, type, klass)
84
+ map[type] = klass
85
+ $log.trace { "registered #{name} plugin '#{type}'" }
86
+ nil
87
+ end
88
+
89
+ def new_impl(name, map, type)
90
+ if klass = map[type]
91
+ return klass.new
92
+ end
93
+ try_load_plugin(name, type)
94
+ if klass = map[type]
95
+ return klass.new
96
+ end
97
+ raise ConfigError, "Unknown #{name} plugin '#{type}'. Run 'gem search fluent-plugin' to find plugins"
98
+ end
99
+
100
+ def try_load_plugin(name, type)
101
+ case name
102
+ when 'input'
103
+ path = "fluent/plugin/in_#{type}"
104
+ when 'output'
105
+ path = "fluent/plugin/out_#{type}"
106
+ when 'buffer'
107
+ path = "fluent/plugin/buf_#{type}"
108
+ else
109
+ return
110
+ end
111
+
112
+ # prefer LOAD_PATH
113
+ files = $LOAD_PATH.map {|lp|
114
+ lpath = File.join(lp, "#{path}.rb")
115
+ File.exist?(lpath) ? lpath : nil
116
+ }.compact
117
+ unless files.empty?
118
+ # prefer newer version
119
+ require files.sort.last
120
+ return
121
+ end
122
+
123
+ # search gems
124
+ if defined?(::Gem) && ::Gem.respond_to?(:searcher)
125
+ #files = Gem.find_files(path).sort
126
+ specs = Gem.searcher.find_all(path)
127
+ specs = specs.sort_by {|spec| spec.version }
128
+
129
+ # prefer newer version
130
+ specs.reverse_each {|spec|
131
+ files = Gem.searcher.matching_files(spec, path)
132
+ unless files.empty?
133
+ require files.first
134
+ return
135
+ end
136
+ }
137
+ end
138
+ end
139
+ end
140
+
141
+ Plugin = PluginClass.new
142
+
143
+
144
+ end
145
+
@@ -0,0 +1,181 @@
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 FileBufferChunk < BufferChunk
22
+ def initialize(key, path, mode="a+")
23
+ super(key)
24
+ @path = path
25
+ @file = File.open(@path, mode)
26
+ @size = @file.stat.size
27
+ end
28
+
29
+ def <<(data)
30
+ while true
31
+ n = @file.syswrite(data)
32
+ @size += n
33
+ if data.bytesize <= n
34
+ break
35
+ else
36
+ data = data[n..-1]
37
+ #data.slice!(0, n-1)
38
+ end
39
+ end
40
+ #@file.write(data)
41
+ #@size += data.bytesize
42
+ #@file.flush
43
+ end
44
+
45
+ def size
46
+ @size
47
+ end
48
+
49
+ def empty?
50
+ @size == 0
51
+ end
52
+
53
+ def close
54
+ @file.close
55
+ if @file.stat.size == 0
56
+ File.unlink(@path)
57
+ end
58
+ end
59
+
60
+ def purge
61
+ @file.close
62
+ File.unlink(@path) rescue nil # TODO rescue?
63
+ end
64
+
65
+ def read
66
+ @file.pos = 0
67
+ @file.read
68
+ end
69
+
70
+ def open(&block)
71
+ @file.pos = 0
72
+ yield @file
73
+ end
74
+
75
+ attr_reader :path
76
+
77
+ def mv(path)
78
+ File.rename(@path, path)
79
+ @path = path
80
+ end
81
+ end
82
+
83
+
84
+ class FileBuffer < BasicBuffer
85
+ Plugin.register_buffer('file', self)
86
+
87
+ def initialize
88
+ require 'uri'
89
+ super
90
+ end
91
+
92
+ config_param :buffer_path, :string
93
+
94
+ def configure(conf)
95
+ super
96
+
97
+ if pos = @buffer_path.index('*')
98
+ @buffer_path_prefix = @buffer_path[0,pos]
99
+ @buffer_path_suffix = @buffer_path[pos+1..-1]
100
+ else
101
+ @buffer_path_prefix = @buffer_path+"."
102
+ @buffer_path_suffix = ".log"
103
+ end
104
+ end
105
+
106
+ def start
107
+ FileUtils.mkdir_p File.dirname(@buffer_path_prefix+"path")
108
+ super
109
+ end
110
+
111
+ PATH_MATCH = /^(.*)[\._](b|q)([0-9a-fA-F]{1,16})$/
112
+
113
+ def new_chunk(key)
114
+ tsuffix = Engine.now.to_i.to_s(16)
115
+ encoded_key = encode_key(key)
116
+ path = "#{@buffer_path_prefix}#{encoded_key}.b#{tsuffix}#{@buffer_path_suffix}"
117
+ FileBufferChunk.new(key, path)
118
+ end
119
+
120
+ def resume
121
+ maps = []
122
+ queues = []
123
+
124
+ Dir.glob("#{@buffer_path_prefix}*#{@buffer_path_suffix}") {|path|
125
+ match = path[@buffer_path_prefix.length..-(@buffer_path_suffix.length+1)]
126
+ if m = PATH_MATCH.match(match)
127
+ key = decode_key(m[1])
128
+ bq = m[2]
129
+ tsuffix = m[3].to_i(16)
130
+
131
+ if bq == 'b'
132
+ chunk = FileBufferChunk.new(key, path, "a+")
133
+ maps << [tsuffix, chunk]
134
+ elsif bq == 'q'
135
+ chunk = FileBufferChunk.new(key, path, "r")
136
+ queues << [tsuffix, chunk]
137
+ end
138
+ end
139
+ }
140
+
141
+ map = {}
142
+ maps.sort_by {|(tsuffix,chunk)|
143
+ tsuffix
144
+ }.each {|(tsuffix,chunk)|
145
+ map[chunk.key] = chunk
146
+ }
147
+
148
+ queue = queues.sort_by {|(tsuffix,chunk)|
149
+ tsuffix
150
+ }.map {|(tsuffix,chunk)|
151
+ chunk
152
+ }
153
+
154
+ return queue, map
155
+ end
156
+
157
+ def enqueue(chunk)
158
+ path = chunk.path
159
+ mp = path[@buffer_path_prefix.length..-(@buffer_path_suffix.length+1)]
160
+
161
+ m = PATH_MATCH.match(mp)
162
+ encoded_key = m ? m[1] : ""
163
+ tsuffix = Engine.now.to_i.to_s(16)
164
+
165
+ path = "#{@buffer_path_prefix}#{encoded_key}.q#{tsuffix}#{@buffer_path_suffix}"
166
+ chunk.mv(path)
167
+ end
168
+
169
+ protected
170
+ def encode_key(key)
171
+ URI.encode(key, /[^-_.a-zA-Z0-9]/n)
172
+ end
173
+
174
+ def decode_key(encoded_key)
175
+ URI.decode(encoded_key, /[^-_.a-zA-Z0-9]/n)
176
+ end
177
+ end
178
+
179
+
180
+ end
181
+
@@ -0,0 +1,97 @@
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 MemoryBufferChunk < BufferChunk
22
+ def initialize(key, data='')
23
+ @data = data
24
+ super(key)
25
+ end
26
+
27
+ def <<(data)
28
+ @data << data
29
+ end
30
+
31
+ def size
32
+ @data.bytesize
33
+ end
34
+
35
+ def close
36
+ end
37
+
38
+ def purge
39
+ end
40
+
41
+ def read
42
+ @data
43
+ end
44
+
45
+ def open(&block)
46
+ StringIO.open(@data, &block)
47
+ end
48
+
49
+ # optimize
50
+ def write_to(io)
51
+ io.write @data
52
+ end
53
+
54
+ # optimize
55
+ def msgpack_each(&block)
56
+ u = MessagePack::Unpacker.new(io)
57
+ u.feed_each(@data, &block)
58
+ end
59
+ end
60
+
61
+
62
+ class MemoryBuffer < BasicBuffer
63
+ Plugin.register_buffer('memory', self)
64
+
65
+ def initialize
66
+ super
67
+ end
68
+
69
+ def configure(conf)
70
+ super
71
+ end
72
+
73
+ def before_shutdown(out)
74
+ synchronize do
75
+ @map.each_key {|key|
76
+ push(key)
77
+ }
78
+ while pop(out)
79
+ end
80
+ end
81
+ end
82
+
83
+ def new_chunk(key)
84
+ MemoryBufferChunk.new(key)
85
+ end
86
+
87
+ def resume
88
+ return [], {}
89
+ end
90
+
91
+ def enqueue(chunk)
92
+ end
93
+ end
94
+
95
+
96
+ end
97
+