我们正在编写一个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 dict中存储一个函数,然后当我们点击一个匹配时,执行该函数。
这是一种做事的Pythonic方法吗?
干杯, 维克多
答案 0 :(得分: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)'),
}
答案 1 :(得分:1)
你认为解析两次吗?例如。第一步提取timestamp
,level
,Application
,Function
,然后解析有效负载?您可能需要进行一些缓存,可能首先构建一个已解析的日志对象列表,然后评估已编译的日志消息(使得更容易跳过前面的3行(就像您提到的那样可能是必要的)而无需解析行两次)< / p>
或者你可以使用字符串连接:
伪代码:
basePattern = "\[\w+\]\[\w+\]\[\w+\]"
foobar_patterns {
'payloadPattern1':'asdf',
'payloadPattern2':'asdff',
}
for patternKey in foobar_patterns:
foobar_patterns[patternKey] = re.compile(basePattern + foobar_patterns[paternKey])
答案 2 :(得分:1)
在构建复杂的正则表达式时,我经常使用“语法”方法。首先,您将“语法”定义为dict,例如:
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
是一个可以处理任何可能的日志行的正则表达式。
答案 3 :(得分:0)
如果存在大量重叠,请考虑使用一次性提取所有字段的正则表达式:
r'(?P<timestamp>\w*) \[(?P<level>\w*)\] \[(?P<application>\w*)\] ...'
与此正则表达式匹配时,生成的匹配对象将具有groupdict()
,您可以从中提取命名组。
答案 4 :(得分: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]'}