python django logging:一个具有多个日志记录级别的记录器

时间:2021-01-24 04:21:07

标签: python django logging

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 使用 info.contract 中定义的 settings.py 记录器,它的 '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...")

这将产生与前一个代码段相同的结果,其中定义了 2 个记录器。

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

谢谢!

3 个答案:

答案 0 :(得分: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")

答案 1 :(得分:0)

一种解决方案可能是这样。

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")

答案 2 :(得分:0)

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

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)
相关问题