fluentd 0.10.7 → 0.10.8

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.

@@ -304,6 +304,7 @@ class BufferedOutput < Output
304
304
  @error_history.clear
305
305
  # Note: don't notify to other threads to prevent
306
306
  # burst to recovered server
307
+ $log.warn "retry suceeded.", :instance=>object_id
307
308
  end
308
309
 
309
310
  if has_next
@@ -328,20 +329,20 @@ class BufferedOutput < Output
328
329
  end
329
330
 
330
331
  if error_count < @retry_limit
331
- $log.warn "failed to flush the buffer, retrying.", :error=>e.to_s
332
+ $log.warn "failed to flush the buffer, retrying.", :error=>e.to_s, :instance=>object_id
332
333
  $log.warn_backtrace e.backtrace
333
334
 
334
335
  elsif @secondary
335
336
  if error_count == @retry_limit
336
- $log.warn "failed to flush the buffer.", :error=>e.to_s
337
+ $log.warn "failed to flush the buffer.", :error=>e.to_s, :instance=>object_id
337
338
  $log.warn "retry count exceededs limit. falling back to secondary output."
338
339
  $log.warn_backtrace e.backtrace
339
340
  retry # retry immediately
340
341
  elsif error_count <= @retry_limit + @secondary_limit
341
- $log.warn "failed to flush the buffer, retrying secondary.", :error=>e.to_s
342
+ $log.warn "failed to flush the buffer, retrying secondary.", :error=>e.to_s, :instance=>object_id
342
343
  $log.warn_backtrace e.backtrace
343
344
  else
344
- $log.warn "failed to flush the buffer.", :error=>e.to_s
345
+ $log.warn "failed to flush the buffer.", :error=>e.to_s, :instance=>object_id
345
346
  $log.warn "secondary retry count exceededs limit."
346
347
  $log.warn_backtrace e.backtrace
347
348
  write_abort
@@ -349,7 +350,7 @@ class BufferedOutput < Output
349
350
  end
350
351
 
351
352
  else
352
- $log.warn "failed to flush the buffer.", :error=>e.to_s
353
+ $log.warn "failed to flush the buffer.", :error=>e.to_s, :instance=>object_id
353
354
  $log.warn "retry count exceededs limit."
354
355
  $log.warn_backtrace e.backtrace
355
356
  write_abort
@@ -109,7 +109,7 @@ class PluginClass
109
109
  return
110
110
  end
111
111
 
