Python logging.handlers模块,RotatingFileHandler、TimedRotatingFileHandler 处理器各参数详细介绍

  • Post author:
  • Post category:python


在python的

logging.handlers

模块中,提供了很多有用的日志处理程序对象,其中比较常用的有:

RotatingFileHandler



TimedRotatingFileHandler

。这两个处理程序主要是用来对输出的日志文件进行切割。



一、RotatingFileHandler 介绍


RotatingFileHandler

是 Python 自带的日志处理器之一,用于将日志写入到指定的文件中,并控制文件大小和数量,可实现日志轮转(即备份旧日志并创建新的日志)。


class logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)


RotatingFileHandler

的各参数解释:


  1. filename

    :文件名,指定日志文件的路径和名称;

  2. mode

    :模式,可选值为 ‘a’ 或 ‘w’。当为 ‘a’ 时,表示以追加方式写入日志;当为 ‘w’ 时,表示以覆盖方式写入日志。默认值为 ‘a’;

  3. maxBytes

    :单个日志文件的最大大小,单位为字节。当日志文件达到该大小时,会自动备份旧日志并创建新的日志文件。默认值为 0,表示不限制日志文件大小。;

  4. backupCount

    :备份文件数目。当生成的日志文件数量超过该数目时,会自动删除旧的备份日志文件。默认值为 0,表示不备份;

  5. encoding

    :日志文件的编码格式,默认为 None,表示使用系统默认编码;

  6. delay

    :是否延时打开文件,可选值为 True 和 False。当为 True 时,表示延时打开文件,即在第一次写入日志时才打开日志文件。当为 False 时,表示在初始化时即打开日志文件。默认值为 False

默认情况下,文件无限增长。您可以指定

maxBytes



backupCount

的特定值,以允许文件以预定的大小滚动。

需要注意的是,如果指定了为

maxBytes

指定了

>0

的值,则

mode

指定为

w

是没有意义的,

RotatingFileHandler

初始化时会自动将

mode

强制改为

a


示例1:

import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger('mylogger')
logger.setLevel(logging.INFO)

handler = RotatingFileHandler(filename='test.log', maxBytes=6, backupCount=3)

logger.addHandler(handler)

for i in range(10):
    logger.info("Hello%d" % i)

输出4个日志文件,文件名及文件内容分别如下:

# test.log
Hello9

# test.log.1
Hello8

# test.log.2
Hello7

# test.log.3
Hello6

解释:

  • 文件最新内容始终输出在

    test.log

    基本日志文件中;

  • test.log

    大小达到设定的

    maxBytes

    时,如果再有日志写入,则新生成一个

    test.log

    ,并将原来的

    test.log

    重命名为

    test.log.1

    ,将原来的

    test.log.1

    (如果有)重命名为

    test.log.2

    ,将原来的

    test.log.2

    (如果有)重命名为

    test.log.3

    ,将原来的

    test.log.3

    (如果有)删除(因为只备份3个)



二、TimedRotatingFileHandler 介绍


TimedRotatingFileHandler

是 Python 自带的日志处理器之一,与

RotatingFileHandler

类似,也用于将日志写入到指定的文件中,并对文件进行切割。不同的是

TimedRotatingFileHandler

可以按照时间轮转日志,例如每天或每小时生成一个新的日志文件。


class logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)


TimedRotatingFileHandler

的各参数的详解:


  1. filename

    :文件名,指定日志文件的路径和名称;

  2. when

    :日志轮转的时间间隔,可选值为 ‘S’、‘M’、‘H’、‘D’、‘W’ 和 ‘midnight’,分别表示秒、分、时、天、周和每天的午夜;默认值为 ‘midnight’,即每天的午夜轮转,值不区分大小写;

  3. interval

    :时间间隔的数量,默认为 1;例如,当 when=‘D’ 且 interval=7 时,表示每周轮转一次;

  4. backupCount

    :备份文件数目;当生成的日志文件数量超过该数目时,会自动删除旧的备份日志文件;默认值为 0,表示不备份;

  5. encoding

    :日志文件的编码格式,默认为 None,表示使用系统默认编码;

  6. delay

    :是否延时打开文件,可选值为 True 和 False;当为 True 时,表示延时打开文件,即在第一次写入日志时才打开日志文件;当为 False 时,表示在初始化时即打开日志文件;默认值为 False;

  7. utc

    :是否使用 UTC 时间,默认为 False,表示使用本地时间

  8. atTime

    :用来设置轮转时间,格式为 ‘%H:%M:%S’,默认为午夜 12 点;需要注意的是该参数仅在

    when



    W

    /

    midnight

    时有效;

