2

我们正在编写一个 Python 脚本来解析应用程序日志文件。

大多数日志文件将遵循类似的格式:

09:05:00.342344343 [DEBUG] [SOME_APPLICATION] [SOME_FUNCTION] Lorem ipsum dolor sic amet

我们有各种正则表达式来解析通过的不同类型的日志行,并将相关字段剥离到 Python 正则表达式组(时间戳、日志级别、原始应用程序/函数以及有效负载中的字段)。

我将这些正则表达式中的每一个都存储在一个字典中:

foobar_patterns = {
    'pattern1': re.compile(r'blahblahblah'),
    'pattern2': re.compile(r'blahblahblahblah'),
}

然而,显然每种模式之间存在相当多的重叠——用于提取时间戳、日志级别等的正则表达式是共享的。

有没有办法消除这种冗余?你能以某种方式从一个通用模板构建不同的正则表达式字符串吗?

扩展 - 我遍历文件中的行,然后对于每个给定的行,遍历每个编译的正则表达式。然后基于此,有不同的函数来处理每种情况 - 例如,如果我们检测到某种类型的消息,我们可能需要向前搜索三行以找到另一行,并从中提取一个字段。

我也在考虑在 foobar_patterns 字典中存储一个函数,然后当我们遇到匹配时,执行它。

这是一种 Pythonic 的做事方式吗?

干杯,维克多

4

5 回答 5

2
MONTH = r'(?P<month>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'
DAY = r'(?P<day>\d{2})'
TIME = r'(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})'
SPC = r'\s'
HOST = r'(?<host>\w+)'
PREFIX = SPC.join(MONTH, DAY, TIME, HOST)
foobar_patterns = {
    'pattern1': re.compile(PREFIX + r'\s(?<payload>blahbla hbla h blah)'),
    'pattern2': re.compile(PREFIX + r'\s(?<payload>bla h blahbla hblah)'),
}
于 2012-09-21T07:35:25.077 回答
1

您是否考虑过 Parsing 两次?例如,第一步提取、、、然后timestamp解析有效负载?您可能需要做一些缓存,也许首先构建一个已解析日志对象的列表,然后评估分离的日志消息(这样更容易跳过前面的 3 行(就像您提到的那样可能是必要的),而无需两次解析行)levelApplicationFunction

或者,您可以使用字符串连接:

伪代码:

basePattern = "\[\w+\]\[\w+\]\[\w+\]"
foobar_patterns {
 'payloadPattern1':'asdf',
 'payloadPattern2':'asdff',
}
for patternKey in foobar_patterns:
    foobar_patterns[patternKey] = re.compile(basePattern + foobar_patterns[paternKey])
于 2012-09-21T07:48:50.163 回答
1

在构建复杂的正则表达式时,我经常使用“语法”方法。首先,您将“语法”定义为字典,例如:

logfile_grammar = {
    'spaces':  '\s+',
    'mname':   '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)',
    'month':   r'\d\d',
    'day':     r'\d\d',
    'year':    r'\d{4}',
    'date':    '(?P<date>($year-$month-$day)|($day $spaces $mname $spaces $year))',
    'payload': '(?P<payload>.+)',
    # more stuff
    'line':    '$date $spaces $payload'
}

如您所见,$xxx右侧指的是左侧的键(符号)。然后你把这个语法翻译成一个正则表达式:

def compile_grammar(grammar):
    g = dict(grammar)
    for _ in range(16):
        c = False
        for k, v in g.items():
            w = re.sub(r'\$(\w+)', lambda m: g[m.group(1)], v)
            if w != v:
                g[k] = w
                c = True
        if not c:
            return g
    raise ValueError('too much recursion')

g = compile_grammar(logfile_grammar)    
line_regex = re.compile(g['line'], re.X)

现在,line_regex是一个可以处理任何可能的日志行的正则表达式。

于 2012-09-21T08:45:22.617 回答
0

如果有很多重叠,请考虑使用一次提取所有字段的单个正则表达式:

r'(?P<timestamp>\w*) \[(?P<level>\w*)\] \[(?P<application>\w*)\] ...'

与此正则表达式匹配时,生成的匹配对象将具有一个groupdict()您可以从中提取命名组的对象。

于 2012-09-21T07:24:57.233 回答
0

怎么样:

import re

line = '09:05:00.342344343 [DEBUG] [SOME_APPLICATION] [SOME_FUNCTION] Lorem ipsum dolor sic amet'

ts_pattern = r'\d\d:\d\d:\d\d\.\d+'
name_pattern = r'\[\w+\]'

patterns = [('timestamp', ts_pattern),
            ('log_level', name_pattern),
            ('app_name', name_pattern),
            ('func_name', name_pattern),
            ('payload', '.*$')]

line_pattern = r'\s+'.join('(?P<%s>%s)' % (name, pattern) for name, pattern in patterns)

regex = re.compile(line_pattern)

matches = regex.match(line)

print matches.groupdict()

这会给你:

{'timestamp': '09:05:00.342344343', 'log_level': '[DEBUG]', 'payload': 'Lorem ipsum dolor sic amet', 'func_name': '[SOME_FUNCTION]', 'app_name': '[SOME_APPLICATION]'}
于 2012-09-21T07:39:25.840 回答