【问题标题】:Watch/read a growing log file观看/阅读不断增长的日志文件
【发布时间】:2010-11-20 14:10:52
【问题描述】:

我有一个不断增长的日志文件。如何通过 Ruby 脚本查看和解析它?

脚本将在写入文件时解析每个新行,并在新行包含字符串 'ERROR' 时将内容输出到屏幕

【问题讨论】:

    标签: ruby logging io


    【解决方案1】:
    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
      end
    end
    
    watch_for("g.txt",/ERROR/)
    

    感谢ezpz的想法,使用select方法你得到你想要的。 select 方法正在监听 IO 的流,读取“迟到”的字节。

    【讨论】:

    • 注意:select 在文件流上使用时总是立即返回:您始终可以从该文件流中读取 EOF,因此您的 ruby​​ 进程在等待文件更新时最终会旋转。不同的操作系统倾向于提供不同的文件等待工具,Linux 有 inotify,OS X 有 fsevents - 也有方便的 ruby​​ gem 封装它们。
    • 我认为它使用 100% CPU 的原因是因为如果你删除了 if line=~pattern 的行,即使没有新的更新,它也只会返回空行。我不确定如何自己解决,因为我也偶然发现了这个问题。
    • 这是 INCORRECT 实现,此处的选择不会阻塞。它之所以有效,是因为您读取了比较失败的 EOF。
    【解决方案2】:

    您可以通过以下方式使用Kernel#select

    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
       end
    end
    

    然后这样称呼它:

    watch_for("some_file", /ERROR/)
    

    我已经省略了所有的错误检查等等——你会希望拥有它,并且可能需要一些机制来打破循环。但是基本的想法就在那里。

    【讨论】:

    • 此解决方案使用 100% CPU,因为在文件中,您的 select 将在文件末尾立即返回 true,因为 read() 将读取 EOF(感谢 @antifuchs)。跨度>
    【解决方案3】:

    有两种方法:

    • 在无限循环中轮询文件(就像在千机贵的回答中一样,但最好在无限循环中放入一些sleep
    • 使用操作系统事件子系统:BSD 上的 kqueue,Linux 上的 inotify

    这是我写的一篇文章:Ruby for Admins: Reading Growing Files。所以结合事件子系统和轮询的程序如下所示:

    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
      end
    end
    
    tail_dash_f ARGV.first do |data|
      print data
      if data =~ /error/i
        # do something else, for example send an email to administrator
      end
    end
    

    【讨论】:

    • 这太棒了。对于不知道如何获取那些可以执行“gem install rb-inotify”(例如)然后在脚本顶部添加“require 'rubygems'”的人来说,应该注意它是不在那里(而且他们没有运行轨道)
    【解决方案4】:

    如果您使用的是 Linux...

    tail -f log/development.log | grep "ERROR"
    

    除非您出于某种原因真的希望它是一个 Ruby 脚本。

    【讨论】:

    • 将其包装在脚本中使您能够以比注意到错误发生更有意义的方式对错误做出反应。当您想要进行后处理时,grepping 很好,但它调用动态行为的能力相当有限。
    • "...当新行包含字符串'ERROR'时向屏幕输出一些东西"不是动态行为:)
    • 这对我来说效果最好。围绕错误日志进行元编程似乎更好地花在了其他地方。
    【解决方案5】:

    查看file-tailgem

    【讨论】:

      【解决方案6】:

      穷人的快捷方式:

      1. 一个 Ruby 脚本

        ARGF.each do |line|
          ...
        
      2. 运行屏幕:

        tail -f file | ruby script 
        

      【讨论】:

        【解决方案7】:

        处理@Qianjigui 的想法,但不使用 100% CPU:

        def watch_for(file, pattern)
          # Replace -n0 with -n+1 if you want to read from the beginning of file
          f = IO.popen(%W[tail -f -n0 #{file}])
          while line = f.gets
            puts "Found it! #{line}" if line =~ pattern
          end
        end
        
        watch_for('g.txt', /ERROR/)
        

        【讨论】:

        • 此解决方案使用 100% CPU,因为在文件中,您的 select 将在文件末尾立即返回 true,因为 read() 将读取 EOF(感谢 @antifuchs)。跨度>
        • @hagello 这就是为什么我直接从tail 管道而不是文件读取,所以tail -f 负责等待并且不会返回EOF(可能直到错误或中断)。我还重新检查了代码,看起来 loopselect 也不需要
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-05
        • 1970-01-01
        • 2013-09-27
        • 2018-11-02
        • 1970-01-01
        • 2012-04-12
        • 1970-01-01
        相关资源
        最近更新 更多