6

我正在编写一个简单的日志嗅探器,它将搜索日志以查找表明我支持的软件存在问题的特定错误。它允许用户指定日志的路径并指定他们想要搜索多少天。

如果用户关闭了日志翻转,日志文件有时会变得非常大。目前我正在做以下事情(虽然还没有完成):

File.open(@log_file, "r") do |file_handle|
    file_handle.each do |line|
        if line.match(/\d+++-\d+-\d+/)
          etc...

line.match 显然会查找我们在日志中使用的日期格式,其余的逻辑将在下面。但是,有没有更好的方法来搜索没有 .each_line 的文件?如果没有,我完全可以接受。我只是想确保我使用的是可用的最佳资源。

谢谢

4

6 回答 6

6
  • fgrep作为独立或调用system('fgrep ...')可能是更快的解决方案
  • file.readlines速度可能会更好,但这是时空权衡
  • 看看这个小研究——最后一种方法似乎相当快。
于 2013-05-06T13:55:11.890 回答
4

如果您的日志文件按日期排序,那么您可以通过执行二进制搜索来避免搜索整个文件。在这种情况下,你会:

  1. 像你一样打开文件
  2. 使用lineo=快进到文件的中间。
  3. 检查行首的日期是否高于或低于您要查找的日期。
  4. 继续将文件分成两半,直到找到所需的内容。

但是,我确实认为您的文件需要非常大才能使上述内容有意义。

编辑

这是一些显示基本思想的代码。它找到包含搜索日期的行,而不是第一行。这可以通过更多的二分搜索或从最后一个不包含日期的中点进行线性搜索来解决。如果日期不在文件中,也没有终止条件。这些小的补充,留给读者作为练习:-)

require 'date'

def bin_fsearch(search_date, file)
  f = File.open file

  search = {min: 0, max: f.size}

  while true
    # go to file midpoint
    f.seek (search[:max] + search[:min]) / 2

    # read in until EOL
    f.gets

    # record the actual mid-point we are using
    pos = f.pos

    # read in next line
    line = f.gets

    # get date from line
    line_date = Date.parse(line)

    if line_date < search_date
      search[:min] = f.pos
    elsif line_date > search_date
      search[:max] = pos
    else
      f.seek pos
      return
    end
  end
end

bin_fsearch(Date.new(2013, 5, 4), '/var/log/system.log')
于 2013-05-06T13:56:52.353 回答
4

这里有一些编码提示...

代替:

File.open(@log_file, "r") do |file_handle|
  file_handle.each do |line|

利用:

File.foreach(@log_file) do |line|
  next unless line[/\A\d+++-\d+-\d+/]

foreach简化文件的打开和循环。

next unless...使一个紧密的循环跳过不以目标字符串开头的每一行。在弄清楚你是否有一条好线之前你做的越少,你的代码运行得越快。

在模式的开头使用锚点,就像\A给正则表达式引擎一个关于在哪里查看行的主要提示,并允许它在行不匹配时非常快速地退出。此外,使用line[/\A\d+++-\d+-\d+/]更简洁。

于 2013-05-06T15:35:51.627 回答
1

试试这个,它会一次搜索一次并且应该很快并且占用更少的内存。

File.open(file, 'r') do |f|
  f.each_line do |line|
    # do stuff here to line
  end
end

另一种更快的选择是将整个文件读入一个数组。它会很快,但会占用大量内存。

File.readlines.each do |line|
  #do stuff with each line
end

此外,如果您需要内存最少的最快方法,请尝试grep专门为搜索大文件而调整的方法。所以应该是快速和记忆响应

`grep -e regex bigfile`.split(/\n/).each do |line|
  # ... (called on each matching line) ...
end
于 2013-05-06T13:52:25.740 回答
0

如果日志文件可能会变得很大,而这是您所关心的,那么也许您可以考虑将错误保存在数据库中。然后,您将获得更快的响应。

于 2013-05-06T14:16:46.307 回答
0

逐行读取比逐行更快:

File.open('file.txt') do |f|
  buff = f.read(10240)
  # ...
end

但是您使用正则表达式来匹配日期,您可能会得到不完整的行。你将不得不在你的逻辑中处理它。

此外,如果性能如此重要,请考虑编写一个非常简单的 C 扩展。

于 2013-05-06T14:03:22.377 回答