生活就是这样,你跟身边的人交际越来越少,变得越发的独来独往。没有人跟你深交,也似乎没有深交的必要,最多是见面点头打个招呼。觉得以前的生活又傻逼又美好,很多东西舍不得删掉但你还是删掉了,生活就是再告别!
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
消息传递流程
- 如果消息在某一个logger对象上产生,这个logger就是当前logger,首先消息level要和当前logger的
EffectiveLeve比较,如果低于当前logger的EffectiveLeve,则流程结束;否则生成log记录;- 日志记录会交给当前logger的所有handle处理,记录还要和每一个handler的级别分别比较,低的不处理
,否则按照handler输出日志记录;- 当前logger的所有handler 处理完后,就要看自己的propagate 属性,如果是True 表示向父logger传递之歌日志记录,否则到此流程结束;
- 如果日志记录传递到了父 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)