Python Logging 模块

go000o · September 18, 2019 · 360 hits

现在项目用的logging是配置的形式,配置文件中Logger只有root。配置文件如下:

[loggers]
keys=root

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
formatter=simpleFormatter
args=('test.log', 'w')


[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

在网上找了些资料后,我给logger加上了notification,目的我也不明确,想说加加看嘛,后面可能会用到,也做个学习。

[loggers]
keys=root,notification

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_notification]
level=WARNING
qualname=notification
handlers=consoleHandler,fileHandler

我在运行下面代码时,发现会打印2次

import logging.config
logging.config.fileConfig('logging.conf')
logging.getLogger('notification').warning('hello')

我一直不明白,后来看了logging的docs才知道为什么,链接贴出来,后面还要继续看https://docs.python.org/2.7/library/logging.html

答案就是这段话,logger notification是root的下级,所以它的消息会在它本身和它的上级的handler都进行打印。
所以改成下面这样就可以了,或者把handlers=空也可以。

[logger_notification]
level=WARNING
qualname=notification
handlers=consoleHandler,fileHandler
propagate=0

如果在加个logger notification.a,这个logger就是notification,root的下级,它的消息会通过本身,上级,上上级来打印。level也有层级考虑,如果自己没设置,就用上级的。如果设置了,就用自己的。


接着总结:
logging有4个主要的class,Logger,Handler, Filter,Formatter.
这4个类在产生log时遵循下面的流程。(图上的字有点小)

Logger01 = logging.getLogger('name')      #name可以不写,默认是root,同一个名字的Logger对象用同一套设置。
Logger01.setLevel(logging.DEBUG) #如果不设置,默认是WARNING等级

hand01 = logging.StreamHandler() #hand01是一个handler对象,为了让Logger对象可以设置
Logger01.addHandler(hand01) #Logger对象如果不设置是默认console输出

FORM01 = '%(asctime)s - %(name)s - %(message)s' #具体格式,让Formatter的对象设置,如果Formatter的对象不设置,则用默认格式
form01 = logging.Formatter(fmt=FORM01)
hand01.setFormatter(form01) #Handler对象设置Fommatter,用到了Fommatter的对象。Logger对象是不能直接设置Fomatter的,只能通过设置
#Handler对象间接设置Fomatter

def should_log(record): #这个是一个具体过滤条件,record是原始filter方法要求带的参数,后面给出的具体过滤条件都只能有一个record
if record.msg == 'right': #record.msg就是传入的信息
return True
else:
return False

filter01 = logging.Filter() #创建Filter的对象
filter01.filter = should_log #用should_log方法覆盖filter01.filter方法,如果不覆盖,就用默认的filter方法,如果覆盖了,就用自己写的方法。
Logger01.addFilter(filter01) #给Logger对象添加filter,这个filter还可以拿给handler对象添加


Logger01.debug('right') #执行这个语句就会在console打印出right,因为debug是符合logger01level的,并且,信息也是符合过滤条件的。
Logger01.debug('work') #不符合过滤条件,不会打印出来

总结就是,Fomatter对象是拿给Handler对象设置,Filter对象可以拿给Handler和Logger设置,Handler对象是拿给Logger设置,所有信息都是通过语句Logger.DEBUG('msg')来实现log的,这里面的DEBUG是可以换成其他等级的。而设置等级是Handler和Logger都可以设置的。如果都设置了,那就要都通过才会输出。
handler的设置默认是从孩子像祖先一层层查找的,如果都设置了,就会打印很多次,可以通过propagate=0来避免这种情况。


fileconfig里面没有filter的配置,因为不支持


import logging.config
import yaml

with open('logging.yml', 'r') as f_conf:
dict_conf = yaml.full_load(f_conf)

logging.config.dictConfig(dict_conf)

logger = logging.getLogger('simpleExample') #对应的是yaml文件中叫simpleExamplelogger对象的配置
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')



logging.yml的内容如下:
version: 1
formatters:
simple:
class: logging.Formatter
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
console_err:
class: logging.StreamHandler
level: ERROR
formatter: simple
stream: ext://sys.stderr
loggers:
simpleExample:
level: DEBUG
handlers: [console]
propagate: yes
root:
level: DEBUG
handlers: [console_err]

这个例子比较好理解,里面的key都是固定的,不能随便变。
但是这个例子不含filter,我也不知道怎么加。

我就改成了下面这样,加了filter,yaml中的内容不变。

import logging.config
import yaml

def should_log(record):
if record.msg == 'right':
return True
else:
return False

with open('logging.yml', 'r') as f_conf:
dict_conf = yaml.full_load(f_conf)

logging.config.dictConfig(dict_conf)

fil01 = logging.Filter()
fil01.filter = should_log

logger = logging.getLogger('simpleExample')
logger.addFilter(fil01)

logger.debug('right')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

出来的结果如下:
2019-09-20 16:52:19,326 - simpleExample - DEBUG - right

共收到 0 条回复 时间 点赞
go000o 关闭了讨论 19 Sep 14:57
go000o 重新开启了讨论 19 Sep 14:57
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up