美文网首页
39.4-日志流、Formater、Filter

39.4-日志流、Formater、Filter

作者: BeautifulSoulpy | 来源:发表于2020-02-05 13:07 被阅读0次

生活就是这样,你跟身边的人交际越来越少,变得越发的独来独往。没有人跟你深交,也似乎没有深交的必要,最多是见面点头打个招呼。觉得以前的生活又傻逼又美好,很多东西舍不得删掉但你还是删掉了,生活就是再告别!

1. 处理器- Handler

Handler 控制日志信息的输出目的地,可以是控制台、文件。
可以单独设置level
可以单独设置格式Formatter
可以设置设置过滤器

Handler类继承

Handler 处理器类型有很多种,比较常用的有三个

StreamHandler # 不指定使用sys.stderr
.FileHandler # 文件
._StderrHandler # 标准输出
NullHandler # 什么都不做

日志输出其实是Handler做的,也就是真正干活的是Handler。
在logging.basicConfig中,如下:

if handlers is None:
    filename = kwargs.pop("filename", None)
    mode = kwargs.pop("filemode", 'a')
    if filename:
        h = FileHandler(filename, mode)
    else:
        stream = kwargs.pop("stream", None)
        h = StreamHandler(stream)
    handlers = [h]
#---------------------------------------------------

如果设置文件名,则为根logger加一个输出到文件的Handler;
如果没有设置文件名,则为根logger加一个StreamHandler,默认输出到sys.stderr。
也就是说,根logger一定会至少有一个handler;

思考
创建的Handler的初始level是什么?是root level = 0

import logging
logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s",datefmt="%Y%m%d!!!!!%H:%M:%s")

#创建一个新的logger ,未设定级别;
logger = logging.getLogger('test')
print(logger.name, type(logger))

logger.info('line 1')

handler = logging.FileHandler('f:/l1.log','w') # 创建handler
logger.addHandler(handler) # 给logger对象绑定一个handler

# 注意看控制台,再看l1.log文件,对比差异
# 思考这是怎么打印的
logger.info('line 2')  # %(message)s

2. 日志流

level的继承

import logging
FORMAT = '%(asctime)s %(name)s %(message)s'

logging.basicConfig(format=FORMAT, level=logging.INFO)
root = logging.getLogger()
log1 = logging.getLogger("s")
log1.setLevel(logging.ERROR) # 分别取INFO、WARNING、ERROR试一试

# 没有设置任何的handler、level
# log2有效级别就是log1的ERROR
log2 = logging.getLogger('s.s1')
log2.warning('log2 warning')

logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level。

继承关系及信息传递

每一个Logger实例的level如同入口,让水流进来,如果这个门槛太高,信息就进不来。例如
log3.warning('log3'),如果log3定义的级别高,就不会有信息通过log3;

如果level没有设置,就用父logger的,如果父logger的level没有设置,继续找父的父的,最终可以找到root
上,如果root设置了就用它的,如果root没有设置,root的默认值是WARNING
消息传递流程

  1. 如果消息在某一个logger对象上产生,这个logger就是当前logger,首先消息level要和当前logger的
    EffectiveLeve比较,如果低于当前logger的EffectiveLeve,则流程结束;否则生成log记录;
  2. 日志记录会交给当前logger的所有handle处理,记录还要和每一个handler的级别分别比较,低的不处理
    ,否则按照handler输出日志记录;
  3. 当前logger的所有handler 处理完后,就要看自己的propagate 属性,如果是True 表示向父logger传递之歌日志记录,否则到此流程结束;
  4. 如果日志记录传递到了父 logger, 不需要和logger的leve比较,而是直接交给父的所有handler,父
    logger成为当前 logger; 重复2,3步骤,直到当前logger的父logger是None退出,也就是说当前logger
    最后一般是root logger(是否能到root logger要看中间的logger是否允许propagate)。

logger实例初始的propagate属性为True,即允许向父logger传递消息
logging.basicConfig
如果root没有handler,就默认创建一个StreamHandler,如果设置了filename,就创建一个FileHandler。
如果设置了format参数,就会用它生成一个formatter对象,并把这个formatter加入到刚才创建的handler
上,然后把这些handler加入到root.handlers列表上。level是设置给root logger的。
如果root.handlers列表不为空,logging.basicConfig的调用什么都不做。

