上下文信息是在LoggerAdapter类的process()方法中被添加到日志记录的输出消息中的,如果要实现自定义需求只需要实现LoggerAdapter的子类并重写process()方法即可;
process()方法的默认实现中,没有修改msg的值,只是为关键词参数插入了一个名为extra的 key,这个extra的值为传递给LoggerAdapter类构造方法的参数值;
LoggerAdapter类构建方法所接收的extra参数,实际上就是为了满足logger的formatter格式要求所提供的默认上下文信息。
关于上面提到的第3个结论,我们来看个例子:
import logging import sys # 初始化一个要传递给LoggerAdapter构造方法的logger实例 fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s") h_console = logging.StreamHandler(sys.stdout) h_console.setFormatter(fmt) init_logger = logging.getLogger("myPro") init_logger.setLevel(logging.DEBUG) init_logger.addHandler(h_console) # 初始化一个要传递给LoggerAdapter构造方法的上下文字典对象 extra_dict = {"ip": "IP", "username": "USERNAME"} # 获取一个LoggerAdapter类的实例 logger = logging.LoggerAdapter(init_logger, extra_dict) # 应用中的日志记录方法调用 logger.info("User Login!") logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"}) logger.extra = {"ip": "113.208.78.29", "username": "Petter"} logger.info("User Login!") logger.info("User Login!")输出结果:
# 使用extra默认值:{"ip": "IP", "username": "USERNAME"} 2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login! # info(msg, extra)方法中传递的extra方法没有覆盖默认值 2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login! # extra默认值被修改了 2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login! 2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login!根据上面的程序输出结果,我们会发现一个问题:传递给LoggerAdapter类构造方法的extra参数值不能被LoggerAdapter实例的日志记录函数(如上面调用的info()方法)中的extra参数覆盖,只能通过修改LoggerAdapter实例的extra属性来修改默认值(如上面使用的logger.extra=xxx),但是这也就意味着默认值被修改了。
解决这个问题的思路应该是:实现一个LoggerAdapter的子类,重写process()方法。其中对于kwargs参数的操作应该是先判断其本身是否包含extra关键字,如果包含则不使用默认值进行替换;如果kwargs参数中不包含extra关键字则取默认值。来看具体实现:
import logging import sys class MyLoggerAdapter(logging.LoggerAdapter): def process(self, msg, kwargs): if 'extra' not in kwargs: kwargs["extra"] = self.extra return msg, kwargs if __name__ == '__main__': # 初始化一个要传递给LoggerAdapter构造方法的logger实例 fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s") h_console = logging.StreamHandler(sys.stdout) h_console.setFormatter(fmt) init_logger = logging.getLogger("myPro") init_logger.setLevel(logging.DEBUG) init_logger.addHandler(h_console) # 初始化一个要传递给LoggerAdapter构造方法的上下文字典对象 extra_dict = {"ip": "IP", "username": "USERNAME"} # 获取一个自定义LoggerAdapter类的实例 logger = MyLoggerAdapter(init_logger, extra_dict) # 应用中的日志记录方法调用 logger.info("User Login!") logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"}) logger.info("User Login!") logger.info("User Login!")