现在项目用的 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是符合logger01的level的,并且,信息也是符合过滤条件的。
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文件中叫simpleExample的logger对象的配置
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,我也不知道怎么加。
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