Logging
Logger
当在import logging的时候就创建了全局的单例logging.root,之后通过logging.getlogger()获得的所有logger都是root的子logger。logger的层级也是用name字段来区分的。 logging模块创建的所有的logger对象全部是全局单例的,通过name字段区分,所以说完全不需要logger的传递,在所有的模块直接gerlogger你想要的logger就行了,包括logging对象本身就是root logger,随便在哪import了就直接用,包括配置,你可以在刚开始就把后面可能会用到的logger给get出来然后先配置好,然后在别的模块直接调用就行了。这个设计有个问题,就是你要接管别的库的logger,必须要知道他们内置的logger的name,只能去文档查了,大部分库都会有logging板块的,比如uvicorn,这个设计很奇葩,我最开始用这个感觉也太奇葩了点,所以说可以用更现代化的loguru。
Handler
控制输出位置的,输出到哪里都行,用docker的话还是统一放到stdout就行了。
Formatter
可以把logrecord的字段通过格式字符串(有点像c的那种),输出成你想要的字符串。
Filter
可以在这里对进入这个过滤器的LogRecord进行逻辑处理,包括增加字段,检查字段过滤等等。
LogRecord
logger.info(msg)函数发射的日志记录本体,他会通过一个个logger向上传递,里面有着很多字段,具体看这个表,通过这个,再在formatter可以控制输出格式和字段。
架构网络
logger和handler是多对多的,并且一个logger可以有多个handler。handler和filter也是多对多的,并且一个handler可以连上多个filter顺序执行,filter和logger也是多对多的。filter在handler上和在logger上的处理的LogRecord作用域不同。handler和formatter是多对一的。
工程实践
配置
工程上我们会在项目启动的刚开始载入下面这个配置的字典来,配置日志系统,通过logging.config.dictConfig(dict)来加载这个字典
class TraceIDFilter(logging.Filter): def filter(self, record): record.trace_id = trace_id_var.get() return TrueLOGGING_CONFIG = { "version": 1, "disable_existing_loggers": False, "formatters": { "standard_format": { "format": "%(levelname)-8s | %(asctime)s | %(name)s | %(trace_id)s | %(message)s", }, "systemd_format": { "format": "%(levelname)-8s | %(asctime)s | %(name)s | %(message)s", }, }, "filters": { "trace_filter": {"()": "app.core.logging_config.TraceIDFilter"},#这个过滤器实际上就是让所有的LogRecord经过TraceIDFilter这个函数统一处理加上trace_id这个字段 }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "standard_format", "filters": ["trace_filter"], "stream": "ext://sys.stdout", }, "uvicorn_output": { "class": "logging.StreamHandler", "formatter": "systemd_format", "stream": "ext://sys.stdout", }, }, "loggers": { "": { #""表示根logger "handlers": ["console"], "level": "INFO", }, "uvicorn.access": { "handlers": [], #覆盖uvicorn原生的logging,让他统一传到uvicorn节点输出 "propagate": True, }, "uvicorn.error": { "handlers": [], #覆盖uvicorn原生的logging,让他统一传到uvicorn节点输出 "propagate": True, }, "uvicorn": { "handlers": ["uvicorn_output"], # 统一输出,并不传到root节点 "propagate": False, }, },}