module File::Tail
This module can be included in your own File
subclasses or used to extend files you want to tail.
Constants
- VERSION
File::Tail
version
Attributes
If this attribute is set to a true value, File::Fail’s tail method raises a BreakException
if the end of the file is reached.
Default buffer size, that is used while going backward from a file’s end. This defaults to nil, which means that File::Tail
attempts to derive this value from the filesystem block size.
The start value of the sleep interval. This value goes against max_interval
if the tailed file is silent for a sufficient time.
Override the default line separator
The maximum interval File::Tail
sleeps, before it tries to take some action like reading the next few lines or reopening the file.
If this attribute is set to a true value, File::Tail
persists on reopening a deleted file waiting max_interval
seconds between the attempts. This is useful if logfiles are moved away while rotation occurs but are recreated at the same place after a while. It defaults to true.
If this attribute is set to a true value, File::Tail
attempts to reopen it’s tailed file after suspicious_interval
seconds of silence.
If this attribute is set to a true value, File::Fail’s tail method just returns if the end of the file is reached.
This attribute is the invterval in seconds before File::Tail
gets suspicious that something has happend to it’s tailed file and an attempt to reopen it is made.
If the attribute reopen_suspicious
is set to a non true value, suspicious_interval
is meaningless. It defaults to 60 seconds.
Public Instance Methods
The callback is called with self as an argument after a reopen has occured. This allows a tailing script to find out, if a logfile has been rotated.
# File lib/file/tail.rb, line 71 def after_reopen(&block) @after_reopen = block end
Rewind the last n
lines of this file, starting from the end. The default is to start tailing directly from the end of the file.
The additional argument bufsize
is used to determine the buffer size that is used to step through the file backwards. It defaults to the block size of the filesystem this file belongs to or 8192 bytes if this cannot be determined.
# File lib/file/tail.rb, line 121 def backward(n = 0, bufsize = nil) preset_attributes unless defined? @lines if n <= 0 seek(0, File::SEEK_END) return self end bufsize ||= default_bufsize || stat.blksize || 8192 size = stat.size begin if bufsize < size seek(0, File::SEEK_END) while n > 0 and tell > 0 do seek(-bufsize, File::SEEK_CUR) buffer = read(bufsize) n -= buffer.count(@line_separator) seek(-bufsize, File::SEEK_CUR) end else rewind buffer = read(size) n -= buffer.count(@line_separator) rewind end rescue Errno::EINVAL size = tell retry end pos = -1 while n < 0 # forward if we are too far back pos = buffer.index(@line_separator, pos + 1) n += 1 end seek(pos + 1, File::SEEK_CUR) self end
Skip the first n
lines of this file. The default is to don’t skip any lines at all and start at the beginning of this file.
# File lib/file/tail.rb, line 102 def forward(n = 0) preset_attributes unless defined? @lines rewind while n > 0 and not eof? readline(@line_separator) n -= 1 end self end
This method tails this file and yields to the given block for every new line that is read. If no block is given an array of those lines is returned instead. (In this case it’s better to use a reasonable value for n
or set the return_if_eof
or break_if_eof
attribute to a true value to stop the method call from blocking.)
If the argument n
is given, only the next n
lines are read and the method call returns. Otherwise this method call doesn’t return, but yields to block for every new line read from this file for ever.
# File lib/file/tail.rb, line 169 def tail(n = nil, &block) # :yields: line @n = n result = [] array_result = false unless block block = lambda { |line| result << line } array_result = true end preset_attributes unless defined? @lines loop do begin restat read_line(&block) redo rescue ReopenException => e until eof? || @n == 0 block.call readline(@line_separator) @n -= 1 if @n end reopen_file(e.mode) @after_reopen.call self if defined? @after_reopen rescue ReturnException return array_result ? result : nil end end end
Private Instance Methods
# File lib/file/tail.rb, line 304 def debug? ENV['FILE_TAIL_DEBUG'].to_i == 1 end
# File lib/file/tail.rb, line 292 def output_debug_information debug? or return STDERR.puts({ :path => path, :lines => @lines, :interval => @interval, :no_read => @no_read, :n => @n, }.inspect) self end
# File lib/file/tail.rb, line 225 def preset_attributes @reopen_deleted = true unless defined? @reopen_deleted @reopen_suspicious = true unless defined? @reopen_suspicious @break_if_eof = false unless defined? @break_if_eof @return_if_eof = false unless defined? @return_if_eof @max_interval ||= 10 @line_separator ||= $/ @interval ||= @max_interval @suspicious_interval ||= 60 @lines = 0 @no_read = 0 @stat = nil end
# File lib/file/tail.rb, line 198 def read_line(&block) if @n until @n == 0 block.call readline(@line_separator) @lines += 1 @no_read = 0 @n -= 1 output_debug_information end raise ReturnException else block.call readline(@line_separator) @lines += 1 @no_read = 0 output_debug_information end rescue EOFError seek(0, File::SEEK_CUR) raise ReopenException if @reopen_suspicious and @no_read > @suspicious_interval raise BreakException if @break_if_eof raise ReturnException if @return_if_eof sleep_interval rescue Errno::ENOENT, Errno::ESTALE, Errno::EBADF raise ReopenException end
# File lib/file/tail.rb, line 274 def reopen_file(mode) debug? and $stdout.print "Reopening '#{path}', mode = #{mode}.\n" @no_read = 0 reopen(path) if mode == :bottom backward elsif mode == :top forward end rescue Errno::ESTALE, Errno::ENOENT if @reopen_deleted sleep @max_interval retry else raise DeletedException end end
# File lib/file/tail.rb, line 239 def restat stat = File.stat(path) if @stat if stat.ino != @stat.ino or stat.dev != @stat.dev @stat = nil raise ReopenException.new(:top) end if stat.size < @stat.size @stat = nil raise ReopenException.new(:top) end end @stat = stat rescue Errno::ENOENT, Errno::ESTALE raise ReopenException end
# File lib/file/tail.rb, line 256 def sleep_interval if @lines > 0 # estimate how much time we will spend on waiting for next line @interval = (@interval.to_f / @lines) @lines = 0 else # exponential backoff if logfile is quiet @interval *= 2 end if @interval > @max_interval # max. wait @max_interval @interval = @max_interval end output_debug_information sleep @interval @no_read += @interval end