zoukankan      html  css  js  c++  java
  • (十七)logging模块

    logging模块是Python内置的标准模块,主要用于输出运行日志。

    简单应用

    import logging
    logging.debug('+++debug+++')
    logging.info('+++info+++')
    logging.warning('+++warning+++')
    logging.error('+++error+++')
    logging.critical('+++critical+++')
    结果:

    WARNING:root:+++warning+++
    ERROR:root:+++error+++
    CRITICAL:root:+++critical+++

    可见,默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),默认的日志格式为     日志级别:Logger名称:用户输出消息

    灵活配置日志级别,日志格式,输出位置

    import logging
    logging.basicConfig(level=logging.DEBUG,                                                             #级别为DEBUG,所以五条都打出来了
                        format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',   #依次为时间、文件名、行号、日志级别、内容
                        datefmt='%a, %d %b %Y %H:%M:%S',                                                 #日期时间的格式
                        filename='test.log',                                                             #日志存储的指定文件
                        filemode='w')                                                                    #默认是a追加,w是写入
    
    logging.debug('+++debug+++')
    logging.info('+++info+++')
    logging.warning('+++warning+++')
    logging.error('+++error+++')
    logging.critical('+++critical+++')
    test.log内容:
    
    Tue, 15 Jan 2019 20:20:06 logging_lesson.py [line:9] DEBUG +++debug+++
    Tue, 15 Jan 2019 20:20:06 logging_lesson.py [line:10] INFO +++info+++
    Tue, 15 Jan 2019 20:20:06 logging_lesson.py [line:11] WARNING +++warning+++
    Tue, 15 Jan 2019 20:20:06 logging_lesson.py [line:12] ERROR +++error+++
    Tue, 15 Jan 2019 20:20:06 logging_lesson.py [line:13] CRITICAL +++critical+++

    可见在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
    filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
    filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”追加,还可指定为“w”写入。
    format:指定handler使用的日志显示格式。 
    datefmt:指定日期时间格式。 
    level:设置rootlogger(后边会讲解具体概念)的日志级别 
    stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

    format参数中可能用到的格式化串:
    %(name)s Logger的名字
    %(levelno)s 数字形式的日志级别
    %(levelname)s 文本形式的日志级别
    %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
    %(filename)s 调用日志输出函数的模块的文件名
    %(module)s 调用日志输出函数的模块名
    %(funcName)s 调用日志输出函数的函数名
    %(lineno)d 调用日志输出函数的语句所在的代码行
    %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
    %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
    %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
    %(message)s用户输出的消息

    logger对象

    import logging
    logger = logging.getLogger()                        #返回一个logger对象
    
    fh = logging.FileHandler('test2_log')               #创建一个handler,用于写入日志文件
    ch = logging.StreamHandler()                        #再创建一个handler,用于输出到控制台
    
    fm = logging.Formatter('%(asctime)s %(message)s')   #创建一个格式,将用于上面的两个handler
    
    fh .setFormatter(fm)                                #两个handler设定格式
    ch.setFormatter(fm)
    
    logger.addHandler(fh)                               #两个handler都添加进logger对象
    logger.addHandler(ch)
    
    logger.setLevel('DEBUG')                            #设置logger的日志级别
    
    logger.debug('+++debug+++')                         #正式调用logger对象打印日志
    logger.info('+++info+++')
    logger.warning('+++warning+++')
    logger.error('+++error+++')
    logger.critical('+++critical+++')

    结果:控制台输出和test2_log文件中的内容都是:
    2019-01-15 23:29:18,465 +++debug+++
    2019-01-15 23:29:18,465 +++info+++
    2019-01-15 23:29:18,465 +++warning+++
    2019-01-15 23:29:18,465 +++error+++
    2019-01-15 23:29:18,465 +++critical+++

    如上图,fh和ch作为两个handler对象(一个负责写入文件,一个负责控制台输出),都"吃"掉了fm这个格式对象,又都被最后负责打印日志的logger对象"吃"掉。

     使用logger对象是我们常用的方式,Logger是一个树形层级结构, logger = logging.getLogger()返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置。
    当然也可以通过Logger.setLevel(lel)指定最低的日志级别,可用的日志级别有logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR、logging.CRITICAL。

    但是有两个注意点:

    1.只要logging.getLogger(name)中名称参数name相同则返回的Logger实例就是同一个,且仅有一个

    import logging
    logger1 = logging.getLogger('mylogger')
    logger1.setLevel(logging.DEBUG)
    
    logger2 = logging.getLogger('mylogger')
    logger2.setLevel(logging.INFO)
    
    fh = logging.FileHandler('test1_log')
    ch = logging.StreamHandler()
    
    logger1.addHandler(fh)
    logger1.addHandler(ch)
    logger2.addHandler(fh)
    logger2.addHandler(ch)
    
    logger1.debug('logger1+++debug+++')
    logger1.info('logger1+++info+++')
    logger1.warning('logger1+++warning+++')
    logger1.error('logger1+++error+++')
    logger1.critical('logger1+++critical+++')
    logger2.debug('logger2+++debug+++')
    logger2.info('logger2+++info+++')
    logger2.warning('logger2+++warning+++')
    logger2.error('logger2+++error+++')
    logger2.critical('logger2+++critical+++')
    结果:控制台输出和test1_log文件中的内容都是:

    logger1+++info+++                               #logger1虽然设置了日志级别为DEBUG,但遵从logger2设置的日志级别INFO,因为两者对应的是同一个Logger实例
    logger1+++warning+++
    logger1+++error+++
    logger1+++critical+++
    logger2+++info+++
    logger2+++warning+++
    logger2+++error+++
    logger2+++critical+++

    logger1.setLevel(logging.DEBUG)将logger1的日志级别设置为了DEBUG,为何显示的时候没有显示出DEBUG级别的日志信息,而是从INFO级别的日志开始显示呢?

    原来logger1和logger2对应的是同一个Logger实例,只要logging.getLogger(name)中名称参数name相同则返回的Logger实例就是同一个,且仅有一个,就是说name与Logger实例一一对应。在logger2实例中通过logger2.setLevel(logging.INFO)设置mylogger的日志级别为logging.INFO,所以最后logger1的输出遵从了后来设置的日志级别。

    所以,创建Logger时,如果是需要两个实例对象,就要在logging.getLogger(name)中把name参数区分开。

    2."子实例"的handler进行处理时,也会传递给所有的"父实例"Logger处理,所以子实例的处理会被执行多次。

    import logging
    logger = logging.getLogger()
    
    logger2 = logging.getLogger('mylogger2')
    logger2.setLevel(logging.INFO)
    
    ch = logging.StreamHandler()
    
    logger.addHandler(ch)
    logger2.addHandler(ch)
    
    logger.debug('logger+++debug+++')
    logger.info('logger+++info+++')
    logger.warning('logger+++warning+++')
    logger.error('logger+++error+++')
    logger.critical('logger+++critical+++')
    logger2.debug('logger2+++debug+++')
    logger2.info('logger2+++info+++')
    logger2.warning('logger2+++warning+++')
    logger2.error('logger2+++error+++')
    logger2.critical('logger2+++critical+++')
    结果:

    logger+++warning+++
    logger+++error+++
    logger+++critical+++
    logger2+++info+++                               #logger2执行了两次
    logger2+++info+++
    logger2+++warning+++
    logger2+++warning+++
    logger2+++error+++
    logger2+++error+++
    logger2+++critical+++
    logger2+++critical+++

    通过logger = logging.getLogger()创建了root Logger,而logger2 = logging.getLogger('mylogger2')创建了root Logger的“子实例”logger2。而孩子,孙子,重孙……的handler进行处理时,也会传递给所有祖先的Logger处理,所以logger2被执行了两边。

    也就是说,子实例工作的时候会依次去找他的父辈(树结构,可能有好几层父辈)有没有在工作,如果有一个父辈在工作,子实例就多工作一次。

    上述例子中,如果把logger.addHandler(ch)注释掉,logger2就只执行一遍。

  • 相关阅读:
    Notes of Daily Scrum Meeting(12.18)
    Notes of Daily Scrum Meeting(12.17)
    Notes of Daily Scrum Meeting(12.16)
    Notes of Daily Scrum Meeting(12.8)
    Notes of Daily Scrum Meeting(12.5)
    Notes of Daily Scrum Meeting(12.3)
    Notes of Daily Scrum Meeting(11.12)
    Linux中profile、bashrc、bash_profile之间的区别和联系
    Linux GCC编译
    mysql 5.7.16 远程连接
  • 原文地址:https://www.cnblogs.com/xulan0922/p/10274432.html
Copyright © 2011-2022 走看看