Python logging 通用模板

发布时间 2023-06-20 10:27:50作者: katsute_不语
# logger.py

import logging
import socket
import threading
import uuid
from logging.config import dictConfig

local = threading.local()


# 定义一个类,用于实现自定义的过滤器功能
class RequestFilter(logging.Filter):
    def filter(self, record):
        get_req_id()
        request_id = getattr(local, "request_id", None)  # 获取当前线程的请求ID
        ip = getattr(local, "ip", None)  # 获取当前线程的IP

        record.request_id = request_id  # 将请求ID添加到日志记录对象的extra属性中
        record.ip = ip  # 将IP添加到日志记录对象的extra属性中
        return True  # 返回True表示该日志记录可以被处理


def generate_uid():
    uid = str(uuid.uuid4())[-8:]
    th_name = threading.currentThread().name
    _uid = f'{th_name}_{uid}'
    # _uid = str(uuid.uuid4())
    return _uid.replace('-', '_').lower()


def get_req_id():
    if hasattr(local, 'request_id'):
        return local.request_id
    else:
        # local.request_id = str(uuid.uuid4()).replace('-', '_')  # 随机生成一个请求ID,并存储在本地变量中, 替换下划线方便复制
        local.request_id = generate_uid()  # 随机生成一个请求ID,并存储在本地变量中
        local.ip = socket.gethostbyname(socket.gethostname())  # 获取本机IP地址,并存储在本地变量中
        return local.request_id


LOGGING_CONFIG = {
    'version': 1,  # 版本号,必须为1
    'disable_existing_loggers': False,  # 是否禁用已存在的logger对象
    'filters': {  # 过滤器对象,用于对日志记录进行过滤操作
        'request_filter': {  # 名称为request_filter 的过滤器
            '()': RequestFilter  # 使用自定义的RequestFilter 类来创建过滤器对象
        }
    },

    'formatters': {  # 格式化器对象,用于定义日志的格式
        'console_fmt': {  # 名称为info的格式化器
            'format': '[{asctime}] [{request_id}] [{module}.{funcName}:{lineno}] [{levelname:^8s}] {message}',
            'style': '{',
        },
        'info': {  # 名称为info的格式化器
            'format': '[{asctime}] [{ip}] [{request_id}] [{module}.{funcName}:{lineno}] [{levelname:^8s}] [{message}]',
            'style': '{',
        },
        'error': {  # 名称为error的格式化器
            'format': '[{asctime}] [{ip}] [{request_id}] [{module}.{funcName}:{lineno}] [{levelname:^8s}] [{message}]',
            'style': '{',

        }
    },
    'handlers': {  # 处理器对象,用于将日志记录发送到不同的目的地
        'info_rotating_file_handler': {  # 名称为info_rotating_file_handler的处理器,使用RotatingFileHandler类创建
            'level': "INFO",  # 处理INFO及以上级别的日志记录(根据参数level决定)
            'formatter': 'info',  # 使用info格式化器来格式化日志记录
            'class': 'logging.handlers.TimedRotatingFileHandler',  # 使用TimedRotatingFileHandler类来创建处理器对象
            'filename': "flask.log",  # 将日志记录写入path指定的文件中(根据参数path决定)
            'when': 'midnight',  # 在每天凌晨进行日志切割
            'backupCount': 10,  # 保留最近10天的日志备份文件
            'filters': ['request_filter'],  # 使用request_filter过滤器来过滤日志记录
            # 'filemode': 'a'
        },
        'error_file_handler': {  # 名称为error_file_handler的处理器,使用FileHandler类创建
            'level': 'ERROR',  # 处理ERROR及以上级别的日志记录(根据参数level决定)
            'formatter': 'error',  # 使用error格式化器来格式化日志记录
            'class': 'logging.FileHandler',  # 使用FileHandler类来创建处理器对象
            'filename': 'flask.log',  # 将日志记录写入path指定的文件中,并加上.err后缀(根据参数path决定)
            'filters': ['request_filter'],  # 使用request_filter过滤器来过滤日志记录
            # 'mode': 'a'
        },
        "console_handler": {  # 名称为console_handler的处理器,使用StreamHandler类创建
            "level": "DEBUG",  # 处理DEBUG及以上级别的日志记录
            "formatter": "console_fmt",  # 使用info格式化器来格式化日志记录
            "class": "logging.StreamHandler",  # 使用StreamHandler类来创建处理器对象
            "stream": "ext://sys.stdout",  # 将日志记录输出到标准输出流中
            'filters': ['request_filter']  #
        },

        'wsgi': {  # 名称为info_rotating_file_handler的处理器,使用RotatingFileHandler类创建
            'level': "DEBUG",  # 处理INFO及以上级别的日志记录(根据参数level决定)
            'formatter': 'info',  # 使用info格式化器来格式化日志记录
            'class': 'logging.handlers.TimedRotatingFileHandler',  # 使用TimedRotatingFileHandler类来创建处理器对象
            'filename': "wsgi.log",  # 将日志记录写入path指定的文件中(根据参数path决定)
            'when': 'midnight',  # 在每天凌晨进行日志切割
            'backupCount': 10,  # 保留最近10天的日志备份文件
            'filters': ['request_filter'],  # 使用request_filter过滤器来过滤日志记录
            # 'filemode': 'a'
        },
        # 'wsgi': {
        #     'class': 'logging.FileHandler',  # 使用FileHandler类来创建处理器对象
        #     "level": "DEBUG",  # 处理DEBUG及以上级别的日志记录
        #     # 'stream': 'ext://flask.logging.wsgi_errors_stream',
        #     'formatter': 'info',
        #     'filename': 'wsgi.log',
        #     'filters': ['request_filter']  #
        # },
    },
    'loggers': {  # 日志收集器对象,用于管理不同的logger对象
        # "root": {  # 名称为name指定的logger对象(根据参数name决定)
        #     'handlers': ['info_rotating_file_handler', 'error_file_handler', 'console_handler', 'wsgi'],
        #     # 使用两个处理器来处理该logger对象的日志记录
        #     'level': "DEBUG",  # 设置该logger对象的级别(根据参数level决定)
        #     'propagate': False  # 设置该logger对象是否向上级传递,默认为False,表示不传递
        # },
        'sqlalchemy.engine': {  # 支持对sqlalchemy日志收集
            'level': "DEBUG",
            'handlers': ['info_rotating_file_handler', 'error_file_handler', 'console_handler'],
        }
    },
    "root": {  # 名称为name指定的logger对象(根据参数name决定)
        'handlers': ['info_rotating_file_handler', 'error_file_handler', 'console_handler', 'wsgi'],
        # 'handlers': ['info_rotating_file_handler', 'error_file_handler', 'console_handler'],
        # 使用两个处理器来处理该logger对象的日志记录
        'level': "DEBUG",  # 设置该logger对象的级别(根据参数level决定)
        'propagate': False  # 设置该logger对象是否向上级传递,默认为False,表示不传递
    },
}

dictConfig(LOGGING_CONFIG)  # 根据字典配置信息,配置logger对象

log = logging.getLogger()

if __name__ == '__main__':
    def log_test():
        log.info('hello world')
        import time
        def worker(num):
            log.info('Worker %d started' % num)
            time.sleep(1)
            log.info('Worker %d finished' % num)

        for i in range(5):
            t = threading.Thread(target=worker, args=(i,))
            t.start()
        log.info('done')


    log_test()