当使用基于星期的轮换时,星期一为 ‘W0’,星期二为 ‘W1’,以此类推直至星期日为 ‘W6’。 在这种情况下,传入的

interval

值不会被使用。


示例1:

日志文件备份规则

import logging
from logging.handlers import TimedRotatingFileHandler
import time

logger = logging.getLogger('mylogger')
logger.setLevel(logging.INFO)

handler = TimedRotatingFileHandler(
    filename="./test.log",
    when="S",
    interval=3,
    backupCount=5,
    encoding="utf8"
)

ffmt = logging.Formatter("%(asctime)s - %(message)s")
handler.setFormatter(ffmt)

logger.addHandler(handler)

for i in range(10):
    logger.info("Hello%d" % i)
    logger.info(handler.suffix)
    logger.info(handler.extMatch)
    time.sleep(3)

输出文件:

示例1生成的日志文件

部分文件内容:

# test.log
2023-08-17 22:06:45,635 - Hello9
2023-08-17 22:06:45,635 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:06:45,635 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)

# test.log.2023-08-17_22-06-30
2023-08-17 22:06:30,615 - Hello4
2023-08-17 22:06:30,615 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:06:30,615 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)

解释:

  • 代码中,

    when='S'



    interval=3

    ,表示每3s备份一次文件,

    backupCount=5

    ,表示最多保留5份旧日志数据;
  • 从输出结果来看,每3s备份一次数据,只保留5份旧的数据,符合预期;
  • 另外,按秒切割的日志文件,备份文件是在原日志文件名后添加

    .%Y-%m-%d_%H-%M-%S

    的后缀,而删除旧日志文件使用的正则表达式为

    re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)


示例2:

轮换时间的计算则仅在轮换发生时执行

还是示例1的代码,这里做一个小改动,将最后一行的

time.sleep(3)

