【问题标题】:python django logging: One single logger with more than one logging levelpython django logging:一个具有多个日志记录级别的单个记录器
【发布时间】:2021-01-24 04:21:07
【问题描述】:

tl;dr:有没有办法让一个记录器管理所有日志级别?

要登录我的项目,我正在运行:

logger = logging.getLogger('info.contract')
logger.info("Some info message...")

2021-01-24 03:05:02,063 [contract_generator] Some info message... 打印到控制台。

info.contract 记录器在我的settings.py 中定义:

    'loggers': {
        'info': {
            'handlers' : ['info_logfile'],
            'level': 'INFO',
        },
        'info.contract': {
            'handlers': ['contract_info_logfile'],
            'level': 'INFO',
            'propagate': True,
        }
    },
    'handlers': {
        'info_logfile': {
            'class': 'logging.FileHandler',
            'filename': 'info.log',
            'formatter': 'default',
        },
        'contract_info_logfile': {
            'class': 'logging.FileHandler',
            'filename': 'contract.info.log',
            'formatter': 'default',
        }
    }
    'formatters': {
        'default': {
            'format': '%(asctime)s [%(module)s] %(message)s',
        }
    }
}

我遇到的问题如下:

为了根据日志级别记录到不同的文件,我必须为每个日志级别创建一个新的记录器。也就是说,以下不起作用:

logger = logging.getLogger('info.contract')
logger.info("Some info message...")

## this part is new
logger.debug("Some debug message...")

由于logger 使用settings.py 中定义的info.contract 记录器,其'level' 设置设置为'INFO'(如上所示)。有趣的是,我没有收到任何错误。当我使用 .debug() 函数时,记录器根本不会记录条目。

因此,为了根据日志级别记录到不同的文件,我确实必须为每个日志级别创建一个新的记录器,即使这会产生大量重复代码:

##---snip---##
'loggers': {
        'info': {
            'handlers' : ['std_err', 'info_logfile'],
            'level': 'INFO',
        },
        'info.contract': {
            'handlers': ['contract_info_logfile'],
            'level': 'INFO',
            'propagate': True,
        }
        'debug': {                                     ## duplicate code
            'handlers' : ['std_err', 'debug_logfile'],
            'level': 'DEBUG',
        },
            'debug.contract': {                        ## duplicate code
            'handlers': ['contract_debug_logfile'],
            'level': 'DEBUG',
            'propagate': True,
        }
},
##---snip---##

那么在我的代码中我必须这样做:

info_logger = logging.getLogger('info.contract')
debug_logger = logging.getLogger('debug.contract')

info_logger.info("Some info message...")
debug_logger.debug("Some debug message...")

这意味着有必要为我需要使用的每个日志级别实例化一个新的记录器对象。这看起来很麻烦,而且可能效率低下。

我的问题是:

有没有办法让一个记录器管理所有日志级别?换句话说,是否可以执行以下操作:

some_multilevel_logger = logging.getLogger('some_multilevel_logger')
some_multilevel_logger.info("Some info message...")
some_multilevel_logger.debug("Some debug message...")

这将产生与之前的 sn-p 相同的结果,其中定义了 2 个记录器。

如果这种行为是不可能的,那么为每个日志级别创建一个记录器是否被认为是不好的做法?具有多个日志级别的大型项目呢?

谢谢!

