基于 Python 的日志操作基于 Python 的日志操作

62 阅读2分钟

在使用 Python 的 logging 模块记录日志时,我们通常希望日志文件包含当前日期,以便于追踪和分析日志。然而,如果我们使用 TimedRotatingFileHandler 处理器,则无法指定日志文件的文件名,该处理器会根据时间自动滚动日志文件。

此外,如果我们的进程每天重新启动,那么 TimedRotatingFileHandler 就无法正常工作,因为它无法识别新的日志文件。

2、解决方案

1. 手动指定日志文件的文件名

我们可以通过手动指定日志文件的文件名来解决这个问题,这样就可以包含当前日期。

import logging
import logging.handlers

LOG_FILENAME = '/tmp/log-{}.txt'.format(datetime.date.today())

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
handler = logging.FileHandler(LOG_FILENAME)
log.addHandler(handler)
# python script
from datetime import datetime
import logging
import logging.handlers

# define a new Handler that formats and writes to a log file
# logfile will be rotated every day at midnight
class DailyRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    def __init__(self, filename, when="midnight", backupCount=7):
        logging.handlers.TimedRotatingFileHandler.__init__(self, filename, when=when, backupCount=backupCount)
    def doRollover(self):
        if self.stream:
            self.stream.close()
        # get the time that this sequence started at and make it a TimeTuple
        t = time.time()
        current_time = time.gmtime(t)
        d, _, h = self.when_to_rollover(current_time)
        dt = datetime(current_time.tm_year, current_time.tm_mon, d, h, 0, 0)
        new_rollover_at = self.computeRollover(dt)
        while new_rollover_at <= t:
            new_rollover_at = self.computeRollover(dt)
            dt += timedelta(days=1)
        # if we are rolling over at xxx Midnight, then the new file to open is
        # xxx YYYYMMDD.log
        target_time = dt.timetuple()
        filename = self.baseFilename + "." + \
                   time.strftime(self.suffix, target_time)
        # compute filename for new file
        # if self.utc then we set self.baseFilename to UTC time, while we use
        # local time for the rolled over files
        if self.utc:
            # if we are in UTC, the filename is in UTC, but we use the local
            # time zone for the rollover computations.
            time_str = time.strftime(self.prefix + "%Y-%m-%d_%H-%M-%S", self.utcnow())
        else:
            # if we are in local time, the filename is in local time, and likewise
            # for the rollover computations.
            time_str = time.strftime(self.prefix + "%Y-%m-%d_%H-%M-%S", current_time)
        dfn = self.baseFilename + "." + time_str
        dfn = os.path.join(self.directory, dfn)
        # when DST ends, the next day the file should have today's date
        # instead of yesterday's
        if self.utc:
            yearday = datetime.now(tz=datetime.timezone.utc).timetuple().tm_yday
        else:
            yearday = datetime.now().timetuple().tm_yday
        # remove the old files
        self.removeOldFiles(yearday)
        # open the new file and make sure it's writable
        stream = open(dfn, "w", encoding=self.encoding, errors=self.errors)
        # make sure that the file has good permissions (in case we're not root)
        try:
            os.chmod(dfn, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP |
                        stat.S_IROTH)
        except OSError:
            pass  # TODO - warn here
        if self.mode == "a":
            stream.write(self.stream.read())
        self.stream = stream
        current_time = time.localtime(t) # don't use localtime() above, as we may
                                         # miss a rollover
        new_rollover_at = self.computeRollover(current_time)
        self.rolloverAt = new_rollover_at

2. 使用自定义 RotatingFileHandler

也可以使用自定义的 RotatingFileHandler,该处理器允许我们指定日志文件的文件名,并根据需要对其进行滚动。

import logging
import logging.handlers

LOG_FILENAME = '/tmp/log-{}.txt'.format(datetime.date.today())

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=1024 * 1024, backupCount=5)
log.addHandler(handler)

这种方法允许我们指定日志文件的最大大小和备份文件数量,当日志文件达到最大大小时,它会自动滚动到一个新的日志文件,同时保留指定数量的备份文件。