python logging的学习

           python中的logging模块提供了日志功能,官网document链接:https://docs.python.org/2/library/logging.html.python 的logging系统主要元素有四个:分别是logger,handler, filter,formatter.

        logger可以提供对日志消息做初步的处理(初步处理指的是对日志消息level的检查和使用logger中设置的filters进行筛选)只有经过初步处理的日志才会进一步交给logger中的Handlers去处理。Handler才是真正实际输出日志消息的地方,

如果没有Handler,则logger什么都干不了的。既然logger不是实际对日志进行输出的地方,所以logger就没有formatter的概念了,formatter只有针对日志消息才有意义,所以formatter需要在Handler中设置。假如logger中没有Handler会怎样呢:

example1:

#coding: utf-8
import os, sys
import logging
log = logging.getLogger()
log.setLevel(logging.INFO)
log.info('oh, no handler')
输出是:

No handlers could be found for logger "root"

通过输出可以看出,虽然有了logger,如果没有handler,则logger是输出不了我们想要的日志消息的。logging.getLogger()函数的原型为getLogger(name=None),name为一个logger对象名字,如果这个名字没有对应的logger对象,则创建它并返回一个logger对象,如果已经存在了,则直接返回。name是以“.”分割的名称。并且表示了各个logger之间的对象的关系的。比如name为'a.b'的logger对象是name为'a.b.c'的logger对象的parent logger,并且每个logger对象都有一个共同的parent logger对象,那就是root logger对象。需要注意的是要想获取root logger对象,只能是getLogger(),不能getLogger(name='root'),getLogger(name='root')获取的是一个名为'root'的logger对象。name以'.'分割表示的logger对象系统实际就是一个树形结构,例如名为'a.b.c.d'的logger对象,其parent logger有a.b.c、a.b、a、root logger对象。从树的遍历来看,则从根开始遍历到目的节点这一路上访问的所有的logger都是目的节点的parent logger对象,只不过只有一个是直接的parent logger。

      既然Handler是实际输出日志消息的地方,那么我们就来增加一个Handler,然后看看效果如何。

example2:

#coding: utf-8
import os, sys
import logging
log = logging.getLogger()
log.setLevel(logging.INFO)
stm_hdr = logging.StreamHandler(sys.stdout)
log.addHandler(stm_hdr)
log.info('oh, finally we got a handler')
则输出为:

oh, finally we got a handler

从输出我们看到,增加了StreamHandler,果然将我们要的信息输了出来。具体logging提供了哪些类别的Handler则可以参考https://docs.python.org/2/library/logging.handlers.html,具体怎么使用,文档说的很清楚了。

在example2中我们可能注意到了setLevel这个函数。对于Handler和logger而言,都有level的概念,只有通过level检查的日志才会被输出。具体的日志级别有。

Level Numeric value
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0
日志级别的高低和对应的数值是一样的。以INFO级别为例,只有>=INFO级别的日志才能够通过level检查。也就是说只有INFO、WARNING、ERROR、CRITICAL级别的日志才能通过level检查,可怜的DEBUG直接被排除在外。

example3:

#coding: utf-8
import os, sys
import logging
log = logging.getLogger()
log.setLevel(logging.INFO)
stm_hdr = logging.StreamHandler(sys.stdout)
log.addHandler(stm_hdr)
log.debug('DEBUG LEVEL')
log.info('INFO LEVEL')
log.warn('WARN LEVEL')
log.error('ERROR LEVEL')
log.critical('CRITICAL LEVEL')
输出为:

INFO LEVEL
WARN LEVEL
ERROR LEVEL
CRITICAL LEVEL
很明显, DEBUG消失了,debug一般是在开发调试程序的时候才使用的,正式环境中,基本上没DEBUG啥事。

NOTSET是创建Handler对象和logger对象的默认值,如果当前的logger对象没有设置level,则会沿着parent logger对象找,直到找到一个有效的level为止,如果都没有设置level,那么使用此logger对象打印的日志信息都将被丢弃,如果有一个parent logger对象设置了有效level,那么将以第一个设置了level的parent logger对象的level为当前logger对象的有效level。

        logger和Handler都有自己的level,日志要想被输出,则其级别至少要 >= max(logger.leve, Handler.level)才行。文章开头就说了,logger的初步处理包含了level的检查和filters的筛选。说完了level,该说filter了。logging模块提供的filter是按照logger的名字来过滤的,当然我们也可以提供自己的filter.

example4:

