Skip to main content
 首页 » 编程设计

Python 日志记录 : Group logs which belong to one request

2025年05月04日16zlslch

有没有办法将属于一个网络请求的 python 网络应用程序的日志分组?

例子:

2015-02-11 13:06:32 myapp.middleware.MYAPPMiddleware: INFO     Login of user foo was successful 
2015-02-11 13:06:32 myapp.middleware.MYAPPMiddleware: INFO     Login of user bar failed 
2015-02-11 13:06:32 myapp.send_mails: INFO     failed to send mail to someone@example.com 

以上日志行彼此无关。

如何用 Pythonic 方式解决这个问题?

请您参考如下方法:

日志条目在本质上被设计成相互独立的。
将它们连接在一起的正确方法是在条目中包含一些上下文信息,以便稍后查看日志时进行过滤。

这是包含此类信息的 Sharepoint 日志记录示例:

Timestamp               Process             TID     Area                    Category                    EventID Level       Message     Correlation 
02/26/2015 17:49:19.65  w3wp.exe (0x1F40)   0x2358  SharePoint Foundation   Logging Correlation Data    xmnv    Medium      Name=Request (POST:http://reserver2:80/pest/_vti_bin/sitedata.asmx) d1e2b688-e0b2-481e-98ce-497a11acab44 

在 Python logging 文档中,Adding contextual information to your logging output推荐两种方法之一:使用 LoggerAdapter 或 Filter。

LoggerAdapter 是这样使用的(示例基于文档中的示例):

class AddConnIdAdapter(logging.LoggerAdapter): 
    def process(self, msg, kwargs): 
        return <augment_message(msg,arbitrary_info)>, kwargs 
la = AddConnIdAdapter(<logger>,extra=<parameters, saved in self.extra>) 
<...> 
la.info(<message>) 

Filter 是这样使用的:

#Either all messages should have custom fields 
# or the Formatter used should support messages 
# both with and without custom fields 
logging.basicConfig(<...>,format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s') 
class AddClientInfo(logging.Filter): 
    #override __init__ or set attributes to specify parameters 
    def filter(self, record): 
        record.ip = <get_client_ip()> 
        record.user = <get_client_name()> 
        return True    #do not filter out anything 
l=<logger()> 
l.addFilter(AddClientInfo()) #can attach to either loggers or handlers 
<...> 
l.info('message') 

如您所见,区别在于 LoggerAdapter 是非透明的,而 Filter 是透明的。在示例中,前者修改消息文本,后者设置自定义属性(实际编写它们需要使用的 Formatter 配合),但实际上两者都可以。

因此,如果您只需要向某些消息添加上下文,则前者更有用,而后者更适合扩充所有或大部分正在记录的消息。