• (十七)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就只执行一遍。

  • 相关阅读:
    五、nginx 配置实例-负载均衡
    四、nginx配置实例-反向代理
    三、nginx配置文件
    二、Nginx 安装
    十二、rpm包的管理
    十一、进程管理
    十、Linux磁盘分区、挂载
    九、定时任务调度
    八、组管理和权限管理
    七、实用指令
  • 原文地址:https://www.cnblogs.com/xulan0922/p/10274432.html
Copyright © 2020-2023  润新知