我们正在编写一个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)'),
}
你考虑过两次解析吗?例如,第一步提取timestamp
、level
、Application
、Function
,然后解析有效载荷?您可能需要进行一些缓存,可能首先构建一个解析的日志对象列表,然后评估解析的日志消息(这样可以更容易地跳过前面的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]'}