使用Django日志模块记录全局报错日志

Django提供了日志模块可以在settings.py中定义LOGGING直接使用

示例 :

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已经存在的日志模块
    'handlers': {
        'file': {  # 定义日志文件记录
            'level': 'ERROR',  # 只记录报错
            # 'class': 'logging.FileHandler',  # 普通的日志记录类,不实用
            # 'class': 'logging.handlers.TimedRotatingFileHandler',  # 原本的按时间分割的日志记录类
            'class': 'utils.tools.MyTimedRotating',  # 使用django日志记录全局报错. 重写父类方法,自定义文件命名和删除
            'filename': os.path.join(BASE_DIR, 'logs', 'err.log'),  # 定义日志文件目录,logs文件夹没有就创建,err.log自动生成
            'formatter': 'verbose',
            'when': 'D',  # 每天切割一次日志
            'interval': 1,  # 时间间隔: 一天
            'backupCount': 10,  # 保留?份日志
            'encoding': 'utf-8'
        },
        'console': {  # 定义终端打印记录
            'level': 'INFO',  # 打印所有信息
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'formatters': {
        'verbose': {  # 定义详细的日志信息
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'simple': {  # 定义简单的日志信息
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'loggers': {
        'django': {  # 使用django日志,指定使用上述定义的各个配置
            'handlers': ['file', 'console'],  # 同时记录到日志文件和在终端打印
            'level': 'INFO',
            'propagate': True,  # 是否向上传播
            'formatter': 'verbose'
        },
    },
}

由于Django提供的这个按时间分割的 TimedRotatingFileHandler 日志类命名很奇怪, 所以我把它的文件命名和删除文件方法重写了, 在自己的 utils/tools.py文件中定义

其中原本的类通过 from logging.handlers import TimedRotatingFileHandler 导入

class MyTimedRotating(TimedRotatingFileHandler):
    """
    重写父类的日志命名及过量日志删除
    """
    def doRollover(self):
        """
        自定义日志命名
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        # get the time that this sequence started at and make it a TimeTuple
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        t = self.rolloverAt - self.interval
        if self.utc:
            timeTuple = time.gmtime(t)
        else:
            timeTuple = time.localtime(t)
            dstThen = timeTuple[-1]
            if dstNow != dstThen:
                if dstNow:
                    addend = 3600
                else:
                    addend = -3600
                timeTuple = time.localtime(t + addend)
        # 重写父类方法, 重写命名日志文件
        # dfn = self.rotation_filename(self.baseFilename + "." +
        #                              time.strftime(self.suffix, timeTuple))
        dfn = self.rotation_filename(self.baseFilename.rstrip(".log") + "_" +
                                     time.strftime(self.suffix, timeTuple) + ".log")
        if os.path.exists(dfn):
            os.remove(dfn)
        self.rotate(self.baseFilename, dfn)
        if self.backupCount > 0:
            for s in self.getFilesToDelete():
                os.remove(s)
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        # If DST changes and midnight or weekly rollover, adjust for this.
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
                    addend = -3600
                else:  # DST bows out before next rollover, so we need to add an hour
                    addend = 3600
                newRolloverAt += addend
        self.rolloverAt = newRolloverAt

    def getFilesToDelete(self):
        """
        超出规定数量日志的删除
        """
        dirName, baseName = os.path.split(self.baseFilename)
        fileNames = os.listdir(dirName)
        result = []
        # prefix = baseName + "."  # err.
        prefix = baseName.rstrip(".log") + "_"  # err_
        tail = ".log"
        plen_head = len(prefix)
        plen_tail = len(tail)
        for fileName in fileNames:
            if fileName[:plen_head] == prefix and fileName[-plen_tail:] == tail:
                suffix = fileName[plen_head: -plen_tail]
                if self.extMatch.match(suffix):
                    result.append(os.path.join(dirName, fileName))
        if len(result) < self.backupCount:
            result = []
        else:
            result.sort()
            result = result[:len(result) - self.backupCount]
        return result

如果懒的话直接使用原本的 logging.handlers.TimedRotatingFileHandler 类, 就不需要重写父类方法了. 写完之后可以使用raise一个错误看看是否能记录到日志.

如果本地启动项目报错 PermissionError: [WinError 32] 另一个程序正在使用此文件,进程无法访问。

django的启动参数改为: python manage.py runserver --noreload 就正常了. 

这样就能自动按时间分割日志文件, 记录全局报错日志了, 大家觉得好用可以帮忙点个赞

原文地址:https://www.cnblogs.com/banbosuiyue/p/15030647.html