fluentd 0.10.8 → 0.10.9

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,16 @@
1
1
 
2
+ Release 0.10.9 - 2012/01/19
3
+
4
+ * Fixed TimeSlicedOutputTestDriver
5
+ * Updated cool.io 1.0.0 -> 1.1.0
6
+ * TextParser: fixed regexp of syslog parser to work with rsyslog on CentOS
7
+ * out_exec_filter: improve performance by using buffering
8
+ * out_exec_filter: added num_children parameter
9
+ * out_exec_filter: added remove_prefix/add_prefix parameters
10
+ * out_tail: show warning if pos_file parameter is not set
11
+ * out_copy: fixed problems when event stream is not repeatable
12
+
13
+
2
14
  Release 0.10.8 - 2011/12/03
3
15
 
4
16
  * Added Supervisor: restart process on SIGHUP or unexpected end of process
@@ -63,6 +63,7 @@ License:: Apache License, Version 2.0
63
63
  Patches contributed by:
64
64
 
65
65
  * {Abhishek Parolkar}[https://github.com/parolkar]
66
+ * {Chad Skidmore}[https://github.com/chad-skidmore]
66
67
  * {Eiichiro Iwata}[http://github.com/eiichiroi]
67
68
  * {Francesc Esplugas}[https://github.com/fesplugas]
68
69
  * {hirochachacha}[https://github.com/hirochachacha]
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ begin
15
15
  gemspec.add_dependency "msgpack", "~> 0.4.4"
16
16
  gemspec.add_dependency "json", ">= 1.4.3"
17
17
  gemspec.add_dependency "yajl-ruby", "~> 1.0.0"
18
- gemspec.add_dependency "cool.io", "~> 1.0.0"
18
+ gemspec.add_dependency "cool.io", "~> 1.1.0"
19
19
  gemspec.add_dependency "http_parser.rb", "~> 0.5.1"
20
20
  gemspec.add_development_dependency "rake", ">= 0.9.2"
21
21
  gemspec.test_files = Dir["test/**/*.rb"]
@@ -29,12 +29,6 @@ rescue LoadError
29
29
  puts "Jeweler not available. Install it with: gem install jeweler"
30
30
  end
31
31
 
32
- Rake::TestTask.new(:test) do |t|
33
- t.test_files = Dir['test/*_test.rb']
34
- t.ruby_opts = ['-rubygems'] if defined? Gem
35
- t.ruby_opts << '-I.'
36
- end
37
-
38
32
  VERSION_FILE = "lib/fluent/version.rb"
39
33
 
40
34
  file VERSION_FILE => ["VERSION"] do |t|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.8
1
+ 0.10.9
@@ -21,7 +21,7 @@ module Fluent
21
21
  class TextParser
22
22
  TEMPLATES = {
23
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"],
24
+ 'syslog' => [/^(?<time>[^ ]*\s*[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/, "%b %d %H:%M:%S"],
25
25
  }
26
26
 
27
27
  def self.register_template(name, regexp, time_format=nil)
@@ -94,7 +94,7 @@ class PluginClass
94
94
  if klass = map[type]
95
95
  return klass.new
96
96
  end
97
- raise ConfigError, "Unknown #{name} plugin '#{type}'. Run 'gem search fluent-plugin' to find plugins"
97
+ raise ConfigError, "Unknown #{name} plugin '#{type}'. Run 'gem search -rd fluent-plugin' to find plugins"
98
98
  end
99
99
 
100
100
  def try_load_plugin(name, type)
@@ -42,6 +42,9 @@ class TailInput < Input
42
42
  @pf_file = File.open(@pos_file, File::RDWR|File::CREAT)
43
43
  @pf_file.sync = true
44
44
  @pf = PositionFile.parse(@pf_file)
45
+ else
46
+ $log.warn "'pos_file PATH' parameter is not set to a 'tail' source."
47
+ $log.warn "this parameter is highly recommended to save the position to resume tailing."
45
48
  end
46
49
 
47
50
  configure_parser(conf)
@@ -61,7 +64,7 @@ class TailInput < Input
61
64
  @loop.attach h
62
65
  }
63
66
  handlers.each {|h|
64
- h.on_change # initialize call
67
+ h.on_change(nil, nil) # initialize call # TODO prev, cur
65
68
  }
66
69
  @thread = Thread.new(&method(:run))
67
70
  end
@@ -116,14 +119,14 @@ class TailInput < Input
116
119
  # seek to the end of file first.
117
120
  # logs never duplicate but may be lost if fluent is down.
118
121
  @pos = stat.size
119
- @pe.update(@inode, stat.size)
122
+ @pe.update(@inode, @pos)
120
123
  end
121
124
  @buffer = ''
122
125
  @callback = callback
123
126
  super(path)
124
127
  end
125
128
 
126
- def on_change
129
+ def on_change(prev, cur)
127
130
  lines = []
128
131
  inode = nil
129
132
 
@@ -170,7 +173,7 @@ class TailInput < Input
170
173
  @pe.update_pos(@pos)
171
174
  end
172
175
 
173
- @callback.call(lines)
176
+ @callback.call(lines) unless lines.empty?
174
177
 
175
178
  rescue Errno::ENOENT
176
179
  # moved or deleted
@@ -57,10 +57,11 @@ class CopyOutput < MultiOutput
57
57
 
58
58
  def emit(tag, es, chain)
59
59
  unless es.repeatable?
60
- es = MultiEventStream.new
61
- array = es.map {|time,record|
62
- es.add(time, record)
60
+ m = MultiEventStream.new
61
+ es.each {|time,record|
62
+ m.add(time, record)
63
63
  }
64
+ es = m
64
65
  end
65
66
  chain = OutputChain.new(@outputs, tag, es, chain)
66
67
  chain.next
@@ -18,7 +18,7 @@
18
18
  module Fluent
19
19
 
20
20
 
21
- class ExecFilterOutput < Output
21
+ class ExecFilterOutput < BufferedOutput
22
22
  Plugin.register_output('exec_filter', self)
23
23
 
24
24
  def initialize
@@ -27,12 +27,17 @@ class ExecFilterOutput < Output
27
27
 
28
28
  config_param :command, :string
29
29
  config_param :in_keys, :string
30
+ config_param :remove_prefix, :string, :default => nil
30
31
  config_param :out_keys, :string
32
+ config_param :add_prefix, :string, :default => nil
31
33
  config_param :tag, :string, :default => nil
32
34
  config_param :tag_key, :string, :default => nil
33
35
  config_param :time_key, :string, :default => nil
34
36
  config_param :time_format, :string, :default => nil
35
37
  config_param :localtime, :bool, :default => true
38
+ config_param :num_children, :integer, :default => 1
39
+
40
+ config_set_default :flush_interval, 1
36
41
 
37
42
  def configure(conf)
38
43
  super
@@ -61,27 +66,55 @@ class ExecFilterOutput < Output
61
66
  @time_parse_proc = Proc.new {|str| str.to_i }
62
67
  end
63
68
  end
69
+
70
+ if @remove_prefix
71
+ @removed_prefix_string = @remove_prefix + '.'
72
+ @removed_length = @removed_prefix_string.length
73
+ end
74
+ if @add_prefix
75
+ @added_prefix_string = @add_prefix + '.'
76
+ end
64
77
  end
65
78
 
66
79
  def start
67
- @io = IO.popen(@command, "r+")
68
- @pid = @io.pid
69
- @io.sync = true
70
- @thread = Thread.new(&method(:run))
80
+ super
81
+
82
+ @children = []
83
+ @rr = 0
84
+ begin
85
+ @num_children.times do
86
+ c = ChildProcess.new(&method(:each_line))
87
+ c.start(@command)
88
+ @children << c
89
+ end
90
+ rescue
91
+ shutdown
92
+ raise
93
+ end
94
+ end
95
+
96
+ def before_shutdown
97
+ super
98
+ sleep 0.5 # TODO wait time before killing child process
71
99
  end
72
100
 
73
101
  def shutdown
74
- Process.kill(:TERM, @pid)
75
- if @thread.join(60) # TODO wait time
76
- return
77
- end
78
- Process.kill(:KILL, @pid)
79
- @thread.join
80
- nil
102
+ super
103
+
104
+ @children.reject! {|c|
105
+ c.shutdown
106
+ true
107
+ }
81
108
  end
82
109
 
83
- def emit(tag, es, chain)
110
+ def format_stream(tag, es)
84
111
  out = ''
112
+ if @remove_prefix
113
+ if (tag[0, @removed_length] == @removed_prefix_string and tag.length > @removed_length) or tag == @removed_prefix
114
+ tag = tag[@removed_length..-1] || ''
115
+ end
116
+ end
117
+
85
118
  es.each {|time,record|
86
119
  last = @in_keys.length-1
87
120
  for i in 0..last
@@ -97,45 +130,99 @@ class ExecFilterOutput < Output
97
130
  end
98
131
  out << "\n"
99
132
  }
100
- @io.write out
101
- chain.next
133
+
134
+ out
102
135
  end
103
136
 
104
- def run
105
- @io.each_line {|line|
106
- begin
107
- line.chomp!
108
- vals = line.split("\t")
109
-
110
- tag = @tag
111
- time = nil
112
- record = {}
113
- for i in 0..@out_keys.length-1
114
- key = @out_keys[i]
115
- val = vals[i]
116
- if key == @time_key
117
- time = @time_parse_proc.call(val)
118
- elsif key == @tag_key
119
- tag = val
120
- else
121
- record[key] = val
122
- end
123
- end
137
+ def write(chunk)
138
+ r = @rr = (@rr + 1) % @children.length
139
+ @children[r].write chunk
140
+ end
124
141
 
125
- if tag
126
- time ||= Engine.now
127
- Engine.emit(tag, time, record)
128
- end
129
- rescue
130
- $log.error "exec_filter failed to emit", :error=>$!, :line=>line
131
- $log.warn_backtrace $!.backtrace
142
+ def each_line(line)
143
+ line.chomp!
144
+ vals = line.split("\t")
145
+
146
+ tag = @tag
147
+ time = nil
148
+ record = {}
149
+ for i in 0..@out_keys.length-1
150
+ key = @out_keys[i]
151
+ val = vals[i]
152
+ if key == @time_key
153
+ time = @time_parse_proc.call(val)
154
+ elsif key == @tag_key
155
+ tag = if @add_prefix
156
+ @added_prefix_string + val
157
+ else
158
+ val
159
+ end
160
+ else
161
+ record[key] = val
132
162
  end
133
- }
134
- Process.waitpid(@pid)
163
+ end
164
+
165
+ if tag
166
+ time ||= Engine.now
167
+ Engine.emit(tag, time, record)
168
+ end
135
169
  rescue
136
- $log.error "exec_filter process exited", :error=>$!
170
+ $log.error "exec_filter failed to emit", :error=>$!, :line=>line
137
171
  $log.warn_backtrace $!.backtrace
138
172
  end
173
+
174
+ class ChildProcess
175
+ def initialize(&each_line)
176
+ @pid = nil
177
+ @thread = nil
178
+ @each_line = each_line
179
+ end
180
+
181
+ def start(command)
182
+ @io = IO.popen(command, "r+")
183
+ @pid = @io.pid
184
+ @io.sync = true
185
+ @thread = Thread.new(&method(:run))
186
+ end
187
+
188
+ def shutdown
189
+ begin
190
+ Process.kill(:TERM, @pid)
191
+ rescue Errno::ESRCH
192
+ if $!.message == 'No such process'
193
+ # child process killed by signal chained from fluentd process
194
+ else
195
+ raise
196
+ end
197
+ end
198
+ if @thread.join(60) # TODO wait time
199
+ return
200
+ end
201
+ begin
202
+ Process.kill(:KILL, @pid)
203
+ rescue Errno::ESRCH
204
+ if $!.message == 'No such process'
205
+ # ignore if successfully killed by :TERM
206
+ else
207
+ raise
208
+ end
209
+ end
210
+ @thread.join
211
+ end
212
+
213
+ def write(chunk)
214
+ chunk.write_to(@io)
215
+ end
216
+
217
+ def run
218
+ @io.each_line(&@each_line)
219
+ rescue
220
+ $log.error "exec_filter process exited", :error=>$!
221
+ $log.warn_backtrace $!.backtrace
222
+ ensure
223
+ Process.waitpid(@pid)
224
+ end
225
+ end
139
226
  end
140
227
 
141
228
 
@@ -112,13 +112,19 @@ class TimeSlicedOutputTestDriver < InputTestDriver
112
112
  def run(&block)
113
113
  result = []
114
114
  super {
115
+ buffer = ''
115
116
  @entries.keys.each {|key|
116
117
  es = ArrayEventStream.new(@entries[key])
117
118
  @instance.emit(@tag, es, NullOutputChain.instance)
119
+ buffer << @instance.format_stream(@tag, es)
118
120
  }
119
121
 
120
122
  block.call if block
121
123
 
124
+ if @expected_buffer
125
+ assert_equal(@expected_buffer, buffer)
126
+ end
127
+
122
128
  chunks = @instance.instance_eval {
123
129
  @buffer.instance_eval {
124
130
  chunks = []
@@ -1,5 +1,5 @@
1
1
  module Fluent
2
2
 
3
- VERSION = '0.10.8'
3
+ VERSION = '0.10.9'
4
4
 
5
5
  end
@@ -14,10 +14,11 @@ class ExecFilterOutputTest < Test::Unit::TestCase
14
14
  time_key time
15
15
  time_format %Y-%m-%d %H:%M:%S
16
16
  localtime
17
+ num_children 3
17
18
  ]
18
19
 
19
- def create_driver(conf = CONFIG)
20
- Fluent::Test::OutputTestDriver.new(Fluent::ExecFilterOutput).configure(conf)
20
+ def create_driver(conf = CONFIG, tag = 'test')
21
+ Fluent::Test::OutputTestDriver.new(Fluent::ExecFilterOutput, tag).configure(conf)
21
22
  end
22
23
 
23
24
  def test_configure
@@ -29,6 +30,24 @@ class ExecFilterOutputTest < Test::Unit::TestCase
29
30
  assert_equal "time", d.instance.time_key
30
31
  assert_equal "%Y-%m-%d %H:%M:%S", d.instance.time_format
31
32
  assert_equal true, d.instance.localtime
33
+ assert_equal 3, d.instance.num_children
34
+
35
+ d = create_driver %[
36
+ command sed -l -e s/foo/bar/
37
+ in_keys time,k1
38
+ out_keys time,k2
39
+ tag xxx
40
+ time_key time
41
+ num_children 3
42
+ ]
43
+ assert_equal "sed -l -e s/foo/bar/", d.instance.command
44
+
45
+ d = create_driver(CONFIG + %[
46
+ remove_prefix before
47
+ add_prefix after
48
+ ])
49
+ assert_equal "before", d.instance.remove_prefix
50
+ assert_equal "after" , d.instance.add_prefix
32
51
  end
33
52
 
34
53
  def test_emit_1
@@ -39,7 +58,6 @@ class ExecFilterOutputTest < Test::Unit::TestCase
39
58
  d.run do
40
59
  d.emit({"k1"=>1}, time)
41
60
  d.emit({"k1"=>2}, time)
42
- sleep 0.5
43
61
  end
44
62
 
45
63
  emits = d.emits
@@ -55,6 +73,7 @@ class ExecFilterOutputTest < Test::Unit::TestCase
55
73
  out_keys time,k2
56
74
  tag xxx
57
75
  time_key time
76
+ num_children 3
58
77
  ]
59
78
 
60
79
  time = Time.parse("2011-01-02 13:14:15").to_i
@@ -62,7 +81,6 @@ class ExecFilterOutputTest < Test::Unit::TestCase
62
81
  d.run do
63
82
  d.emit({"k1"=>1}, time)
64
83
  d.emit({"k1"=>2}, time)
65
- sleep 0.5
66
84
  end
67
85
 
68
86
  emits = d.emits
@@ -70,5 +88,73 @@ class ExecFilterOutputTest < Test::Unit::TestCase
70
88
  assert_equal ["xxx", time, {"k2"=>"1"}], emits[0]
71
89
  assert_equal ["xxx", time, {"k2"=>"2"}], emits[1]
72
90
  end
91
+
92
+ def test_emit_3
93
+ d = create_driver %[
94
+ command grep --line-buffered -v poo
95
+ in_keys time,val1
96
+ out_keys time,val2
97
+ tag xxx
98
+ time_key time
99
+ num_children 3
100
+ ]
101
+
102
+ time = Time.parse("2011-01-02 13:14:15").to_i
103
+
104
+ d.run do
105
+ d.emit({"val1"=>"sed-ed value foo"}, time)
106
+ d.emit({"val1"=>"sed-ed value poo"}, time)
107
+ end
108
+
109
+ emits = d.emits
110
+ assert_equal 1, emits.length
111
+ assert_equal ["xxx", time, {"val2"=>"sed-ed value foo"}], emits[0]
112
+
113
+ d = create_driver %[
114
+ command sed -l -e s/foo/bar/
115
+ in_keys time,val1
116
+ out_keys time,val2
117
+ tag xxx
118
+ time_key time
119
+ num_children 3
120
+ ]
121
+
122
+ time = Time.parse("2011-01-02 13:14:15").to_i
123
+
124
+ d.run do
125
+ d.emit({"val1"=>"sed-ed value foo"}, time)
126
+ d.emit({"val1"=>"sed-ed value poo"}, time)
127
+ end
128
+
129
+ emits = d.emits
130
+ assert_equal 2, emits.length
131
+ assert_equal ["xxx", time, {"val2"=>"sed-ed value bar"}], emits[0]
132
+ assert_equal ["xxx", time, {"val2"=>"sed-ed value poo"}], emits[1]
133
+ end
134
+
135
+ def test_emit_4
136
+ d = create_driver(%[
137
+ command sed -l -e s/foo/bar/
138
+ in_keys tag,time,val1
139
+ remove_prefix input
140
+ out_keys tag,time,val2
141
+ add_prefix output
142
+ tag_key tag
143
+ time_key time
144
+ num_children 3
145
+ ], 'input.test')
146
+
147
+ time = Time.parse("2011-01-02 13:14:15").to_i
148
+
149
+ d.run do
150
+ d.emit({"val1"=>"sed-ed value foo"}, time)
151
+ d.emit({"val1"=>"sed-ed value poo"}, time)
152
+ end
153
+
154
+ emits = d.emits
155
+ assert_equal 2, emits.length
156
+ assert_equal ["output.test", time, {"val2"=>"sed-ed value bar"}], emits[0]
157
+ assert_equal ["output.test", time, {"val2"=>"sed-ed value poo"}], emits[1]
158
+ end
73
159
  end
74
160
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.8
4
+ version: 0.10.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-04 00:00:00.000000000Z
12
+ date: 2012-01-20 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: msgpack
16
- requirement: &70282132235300 !ruby/object:Gem::Requirement
16
+ requirement: &70352789356340 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.4.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70282132235300
24
+ version_requirements: *70352789356340
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &70282132234740 !ruby/object:Gem::Requirement
27
+ requirement: &70352789355760 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.4.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70282132234740
35
+ version_requirements: *70352789355760
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: yajl-ruby
38
- requirement: &70282132234020 !ruby/object:Gem::Requirement
38
+ requirement: &70352789355160 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,21 +43,21 @@ dependencies:
43
43
  version: 1.0.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70282132234020
46
+ version_requirements: *70352789355160
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: cool.io
49
- requirement: &70282132233440 !ruby/object:Gem::Requirement
49
+ requirement: &70352789343800 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 1.0.0
54
+ version: 1.1.0
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70282132233440
57
+ version_requirements: *70352789343800
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: http_parser.rb
60
- requirement: &70282132232700 !ruby/object:Gem::Requirement
60
+ requirement: &70352789343300 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 0.5.1
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70282132232700
68
+ version_requirements: *70352789343300
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
- requirement: &70282132231740 !ruby/object:Gem::Requirement
71
+ requirement: &70352789342760 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: 0.9.2
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70282132231740
79
+ version_requirements: *70352789342760
80
80
  description:
81
81
  email: frsyuki@gmail.com
82
82
  executables:
@@ -176,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  version: '0'
177
177
  requirements: []
178
178
  rubyforge_project:
179
- rubygems_version: 1.8.10
179
+ rubygems_version: 1.8.12
180
180
  signing_key:
181
181
  specification_version: 3
182
182
  summary: Fluent event collector