【问题讨论】:

    标签: python django logging


    【解决方案1】:

    我认为将一个日志文件用于不同目的的一种方法是更改​​日志配置中的日志级别,因为基于this document,您无法将调试级别记录到信息级别。例如,查看以下我通常用作最佳实践的配置:

    LOG_DIR = BASE_DIR / 'logs'
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'simple': {
                'format': '[%(asctime)s] %(levelname)s %(message)s',
                'datefmt': '%Y-%m-%d %H:%M:%S'
            },
            'verbose': {
                'format': '[%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s',
                'datefmt': '%Y-%m-%d %H:%M:%S'
            },
        },
        'filters': {
            'require_debug_false': {
                '()': 'django.utils.log.RequireDebugFalse',
            },
            'require_debug_true': {
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {
            'console': {
                'level': 'DEBUG',
                'filters': ['require_debug_true'],
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            'mail_admins': {
                'level': 'ERROR',
                'filters': ['require_debug_false'],
                'class': 'django.utils.log.AdminEmailHandler',
                'formatter': 'verbose'
            },
            'file': {
                'level': 'DEBUG' if DEBUG else 'INFO',
                'class': 'logging.FileHandler',
                'filename': LOG_DIR / 'django.log',
                'formatter': 'verbose' if DEBUG else 'simple',
            },
            'db_queries': {
                'level': 'DEBUG',
                'filters': ['require_debug_true'],
                'class': 'logging.FileHandler',
                'filename': LOG_DIR / 'db_queries.log',
            },
            'null': {
                'level': 'DEBUG',
                'class': 'logging.NullHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'level': 'DEBUG',
                'handlers': ['db_queries'],
                'propagate': False,
            },
            'accounts': {
                'handlers': ['file', 'console'],
                'level': 'DEBUG',
            },
        },
    }
    

    对于日志记录,只需使用以下几行:

    logger = logging.getLogger('accounts')
    
    logger.info("some info logs")
    logger.debug("some debug logs")
    

    【讨论】:

      【解决方案2】:

      一个解决方案可能是这样的。

      import logging
      
      class MultiLogger(logging.Logger):
          def __init__(self, name, level=logging.INFO, formatter=None):
              self.name = name
              self.level = level
              self.formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
              logger = logging.getLogger(self.name)
              logger.setLevel(self.level)
              self.logger = logger
              super().__init__(name=name, level=level)
      
          def debug(self, msg, log_file="debug.log", *args, **kwargs):
              handler = logging.FileHandler(log_file)
              handler.setFormatter(self.formatter)
      
              for h in self.handlers:
                  self.removeHandler(h)
      
              self.addHandler(handler)
              self.level = 10
              return super().debug(msg, *args, **kwargs)
          
          def error(self, msg, log_file="error.log", *args, **kwargs):
              handler = logging.FileHandler(log_file)
              handler.setFormatter(self.formatter)
              for h in self.handlers:
                  self.removeHandler(h)
              self.addHandler(handler)
              self.level = 40
              return super().error(msg, *args, **kwargs)
          
          def warning(self, msg, log_file="warning.log", *args, **kwargs):
              handler = logging.FileHandler(log_file)
              handler.setFormatter(self.formatter)
              for h in self.handlers:
                  self.removeHandler(h)
              self.addHandler(handler)
              self.level = 30
              return super().warning(msg, *args, **kwargs)
          
          def info(self, msg, log_file="info.log", *args, **kwargs):
              handler = logging.FileHandler(log_file)
              handler.setFormatter(self.formatter)
              for h in self.handlers:
                  self.removeHandler(h)
              self.addHandler(handler)
              self.level = 20
              return super().info(msg, *args, **kwargs)
          
          
      
      logger = MultiLogger(name="info_logger", level=40)
      logger.debug("This is debug message", log_file="debug.log")
      logger.error("This is error message", log_file="error.log")
      logger.info("This is info message", log_file="info.log")
      logger.warning("This is warning message", log_file="warn.log")
      

      【讨论】:

        【解决方案3】:

        我发现的最佳解决方案是为一个记录器创建多个处理程序:

        LOGGING = {
            'version': 1,
            'disable_existing_loggers' : False,
            'loggers': {
                'general': {
                    'handlers': ['error', 'info', 'debug'],
                    'level': 1
                }
            },
            'handlers': {
                'std_err': {
                    'class': 'logging.StreamHandler'
                },
                'info': {
                    'class': 'logging.FileHandler',
                    'filename': 'info.log',
                    'level': 'INFO',
                    'formatter': 'default',
                },
                'error': {
                    'class': 'logging.FileHandler',
                    'filename': 'error.log',
                    'level': 'ERROR',
                    'formatter': 'error',
                },
                'debug': {
                    'class': 'logging.FileHandler',
                    'filename': 'debug.log',
                    'level': 'DEBUG',
                    'formatter': 'default',
                },
            },
            'formatters': {
                'default': {
                    'format': '%(asctime)s [%(module)s | %(levelname)s] %(message)s',
                },
                'error': {
                    'format': '%(asctime)s [%(module)s | %(levelname)s] %(message)s @ %(pathname)s : %(lineno)d : %(funcName)s',
                },
            },
        }
        

        这允许:

        logger = logging.getLogger('general')
        logger.error("Some error message...")
        logger.debug("Debug message...")
        logger.info("Some info message..")
        

        它甚至允许:

        logger.exception("Exception message", fmt=std.FAIL)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-11
          • 2020-07-21
          • 2011-12-16
          • 2020-11-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多