Python logging模块

发布时间:2019-08-26 07:19:24编辑:auto阅读(1759)

    logging模块简介

    logging模块是Python的一个标准库模块,开发过程中,可以通过该模块,灵活的完成日志的记录。

    logging模块提供了两种记录日志的方式:
        1)使用logging提供的模块级别的函数(logging.basicConfig,logging.debug,logging.info...)
        2)使用logging模块的组件(loggers,handlers,filters,formatters)

    下面会分别介绍这两种方式,这里建议使用第二种方式,使用 logging模块的组件可以更灵活的完成日志记录~

    logging模块的日志级别

    logging模块的日志级别(level)包括:DEBUG,INFO,WARNING,ERROR,CRITICAL~

    日志级别(level) 描述
    DEBUG 调试级别,一般用于问题的排查,日志的信息最为详细
    INFO 仅记录普通的信息,日志信息的详细程度仅次于DEBUG
    WARNING 警告信息,一般这类信息不会影响程序的正常运行
    ERROR 错误信息, 出现错误信息时,程序一般已不能正常运行
    CRITICAL 更严重的错误信息,程序不能继续运行

    从 DEBUG 到 CRITICAL,日志等级依次提高,即严重性逐步提升,日志的信息量依次减少:
    DEBUG < INFO < WARNING < ERROR < CRITICAL

    通过logging模块级别的函数记录日志

    示例如下:

    import logging  
    logging.debug('debug message')  
    logging.info('info message')  
    logging.warning('warning message')  
    logging.error('error message')  
    logging.critical('critical message')  
    
    输出结果:
    WARNING:root:warning message     # 默认的日志格式:日志级别 : Logger名称 : 用户输出消息。
    ERROR:root:error message
    CRITICAL:root:critical message

    这里仅输出了大于等于WARNING级别的日志,说明 logging模块 默认的日志级别为WARNING,即日志级别大于等于WARNING的才会被输出,且默认情况下,日志会直接打印到标准输出中~
     
    通过 logging模块 的basicConfig函数可灵活地配置日志级别,日志格式以及日志的输出位置:

    import logging
    
    logging.basicConfig(level=logging.DEBUG, 
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',                       # 日志格式
                        datefmt='%Y-%m-%d %H:%M:%S',    # 时间格式:2018-11-12 23:50:21
                        filename='/tmp/test.log',    # 日志的输出路径
                        filemode='a')                      # 追加模式
    
    logging.debug('debug message')
    logging.info('info message')
    logging.warning('warning message')
    logging.error('error message')
    logging.critical('critical message')

    程序运行后,日志信息会直接记录到 指定的文件中(/tmp/test.log),且日志级别为DEBUG,所以所有的日志都会被输出,日志信息如下:

    ➜  ~ cat /tmp/test.log
    
    2018-11-12 23:50:49 test.py[line:9] DEBUG debug message
    2018-11-12 23:50:49 test.py[line:10] INFO info message
    2018-11-12 23:50:49 test.py[line:11] WARNING warning message
    2018-11-12 23:50:49 test.py[line:12] ERROR error message
    2018-11-12 23:50:49 test.py[line:13] CRITICAL critical message

     
    logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:

    filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
    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 字符串形式的当前时间。默认格式是 “2018-11-13 00:00:00,896”。逗号后面的是毫秒
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
    %(message)s用户输出的消息

    通过logging模块的组件记录日志

    使用logging模块级别的函数记录日志,无法实现将日志输出到多个路径下。这里logging模块提供了4个组件,通过这些组件可实现将日志输出到多个路径下,且每个路径下的日志格式可单独配置~

    logging模块中用于记录日志的4大组件

    组件名称 功能描述
    Logger 日志器,提供了应用程序可一直使用的接口
    Handler 将 logger 产生的日志发送到指定的路径下(例如可以是终端,也可以是文件)
    Filter 若有多个 Logger,可根据名称过滤出指定的 Logger 来记录日志
    Formatter 定义日志格式

    logging模块组件的使用

    使用组件记录日志的大致步骤如下:
    1)logging.getLogger() 获取 logger对象
    2)创建一个或多个 handler,用于指定日志信息的输出流向
    3)创建一个或多个 formatter,指定日志的格式,并分别将 formatter 绑定到 上
    4)将 handler 绑定到 logger对象 上
    5)logger.setLevel(logging.DEBUG) 设置日志级别
    5)最后便可使用 logger对象 记录日志~
     
    示例

    import logging
    
    # 获取 logger对象
    logger = logging.getLogger()
    
    # 创建一个 handler,用于写入日志文件
    fh = logging.FileHandler('/tmp/test.log')
    
    # 再创建一个 handler,用于输出到控制台
    ch = logging.StreamHandler()
    
    # 创建一个 formatter,两个 handler 使用相同的日志格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    # 绑定 formatter 到 handler 上
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    
    # 绑定 handler 到 logger对象 上
    logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
    logger.addHandler(ch)
    
    # 设置日志级别
    logger.setLevel(logging.WARNING)
    
    logger.debug('logger debug message')
    logger.info('logger info message')
    logger.warning('logger warning message')
    logger.error('logger error message')
    logger.critical('logger critical message')

    查看输出结果:

    终端输出:
    2018-11-13 23:14:09,161 - root - WARNING - logger warning message
    2018-11-13 23:14:09,161 - root - ERROR - logger error message
    2018-11-13 23:14:09,161 - root - CRITICAL - logger critical message
    
    文件输出:
    ➜  ~ cat /tmp/test.log
    2018-11-13 23:14:09,161 - root - WARNING - logger warning message
    2018-11-13 23:14:09,161 - root - ERROR - logger error message
    2018-11-13 23:14:09,161 - root - CRITICAL - logger critical message

    也可以通过 handler 来设置日志级别,当使用 handler 来设置日志级别时,存在如下两种情况:
       1)若 handler 设置的日志级别小于等于 logger 的日志级别时,则以logger 的日志级别为准;
       2)若 handler 设置的日志级别大于 logger 的日志级别时,则以handler 的日志级别为准;
    简单而言就是,两者同时设置日志级别,以日志级别高的为准。由于不设置 logger 的日志级别,其默认日志级别就是 WARNING,所以不存在 handler 单独设置日志级别的情况~

    通过示例来验证:
    1)logger 日志级别设置为 DEBUG,logger 不设置(默认为WARNING)

    import logging
    
    logger = logging.getLogger()
    ch = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    ch.setLevel(logging.DEBUG)
    # logger.setLevel(logging.CRITICAL)
    
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    
    logger.debug('logger debug message')
    logger.info('logger info message')
    logger.warning('logger warning message')
    logger.error('logger error message')
    logger.critical('logger critical message')
    
    输出结果:
    2018-11-13 23:40:13,829 - root - WARNING - logger warning message
    2018-11-13 23:40:13,829 - root - ERROR - logger error message
    2018-11-13 23:40:13,829 - root - CRITICAL - logger critical message

    2)logger 日志级别设置为 CRITICAL,logger 设置为 ERROR

    import logging
    
    logger = logging.getLogger()
    ch = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    ch.setLevel(logging.CRITICAL)
    logger.setLevel(logging.ERROR)
    
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    
    logger.debug('logger debug message')
    logger.info('logger info message')
    logger.warning('logger warning message')
    logger.error('logger error message')
    logger.critical('logger critical message')
    
    输出结果:
    2018-11-13 23:41:51,279 - root - CRITICAL - logger critical message

    filter组件 的使用

    filter组件用来过滤 logger 对象,一个 filter 可以直接添加到 logger对象上,也可以添加到 handler 对象上。
     
    例如,定义一个filter: filter = logging.Filter('a.b'),当把这个 filter 添加到一个 handler 上,那么绑定了该 handler 的 多个 logger对象中,只有名字是 'a.b' 前缀的才能通过该 handler 输出日志~
     
    在 handler 上添加 filter:

    import logging
    
    logger1 = logging.getLogger('a.b.c')
    logger2 = logging.getLogger('a.c')
    
    # 定义一个 filter
    filter = logging.Filter(name='a.b')
    
    # 定义一个 handler
    ch = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    
    # 若两个 logger对象 的日志级别相同,且都是用通过一个 handler,可以在这个 handler 上设置日志级别
    ch.setLevel(logging.ERROR)
    
    # 在 handler 上放置过滤器
    ch.addFilter(filter)
    
    logger1.addHandler(ch)
    logger2.addHandler(ch)
    logger1.error('logger1 error message')
    logger2.error('logger2 error message')
    
    输出结果:
    2018-11-15 21:58:33,409 - a.b.c - ERROR - logger1 error message

    可以看到 logger2 被过滤,只有 logger1 输出日志~
     
    在 handler 上添加 filter:

    import logging
    
    logger1 = logging.getLogger('a.b.c')
    logger2 = logging.getLogger('a.c')
    
    # 定义一个 filter
    filter = logging.Filter(name='a.b')
    
    # 定义一个 handler
    ch = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    
    # 若两个 logger对象 的日志级别相同,且都是用通过一个 handler,可以在这个 handler 上设置日志级别
    ch.setLevel(logging.ERROR)
    
    # 在 logger 上放置过滤器
    logger1.addFilter(filter)
    logger2.addFilter(filter)
    
    logger1.addHandler(ch)
    logger2.addHandler(ch)
    logger1.error('logger1 error message')
    logger2.error('logger2 error message')

    输出结果一致,即仅有 logger1 输出日志。将 filter 添加到 logger 上,这个filter将直接作用于这个 logger,貌似这么意义不大,很少这样使用

    .................^_^

关键字