Python 解析日志之命名元组

  • Post category:Python

Python内置了很多解析日志的库,例如logging和re,但这些库用起来可能比较复杂。而使用命名元组,则是一种相对简单,易于理解的解析日志数据的方法。

什么是命名元组

命名元组是一种特殊的元组,它可以被命名,并且每一个字段也可以被命名。

命名元组的定义方式与元组类似,但需要使用collections模块中的namedtuple函数来进行定义。其语法为:

from collections import namedtuple

class_name = namedtuple('class_name', ['field1', 'field2', ...])

其中,class_name为命名元组的名称,field1、field2等为每个字段的名称。

例如,定义一个简单的Person命名元组如下:

from collections import namedtuple

Person = namedtuple('Person', ['name', 'age'])

这样就定义了一个Person命名元组,包括两个字段:name和age。可以使用Person(name=’John’, age=22)来创建一个Person对象。

使用命名元组解析日志

使用命名元组解析日志,需要先定义一个命名元组,来表示日志中的每一行。接着,对于每一行,我们可以使用字符串的split方法来将其按照空格分割成多个部分,并将其转化为一个命名元组对象。

例如我们有如下的一条日志记录:

2019-12-11 17:41:25 INFO [main] com.example.MyClass: This is a log message.

可以使用如下方式来解析该日志:

from collections import namedtuple

LogEntry = namedtuple('LogEntry', ['datetime', 'level', 'thread', 'logger', 'message'])

log_line = '2019-12-11 17:41:25 INFO [main] com.example.MyClass: This is a log message.'
log_parts = log_line.split()

log_entry = LogEntry(
    datetime=log_parts[0] + ' ' + log_parts[1],  # 将日期和时间组合成一个字符串作为datetime字段
    level=log_parts[2],
    thread=log_parts[3][1:-1],  # 去掉[]以获取thread字段
    logger=log_parts[4][:-1],  # 去掉:以获取logger字段
    message=' '.join(log_parts[5:])  # 将剩余的部分组合成一个字符串作为message字段
)

print(log_entry)

输出结果为:

LogEntry(datetime='2019-12-11 17:41:25', level='INFO', thread='main', logger='com.example.MyClass', message='This is a log message.')

这样,我们就成功地将一条日志转化为了一个命名元组对象。

使用命名元组解析Nginx访问日志

另一个常见的日志类型是Nginx的访问日志。可以使用如下方式来解析一条Nginx访问日志:

from collections import namedtuple

LogEntry = namedtuple('LogEntry', ['remote_addr', 'time_local', 'request', 'status', 'body_bytes_sent', 'http_referer', 'http_user_agent'])

log_line = '192.0.2.1 - - [12/Sep/2021:08:10:23 +0800] "GET /index.html HTTP/1.1" 200 455 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0"'

# 使用正则表达式来匹配日志中的各个部分
match = re.match(r'^(\S+) (\S+) (\S+) \[(.+)\] "(.+)" (\d+) (\d+) "([^"]*)" "([^"]*)"$', log_line)

log_entry = LogEntry(
    remote_addr=match.group(1),
    time_local=match.group(4),
    request=match.group(5),
    status=match.group(6),
    body_bytes_sent=match.group(7),
    http_referer=match.group(8),
    http_user_agent=match.group(9)
)

print(log_entry)

输出结果为:

LogEntry(remote_addr='192.0.2.1', time_local='12/Sep/2021:08:10:23 +0800', request='GET /index.html HTTP/1.1', status='200', body_bytes_sent='455', http_referer='-', http_user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0')

这样,我们就成功地将一条Nginx访问日志转化为了一个命名元组对象。