一:需求
- 需求:将自定义的参数,放在日志的指定格式中。
二:实现方式
- 例如:让debug, info,warning,error函数都允许传递一个trace_id, 并将这个trace_id输出到我们自定义日志指定的格式中。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2023/10/19 10:08 # @Author : shanwen.ren import logging import inspect import logging.config self_logger = logging.getLogger("debug") class Logger(logging.Logger): @staticmethod def check_trace_id(kwargs): """kwargs mush has trace_id and trace_id value type mush is str.""" trace_id = str(kwargs.get("trace_id", "")) if "extra" not in kwargs: extra = {"trace_id": trace_id} else: assert isinstance(kwargs["extra"], dict) extra = kwargs["extra"] extra.update({"trace_id": trace_id}) if "trace_id" in kwargs.keys(): kwargs.pop("trace_id") return extra, kwargs def debug(self, msg, *args, **kwargs): extra, kwargs = self.check_trace_id(kwargs) self_logger.debug(msg, *args, extra=extra, **kwargs) def info(self, msg, *args, **kwargs): extra, kwargs = self.check_trace_id(kwargs) self_logger.info(msg, *args, extra=extra, **kwargs) def warning(self, msg, *args, **kwargs): extra, kwargs = self.check_trace_id(kwargs) self_logger.warning(msg, *args, extra=extra, **kwargs) def error(self, msg, *args, **kwargs): extra, kwargs = self.check_trace_id(kwargs) self_logger.error(msg, *args, extra=extra, **kwargs) logger = Logger() def init_log(log_path, log_name, log_level="DEBUG"): log_level = log_level.upper() LOG_PATH_DEBUG = "%s/%s.log" % (log_path, log_name) LOG_FILE_BACKUP_COUNT = 10 log_conf = { "version": 1, "formatters": { "format1": { "format": '|%(asctime)s.%(msecs)03d|%(levelname)s|%(trace_id)s|%(thread)d|%(filename)s %(lineno)d|%(message)s', "datefmt": '%Y-%m-%d %H:%M:%S', }, }, "handlers": { "handler": { "class": "logging.handlers.TimedRotatingFileHandler", "level": log_level, "formatter": "format1", "when": "midnight", "backupCount": LOG_FILE_BACKUP_COUNT, "filename": LOG_PATH_DEBUG }, }, "loggers": { "debug": { "handlers": ["handler"], "level": log_level }, } } logging.config.dictConfig(log_conf) def close_log(): logging.shutdown()
三:存在的问题
3.1: 问题描述
- 上面代码中由于本质是使用的self_logger对象, 因此filename和lineno都会定位到我们自定义的Logger对象的debug, info,warning,error方法中, 无法真正的定位到真实代码所在地。
3.2: 源码分析
- 调用warning后会调用_log方法。
- findCaller会去取调用位置。
- 底层使用f_back获取上层调用, 返回上层调用的行号lno等信息。
- makeRecord最终作用于日志格式中。
3.3: 解决方案
- 基于源码分析, 发现底层用的inspect.currentframe().f_back,返回的上层调用者, 因此产生一个思路:废弃日志格式中的文件名和行号, 改成自己传递N-任意层调用的位置和行号。
- 代码调整:
- 改为自定义名称原因:
四:相关链接
- 官方文档:https://docs.python.org/zh-cn/3/library/logging.html