class FileWatch::Ext::FileTail

Public Instance Methods

subscribe(&block) click to toggle source
# File lib/filewatch/ext/filetail.rb, line 8
def subscribe(&block)
  # subscribe(stat_interval = 1, discover_interval = 5, &block)
  @watch.subscribe(@opts[:stat_interval],
                   @opts[:discover_interval]) do |event, path|
    case event
    when :create, :create_initial
      if @files.member?(path)
        @logger.debug("#{event} for #{path}: already exists in @files")
        next
      end
      if _open_file(path, event)
        _read_file(path, &block)
      end
    when :modify
      _open_file(path, event) if @opts[:eof_close]

      if !@files.member?(path)
        @logger.debug(":modify for #{path}, does not exist in @files")
        if _open_file(path, event)
          _read_file(path, &block)
        end
      else
        _read_file(path, &block)
      end
    when :delete
      @logger.debug(":delete for #{path}, deleted from @files")

      _read_file(path, &block) if !@opts[:eof_close]
      _progressdb_delete(path, &block) if @opts[:progressdb] && @opts[:progressdb_del]

      @files[path].close if !@opts[:eof_close]
      @files.delete(path)
      _sincedb_delete(path)
      @statcache.delete(path)
    else
      @logger.warn("unknown event type #{event} for #{path}")
    end
  end # @watch.subscribe
end

Private Instance Methods

_open_file(path, event) click to toggle source
# File lib/filewatch/ext/filetail.rb, line 49
def _open_file(path, event)
  @logger.debug("_open_file: #{path}: opening")
  begin
    if @iswindows && defined? JRUBY_VERSION
        @files[path] = Java::RubyFileExt::getRubyFile(path)
    else
        @files[path] = File.open(path)
    end
  rescue
    # don't emit this message too often. if a file that we can't
    # read is changing a lot, we'll try to open it more often,
    # and might be spammy.
    now = Time.now.to_i
    if now - @lastwarn[path] > OPEN_WARN_INTERVAL
      @logger.warn("failed to open #{path}: #{$!}")
      @lastwarn[path] = now
    else
      @logger.debug("(warn supressed) failed to open #{path}: #{$!}")
    end
    @files.delete(path)
    return false
  end

  stat = File::Stat.new(path)

  if @iswindows
    fileId = Winhelper.GetWindowsUniqueFileIdentifier(path)
    inode = [fileId, stat.dev_major, stat.dev_minor]
  else
    inode = [stat.ino.to_s, stat.dev_major, stat.dev_minor]
  end

  @statcache[path] = inode
  if @sincedb.member?(inode)
    last_size = @sincedb[inode][:pos]
    @logger.debug("#{path}: sincedb last value #{@sincedb[inode]}, cur size #{stat.size}")
    if last_size <= stat.size
      @logger.debug("#{path}: sincedb: seeking to #{last_size}")
      @files[path].sysseek(last_size, IO::SEEK_SET)
    else
      @logger.debug("#{path}: last value size is greater than current value, starting over")
      @sincedb[inode] = {:size => stat.size, :pos => 0}
    end
  elsif (event == :create || event == :create_initial) && @files[path]
    # TODO(sissel): Allow starting at beginning of the file.
    if @opts[:start_new_files_at] == :beginning
      @logger.debug("#{path}: initial create, no sincedb, seeking to beginning of file")
      @files[path].sysseek(0, IO::SEEK_SET)
      @sincedb[inode] = {:size => stat.size, :pos => 0}
    else
      # seek to end
      @logger.debug("#{path}: initial create, no sincedb, seeking to end #{stat.size}")
      @files[path].sysseek(stat.size, IO::SEEK_SET)
      @sincedb[inode] = {:size => stat.size, :pos => stat.size}
    end
  else
    @logger.debug("#{path}: staying at position 0, no sincedb")
  end

  return true
end
_read_file(path) { |path, data, :log| ... } click to toggle source
# File lib/filewatch/ext/filetail.rb, line 112
def _read_file(path, &block)
  # BufferedTokenizer is now in codec

  changed = false
  loop do
    begin
      data = @files[path].sysread(32768)
      changed = true
      yield(path, data, :log)

      @sincedb[@statcache[path]][:pos] = @files[path].pos
      _check_sincedb(false, path, &block)
    rescue EOFError
      _check_sincedb(true, path, &block) if changed
      _close_file(path) if @opts[:eof_close]
      @logger.debug("End of file reached for #{path}")
      break
    rescue Errno::EWOULDBLOCK, Errno::EINTR
      break
    end
  end
end