Django项目中的日志处理

项目中的日志

     Django 中使用python的logging模块记录log,logging模块为应用程序提供了灵活的手段记录事件、错误、警告和调试信息。对这些信息可以进行收集、筛选、写入文件、发送给系统日志等操作,甚至还可以通过网络发送给远程计算机。
     本文针对loggering模块做了简单的封装,实现了默认日志,错误日志以及自定义日志三个方法来满足各类情况。其中默认日志与错误日志都采用了TimedRotatingFileHandler,每天零点会自动备份当天日志。自定义日志采用RotatingFileHandler根据日志大小进行备份。

     注意事项:多进程环境中,使用TimedRotatingFileHandler进行处理,会出现前一个进程备份的日志被后一个进程的操作覆盖,所以,SafeTimedRotatingFileHandler继承了TimedRotatingFileHandler并做了简单修改,保证多进程中日志不会丢失。

日志代码示例

# -*- coding: utf-8 -*-


import logging.config
import os
import time
from logging.handlers import TimedRotatingFileHandler

from ZetCore.base_settings import LOG_PATH


class SafeTimedRotatingFileHandler(TimedRotatingFileHandler):
    """
    Handler for logging to a file, rotating the log file at certain timed
    intervals.

    If backupCount is > 0, when rollover is done, no more than backupCount
    files are kept - the oldest ones are deleted.
    """

    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False,
                 atTime=None):
        TimedRotatingFileHandler.__init__(self, filename, when, interval, backupCount, encoding, delay, utc, atTime)

    def doRollover(self):
        """
        do a rollover; in this case, a date/time stamp is appended to the filename
        when the rollover happens.  However, you want the file to be named for the
        start of the interval, not the current time.  If there is a backup count,
        then we have to get a list of matching filenames, sort them and remove
        the one with the oldest suffix.
        """
        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))
        # 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 rotate(self, source, dest):
        """
        When rotating, rotate the current log.

        The default implementation calls the 'rotator' attribute of the
        handler, if it's callable, passing the source and dest arguments to
        it. If the attribute isn't callable (the default is None), the source
        is simply renamed to the destination.

        :param source: The source filename. This is normally the base
                       filename, e.g. 'test.log'
        :param dest:   The destination filename. This is normally
                       what the source is rotated to, e.g. 'test.log.1'.
        """
        if not callable(self.rotator):
            # Issue 18940: A file may not have been created if delay is True.
            if os.path.exists(source) and not os.path.exists(dest):
                os.rename(source, dest)
        else:
            self.rotator(source, dest)


def logger_default():
    zet_core_path = LOG_PATH if LOG_PATH else os.path.dirname(os.path.abspath(__file__))  # 当前文件所在的文件夹目录

    filename = 'default.log'
    log_path = os.path.join(zet_core_path, 'logs', 'all')
    if not os.path.exists(log_path):
        os.makedirs(log_path)
    log_file = os.path.join(log_path, filename)  # 项目的log文件,不要修改本行
    # handler_default = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=1024 * 1024 * 10,
    #                                                        backupCount=2)  # 日志信息输出到文件
    handler_default = SafeTimedRotatingFileHandler(log_file, when='MIDNIGHT', interval=1, backupCount=365,
                                                   encoding='utf-8')  # 日志信息输出到文件
    handler_console = logging.StreamHandler()  # 日志信息输出到控制台

    # 定义输出格式
    fmt = ' %(levelname)s : %(asctime)s - %(filename)s:%(lineno)s - %(funcName)s - %(message)s'
    formatter = logging.Formatter(fmt)  # 实例化formatter
    handler_default.setFormatter(formatter)  # 为handler添加formatter
    handler_console.setFormatter(formatter)

    logger = logging.getLogger('tst')  # 获取名为tst的logger
    logger.addHandler(handler_default)  # 为logger添加handler
    logger.addHandler(handler_console)

    logger.setLevel(logging.DEBUG)  # 设置日志记录级别,大于等于DEBUG级别的都会记录
    return logger


