【问题标题】:Merging and sorting log files in Python在 Python 中合并和排序日志文件
【发布时间】:2011-10-02 23:03:20
【问题描述】:

我对 python 完全陌生,但我遇到了一个无法解决的严重问题。

我有几个结构相同的日志文件:

[timestamp] [level] [source] message

例如:

[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] error message

我需要用纯 Python 编写一个程序,它将这些日志文件合并到一个文件中,然后按时间戳对合并后的文件进行排序。完成此操作后,我希望将此结果(合并文件的内容)打印到STDOUT(控制台)。

我不明白该怎么做,希望得到帮助。这可能吗?

【问题讨论】:

    标签: python sorting merge timestamp


    【解决方案1】:

    你可以这样做

    import fileinput
    import re
    from time import strptime
    
    f_names = ['1.log', '2.log'] # names of log files
    lines = list(fileinput.input(f_names))
    t_fmt = '%a %b %d %H:%M:%S %Y' # format of time stamps
    t_pat = re.compile(r'\[(.+?)\]') # pattern to extract timestamp
    for l in sorted(lines, key=lambda l: strptime(t_pat.search(l).group(1), t_fmt)):
        print l,
    

    【讨论】:

    • 嗯,很有趣也很有帮助。我想问,如果我想根据[级别]显示合并文件的内容怎么办。例如:我只想显示带有 [level] “error” 的值以及高于此的所有级别。在这种情况下我该怎么办?
    • 虽然从技术上讲这是可行的,但对于大型日志来说,这将是非常浪费的,因为我们可以假设日志本身是经过排序的。 “合并排序”在这里更合适,如果我有时间写它,我会发布。
    【解决方案2】:

    首先,您需要使用fileinput 模块从多个文件中获取数据,例如:

    data = fileinput.FileInput()
    for line in data.readlines():
        print line
    

    然后将所有行打印在一起。您还想排序,您可以使用 sorted 关键字。

    假设您的行以 [2011-07-20 19:20:12] 开头,那么您就是黄金,因为该格式不需要任何超出字母数字的排序,所以这样做:

    data = fileinput.FileInput()
    for line in sorted(data.readlines()):
        print line
    

    但是,您需要做一些更复杂的事情:

    def compareDates(line1, line2):
       # parse the date here into datetime objects
       NotImplemented
       # Then use those for the sorting
       return cmp(parseddate1, parseddate2)
    
    data = fileinput.FileInput()
    for line in sorted(data.readlines(), cmp=compareDates):
        print line
    

    对于奖励积分,你甚至可以这样做

    data = fileinput.FileInput(openhook=fileinput.hook_compressed)
    

    这将使您能够读取压缩后的日志文件。

    那么用法是:

    $ python yourscript.py access.log.1 access.log.*.gz
    

    或类似的。

    【讨论】:

    • 可以将所有日志文件加载到 RAM 中。
    • 感谢您的回答。很有帮助。
    • @FogleBird 如果您可以保证您的数据文件,例如它们是内部排序的,并且您按排序顺序传递它们,则可以删除 cmp。 FileInput 将在不加载到内存的情况下进行迭代。
    • 但是 FileInput 没有 readlines() 方法?是否已弃用?
    【解决方案3】:

    至于关键排序功能:

    def sort_key(line):
        return datetime.strptime(line.split(']')[0], '[%a %b %d %H:%M:%S %Y')
    

    这应该用作sortsortedkey 参数,而不是cmp。这样更快。

    哦,你应该有

    from datetime import datetime
    

    在您的代码中完成这项工作。

    【讨论】:

      【解决方案4】:

      将两个文件的行读入一个列表(它们现在将被合并),提供一个用户定义的比较函数,它将时间戳转换为自纪元以来的秒数,使用用户定义的比较调用排序,将行写入合并文件...

      def compare_func():
          # comparison code
          pass
      
      
      lst = []
      
      for line in open("file_1.log", "r"):
         lst.append(line)
      
      for line in open("file_2.log", "r"):
         lst.append(line)
      
      # create compare function from timestamp to epoch called compare_func
      
      lst.sort(cmp=compare_func)  # this could be a lambda if it is simple enough
      

      应该这样做

      【讨论】:

        【解决方案5】:

        在打印第一行之前,这里的所有其他答案都在所有日志中读取,这可能非常慢,如果日志太大,甚至会破坏东西。

        与上述解决方案一样,此解决方案使用正则表达式和 strptime 格式,但它会“合并”日志。

        这意味着您可以通过管道将输出传递到“head”或“less”,并期望它很活泼。

        import typing
        import time
        from dataclasses import dataclass
        
        
        t_fmt = "%Y%m%d.%H%M%S.%f"      # format of time stamps
        t_pat = re.compile(r"([^ ]+)")  # pattern to extract timestamp
        
        def get_time(line, prev_t):
            # uses the prev time if the time isn't found
            res = t_pat.search(line)
            if not res:
                return prev_t
            try:
                cur = time.strptime(res.group(1), t_fmt)
            except ValueError:
                return prev_t   
            return cur
        
        def print_sorted(files):
            @dataclass
            class FInfo:
                path: str
                fh: typing.TextIO
                cur_l = ""
                cur_t = None
        
                def __read(self):
                    self.cur_l += self.fh.readline()
                    if not self.cur_l:
                        # eof found, set time so file is sorted last
                        self.cur_t = time.localtime(time.time() + 86400)
                    else:
                        self.cur_t = get_time(self.cur_l, self.cur_t)
        
                def read(self):
                    # clear out the current line, and read
                    self.cur_l = ""
                    self.__read()
                    while self.cur_t is None:
                        self.__read()
        
            finfos = []
            for f in files:
                try:
                    fh = open(f, "r")
                except FileNotFoundError:
                    continue
                fi = FInfo(f, fh)
                fi.read()
                finfos.append(fi)
        
            while True:
                # get file with first log entry
                fi = sorted(finfos, key=lambda x: x.cur_t)[0]
                if not fi.cur_l:
                    break
                print(fi.cur_l, end="")
                fi.read()
        
        

        【讨论】:

          猜你喜欢
          • 2016-06-24
          • 1970-01-01
          • 2015-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-18
          • 1970-01-01
          • 2019-03-25
          相关资源
          最近更新 更多