module Resque class Pool module Logging extend self # This reopens ALL logfiles in the process that have been rotated # using logrotate(8) (without copytruncate) or similar tools. # A +File+ object is considered for reopening if it is: # 1) opened with the O_APPEND and O_WRONLY flags # 2) the current open file handle does not match its original open path # 3) unbuffered (as far as userspace buffering goes, not O_SYNC) # Returns the number of files reopened # # This was mostly copied from Unicorn 4.8.2 to simplify reopening # logs in the same way that Unicorn does. Original comments and # explanations are left intact. def self.reopen_logs! to_reopen = [ ] reopened_count = 0 ObjectSpace.each_object(File) { |fp| is_log?(fp) and to_reopen << fp } log "Flushing #{to_reopen.length} logs" to_reopen.each do |fp| orig_st = begin fp.stat rescue IOError, Errno::EBADF # race next end begin b = File.stat(fp.path) # Skip if reopening wouldn't do anything next if orig_st.ino == b.ino && orig_st.dev == b.dev rescue Errno::ENOENT end begin # stdin, stdout, stderr are special. The following dance should # guarantee there is no window where `fp' is unwritable in MRI # (or any correct Ruby implementation). # # Fwiw, GVL has zero bearing here. This is tricky because of # the unavoidable existence of stdio FILE * pointers for # std{in,out,err} in all programs which may use the standard C library if fp.fileno <= 2 # We do not want to hit fclose(3)->dup(2) window for std{in,out,err} # MRI will use freopen(3) here internally on std{in,out,err} fp.reopen(fp.path, "a") else # We should not need this workaround, Ruby can be fixed: # http://bugs.ruby-lang.org/issues/9036 # MRI will not call call fclose(3) or freopen(3) here # since there's no associated std{in,out,err} FILE * pointer # This should atomically use dup3(2) (or dup2(2)) syscall File.open(fp.path, "a") { |tmpfp| fp.reopen(tmpfp) } end fp.sync = true fp.flush # IO#sync=true may not implicitly flush new_st = fp.stat # this should only happen in the master: if orig_st.uid != new_st.uid || orig_st.gid != new_st.gid fp.chown(orig_st.uid, orig_st.gid) end log "Reopened logfile: #{fp.path}" reopened_count += 1 rescue IOError, Errno::EBADF # not much we can do... end end reopened_count end PROCLINE_PREFIX="resque-pool-master" # Given a string, sets the procline ($0) # Procline is always in the format of: # resque-pool-master: STRING def procline(string) $0 = "#{PROCLINE_PREFIX}#{app}: #{string}" end # TODO: make this use an actual logger def log(message) return if $skip_logging puts "resque-pool-manager#{app}[#{Process.pid}]: #{message}" #$stdout.fsync end # TODO: make this use an actual logger def log_worker(message) return if $skip_logging puts "resque-pool-worker#{app}[#{Process.pid}]: #{message}" #$stdout.fsync end # Include optional app name in procline def app app_name = self.respond_to?(:app_name) && self.app_name app_name ||= self.class.respond_to?(:app_name) && self.class.app_name app_name ? "[#{app_name}]" : "" end private # Used by reopen_logs, borrowed from Unicorn... def self.is_log?(fp) append_flags = File::WRONLY | File::APPEND ! fp.closed? && fp.stat.file? && fp.sync && (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags rescue IOError, Errno::EBADF false end end end end