zoukankan      html  css  js  c++  java
  • Python开发之日志记录模块:logging

    1 引言

    最近在开发一个应用软件,为方便调试和后期维护,在代码中添加了日志,用的是Python内置的logging模块,看了许多博主的博文,颇有所得。不得不说,有许多博主大牛总结得确实很好。似乎我再写关于logging的博文有些多余,但不写总结又总觉得没掌握。那就写写吧,也方便日后回顾。
    开始总结之前,先感谢几位博主,他们的博客写得很是详尽:
    说说为什么需要添加日志?
    就像上面说的,为了调试和后期维护方便。也许在开发中没有太大体会,但是如果将软件部署到了生产环境中,一旦出现bug,没有日志,就很难对当时的情况进行追踪,有了日志,就可以根据日志尽可能的对当时的数据环境进行还原,方便debug。甚至,只要日志设计得足够合理,还可以用于后续业务数据分析等。

    2 日志等级

    为什么需要对日志进行划分等级呢?
    当我们出于开发时debug的目的使用日志时,我们自然是想尽可能详尽得记录日志,但是如果部署到生产环境中,这样做就可能因为大量的IO占用服务器资源,所以在生产环境中就只需要记录异常信息、错误情况等就好了。
    所以,给日志设置等级,可以方便得、因地制宜控制日志输出。
    这里只介绍Python的logging模块的日志等级(当然,其他日志系统的日志等级划分事实上也基本相同)。logging的日志等级包括5个:
    日志等级(level)
    描述
    DEBUG
    最详细的日志信息,典型应用场景是 问题诊断
    INFO
    信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
    WARNING
    当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
    ERROR
    由于一个更严重的问题导致某些功能不能正常运行时记录的信息
    CRITICAL
    当发生严重错误,导致应用程序不能继续运行时记录的信息
    日志等级从上到下依次提高,当在程序中设定某个日志等级之后,比设定的日志等级低的日志记录将会被忽略,即logging就只会输出大于和等于设定的等级的日志。我们将在下文中通过代码示例证明这一点。

    3 记录日志

    logging模块提供两种方法记录日志:
    (1)通过logging模块提供的模块级函数记录日志;
    (2)通过logging模块提供的4大组件记录日志。

    3.1 记录日志之logging模块级函数

    在logging模块中,分别给出一个模块级别函数与上面说到的日志级别相对应,用于输出对应级别日志记录:
    函数
    说明
    logging.debug(msg, *args, **kwargs)
    创建一条严重级别为DEBUG的日志记录
    logging.info(msg, *args, **kwargs)
    创建一条严重级别为INFO的日志记录
    logging.warning(msg, *args, **kwargs)
    创建一条严重级别为WARNING的日志记录
    logging.error(msg, *args, **kwargs)
    创建一条严重级别为ERROR的日志记录
    logging.critical(msg, *args, **kwargs)
    创建一条严重级别为CRITICAL的日志记录
    也有一个函数汇总了上面5个函数的功能:
    函数
    说明
    logging.log(level, *args, **kwargs)
    创建一条严重级别为level的日志记录
    现在可以来尝试使用一下上面的函数了:
    import logging
     
    logging.debug('debug')
    logging.info('info')
    logging.warn('warn')
    logging.error('error')
    logging.critical('critical')
    logging.warn('Today is %s',datetime.date.today())
    运行结果如下:
    WARNING:root:warn
    ERROR:root:error
    CRITICAL:root:critical
    WARNING:root:Today is 2019-03-28
     上面的函数都有*args, **kwargs这两个参数,所以这些函数可以接受任意位置参数和关键字参数,这些参数填充到第一个参数msg,最后一条日志输出中添加了当前日期就是利用了这个功能。
    那为什么会只输出后面3条日志记录呢?上面说到过,logging就只会输出大于和等于设定的等级的日志记录,而logging的默认日志等级是WARNING,所以日志等级为DEBUG和INFO的两条记录都没有被输出。
    如果想要输入日志等级为DEBUG和INFO的日志记录,就要对logging进行配置。logging也提供了一个模块级别的专用于配置logging的函数:
    函数
    说明
    logging.basicConfig(**kwargs)
    对root logger进行一次性配置
    尝试使用一下这个配置函数:
    import logging
    logging.basicConfig(level=logging.DEBUG) # 设置日志等级
    logging.debug('debug')
    logging.info('info')
    logging.warn('warn')
    logging.error('error')
    logging.critical('critical')
    运行结果如下:
    DEBUG:root:debug
    INFO:root:info
    WARNING:root:warn
    ERROR:root:error
    CRITICAL:root:critical
    看,日志等级为DEBUG和INFO的两条记录也都得到了输出。
    上面表格对logging.basicConfig函数的说明中指出,logging.basicConfig函数时一次性配置,什么意思呢?意思就是说,logging.basicConfig函数只在第一次运行(第一次对logging进行配置)时起作用,后面在此设置其他参数是不会生效的。通过代码证明一下:
    import logging
    logging.basicConfig(level=logging.DEBUG) # 设置日志等级
    logging.basicConfig(level=logging.INFO) # 重新设置日志等级
    logging.debug('debug')
    logging.info('info')
    logging.warn('warn')
    logging.error('error')
    logging.critical('critical')
    运行结果:
    DEBUG:root:debug
    INFO:root:info
    WARNING:root:warn
    ERROR:root:error
    CRITICAL:root:critical
    看到没,DEBUG级别日志记录还是输出了,证明重新运行logging.basicConfig函数设置日志级别没有生效。
    另外需要注意的是,一定要在使用logging记录日志之前使用logging.basicConfig进行配置,否则,不会有任何输出。
    我们再观察一下上面的程序输出,可以发现,每一条输出的结果里,不仅仅只有我们输出的字符串参数,还有其它的一些信息,例如日志等级,日志器名称(默认是root),分隔符(这里是冒号)等,这些都是logging默认给我配置好的,当然,我们也可以通过logging.basicConfig函数的各参数自定义logging的输出。
    参数名称
    描述
    filename
    指定日志输出目标文件的文件名,指定该设置项后日志信心就不会被输出到控制台了
    filemode
    指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效
    format
    指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
    datefmt
    指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
    level
    指定日志器的日志级别
    stream
    指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
    style
    Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
    handlers
    Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
    上表中的参数format可以通过logging模块中定义好模式来设定值:
    字段/属性名称
    使用格式
    描述
    asctime
    %(asctime)s
    将日志的时间构造成可读的形式,默认情况下是‘2019-03-28 00:00:00,000’的形式,精确到毫秒
    name
    %(name)s
    所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
    filename
    %(filename)s
    调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
    funcName
    %(funcName)s
    由哪个function发出的log, 调用日志输出函数的函数名
    levelname
    %(levelname)s
    日志的最终等级(被filter修改后的)
    message
    %(message)s
    日志信息, 日志记录的文本内容
    lineno
    %(lineno)d
    当前日志的行号, 调用日志输出函数的语句所在的代码行
    levelno
    %(levelno)s
    该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
    pathname
    %(pathname)s
    完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
    process
    %(process)s
    当前进程, 进程ID。可能没有
    processName
    %(processName)s
    进程名称,Python 3.1新增
    thread
    %(thread)s
    当前线程, 线程ID。可能没有
    threadName
    %(thread)s
    线程名称
    module
    %(module)s
    调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文件名
    created
    %(created)f
    当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
    relativeCreated
    %(relativeCreated)d
    输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging模块加载时间的相对毫秒数
    msecs
    %(msecs)d
    日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉asctime中产生的毫秒部分,可以用这个加上
    所以,结合上表中的内容,我们可以实现让每一条日志记录输出事件发生时间、事件发生位置、日志级别、事件内容等信息。
    现在,我们来给刚才的日志添加一些输出,例如每条日志输出日志时间、日志级别、所在模块名、函数名、行号等信息,并指定时间输出格式,最后把日志输出到当前目录下的.log文件中。代码如下:
    import logging
    fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
    datefmt = '%Y-%m-%d %H:%M:%S %a'
    logging.basicConfig(level=logging.DEBUG,
    format=fmt,
    datefmt=datefmt,
    filename=".log")
    logging.debug('debug')
    logging.info('info')
    logging.warn('warn')
    logging.error('error')
    logging.critical('critical')
    运行上述代码后,控制台不会再有输出了,但当前目录下的.log文件会写入一下内容:
    2019-03-28 16:34:08 Thu , DEBUG , log_test.py <module> line 8 , debug
    2019-03-28 16:34:08 Thu , INFO , log_test.py <module> line 9 , info
    2019-03-28 16:34:08 Thu , WARNING , log_test.py <module> line 10 , warn
    2019-03-28 16:34:08 Thu , ERROR , log_test.py <module> line 11 , error
    2019-03-28 16:34:08 Thu , CRITICAL , log_test.py <module> line 12 , critical

    3.2 记录日志之logging四大组件

    logging四大组件是logging日志记录的高级用法。四大组件包括Logger、Handelr、Filter、Formater,且都是以类的形式来使用。logging四大组件协同工作流如下如所示:
    各组件功能如下:
    组件名称
    对应类名
    功能描述
    日志器
    Logger
    用于提供日志接口,常用于配置和发送日志消息
    处理器
    Handler
    用于写入日志并输出到指定位置,例如控制台、文件或网络位置等
    过滤器
    Filter
    对日志记录进行进一步过滤,输出符合条件的日志记录
    格式器
    Formatter
    配置日志记录的最终输出格式
    (1)日志器:Logger
    日志器Logger以工厂化的形式返回一个Logger类实例。一般而言,大多使用下面的方法获得Logger类实例:
    logging.getLogger(name)
    属性name是为Logger实例指定的名称,如果使用同一个名称进行实例化,则实际上只是将后面实例对象名指向前面的同名Logger实例。在使用logging模块时,系统会自动实例化一个名为root的日志器(根日志器),当未指定name属性时,事实上就是将变量名指向跟日志器
    另外,Logger实例具有层级继承的特点,层级之间已“.”连接,例如:“a.b”,“a.b.c”,a是父日志器,b是子日志器,在未对子日志器进行配置情况下,子日志器默认继承父日志器的配置,对子日志器重新配置不会影响父日志器。这一点很重要,在多模块中记录日子是可以使用这一特性,我们在下文代码中实践这一特性。根日志器是所有日志器的默认父日志器。
    Logger类还有以下的常用方法:
    • logger.setLevel() :设置日志器处理日志信息的最低级别
    • logger.addHandler():为该logger对象添加一个handler对象
    • logger.removeHandler():为该logger对象添加移除一个handler对象
    • logger.addFilter():为该logger对象添加一个filter对象
    • logger.removeFilter():为该logger对象移除一个filter对象
    • logger.debug(),logger.info(),logger.warning(),logger.error(),logger.critical():创建一个对应等级的日志记录
    (2)处理器:Handler
    Handler实例用于将日志记录发送到指定的位置进行输出。一个logger对象可以添加多个handler(例如既要在控制台输出日志,又要将日志写入到文件A,还要讲日志写入文件B,这就可以配置3个handler),每个handler又可以定义不同日志级别,以实现日志分级过滤显示。常用的方法包括:
    • handler.setLevel():设置handler处理的日志信息最低级别
    • handler.setFormatter():为handler设置一个格式器对象
    • handler.addFilter():为handler添加一个过滤器对象
    • handler.removeFilter():为handler删除一个过滤器对象
    要注意的是,在实际开发中,最好不要直接使用Handler类,应根据实际的功能需要,实例化Handler类的子类。Handler类的之类包括:
    Handler
    描述
    logging.StreamHandler
    将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
    logging.FileHandler
    将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
    logging.handlers.RotatingFileHandler
    将日志消息发送到磁盘文件,并支持日志文件按大小切割
    logging.hanlders.TimedRotatingFileHandler
    将日志消息发送到磁盘文件,并支持日志文件按时间切割
    logging.handlers.HTTPHandler
    将日志消息以GET或POST的方式发送给一个HTTP服务器
    logging.handlers.SMTPHandler
    将日志消息发送给一个指定的email地址
    logging.NullHandler
    该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。
    通过代码来演示一下,功能如下:在控制台输出日志(日志级别为debug),同时将日志写入到文件a.log文件(日志级别为debug),还要讲日志写入文件b.log文件(日志级别为warn):
    import logging
     
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
     
    # 控制台输出
    con_handler = logging.StreamHandler()
    con_handler.setLevel(logging.INFO)
    logger.addHandler(con_handler)
     
    # 输出到文件a.log
    file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
    file_a_handler.setLevel(logging.DEBUG)
    logger.addHandler(file_a_handler)
     
    # 输出到文件b.log
    file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
    file_b_handler.setLevel(logging.WARNING)
    logger.addHandler(file_b_handler)
     
    if __name__=='__main__':
    logger.debug('debug msg')
    logger.info('info msg')
    logger.warning('warn msg')
    运行上面代码后,控制台输出如下:
    info msg
    warn msg
    文件a.log会写入一下内容:
    debug msg
    info msg
    warn msg
    文件b.log会写入以下内容:
    warn msg
    注意:在一个日志器中添加多个handler时要注意,最好通过logger.setLevel(logging.DEBUG)先设置一下logger本身的日志级别,如果某个handler的级别比logger的日志级别低,那么该handler的日志级别无效,handler会以logger的级别来处理。
    (3)格式器:Formatter
    Formatter类实例用于配置日志记录的内容、结构等信息。可以通过以下三个参数进行配置:
    • fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
    • datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
    • style:指定格式化占位符,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'
    fmt的使用方法可以参照上文中介绍过的logging.basicConfig函数format参数的配制方法。
    例:每条日志输出日志时间、日制定及、所在模块名、函数名、行号等信息,并指定时间输出格式,最后把日志输出到控制台。代码如下:
    import logging
     
    logger = logging.getLogger(__name__)
    handler = logging.StreamHandler()
    logger.setLevel(logging.DEBUG)
    # 定义格式器,添加到处理器中
    fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
    datefmt = '%Y-%m-%d %H:%M:%S %a'
    log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
    handler.setFormatter(log_fmt)
     
    logger.addHandler(handler)
     
    logger.debug('debug msg')
    logger.info('info msg')
    控制台输出如下:
    2019-03-29 19:36:03 Fri , DEBUG , log_test2.py <module> line 14 , debug msg
    2019-03-29 19:36:03 Fri , INFO , log_test2.py <module> line 15 , info msg
     
    (4)过滤器:Filter
    在我们已经知道的logging使用方法中,都是通过日志级别来控制日志是否输出,Filter能够实现更加强大的过滤功能,控制日志输出。自定义的过滤器中必须覆写filter方法,当filter的返回值判断为True则允许输出,反之不允许输出。例如过滤包含敏感信息的日志,过滤器定义如下:
    import logging
     
    class CountryFilter(logging.Filter):
    def filter(self,record):
    return "America" not in record.getMessage()
     
    logger = logging.getLogger()
    handler = logging.StreamHandler()
    logger.addHandler(handler)
    logger.setLevel(logging.DEBUG)
    logger.addFilter(CountryFilter())
     
    logger.critical('I love America')
    logger.debug('I love China')
    输出结果:
    I love China
    可以看到,虽然第一条日志记录的日志等级更高,但是因为包含了过滤器中包含的敏感信息,所以不被允许输出。

     4 logging奇淫巧技

    4.1 记录异常信息:捕获traceback

    如果在日志中,只是记录发生了异常,那其实作用不大,如果traceback也记录到日志中,那就完美了。强大的logging确实也提供了这一功能,而且使用也很简单:
    import logging
     
    logger = logging.getLogger(__name__)
    handler = logging.FileHandler('./.log',encoding='utf-8')
    logger.setLevel(logging.DEBUG)
    # 定义格式器,添加到处理器中
    fmt = '%(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
    datefmt = '%Y-%m-%d %H:%M:%S %a'
    log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
    handler.setFormatter(log_fmt)
     
    logger.addHandler(handler)
     
    try:
    logger.info('Running …')
    1/0
    except Exception as e:
    logger.error('Exception occurs!',exc_info = True)
    # logger.exception(e) # 与上面这行效果一样
    运行后,文件.log会被写入以下内容:
    2019-03-29 19:53:14 Fri , INFO , log_test2.py <module> line 15 , Running …
    2019-03-29 19:53:14 Fri , ERROR , log_test2.py <module> line 18 , Exception occurs!
    Traceback (most recent call last):
    File "E:/myCode/test1/log_test2.py", line 16, in <module>
    1/0
    ZeroDivisionError: division by zero

    4.2 多模块共享日志

    在开发过程中,经常出现多个模块都需要记录日志的情况,也许你想到的做法是在一个模块中配置好一个logger并实例化,在需要用到的模块中进行导入,但如果不同模块的日志器配置有区别时,这种方法就不适用了,若是为每个模块都定义一个logger,所有配置都需要重新写入,有些繁琐。还记得上文中提到logging的日志器可以通过name属性进行分层吗?子日志器可以继承父日志器的配置,也可以重新配置,这就是logging给我们提供的多模块共享日志的解决方案。看代码:
    模块main.py中的代码:
    import logging
    import log_child
    logger = logging.getLogger('main')
    logger.setLevel(logging.DEBUG)
     
    fmt = '%(name)s , %(asctime)s , %(levelname)s , %(filename)s %(funcName)s line %(lineno)s , %(message)s'
    datefmt = '%Y-%m-%d %H:%M:%S %a'
    log_fmt = logging.Formatter(fmt=fmt, datefmt=datefmt)
     
    handler = logging.FileHandler('./.log',encoding='utf-8')
    handler.setFormatter(log_fmt)
     
    logger.addHandler(handler)
     
    if __name__=='__main__':
    logger.debug('Running …')
    log_child.fun_child()
    模块child_log.py中的代码:
     
    import logging
     
    logger = logging.getLogger('main.child')
    logger.setLevel(logging.DEBUG)
    def fun_child():
    try:
        logger.info('Running …')
        1 / 0
    except Exception as e:
        logger.exception(e)
    运行main.py后,.log文件会被写入一下内容:
    main , 2019-03-29 20:23:32 Fri , DEBUG , main.py <module> line 16 , Running …
    main.child , 2019-03-29 20:23:32 Fri , INFO , log_child.py fun_child line 7 , Running …
    main.child , 2019-03-29 20:23:32 Fri , ERROR , log_child.py fun_child line 10 , division by zero
    Traceback (most recent call last):
    File "E:myCode est1log_child.py", line 8, in fun_child
    1 / 0
    ZeroDivisionError: division by zero

    4.3 使用配置文件配置logger

    我们之前的程序中都是将对logger的配置一并写在程序中,但事实上,采用配置化编程的方式,将对logger的配置写在专门的配置文件中,例如写入json文件、conf文件、yaml文件等文件中,当需要实例化logger时,读取即可。下面以conf文件为例,通过代码注释的方式,介绍logging配置文件的书写方式。配置文件log.conf如下:
     
    [loggers] #固定写法,定义logger的模块
    keys=root,log_1,log_2 #创建三个logger,root是父类,必需存在的,其他两个logger的name分别为
     
    [logger_root] # 定制上面的logger,严格要求格式为"logger_loggername",必须通过loggername与上面的logger一一对应
    level=DEBUG # 设置日志级别
    qualname=root # 对于root,其实这里可以不填,默认就是root
    handlers=debugs #设置指定处理器,如果有多个处理器,中间以逗号分隔,这个名字待会儿 我们会以固定格式"handler_(value)"创建
     
    [logger_log_1]
    level=INFO
    qualname=log_1 #除了root以外,必须要设置这个属性,用于定义打印输出时候的logger名
    handlers=infos
    propagate=0 # 是否将消息想父日志传递,0表示不传递,1表示传递。如果向上传递,父日志器接收到消息后会以父日志器的配置再次处理该消息,所以可能所有多次输出
     
    [logger_log_2]
    level=WARNING
    qualname=log_2
    handlers=warns
     
    [handlers] #固定格式, 开始定义处理器
    keys=debugs,infos,warns#定义过滤器名称,与上面出现的handlers的值一一对应,下面定义以handler_handlername格式定义
     
    [handler_debugs]
    class=StreamHandler # 指定处理器的类名
    level=DEBUG # 设置级别
    formatter=form01 #定义格式器,名称为form01,下面会创建formatters,格式也是严格要求为formatter_formattername
    args=(sys.stdout,) # 控制台输出
     
    [handler_infos]
    class=FileHandler
    level=INFO
    formatter=form02
    args=('b.log','a')
     
    [handler_warns]
    class=FileHandler
    level=WARNING
    formatter=form02
    args=('a.log','a')# 写入到文件,写入方式
     
    [formatters] #固定格式
    keys=form01,form02 #定义名称,下面会引用格式,与上面出现的formatter的值对应
     
    [formatter_form01]
    format=%(asctime)s %(message)s # 定义消息输出格式,内容
    datefmt=%Y-%m-%d %H:%M:%S #日期输出格式
     
    [formatter_form02]
    format=%(asctime)s %(filename)s %(levelname)s %(message)s
    datefmt=%Y-%m-%d %H:%M:%S
    实例化logger:
    # _*_coding:utf-8_*_
    import logging
    from logging.config import fileConfig
     
    fileConfig('log.conf')
    root= logging.getLogger(name="root")
    log_1= logging.getLogger(name="log_1")
    log_2= logging.getLogger(name="log_2")
     
    root.debug('root_debug')
    root.info('root_info')
    root.warning('root_warning')
    log_1.debug('log_1_debug')
    log_1.info('log_1_info')
    log_1.warning('log_1_warning')
    log_2.debug('log_2_debug')
    log_2.info('log_2_info')
    log_2.warning('log_2_warning')
    程序运行后,控制台输出如下:
    2019-03-29 21:43:24 root_debug
    2019-03-29 21:43:24 root_info
    2019-03-29 21:43:24 root_warning
    a.log文件将被写入以下内容:
    2019-03-29 21:43:24 main.py INFO log_1_info
    2019-03-29 21:43:24 main.py WARNING log_1_warning
    b.log文件将被写入以下内容:
    2019-03-29 21:43:24 main.py WARNING log_2_warning

    4.3 日志回滚

    什么是日志回滚呢?咋一听,好像不知道是什么东西。日志回滚就是按照日期或者时间(有时候甚至是日志和时间综合作用),对日志进行分割或者删除。实际开发中经常需要用到,因为随着应用的持续运行,日志文件会越来越庞大,对系统的性能产生影响,所以有必要删除早起的日志。
    logging中提供了两个处理器用于日志回滚,一个是RotatingFileHandler,它主要是根据日志文件的大小进行滚动,另一个是TimeRotatingFileHandler,它主要是根据时间进行滚动。
    (1)根据文件大小进行回滚
    按文件大小回滚的类是RotatingFileHandler:
     
    # -*- coding:utf-8 -*-
    import logging
    from logging.handlers import RotatingFileHandler
     
    logger = logging.getLogger('main')
    logger.setLevel(level = logging.INFO)
    # 定义一个RotatingFileHandler,最多备份三个日志文件, 每个日志文件最大1k
    file_handler = RotatingFileHandler(".log",maxBytes = 1*1024,backupCount = 3)
     
    file_handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(formatter)
     
    cons_handler = logging.StreamHandler()
    cons_handler.setLevel(logging.DEBUG)
    cons_handler.setFormatter(formatter)
     
    logger.addHandler(file_handler)
    logger.addHandler(cons_handler)
     
    if __name__=='__main__':
        while True:
            logger.debug("debug")
            logger.info("info")
            logger.warning("warning")
            logger.critical("critical")
    上述程序执行后,将持续在控制台输出所有的日志记录,日志记录文件有三个,循环向日志文件中写入日志,当文件大小达到1kb时,开始在另一个文件删除日志记录,并写入新的日志记录。
    (2)根据时间进行回滚。
    按文件时间回滚的类时TimeRotatingFileHandler,这一个类包含以下参数:
    filename :输出日志文件名的前缀,比如main.log
    when 是一个字符串的定义如下:
    “S”: Seconds
    “M”: Minutes
    “H”: Hours
    “D”: Days
    “W”: Week day (0=Monday)
    “midnight”: Roll over at midnight
    interval 是指等待多少个单位when的时间后
     
    import time
    import logging
    import logging.handlers
     
    # logging初始化工作
    logging.basicConfig()
     
    # logger的初始化工作
    logger = logging.getLogger('main')
    logger.setLevel(logging.INFO)
     
    # 添加TimedRotatingFileHandler
    # 定义一个1秒换一次log文件的handler
    # 保留3个旧log文件
    timefilehandler = logging.handlers.TimedRotatingFileHandler(".log", when='S', interval=1, backupCount=3)
    # 设置后缀名称,跟strftime的格式一样
    timefilehandler.suffix = "%Y-%m-%d_%H-%M-%S.log"
     
    formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
    timefilehandler.setFormatter(formatter)
    logger.addHandler(timefilehandler)
     
    while True:
        time.sleep(0.1)
        logger.debug("debug")
        logger.info("info")
        logger.warning("warning")
        logger.critical("critical")

    5 总结

    本篇系统得总结了Python内容的日志记录模块logging的用法,囊括了logging的大部分内容。掌握本篇内容,感觉在开发中基本没有问题。
    参考:
     
  • 相关阅读:
    禁止使用U盘和移动硬盘
    Linux下Red5安装和配置
    ORACLE 10g下载地址
    常挂在美国人嘴边的最酷口语
    关于oracle中spfile和pfile的一个实验
    nginx搭建流媒体服务器
    powerDesigner 把name项添加到注释(comment),完美方案!
    memcached全面剖析–PDF总结篇
    PD 导出SQL语句
    本地读取虚拟机系统中的资源
  • 原文地址:https://www.cnblogs.com/chenhuabin/p/10633731.html
Copyright © 2011-2022 走看看