112
- # prefer LOAD_PATH
112
+ # prefer LOAD_PATH than gems
113
113
  files = $LOAD_PATH.map {|lp|
114
114
  lpath = File.join(lp, "#{path}.rb")
115
115
  File.exist?(lpath) ? lpath : nil
@@ -121,17 +121,32 @@ class PluginClass
121
121
  end
122
122
 
123
123
  # search gems
124
- if defined?(::Gem) && ::Gem.respond_to?(:searcher)
124
+ if defined?(::Gem::Specification) && ::Gem::Specification.respond_to?(:find_all)
125
+ specs = Gem::Specification.find_all {|spec|
126
+ spec.contains_requirable_file? path
127
+ }
128
+
129
+ # prefer newer version
130
+ specs = specs.sort_by {|spec| spec.version }
131
+ if spec = specs.last
132
+ spec.require_paths.each {|lib|
133
+ file = "#{spec.full_gem_path}/#{lib}/#{path}"
134
+ require file
135
+ }
136
+ end
137
+
138
+ # backward compatibility for rubygems < 1.8
139
+ elsif defined?(::Gem) && ::Gem.respond_to?(:searcher)
125
140
  #files = Gem.find_files(path).sort
126
141
  specs = Gem.searcher.find_all(path)
127
- specs = specs.sort_by {|spec| spec.version }
128
142
 
129
143
  # prefer newer version
144
+ specs = specs.sort_by {|spec| spec.version }
130
145
  specs.reverse_each {|spec|
131
146
  files = Gem.searcher.matching_files(spec, path)
132
147
  unless files.empty?
133
148
  require files.first
134
- return
149
+ break
135
150
  end
136
151
  }
137
152
  end
@@ -157,11 +157,11 @@ class FileBuffer < BasicBuffer
157
157
 
158
158
  protected
159
159
  def encode_key(key)
160
- URI.encode(key, /[^-_.a-zA-Z0-9]/n)
160
+ URI.escape(key, /[^-_.a-zA-Z0-9]/n)
161
161
  end
162
162
 
163
163
  def decode_key(encoded_key)
164
- URI.decode(encoded_key, /[^-_.a-zA-Z0-9]/n)
164
+ URI.unescape(encoded_key)
165
165
  end
166
166
 
167
167
  def make_path(encoded_key, bq)
@@ -18,10 +18,6 @@
18
18
  module Fluent
19
19
 
20
20
 
21
- class ProcessDetachInterrupt < StandardError
22
- end
23
-
24
-
25
21
  class DetachProcessManager
26
22
  require 'singleton'
27
23
  include Singleton
@@ -37,13 +33,13 @@ class DetachProcessManager
37
33
 
38
34
  def initialize
39
35
  require 'drb'
40
- DRb.start_service("drbunix:", Broker.new)
36
+ DRb.start_service(create_drb_uri, Broker.new)
41
37
  @parent_uri = DRb.uri
42
38
  end
43
39
 
44
40
  def fork(delegate_object)
45
- ipr, ipw = IO.pipe
46
- opr, opw = IO.pipe
41
+ ipr, ipw = IO.pipe # child Engine.emit_stream -> parent Engine.emit_stream
42
+ opr, opw = IO.pipe # parent target.emit -> child target.emit
47
43
 
48
44
  pid = Process.fork
49
45
  if pid
@@ -61,9 +57,6 @@ class DetachProcessManager
61
57
  return nil, forward_thread
62
58
  end
63
59
 
64
- def setup_delegate
65
- end
66
-
67
60
  private
68
61
  def read_header(ipr)
69
62
  sz = ipr.read(4).unpack('N')[0]
@@ -76,20 +69,27 @@ class DetachProcessManager
76
69
  ipw.flush
77
70
  end
78
71
 
72
+ def create_drb_uri
73
+ "drbunix:" # TODO
74
+ end
75
+
79
76
  private
80
77
  def process_child(ipw, opr, delegate_object)
78
+ DRb.start_service(create_drb_uri, delegate_object)
79
+ child_uri = DRb.uri
80
+
81
+ send_header(ipw, child_uri)
82
+
83
+ # override target.emit_stream to write event stream to the pipe
81
84
  fwd = new_forwarder(ipw, 0.5) # TODO interval
82
85
  Engine.define_singleton_method(:emit_stream) do |tag,es|
83
86
  fwd.emit(tag, es)
84
87
  end
85
88
 
86
- DRb.start_service("drbunix:", delegate_object)
87
- child_uri = DRb.uri
88
-
89
- send_header(ipw, child_uri)
90
-
89
+ # read event stream from the pipe and forward to target.emit
91
90
  forward_thread = Thread.new(opr, delegate_object, &method(:output_forward_main))
92
91
 
92
+ # override global methods to call parent process
93
93
  override_shared_methods(@parent_uri)
94
94
 
95
95
  return forward_thread
@@ -115,16 +115,17 @@ class DetachProcessManager
115
115
  def process_parent(ipr, opw, pid, delegate_object)
116
116
  child_uri = read_header(ipr)
117
117
 
118
+ # read event stream from the pipe and forward to Engine.emit_stream
118
119
  forward_thread = Thread.new(ipr, pid, &method(:input_forward_main))
119
120
 
121
+ # note: don't override methods in parent process
122
+ # because another process may fork after overriding
120
123
  #override_delegate_methods(delegate_object, child_uri)
121
124
 
125
+ # return forwarder for DetachProcessMixin to
126
+ # override target.emit and write event stream to the pipe
122
127
  fwd = new_forwarder(opw, 0.5) # TODO interval
123
- #delegate_object.define_singleton_method(:emit) do |tag,es,chain|
124
- # chain.next
125
- # fwd.emit(tag, es)
126
- #end
127
-
128
+ # note: override emit method on DetachProcessMixin
128
129
  forward_thread.define_singleton_method(:forwarder) do
129
130
  fwd
130
131
  end
@@ -147,7 +148,7 @@ class DetachProcessManager
147
148
 
148
149
  def output_forward_main(opr, target)
149
150
  read_event_stream(opr) {|tag,es|
150
- # FIXME error
151
+ # FIXME error handling
151
152
  begin
152
153
  target.emit(tag, es, NullOutputChain.instance)
153
154
  rescue
@@ -163,7 +164,13 @@ class DetachProcessManager
163
164
 
164
165
  def input_forward_main(ipr, pid)
165
166
  read_event_stream(ipr) {|tag,es|
166
- Engine.emit_stream(tag, es)
167
+ # FIXME error handling
168
+ begin
169
+ Engine.emit_stream(tag, es)
170
+ rescue
171
+ $log.warn "failed to emit", :error=>$!.to_s, :pid=>Process.pid
172
+ $log.warn_backtrace
173
+ end
167
174
  }
168
175
  rescue
169
176
  $log.error "error on input process forwarding thread", :error=>$!.to_s, :pid=>Process.pid
@@ -309,7 +316,7 @@ module DetachProcessImpl
309
316
  trap :TERM do
310
317
  fin.stop
311
318
  end
312
- #forward_thread.join
319
+ #forward_thread.join # TODO this thread won't stop because parent doesn't close pipe
313
320
  fin.wait
314
321
 
315
322
  exit! 0
@@ -322,28 +329,29 @@ module DetachProcessImpl
322
329
  end
323
330
 
324
331
  # parent process
325
- # override shutdown method
332
+ # override shutdown method to kill child processes
326
333
  define_singleton_method(:shutdown) do
327
- begin
328
- children.each {|pair|
334
+ children.each {|pair|
335
+ begin
329
336
  pid = pair[0]
330
337
  forward_thread = pair[1]
331
338
  if pid
332
339
  Process.kill(:TERM, pid)
333
- forward_thread.join
340
+ forward_thread.join # wait until child closes pipe
334
341
  Process.waitpid(pid)
335
342
  pair[0] = nil
336
343
  end
337
- }
338
- rescue
339
- $log.error "unknown error while shutting down remote child process", :error=>$!.to_s
340
- $log.error_backtrace
341
- end
344
+ rescue
345
+ $log.error "unknown error while shutting down remote child process", :error=>$!.to_s
346
+ $log.error_backtrace
347
+ end
348
+ }
342
349
  end
343
350
 
344
- # override emit method
351
+ # override target.emit and write event stream to the pipe
345
352
  forwarders = children.map {|pair| pair[1].forwarder }
346
353
  if forwarders.length > 1
354
+ # use roundrobin
347
355
  fwd = DetachProcessManager::MultiForwarder.new(forwarders)
348
356
  else
349
357
  fwd = forwarders[0]
@@ -0,0 +1,307 @@
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 Supervisor
22
+ class LoggerInitializer
23
+ def initialize(path, level)
24
+ @path = path
25
+ @level = level
26
+ end
27
+
28
+ def init
29
+ if @path && @path != "-"
30
+ @io = File.open(@path, "a")
31
+ else
32
+ @io = STDOUT
33
+ end
34
+
35
+ $log = Fluent::Log.new(@io, @level)
36
+
37
+ $log.enable_color(false) if @path
38
+ $log.enable_debug if @level <= Fluent::Log::LEVEL_DEBUG
39
+ end
40
+
41
+ def stdout?
42
+ @io == STDOUT
43
+ end
44
+
45
+ def reopen!
46
+ if @path && @path != "-"
47
+ @io.reopen(@path, "a")
48
+ end
49
+ self
50
+ end
51
+ end
52
+
53
+ def initialize(opt)
54
+ @config_path = opt[:config_path]
55
+ @log_path = opt[:log_path]
56
+ @log_level = opt[:log_level]
57
+ @daemonize = opt[:daemonize]
58
+ @chgroup = opt[:chgroup]
59
+ @chuser = opt[:chuser]
60
+ @libs = opt[:libs]
61
+ @plugin_dirs = opt[:plugin_dirs]
62
+ @inline_config = opt[:inline_config]
63
+
64
+ @log = LoggerInitializer.new(@log_path, @log_level)
65
+ @finished = false
66
+ @main_pid = nil
67
+ end
68
+
69
+ def start
70
+ require 'fluent/load'
71
+ @log.init
72
+
73
+ start_daemonize if @daemonize
74
+ install_supervisor_signal_handlers
75
+ until @finished
76
+ supervise do
77
+ read_config
78
+ change_privilege
79
+ init_engine
80
+ install_main_process_signal_handlers
81
+ run_configure
82
+ finish_daemonize if @daemonize
83
+ run_engine
84
+ exit 0
85
+ end
86
+ end
87
+ end
88
+
89
+ private
90
+ def start_daemonize
91
+ @wait_daemonize_pipe_r, @wait_daemonize_pipe_w = IO.pipe
92
+
93
+ if fork
94
+ # console process
95
+ @wait_daemonize_pipe_w.close
96
+ @wait_daemonize_pipe_w = nil
97
+ wait_daemonize
98
+ exit 0
99
+ end
100
+
101
+ # daemonize intermediate process
102
+ @wait_daemonize_pipe_r.close
103
+ @wait_daemonize_pipe_r = nil
104
+
105
+ Process.setsid
106
+ exit!(0) if fork
107
+ File.umask(0)
108
+
109
+ # supervisor process
110
+ @supervisor_pid = Process.pid
111
+ end
112
+
113
+ def wait_daemonize
114
+ supervisor_pid = @wait_daemonize_pipe_r.read
115
+ if supervisor_pid.empty?
116
+ # initialization failed
117
+ exit! 1
118
+ end
119
+
120
+ @wait_daemonize_pipe_r.close
121
+ @wait_daemonize_pipe_r = nil
122
+
123
+ # write pid file
124
+ File.open(@daemonize, "w") {|f|
125
+ f.write supervisor_pid
126
+ }
127
+ end
128
+
129
+ def finish_daemonize
130
+ if @wait_daemonize_pipe_w
131
+ STDIN.reopen("/dev/null")
132
+ STDOUT.reopen("/dev/null", "w")
133
+ STDERR.reopen("/dev/null", "w")
134
+ @wait_daemonize_pipe_w.write @supervisor_pid.to_s
135
+ @wait_daemonize_pipe_w.close
136
+ @wait_daemonize_pipe_w = nil
137
+ end
138
+ end
139
+
140
+ def supervise(&block)
141
+ start_time = Time.now
142
+
143
+ $log.info "starting fluentd-#{Fluent::VERSION}"
144
+ @main_pid = fork do
145
+ main_process(&block)
146
+ end
147
+
148
+ if @daemonize && @wait_daemonize_pipe_w
149
+ STDIN.reopen("/dev/null")
150
+ STDOUT.reopen("/dev/null", "w")
151
+ STDERR.reopen("/dev/null", "w")
152
+ @wait_daemonize_pipe_w.close
153
+ @wait_daemonize_pipe_w = nil
154
+ end
155
+
156
+ Process.waitpid(@main_pid)
157
+ @main_pid = nil
158
+ ecode = $?.to_i
159
+
160
+ $log.info "process finished", :code=>ecode
161
+
162
+ if !@finished && Time.now - start_time < 1
163
+ $log.warn "process died within 1 second. exit."
164
+ exit ecode
165
+ end
166
+ end
167
+
168
+ def main_process(&block)
169
+ begin
170
+ block.call
171
+
172
+ rescue Fluent::ConfigError
173
+ $log.error "config error", :file=>@config_path, :error=>$!.to_s
174
+ $log.debug_backtrace
175
+ unless @log.stdout?
176
+ console = Fluent::Log.new(STDOUT, @log_level).enable_debug
177
+ console.error "config error", :file=>@config_path, :error=>$!.to_s
178
+ console.debug_backtrace
179
+ end
180
+
181
+ rescue
182
+ $log.error "unexpected error", :error=>$!.to_s
183
+ $log.error_backtrace
184
+ unless @log.stdout?
185
+ console = Fluent::Log.new(STDOUT, @log_level).enable_debug
186
+ console.error "unexpected error", :error=>$!.to_s
187
+ console.error_backtrace
188
+ end
189
+ end
190
+
191
+ exit! 1
192
+ end
193
+
194
+ def install_supervisor_signal_handlers
195
+ trap :INT do
196
+ @finished = true
197
+ if pid = @main_pid
198
+ Process.kill(:INT, pid)
199
+ end
200
+ end
201
+
202
+ trap :TERM do
203
+ @finished = true
204
+ if pid = @main_pid
205
+ Process.kill(:TERM, pid)
206
+ end
207
+ end
208
+
209
+ trap :HUP do
210
+ $log.info "restarting"
211
+ if pid = @main_pid
212
+ Process.kill(:TERM, pid)
213
+ end
214
+ end
215
+
216
+ trap :USR1 do
217
+ @log.reopen!
218
+ if pid = @main_pid
219
+ Process.kill(:USR1, pid)
220
+ end
221
+ end
222
+ end
223
+
224
+ def read_config
225
+ $log.info "reading config file", :path=>@config_path
226
+ @config_fname = File.basename(@config_path)
227
+ @config_basedir = File.dirname(@config_path)
228
+ @config_data = File.read(@config_path)
229
+ if @inline_config == '-'
230
+ @config_data << "\n" << STDIN.read
231
+ elsif @inline_config
232
+ @config_data << "\n" << @inline_config.gsub("\\n","\n")
233
+ end
234
+ end
235
+
236
+ def run_configure
237
+ Fluent::Engine.parse_config(@config_data, @config_fname, @config_basedir)
238
+ end
239
+
240
+ def change_privilege
241
+ if @chgroup
242
+ chgid = @chgroup.to_i
243
+ if chgid.to_s != @chgroup
244
+ chgid = `id -g #{@chgroup}`.to_i
245
+ if $?.to_i != 0
246
+ exit 1
247
+ end
248
+ end
249
+ Process::GID.change_privilege(chgid)
250
+ end
251
+
252
+ if @chuser
253
+ chuid = @chuser.to_i
254
+ if chuid.to_s != @chuser
255
+ chuid = `id -u #{@chuser}`.to_i
256
+ if $?.to_i != 0
257
+ exit 1
258
+ end
259
+ end
260
+ Process::UID.change_privilege(chuid)
261
+ end
262
+ end
263
+
264
+ def init_engine
265
+ require 'fluent/load'
266
+ Fluent::Engine.init
267
+
268
+ @libs.each {|lib|
269
+ require lib
270
+ }
271
+
272
+ @plugin_dirs.each {|dir|
273
+ if Dir.exist?(dir)
274
+ dir = File.expand_path(dir)
275
+ Fluent::Engine.load_plugin_dir(dir)
276
+ end
277
+ }
278
+ end
279
+
280
+ def install_main_process_signal_handlers
281
+ trap :INT do
282
+ Fluent::Engine.stop
283
+ end
284
+
285
+ trap :TERM do
286
+ Fluent::Engine.stop
287
+ end
288
+
289
+ trap :HUP do
290
+ # TODO
291
+ end
292
+
293
+ trap :USR1 do
294
+ $log.info "force flushing buffered events"
295
+ @log.reopen!
296
+ Fluent::Engine.flush!
297
+ end
298
+ end
299
+
300
+ def run_engine
301
+ Fluent::Engine.run
302
+ end
303
+ end
304
+
305
+
306
+ end
307
+