修剪大日志文件

Nik*_*lub 7 python bash performance logging sed

我为一些Java应用程序执行性能测试.应用程序在测试期间会生成非常大的日志文件(可能是7-10 GB).我需要在特定日期和时间之间修剪这些日志文件.目前,我使用python脚本,它解析datetime python对象中的日志时间戳并仅打印匹配的字符串.但这个解决方案非常缓慢.解析5 GB日志大约25分钟显然日志文件中的条目是顺序的,我不需要逐行读取所有文件.我考虑从开始和结束读取文件,直到条件匹配并在匹配的行数之间打印文件.但我不知道如何从后面读取文件,而不将其下载到内存中.

拜托,您能为我推荐一些针对此案例的套装解决方案.

这是python脚本的一部分:

      lfmt = '%Y-%m-%d %H:%M:%S'
      file = open(filename, 'rU')
      normal_line = ''
      for line in file:
        if line[0] == '[':
          ltimestamp = datetime.strptime(line[1:20], lfmt)

          if ltimestamp >= str and ltimestamp <= end:
            normal_line = 'True'
        else:
          normal_line = ''

      if normal_line:
        print line,
Run Code Online (Sandbox Code Playgroud)

fra*_*xel 5

如果感兴趣区域的开始和结束接近文件的开头,则数据是顺序的,那么从文件末尾读取(以找到匹配的终点)仍然是一个糟糕的解决方案!

我写了一些代码,可以根据需要快速找到起点和终点,这种方法称为二分搜索,类似于clasic儿童的"更高或更低"的猜谜游戏!

该脚本在中间lower_boundsupper_bounds(最初是SOF和EOF)之间读取试验线,并检查匹配标准.如果所寻找的行更早,则通过读取lower_bound前一次读取试验和中一次读取试验之间的中间线来再次猜测(如果它更高,则它在其猜测和上限之间分开).因此,您继续在上限和下限之间进行迭代 - 这会产生尽可能快的"平均"解决方案.

这应该是一个真正的快速解决方案(登录到行数的基数2).例如,在最糟糕的情况下(在1000行中找到999行),使用二进制搜索只需要9行读取!(十亿行只需30 ...)

以下代码的假设:

  • 每一行都以时间信息开头.
  • 时间是唯一的 - 如果没有,当找到匹配时,您将必须向后或向前检查以包括或排除具有匹配时间的所有条目(如果需要).
  • 有趣的是,这是一个递归函数,因此文件的行数限制为2**1000(幸运的是,这允许相当大的文件...)

进一步:

  • 如果愿意,这可以适用于以任意块读取,而不是按行读取.正如JF Sebastian所说.
  • 在我的原始答案中我提出了这种方法,但使用了linecache.getline,虽然这可能不适合大文件,因为它将整个文件读入内存(因此file.seek()是优越的),感谢TerryE和JF Sebastian指出这一点.

导入日期时间

def match(line):
    lfmt = '%Y-%m-%d %H:%M:%S'
    if line[0] == '[':
        return datetime.datetime.strptime(line[1:20], lfmt)

def retrieve_test_line(position):
    file.seek(position,0)
    file.readline()  # avoids reading partial line, which will mess up match attempt
    new_position = file.tell() # gets start of line position
    return file.readline(), new_position

def check_lower_bound(position):
    file.seek(position,0)
    new_position = file.tell() # gets start of line position
    return file.readline(), new_position

def find_line(target, lower_bound, upper_bound):
    trial = int((lower_bound + upper_bound) /2)
    inspection_text, position = retrieve_test_line(trial)
    if position == upper_bound:
        text, position = check_lower_bound(lower_bound)
        if match(text) == target:
            return position
        return # no match for target within range
    matched_position = match(inspection_text)
    if matched_position == target:
        return position
    elif matched_position < target:
        return find_line(target, position, upper_bound)
    elif matched_position > target:
        return find_line(target, lower_bound, position)
    else:
        return # no match for target within range

lfmt = '%Y-%m-%d %H:%M:%S'
# start_target =  # first line you are trying to find:
start_target =  datetime.datetime.strptime("2012-02-01 13:10:00", lfmt)
# end_target =  # last line you are trying to find:
end_target =  datetime.datetime.strptime("2012-02-01 13:39:00", lfmt)
file = open("log_file.txt","r")
lower_bound = 0
file.seek(0,2) # find upper bound
upper_bound = file.tell()

sequence_start = find_line(start_target, lower_bound, upper_bound)

if sequence_start or sequence_start == 0: #allow for starting at zero - corner case
    sequence_end = find_line(end_target, sequence_start, upper_bound)
    if not sequence_end:
        print "start_target match: ", sequence_start
        print "end match is not present in the current file"
else:
    print "start match is not present in the current file"

if (sequence_start or sequence_start == 0) and sequence_end:
    print "start_target match: ", sequence_start
    print "end_target match: ", sequence_end
    print
    print start_target, 'target'
    file.seek(sequence_start,0)
    print file.readline()
    print end_target, 'target'
    file.seek(sequence_end,0)
    print file.readline()
Run Code Online (Sandbox Code Playgroud)