#coding: utf-8
import os, sys
import logging
class Filter(object):
    def __init__(self, level):
        self.level = level

    #返回True则表示放行,False则表示被过滤掉
    def filter(self, record):
        if record.levelno == self.level:
            return True 
        return False 

log = logging.getLogger()
log.setLevel(logging.INFO)
stm_hdr = logging.StreamHandler(sys.stdout)
log.addHandler(stm_hdr)
filter = Filter(logging.INFO)
log.addFilter(filter)
log.debug('DEBUG LEVEL')
log.info('INFO LEVEL')
log.warn('WARN LEVEL')
log.error('ERROR LEVEL')
log.critical('CRITICAL LEVEL')
则输出结果为:

INFO LEVEL

很明显,filter起到作用了,在我们自定义的Filter类中,必须提供filter函数,参数为LogRecord对象。这个LogRecord对象的属性参见:https://docs.python.org/2/library/logging.html#logrecord-attributes

logger中有filter,Handler也可以使用filter。一个特别需要注意的点是,logger中对日志信息的初步处理只有调用的logger对象才会进行,而其parent logger对象是不会进行的。看例子

example5:

#coding: utf-8
import logging
import sys, os

child_log = logging.getLogger('parent.child')
child_log.setLevel(logging.INFO)
child_hdr = logging.StreamHandler(sys.stdout)
child_hdr.setLevel(logging.INFO)
child_log.addHandler(child_hdr)

parent_log = logging.getLogger('parent')
parent_log.setLevel(logging.WARN)
parent_hdr = logging.StreamHandler(sys.stdout)
parent_hdr.setLevel(logging.INFO)
parent_log.addHandler(parent_hdr)

print '-------------------------------------------------'
child_log.info('this is from child: [%s]' % child_log.name)
print '-------------------------------------------------'
parent_log.info('this is from parent: [%s]' % parent_log.name)
print '-------------------------------------------------'
输出为:

------------------------------------------------
this is from child: [parent.child]
this is from child: [parent.child]
-------------------------------------------------
-------------------------------------------------

从输出的结果可以看出child_log.info(...)输出了两句,parent_log的level为WARN,直接使用parent_log.info(...),INFO < WARN,所以level检查没有通过,但是当使用child_log的时候,parent_log却输出了,这里说明了logger对象对日志信息的“初步处理”(见文章开头对初步处理的说明)只在调用的logger中进行,在其parent logger中是不进行的。不然这里child_log.info应该只能输出一条才对。

        说到parent logger,不得不提logger的propagate属性,这个属性就决定了当前logger对象是否将处理的日志消息向parent传递下去,propagete默认为True,也就是传递,如果只不想parent logger处理,那么将propagate设置成False即可。

        介绍完了logger、handler、filter,那么就剩下formatter了,formatter是用来格式化日志信息的,具体能怎么格式化,可以参考:https://docs.python.org/2/library/logging.html#logrecord-attributes中的表格的format列,并且有详细的说明。

example6:

#coding: utf-8
import logging
import sys, os

def print_log():
    log = logging.getLogger()
    format_str = '|asctime:%(asctime)s|created:%(created)f|filename:%(filename)s|funcName:%(funcName)s|levelname:%(levelname)s|levelno:%(levelno)d|module:%(module)s|msecs:%(msecs)d|name:%(name)s|pathname:%(pathname)s|process:%(process)d|processName:%(processName)s|relativeCreated:%(relativeCreated)d|thread:%(thread)d|threadName:%(threadName)s|message:%(message)s'
    formatter = logging.Formatter(format_str)
    hdr = logging.StreamHandler(sys.stdout)
    hdr.setFormatter(formatter)
    log.addHandler(hdr)
    log.setLevel(logging.INFO)
    log.info('this message special for formatter!')

if __name__ == '__main__':
    print_log()
输出为:

|asctime:2015-05-09 14:37:08,919|created:1431153428.919038|filename:log1.py|funcName:print_log

|levelname:INFO|levelno:20|module:log1|msecs:919|name:root|pathname:log1.py|process:17323

|processName:MainProcess|relativeCreated:1|thread:139685574592256|threadName:MainThread

|message:this message special for formatter!

基本上已经把logging系统的四要素讲完了。个人总结一下,logger对象和Handler对象都可以拥有自己的level和filter,但是formatter是Handler独有的,Handler中的level和filter只针对自己有效,而logger中的level和filter则影响到此logger对象所有的Handler,可以说初步决定了日志信息的留舍。

另外对日志信息的加工过程可以参考这个图:https://docs.python.org/2/howto/logging.html#logging-flow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值