- 不改动任何代码,还是使用logging的接口logging.info(),没有loguru包一样起飞
loguru简介
懒的写了,自己看吧
https://loguru.readthedocs.io/en/stable/resources/migration.h...
代码原理
根据文档构建一个logging.handler代理,所有的logging日志,这个handler下,其实是loguru在emit日志,这样通过一些简单的配置就能实现上层接口不变的情况下,使用不同的日志管理器,除了loguru、nb_log你甚至可以自己做一个,替换进去就可以。
代码
settings
os.makedirs(os.path.join(BASE_DIR, "logs"), mode=0o775, exist_ok=True) LOG_CLASS = "utils.middleware.log.LoguruBaseRotatingHandler" LOGFILTER = "utils.middleware.log.LevelFilter" # log file size max_bytes = 1024_000 # log file count backup_count = 10 LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "generic": { "format": "[%(process)d] [%(thread)d] [%(asctime)s] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s", # 打日志的格式 "datefmt": "%Y-%m-%d %H:%M:%S %z", # 时间显示方法 # "class": "logging.Formatter" }, }, "filters": { "error_filter": {"()": LOGFILTER, "level": logging.ERROR}, "warn_filter": {"()": LOGFILTER, "level": logging.WARN}, "info_filter": {"()": LOGFILTER, "level": logging.INFO}, "debug_filter": {"()": LOGFILTER, "level": logging.DEBUG}, }, "handlers": { "error": { "level": "ERROR", "class": LOG_CLASS, "formatter": "generic", "filters": ["error_filter"], "filename": os.path.join(BASE_DIR, "logs/error.log"), "maxBytes": max_bytes, "backupCount": backup_count, }, "warn": { "level": "WARNING", "class": LOG_CLASS, "formatter": "generic", "filters": ["warn_filter"], "filename": os.path.join(BASE_DIR, "logs/warn.log"), "maxBytes": max_bytes, "backupCount": backup_count, }, "info": { "level": "INFO", "class": LOG_CLASS, "formatter": "generic", "filters": ["info_filter"], "filename": os.path.join(BASE_DIR, "logs/info.log"), "maxBytes": max_bytes, "backupCount": backup_count, }, "debug": { "level": "DEBUG", "class": LOG_CLASS, "formatter": "generic", "filters": ["debug_filter"], "filename": os.path.join(BASE_DIR, "logs/debug.log"), "maxBytes": max_bytes, "backupCount": backup_count, }, # 控制台输出 "console": {"level": "DEBUG", "class": "logging.StreamHandler", "formatter": "generic"}, }, "loggers": { "gunicorn.error": { "level": "ERROR", "handlers": ["error"], "propagate": True, "qualname": "gunicorn.error", }, "gunicorn.access": { "level": "DEBUG", "handlers": ["debug"], "propagate": False, "qualname": "gunicorn.access", }, "django": {"handlers": ["error", "warn", "info", "debug"], "level": "INFO", "propagate": True}, "uvicorn": { "handlers": ["warn", "info", "debug", "error"], "propagate": True, "level": "INFO", }, "uvicorn.error": {"level": "ERROR", "handlers": ["error"], "propagate": True}, "uvicorn.access": {"handlers": ["info"], "level": "INFO", "propagate": False}, "celery.task": {"handlers": ["error", "warn", "info", "debug"], "level": "DEBUG", "propagate": True}, APPLICATION: { "handlers": ["info", "error", "warn", "debug"], "level": "DEBUG", "propagate": True, }, }, } LOGGING_CONFIG = "utils.middleware.log.simple_log_injector"
log.py
import copy import importlib import logging import os from logging.handlers import RotatingFileHandler from typing import Dict, List from django.conf import settings from loguru import logger class LoadLoggingConfig(object): def __init__(self): self.config: Dict = copy.deepcopy(settings.LOGGING) @property def get_formatters(self) -> Dict: return self.config.get("formatters") @property def get_handlers(self) -> Dict[str, Dict]: hanlders = self.config.get("handlers") for _, handler in hanlders.items(): handler["formatter"] = self.get_formatters[handler["formatter"]] filters_key = handler.get("filters", None) if filters_key is not None: handler["filters"] = self.get_filters(filters_key) return hanlders def get_filters(self, filter_: List[str]) -> List[Dict]: filters = self.config.get("filters") return [filters.get(item) for item in filter_ if filters.get(item, None) is not None] class LevelFilter(logging.Filter): def __init__(self, level): logging.Filter.__init__(self) self.level = level def filter(self, record): return record.levelno == self.level class LoguruBaseRotatingHandler(logging.Handler): def __init__( self, filename, mode="a", maxBytes=0, backupCount=0, encoding="utf-8", delay=False, errors=None, ): logging.Handler.__init__(self) filename = os.path.abspath(filename) filename_suffix = os.path.basename(filename) level, _ = os.path.splitext(filename_suffix) self._logger = logger.bind(filename=filename_suffix) # 所有已经add的handler handler_names = [_._name for _, _ in self._logger._core.handlers.items()] _load_config = LoadLoggingConfig() for k, handlers in _load_config.get_handlers.items(): if k == level: if filename == handlers.get("filename", None): file_handler = RotatingFileHandler(filename, mode, maxBytes, backupCount, encoding, delay, errors) handler_repr = repr(file_handler) if handler_repr not in handler_names: _formatter = handlers["formatter"] formatter_class = logging.Formatter( _formatter["format"], datefmt=_formatter.get("datefmt", None) ) file_handler.setFormatter(formatter_class) filter_list = handlers["filters"] for filter_dict in filter_list: module_path, class_name = filter_dict["()"].rsplit(".", 1) module = importlib.import_module(module_path) filter_obj = getattr(module, class_name)(filter_dict["level"]) file_handler.addFilter(filter_obj) """ 这个format重置默认的message格式,不然loguru的format会覆盖logging的format [message]字段格式 """ self._logger.add( file_handler, enqueue=True, backtrace=False, diagnose=True, format="{message}", ) del _load_config def emit(self, record): # Get corresponding Loguru level if it exists try: level = self._logger.level(record.levelname).name except ValueError: level = record.levelno # Find caller from where originated the logged message frame, depth = logging.currentframe(), 2 while frame.f_code.co_filename == logging.__file__: frame = frame.f_back depth += 1 self._logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) def simple_log_injector(conf): """ 对内置日志进行反射 修改默认的logging root输出 """ logging.config.dictConfig(conf) logging.setLoggerClass(logging.getLogger(settings.APPLICATION).__class__) logging.root = logging.getLogger(settings.APPLICATION)
使用
async def get(self, request): """ 用户列表 :return: """ import logging logging.error("123") search_filter = filters.AsyncSearchFilter() queryset = await self.get_queryset() search_queryset = await search_filter.filter_queryset(request=request, queryset=queryset, view=self) filter_queryset = await self.filter_queryset(search_queryset) object_list = await self.paginate_queryset(filter_queryset) return await self.get_paginated_response(object_list)
结果图
- error.log
- info.log
- warning
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。