39.4-日志流、Formater、Filter

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

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)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容