Watch/read a growing log file Watch/read a growing log file ruby ruby

Watch/read a growing log file


def watch_for(file, pattern)  f = File.open(file,"r")  f.seek(0,IO::SEEK_END)  while true do    select([f])    line = f.gets    puts "Found it! #{line}" if line=~pattern  endendwatch_for("g.txt",/ERROR/)

Thanks for the ezpz's idea, using the select method you get get what you want.The select method is listening the IO's stream, read the bytes what comes 'late'.


You can use Kernel#select in the following way:

def watch_for(file,pattern)   f = File.open(file,"r")   # Since this file exists and is growing, seek to the end of the most recent entry   f.seek(0,IO::SEEK_END)   while true      select([f])      puts "Found it!" if f.gets =~ pattern   endend

Then call it like:

watch_for("some_file", /ERROR/)

I've elided all error checking and such - you will want to have that and probably some mechanism to break out of the loop. But the basic idea is there.


There are two approach:

  • poll the file in an infinite loop (like in Qianjigui's answer, but it is good to put some sleep inside the infinite loop)
  • use OS event subsystem: kqueue on BSD, inotify on Linux

Here is an article I wrote about this: Ruby for Admins: Reading Growing Files. So the program combining both event subsystem and polling looks like this:

def tail_dash_f(filename)  open(filename) do |file|    file.read              case RUBY_PLATFORM   # string with OS name, like "amd64-freebsd8"    when /bsd/, /darwin/      require 'rb-kqueue'      queue = KQueue::Queue.new           queue.watch_file(filename, :extend) do        yield file.read                   end      queue.run                         when /linux/      require 'rb-inotify'      queue = INotify::Notifier.new        queue.watch(filename, :modify) do        yield file.read                   end      queue.run                          else      loop do                   changes = file.read        unless changes.empty?            yield changes        end        sleep 1.0             end    end  endendtail_dash_f ARGV.first do |data|  print data  if data =~ /error/i    # do something else, for example send an email to administrator  endend