Python减少模式中的冗余



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

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

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

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

我已经将这些正则表达式中的每一个存储在dict:中

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

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

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

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

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

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

干杯,Victor

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)'),
}

你考虑过两次解析吗?例如,第一步提取timestamplevelApplicationFunction,然后解析有效载荷?您可能需要进行一些缓存,可能首先构建一个解析的日志对象列表,然后评估解析的日志消息(这样可以更容易地跳过前面的3行(就像您提到的那样,可能是必要的),而无需解析两行)

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

伪码:

basePattern = "[w+][w+][w+]"
foobar_patterns {
'payloadPattern1':'asdf',
'payloadPattern2':'asdff',
}
for patternKey in foobar_patterns:
foobar_patterns[patternKey] = re.compile(basePattern + foobar_patterns[paternKey])

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

logfile_grammar = {
'spaces':  's+',
'mname':   '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)',
'month':   r'dd',
'day':     r'dd',
'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是一个可以处理任何可能的日志行的正则表达式。

如果有很多重叠,可以考虑使用一个同时提取所有字段的正则表达式:

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

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

怎么样:

import re
line = '09:05:00.342344343 [DEBUG] [SOME_APPLICATION] [SOME_FUNCTION] Lorem ipsum dolor sic amet'
ts_pattern = r'dd:dd:dd.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]'}

最新更新