改为`time.sleep(2)

将已有日志文件全删除,再次运行代码。

输出文件:

示例2生成的日志文件

部分文件内容:

# test.log
2023-08-17 22:24:16,973 - Hello8
2023-08-17 22:24:16,973 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:24:16,973 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)
2023-08-17 22:24:18,984 - Hello9
2023-08-17 22:24:18,984 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:24:18,984 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)

# test.log.2023-08-17_22-24-12
2023-08-17 22:24:12,957 - Hello6
2023-08-17 22:24:12,957 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:24:12,957 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)
2023-08-17 22:24:14,960 - Hello7
2023-08-17 22:24:14,960 - %Y-%m-%d_%H-%M-%S
2023-08-17 22:24:14,960 - re.compile('^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}(\\.\\w+)?$', re.ASCII)

解释:



  • 示例1

    结果不同的是,备份的日志文件名后添加的后缀时间间隔是4,不是

    interval=3

    设置的间隔。这是因为:初始轮换时间的计算是在处理程序被初始化时执行的。 后续轮换时间的计算则仅在轮换发生时执行,而只有当提交输出时轮换才会发生。

    例如,如果设置时间间隔为“每分钟”,那并不意味着你总会看到(文件名中)带有间隔一分钟时间的日志文件;如果在应用程序执行期间,日志记录输出的生成频率高于每分钟一次,那么你可以预期看到间隔一分钟时间的日志文件。 但如果日志记录消息每五分钟才输出一次,那么你将看到的是间隔五分钟时间的日志文件。


示例3:

自定义备份文件的后缀

import logging
from logging.handlers import TimedRotatingFileHandler
import time
import re

logger = logging.getLogger('mylogger')
logger.setLevel(logging.INFO)

handler = TimedRotatingFileHandler(
    filename="./test.log",
    when="S",
    interval=3,
    backupCount=5,
    encoding="utf8"
)

ffmt = logging.Formatter("%(asctime)s - %(message)s")
handler.setFormatter(ffmt)

handler.suffix = "%Y%m%d_%H%M%S"+".log"
extMatch = r"^\d{4}\d{2}\d{2}_\d{2}\d{2}\d{2}(\.\w+)?$"
handler.extMatch = re.compile(extMatch, re.ASCII)

logger.addHandler(handler)

for i in range(10):
    logger.info("Hello%d" % i)
    time.sleep(5)

文件输出:

示例3生成的日志文件

解释:

  • 使用

    handler.suffix = "%Y%m%d_%H%M%S"+".log"

    自定义备份文件的后缀,与此同时使用

    extMatch = r"^\d{4}\d{2}\d{2}_\d{2}\d{2}\d{2}(\.\w+)?$"



    handler.extMatch = re.compile(extMatch, re.ASCII)

    重新定义了删除旧备份文件使用的正则表达式,以保证新后缀的旧的备份文件能正常按备份数量进行删除。


示例4:


由于

when

=

'M'



'H'



'W0'~'W6'



midnight

时,测试起来不太方便,我这里只解释规则,部分给出关键代码。

# when='M'时,suffix、extMatch默认值为
handler.suffix = "%Y-%m-%d_%H-%M"
extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
handler.extMatch = re.compile(self.extMatch, re.ASCII)

# when='H'时,suffix、extMatch默认值为
handler.suffix = "%Y-%m-%d_%H"
extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
handler.extMatch = re.compile(self.extMatch, re.ASCII)

# when='W'时,suffix、extMatch默认值为
handler.suffix = "%Y-%m-%d"
extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
handler.extMatch = re.compile(self.extMatch, re.ASCII)

# when='midnight'时,suffix、extMatch默认值为
handler.suffix = "%Y-%m-%d"
extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
handler.extMatch = re.compile(self.extMatch, re.ASCII)

  • when='M'



    interval=1

    时,表示日志按分钟进行切割,但需要注意的是下次轮换时间是:处理器初始化时间+

    60s

    ,比如:日志处理器初始化是在

    10:00:25

    ,那么下次备份文件的生成是在

    10:01:25

    ,而不是

    10:01:00

    (当然前提是

    10:01:25

    这个时间有日志提交,如果这个时间没日志提交,那么备份文件的生成时间为这个时间之后日志首次提交的时间)

  • when='M'



    interval=1

    时,表示日志按每小时进行切割,但同样需要注意下次轮换时间是:处理器初始化时间+

    60*60s

    ,比如:日志处理器初始化是在

    10:20:30

    ,那么下次备份文件生成是在

    11:20:30

    (当然前提是

    11:20:30

    这个时间有日志提交,如果这个时间没日志提交,那么备份文件的生成时间为这个时间之后日志首次提交的时间)

  • when='w2'

    时,如果不指定

    atTime

    ,则意思为每周三,午夜12点进行日志轮转,即 周四 00:00:00 进行日志轮转;

  • when='w3'

    时,如果指定了

    atTime

    ,如

    atTime=datetime.time(22,00,00)

    ,表示每周四,晚上22:00:00 进行日志轮转;
from logging.handlers import TimedRotatingFileHandler
from datetime import time

# 每周五,早上 02:00:00 进行日志轮换
handler = TimedRotatingFileHandler(filename, when='w4', atTime=time(02, 20, 00))

  • when='midnight'

    时,如果未指定

    atTime

    ,则意思为每天午夜12点进行日志轮转;

  • when='midnight'

    时,如果指定了

    atTime

    ,如

    atTime=datetime.time(23,00,00)

    ,则意思为每天晚上23:00:00,进行日志轮转;
from logging.handlers import TimedRotatingFileHandler
from datetime import time

# 每天,早上 10:00:00 进行日志轮换
handler = TimedRotatingFileHandler(filename, when='midnight', atTime=time(10, 00, 00))


参考资料:


logging.handlers — 日志处理



版权声明:本文为B11050729原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。