• 不改动任何代码,还是使用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
    image.png
  • info.log
    image.png
  • warning
    image.png

莫愁
95 声望2 粉丝