python logging 添加上下文信息

官方文档中有两种方式,适配器(Adapter)和过滤器(filter)。具体地址在文末。

第一种,使用适配器。

使用LoggerAdapter 类,重写它的process方法,将上下文信息添加到日志的输出中。我理解就是,专为logger自定义输出日志而写的类,接收logger和extra。在process方法里,按照自己的想法把message信息格式化。并返回message和kw。

 博客1的例子非常详细,同时也指出了当时我按照例子写后发现不妥的地方。就是loggerAdapter已经不是原来的logger,尽管同样可以error,info。但是在不同的地方去获取logger时,取到的还是原来的。这和我预想的不一致,如果原来的代码已经大量使用,这时就不适合在改动了。

from logging import LoggerAdapter
import logging


logger = logging.getLogger(__name__)


class SesssionLoggerAdapter(LoggerAdapter):

    def process(self, msg, kwargs):
        if 'session' not in self.extra or self.extra['session'] is None:
            return msg, kwargs
        session = self.extra['session']
        if hasattr(session, 'request_id'):
            msg += ', request_id({})'.format(session.request_id)
        if 'extra' not in kwargs:
            kwargs["extra"] = self.extra
        else:
            kwargs['extra'].update(self.extra)
        return super().process(msg, kwargs)


class Session():
    def __init__(self):
        super().__init__()

    @property
    def request_id(self):
        return self._request_id

    @request_id.setter
    def request_id(self, request_id):
        self._request_id = request_id


session = Session()
logger.warning('abc')
# 这里尽管覆盖了原logger,但是只在这里覆盖,其它地方获取logger = logging.getLogger(__name__)结果并没有用。
logger = SesssionLoggerAdapter(logger, {'session': session})
session.request_id = '0'
logger.warning('hello world')
session.request_id = '1'
logger.warning('hello world')

https://segmentfault.com/a/1190000022506342

第二种,就是使用过滤器。

按道理,看名字,过滤器不应该修改message内容信息的,只能说,用filter过滤器这个词不太合适。还是看官方文档。继承logging.Filter类,重写filter方法,然后给logger添加自己重写的过滤器即可。在重写的过滤器里重新组织message。不过官方文档给的例子,写的太复杂了,最后一行,我还以为必须要每个log时要手动改。

import logging
from random import choice

class ContextFilter(logging.Filter):
    """
    This is a filter which injects contextual information into the log.

    Rather than use actual contextual information, we just use random
    data in this demo.
    """

    USERS = ['jim', 'fred', 'sheila']
    IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']

    def filter(self, record):

        record.ip = choice(ContextFilter.IPS)
        record.user = choice(ContextFilter.USERS)
        return True

if __name__ == '__main__':
    levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s')
    a1 = logging.getLogger('a.b.c')
    a2 = logging.getLogger('d.e.f')

    f = ContextFilter()
    a1.addFilter(f)
    a2.addFilter(f)
    a1.debug('A debug message')
    a1.info('An info message with %s', 'some parameters')
    for x in range(10):
        lvl = choice(levels)
        lvlname = logging.getLevelName(lvl)
        a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')

官方文档下filter下的例子就很简单,就是在filter里重新组织message,最后返回true就行。这里就和上面的适配器不一样了,这里修改的就是原本的logger,这样,原本的代码就不用动。官网是使用dict来配置logger的,我们不管怎么配置,拿到logger后添加filter即可,如下直接添加filter的代码片段。

import logging
import logging.config
import sys

class MyFilter(logging.Filter):
    def __init__(self, param=None):
        self.param = param

    def filter(self, record):
        if self.param is None:
            allow = True
        else:
            allow = self.param not in record.msg
        if allow:
            record.msg = 'changed: ' + record.msg
        return allow

LOGGING = {
    'version': 1,
    'filters': {
        'myfilter': {
            '()': MyFilter,
            'param': 'noshow',
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'filters': ['myfilter']
        }
    },
    'root': {
        'level': 'DEBUG',
        'handlers': ['console']
    },
}

if __name__ == '__main__':
    logging.config.dictConfig(LOGGING)
    logging.debug('hello')
    logging.debug('hello - noshow')

直接添加filter的代码片段:

import logging

logger = logging.getLogger("arview")


class MyFilter(logging.Filter):
    def __init__(self, param=None):
        self.param = param

    def filter(self, record):
        if self.param is None:
            allow = True
        else:
            allow = self.param not in record.msg
        if allow:
            request_id = "-"
            if hasattr(request, "requestId"):
                request_id = getattr(request, "requestId")
            record.msg = ',request_id({}),'.format(request_id) + record.msg
        return allow

user_id_filter = MyFilter()
logger.addFilter(user_id_filter)

当然还有一种方法,在每次log时传入extra,但是大部分情况下,不适合这种方式,还是希望每次log时自动加入上下文信息。具体可见博客2。

参考资料:

官方文档:

日志操作手册 — Python 3.7.13 文档

博客1:

https://segmentfault.com/a/1190000022506342

博客2:

Python 日志输出中添加上下文信息 - 月缺一格 - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值