def logger_errors():
    """
    标准错误日志,只记录错误信息
    :return:
    """
    zet_core_path = LOG_PATH if LOG_PATH else os.path.dirname(os.path.abspath(__file__))  # 当前文件所在的文件夹目录

    filename = 'error.log'
    log_path = os.path.join(zet_core_path, 'logs', 'errors')
    if not os.path.exists(log_path):
        os.makedirs(log_path)
    log_file = os.path.join(log_path, filename)  # 项目的log文件,不要修改本行
    handler_default = SafeTimedRotatingFileHandler(log_file, when='MIDNIGHT', interval=1, backupCount=365,
                                                   encoding='utf-8')  # 日志信息输出到文件

    # 定义输出格式
    fmt = ' %(levelname)s : %(asctime)s - %(filename)s:%(lineno)s - %(funcName)s - %(message)s'
    formatter = logging.Formatter(fmt)  # 实例化formatter
    handler_default.setFormatter(formatter)  # 为handler添加formatter

    logger = logging.getLogger('error')  # 获取名为tst的logger
    logger.addHandler(handler_default)  # 为logger添加handler

    logger.setLevel(logging.ERROR)

    return logger


def logger_custom(filename, dirname='custom', logger_level=logging.DEBUG):
    """
    自定义日志,供用户自己调用使用。
    :param filename: 日志名称
    :param dirname: 日志所在目录
    :param logger_level: 日志记录级别
    :return:
    """
    zet_core_path = LOG_PATH if LOG_PATH else os.path.dirname(os.path.abspath(__file__))  # 当前文件所在的文件夹目录
    log_path = os.path.join(zet_core_path, 'logs', dirname)
    if not os.path.exists(log_path):
        os.makedirs(log_path)

    log_file = os.path.join(log_path, filename)  # 项目的log文件,不要修改本行
    log_path = os.path.dirname(log_file)

    if not os.path.exists(log_path):
        os.makedirs(log_path)
    handler_default = logging.handlers.RotatingFileHandler(log_file, maxBytes=1024 * 1024 * 10,
                                                           backupCount=2)  # 日志信息输出到文件
    # 定义输出格式
    fmt = ' %(levelname)s : %(asctime)s - %(filename)s:%(lineno)s - %(funcName)s - %(message)s'
    formatter = logging.Formatter(fmt)  # 实例化formatter
    handler_default.setFormatter(formatter)  # 为handler添加formatter

    file_name_without_suffix = os.path.basename(log_file).split('.')[0]
    logger = logging.getLogger(file_name_without_suffix)  # 获取名为tst的logger
    logger.addHandler(handler_default)  # 为logger添加handler

    logger.setLevel(logger_level)

    return logger


logger = logger_default()
logger_error = logger_errors()

使用日志文件

一定要转成字符串再输出



from log_config import logger

logger.debug('调试信息')
logger.error('错误信息')
logger.info('提示信息')

logs = ['a','b','c','d']

logger.info(str(logs))

日志显示参数

如果提供的默认方法的输出格式不是你想要的格式,可以参考下表,修改输出格式即可。

参数作用
%(levelno)s打印日志级别的数值
%(levelname)s打印日志级别的名称
%(pathname)s打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s打印当前执行程序名
%(funcName)s打印日志的当前函数
%(lineno)d打印日志的当前行号
%(asctime)s打印日志的时间
%(thread)d打印线程ID
%(threadName)s打印线程名称
%(process)d打印进程ID
%(message)s打印日志信息

日志等级

严重级别从上往下依次有FATAL,CRITICAL,ERROR,WARNING,INFO,DEBUG。

日志等级使用范围
FATAL致命错误
CRITICAL特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
ERROR发生错误时,如IO操作失败或者连接问题
WARNING发生很重要的事件,但是并不是错误时,如用户登录密码错误
INFO处理请求或者状态变化等日常事务
DEBUG调试过程中使用DEBUG等级,如算法中每个循环的中间状态
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值