观看/阅读不断增长的日志文件
Posted
技术标签:
【中文标题】观看/阅读不断增长的日志文件【英文标题】:Watch/read a growing log file 【发布时间】:2010-11-20 14:10:52 【问题描述】:我有一个不断增长的日志文件。如何通过 Ruby 脚本查看和解析它?
脚本将在写入文件时解析每个新行,并在新行包含字符串 'ERROR' 时将内容输出到屏幕
【问题讨论】:
【参考方案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】:穷人的快捷方式:
一个 Ruby 脚本
ARGF.each do |line|
...
运行屏幕:
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(可能直到错误或中断)。我还重新检查了代码,看起来 loop
和 select
也不需要以上是关于观看/阅读不断增长的日志文件的主要内容,如果未能解决你的问题,请参考以下文章