官方日志流程图

import logging

logging.basicConfig(format='%(name)s %(asctime)s %(message)s',level=logging.INFO)

root = logging.getLogger()
root.setLevel(logging.ERROR)
print('root ', root.handlers)

h0 = logging.StreamHandler()
h0.setLevel(logging.WARNING)

root.addHandler(h0)
print('root ', root.handlers)

for h in root.handlers:
    print("root handler = {}, formatter = {}".format(h, h.formatter))

log1 = logging.getLogger('s')
log1.setLevel(logging.ERROR)
h1 = logging.FileHandler('o:/test.log')
h1.setLevel(logging.WARNING)
log1.addHandler(h1)
print('log1 ', log1.handlers)

log2 = logging.getLogger('s.s1')
log2.setLevel(logging.CRITICAL)
h2 = logging.FileHandler('o:/test.log')
h2.setLevel(logging.WARNING)
log2.addHandler(h2)
print('log2 ', log2.handlers)

log3 = logging.getLogger('s.s1.s2')
log3.setLevel(logging.INFO)
print(log3.getEffectiveLevel())
log3.warning('log3')
print('log3 ', log3.handlers)


Formatter

loggingd的Formatter类,它允许指定某个格式的字符串。如果提供None,那么'%(message)s'将会作为默认值。修改上面的例子,让它看的更加明显。


import logging

logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s")

root = logging.getLogger()
root.setLevel(logging.ERROR)
print('root ', root.handlers)
h0 = logging.StreamHandler()
h0.setLevel(logging.WARNING)
root.addHandler(h0)
print('root ', root.handlers)
for h in root.handlers:
    print("root handler = {}, formatter = {}".format(h, h.formatter))
    
log1 = logging.getLogger('s')
log1.setLevel(logging.ERROR)
h1 = logging.FileHandler('o:/test.log')
h1.setLevel(logging.WARNING)
print('log1 formatter', h1.formatter) # 没有设置formatter使用缺省值'%(message)s'
log1.addHandler(h1)
print('log1 ', log1.handlers)
log2 = logging.getLogger('s.s1')
log2.setLevel(logging.CRITICAL)
h2 = logging.FileHandler('o:/test.log')
h2.setLevel(logging.WARNING)
print('log2 formatter', h2.formatter)

f2 = logging.Formatter("log2 %(name)s %(asctime)s %(message)s")
h2.setFormatter(f2)
print('log2 formatter', h2.formatter)
log2.addHandler(h2)
print('log2 ', log2.handlers)
log3 = logging.getLogger('s.s1.s2')
log3.setLevel(logging.INFO)
print(log3.getEffectiveLevel())
log3.warning('log3')
print('log3 ', log3.handlers)
Filter

可以为handler增加过滤器,所以这种过滤器只影响某一个handler,不会影响整个处理流程。但是,如果过滤器增加到logger上,就会影响流程。

import logging
FORMAT = '%(asctime)-15s\tThread info: %(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)

log1 = logging.getLogger('s')
log1.setLevel(logging.WARNING)  # ERROR试一试;

h1 = logging.StreamHandler()
h1.setLevel(logging.INFO)
fmt1 = logging.Formatter('log1-h1 %(message)s')
h1.setFormatter(fmt1)
log1.addHandler(h1)

log2 = logging.getLogger('s.s1')
#log2.setLevel(logging.CRITICAL)
print(log2.getEffectiveLevel()) # 继承父的level,WARNING

h2 = logging.StreamHandler()
h2.setLevel(logging.INFO)

fmt2 = logging.Formatter('log2-h2 %(message)s')
h2.setFormatter(fmt2)

f2 = logging.Filter('s') # 过滤器 s s.s1 s.s2
h2.addFilter(f2)

log2.addHandler(h2)
log2.warning('log2 warning')

消息log2的,它的名字是s.s1,因此过滤器名字设置为s或s.s1,消息就可以通过,但是如果是其他就不能通过,不
设置过滤器名字,所有消息通过
过滤器核心就这一句,在logging.Filter类的filter方法中

record.name.find(self.name, 0, self.nlen) != 0

本质上就是等价于 record.name.startswith(filter.name)

相关文章

网友评论

      本文标题:39.4-日志流、Formater、Filter

      本文链接:https://www.haomeiwen.com/subject/oitxzctx.html