Python-logging模块
1、日志级别:
日志级别指的是产生日志的事件的严重程度;
设置一个级别后,严重程度低于设置值的日志消息将被忽略 。
2、格式字符串
注意:funcName,threadName,processName 都是小驼峰命名
2、默认级别:
1 import logging 2 3 FORMAT = '%(asctime)-15s\t Thread info: %(thread)d %(threadName)s %(message)s' 4 logging.basicConfig(format=FORMAT) 5 6 logging.info('I am {}'.format(20)) 7 logging.warning('I am {}'.format(20))
1 2018-10-30 13:57:32,714 Thread info: 9568 MainThread I am 20
默认级别是warning,所以低于warning的就无法显示。
3、构建消息:(格式定义使用的是C 风格的)
级别修改为 INFO 级别,两条都显示了
1 import logging 2 3 FORMAT = '%(asctime)-15s\t Thread info: %(thread)d %(threadName)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logging.info('I am {}'.format(20)) 7 logging.warning('I am {}'.format(30))
1 2018-10-30 14:00:24,329 Thread info: 8724 MainThread I am 20 2 2018-10-30 14:00:24,329 Thread info: 8724 MainThread I am 30
使用字典,自定义一些显示信息(但是一般不会这么用,要不就直接定义到 FROMAT中)
1 import logging 2 3 FORMAT = '%(asctime)-15s\t Thread info: %(thread)d %(threadName)s %(message)s %(school)s' 4 logging.basicConfig(format=FORMAT, level=logging.WARNING) 5 6 d = {'school':'magedu'} 7 8 logging.info('I am {}'.format(20), extra=d) 9 logging.warning('I am {}'.format(30), extra=d)
1 2018-10-30 14:04:28,030 Thread info: 10184 MainThread I am 30 magedu
4、修改日期格式:注意定义格式的位置 basicConfig
1 import logging 2 3 FORMAT = '%(asctime)-15s\t Thread info: %(thread)d %(threadName)s %(message)s ' 4 logging.basicConfig(format=FORMAT, level=logging.INFO, datefmt="%Y:%m:%d -- %H:%M:%S") 5 6 logging.info('I am {}'.format(20)) 7 logging.warning('I am {}'.format(30))
1 2018:10:30 -- 14:15:49 Thread info: 9864 MainThread I am 20 2 2018:10:30 -- 14:15:49 Thread info: 9864 MainThread I am 30
5、输出到文件: 注意定义格式的位置 basicConfig
1 import logging 2 3 FORMAT = '%(asctime)-15s\t Thread info: %(thread)d %(threadName)s %(message)s ' - 是向左靠齐,如果数字小于长度,不会截断 4 logging.basicConfig(format=FORMAT, level=logging.INFO, 5 datefmt="%Y:%m:%d -- %H:%M:%S", 6 filename='f:/test.log' 7 ) 8 9 logging.info('I am {}'.format(20)) 10 logging.warning('I am {}'.format(30))
注:默认是追加模式,也就是追加模式,只为了输出到文件。
6、Logger类:
logging模块加载的时候,会创建一个root logger, 根Logger对象的默认级别是WARNING。
调用 logging.basicConfig 来调整级别,就是对这个根 Logger的级别进行修改
7、构造
logging.getLogger( [name= None) ]
使用工厂方法 返回 一个Logger实例
指定name,返回一个名称为 name 的 Logger实例,如果再次使用相同的名字,是实例化一个对象。
未指定 name, 返回的是 root Logger实例
1 import logging 2 3 FORMAT = '%(name)s %(asctime)s %(thread)d %(threadName)s %(message)s ' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logging.info('I am {}'.format(20)) 7 8 log = logging.getLogger() 9 print(log.name) 10 print(logging.root.name) 11 12 log1 = logging.getLogger('s') 13 print(log1.name) 14 15 log2 = logging.getLogger('s') 16 print(log2.name) 17 18 log3 = logging.getLogger('s1') 19 print(log3.name)
1 root 2 root 3 s 4 s 5 s1 6 root 2018-10-30 14:44:44,900 10024 MainThread I am 20
总结:
利用工程函数logging.getLogger(), 如果没有指定名字,就返回root ,如果执行,相当于创建一个新的logger对象,name是自己定义的。
如果在使用 工厂方法,name是之前的,也就是调用之前的logging对象,并没有创建新的 对象
8、层次构造:
Logger是层次结构的,使用 点号分割,如 a -- a.b ---a.b.c , a 是a.b的父,a.b是a的子。
1 import logging 2 3 FORMAT = '%(name)s %(asctime)s %(thread)d %(threadName)s %(message)s ' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logging.info('I am {}'.format(20)) 7 8 log = logging.getLogger() 9 print(log.name) 10 print(logging.root.name) 11 12 log1 = logging.getLogger('s') 13 print(log1.name) 14 15 log2 = logging.getLogger('s.s1') 16 print(log2.name) 17 18 print(log2.parent) 19 print(log2.parent.name) 20 print(log2.parent.parent) 21 print(log2.parent.parent.name)
root 2018-10-30 14:52:48,827 5520 MainThread I am 20 root root s s.s1 <Logger s (INFO)> s <RootLogger root (INFO)> root
测试: 级别测试
1 import logging 2 3 FORMAT = '%(asctime)s %(thread)d %(threadName)s %(message)s ' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logger = logging.getLogger(__name__) # 创建一个新的logger,未定义级别 7 print(logger.name, type(logger)) 8 print(logger.level) # 新建的 logger 是未定义级别的,所以自己的级别是 0 9 print(logger.getEffectiveLevel()) # 有效级别 也就是从父类流过来的, 20(这里并不是继承) 10 11 logger.debug('==== hello ====') # 不打印,级别小于INFO 12 logger.info('==== hello ====') #输出 13 logger.warning('==== hello ====') # 输出 14 15 16 logger.setLevel(10)# 这里是0,就从父logger来,不是0 就用自己的跟下面的比较 17 print(logger.level) # 30 18 logger.debug('==== h1 ====') # 输出,大于10(debug) 19 logger.info('==== h2 ====') # 20 logger.warning('==== h3 ====') # 输出
1 D:\python3.7\python.exe E:/code_pycharm/tt.py 2 2018-10-30 15:09:37,638 8468 MainThread ==== hello ==== 3 __main__ <class 'logging.Logger'> 4 2018-10-30 15:09:37,638 8468 MainThread ==== hello ==== 5 0 6 2018-10-30 15:09:37,638 8468 MainThread ==== h1 ==== 7 2018-10-30 15:09:37,638 8468 MainThread ==== h2 ==== 8 20 9 10 10 2018-10-30 15:09:37,638 8468 MainThread ==== h3 ==== 11 12 Process finished with exit code 0
1 import logging 2 3 FORMAT = '%(asctime)s %(thread)d %(threadName)s %(message)s ' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logger = logging.getLogger(__name__) # 创建一个新的logger,未定义级别 7 print(logger.name, type(logger)) 8 print(logger.level) # 新建的 logger 是未定义级别的,所以自己的级别是 0 9 print(logger.getEffectiveLevel()) # 有效级别 也就是从父类流过来的, 20(这里并不是继承) 10 11 logger.debug('==== hello ====') # 不打印,级别小于INFO 12 logger.info('==== hello ====') #输出 13 logger.warning('==== hello ====') # 输出 14 15 16 logger.setLevel(18)# 这里是0,就从父logger来,不是0 就用自己的跟下面的比较 17 print(logger.level) # 30 18 logger.debug('==== h1 ====') # 输出,大于10(debug) 19 logger.info('==== h2 ====') # 20 logger.warning('==== h3 ====') # 输出 21 print(logger.getEffectiveLevel(), '===') 22 23 print(logging.getLogger().getEffectiveLevel())
1 D:\python3.7\python.exe E:/code_pycharm/tt.py 2 2018-10-30 15:15:16,786 6624 MainThread ==== hello ==== 3 __main__ <class 'logging.Logger'> 4 0 5 2018-10-30 15:15:16,786 6624 MainThread ==== hello ==== 6 20 7 18 8 2018-10-30 15:15:16,787 6624 MainThread ==== h2 ==== 9 18 === 10 20 11 2018-10-30 15:15:16,787 6624 MainThread ==== h3 ==== 12 13 Process finished with exit code 0
每一个 logger创建后 ,都有一个等效的level
logger对象可以在创建后动态的修改自己的 level
9、Hander:
Hander 控制 日志信息的输出目的地,可以是控制台,文件
可以单独设置level
可以单独设置格式
可以设置过滤器
Handler 类 继承
Handler:
StreamHandler: # 不指定输出方式,默认是 sys.stderr
FileHandler: #文件
_SterrHandlerr # 标准输出
NullHandler # 什么都不做
日志输出其实是Handler 做的,也就是真正干活的是Handler。
在 logging.basicConfig 中,如下:
1 if handlers is None: 2 filename = kwargs.pop('filename', None) 3 mode = kwargs.pop('filemode','a') 4 if filename: 5 h = FileHandler(filename, mode) 6 else: 7 stream = kwargs.pop('stream', None) 8 h = StreamHandler(stream) 9 handlers = [h]
注:
如果 设置文件名,则为根 logger 加一个输出到文件的Handler;如果没有设置文件名,则为 根logger 加一个StreamHadler,默认输出到sys.stderr
也就是说, 根logger 一定会至少有一个handler的
新创建的logger 是没有 handler的,但是 根handler 是有一个默认的 streamhandler,所以新建的logger就用根logger的。
测试:Handler的默认level
1 import logging 2 3 FORMAT = '%(asctime)s %(name)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logger = logging.getLogger(__name__) 7 print(logger.name, type(logger)) 8 9 logger.info('line ---- 1') 10 11 handler = logging.FileHandler('f:\\ll.log', 'a') # filename, mode='a', encoding=None, delay=False 12 logger.addHandler(handler) 13 print(logger.handlers) # [<FileHandler f:\ll.log (NOTSET)>] 14 15 logger.info('line --- 2')
结果可以看出:
默认handler是没有设置的 NOTSET
定义的FileHanfler 是最简单的 就一个message 输出。
logger 这个对象,一个是利用的root的streamhandler 打印到stderr, 一个是自定义的filehandler ,输出到文件
测试:handler的level, 为了方便看文件,使用w 模式
1 import logging 2 3 FORMAT = '%(asctime)s %(name)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 logger = logging.getLogger(__name__) 7 print(logger.name, type(logger)) 8 9 logger.info('line ---- 1') 10 11 handler = logging.FileHandler('f:\\ll.log', 'w') # filename, mode='a', encoding=None, delay=False 12 logger.addHandler(handler) 13 handler.setLevel(50) 14 15 logger.info('line --- 2') 16 print(logger.handlers) # [<FileHandler f:\ll.log (NOTSET)>]
结果可以看到:
filehandler的level设置为50 ,不影响stderr 输出,只影响了文件输出。
因为logger的输出级别是info,小于 50,所以输出不到文件
10、继承关系 及信息传递:
propagate的使用:
官方日志流转图:
实例:
1 import logging 2 3 FORMAT = '%(asctime)s %(name)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 root = logging.getLogger() 7 root.setLevel(logging.ERROR) 8 print('root', root.handlers) 9 10 h0 = logging.StreamHandler() 11 h0.setLevel(logging.WARNING) 12 13 root.addHandler(h0) 14 print('root', root.handlers) 15 16 for h in root.handlers: 17 print('root handler = {}, formatter = {}'.format(h, h.formatter)) 18 19 ''' 20 root [<StreamHandler <stderr> (NOTSET)>] 21 root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>] 22 root handler = <StreamHandler <stderr> (NOTSET)>, formatter = <logging.Formatter object at 0x000000000224C908> 23 root handler = <StreamHandler <stderr> (WARNING)>, formatter = None 24 ''' 25 ################################################################################ 26 print('==================================================') 27 28 log1 = logging.getLogger('s') 29 log1.setLevel(logging.ERROR) 30 h1 = logging.FileHandler('f:/tets.log') 31 h1.setLevel(logging.WARNING) 32 log1.addHandler(h1) 33 print('h1', log1.handlers) 34 35 print('==================================================') 36 37 log2 = logging.getLogger('s.s1') 38 log2.setLevel(logging.CRITICAL) 39 h2 = logging.FileHandler('f:/tets.log') 40 h2.setLevel(logging.WARNING) 41 log2.addHandler(h2) 42 print('h2', log2.handlers) 43 44 print('==================================================') 45 46 log3 = logging.getLogger('s.s1.s2') 47 log3.setLevel(logging.INFO) 48 print(log3.getEffectiveLevel()) 49 h3 = logging.FileHandler('f:/tets.log') 50 h3.setLevel(logging.WARNING) 51 log3.addHandler(h3) 52 print('h3', log3.handlers)
1 D:\python3.7\python.exe E:/code_pycharm/tt2.py 2 root [<StreamHandler <stderr> (NOTSET)>] 3 root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>] 4 root handler = <StreamHandler <stderr> (NOTSET)>, formatter = <logging.Formatter object at 0x0000000001DD73C8> 5 root handler = <StreamHandler <stderr> (WARNING)>, formatter = None 6 ================================================== 7 h1 [<FileHandler f:\tets.log (WARNING)>] 8 ================================================== 9 h2 [<FileHandler f:\tets.log (WARNING)>] 10 ================================================== 11 20 12 h3 [<FileHandler f:\tets.log (WARNING)>] 13 14 Process finished with exit code 0
11、Formatter
logging的Formatter类,它允许指定某个格式的字符串,如果提供None,那么 %(message)s作为默认值
修改上面的实例:
1 import logging 2 3 FORMAT = '%(asctime)s %(name)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 root = logging.getLogger() 7 root.setLevel(logging.ERROR) 8 print('root', root.handlers) 9 10 h0 = logging.StreamHandler() 11 h0.setLevel(logging.WARNING) 12 13 root.addHandler(h0) 14 print('root', root.handlers) 15 16 for h in root.handlers: 17 print('root handler = {}, formatter = {}'.format(h, h.formatter)) 18 19 ''' 20 root [<StreamHandler <stderr> (NOTSET)>] 21 root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>] 22 root handler = <StreamHandler <stderr> (NOTSET)>, formatter = <logging.Formatter object at 0x000000000224C908> 23 root handler = <StreamHandler <stderr> (WARNING)>, formatter = None 24 ''' 25 ################################################################################ 26 print('==================================================') 27 28 log1 = logging.getLogger('s') 29 log1.setLevel(logging.ERROR) 30 h1 = logging.FileHandler('f:/tets.log') 31 h1.setLevel(logging.WARNING) 32 print('h1 handler = {}, formatter = {}'.format(h1, h1.formatter)) 33 log1.addHandler(h1) 34 print('h1', log1.handlers) 35 36 print('==================================================') 37 38 log2 = logging.getLogger('s.s1') 39 log2.setLevel(logging.DEBUG) 40 h2 = logging.FileHandler('f:/tets.log') 41 h2.setLevel(logging.WARNING) 42 43 print('log2 formatter', h2.formatter) 44 f2 = logging.Formatter('log2 %(name)s %(asctime)s %(message)s') 45 h2.setFormatter(f2) 46 log2.addHandler(h2) 47 log2.info('log2----') 48 print('log2 formatter', h2.formatter) 49 print('h2', log2.handlers)
D:\python3.7\python.exe E:/code_pycharm/tt2.py root [<StreamHandler <stderr> (NOTSET)>] root [<StreamHandler <stderr> (NOTSET)>, <StreamHandler <stderr> (WARNING)>] root handler = <StreamHandler <stderr> (NOTSET)>, formatter = <logging.Formatter object at 0x0000000001D773C8> root handler = <StreamHandler <stderr> (WARNING)>, formatter = None ================================================== h1 handler = <FileHandler f:\tets.log (WARNING)>, formatter = None h1 [<FileHandler f:\tets.log (WARNING)>] ================================================== log2 formatter None log2 formatter <logging.Formatter object at 0x0000000002A09128> h2 [<FileHandler f:\tets.log (WARNING)>] 2018-10-30 17:14:11,489 s.s1 log2---- Process finished with exit code 0
如何设置格式:
1 log2 = logging.getLogger('s.s1') 2 log2.setLevel(logging.DEBUG) 3 h2 = logging.FileHandler('f:/tets.log') 4 h2.setLevel(logging.WARNING) 5 6 print('log2 formatter', h2.formatter) 7 f2 = logging.Formatter('log2 %(name)s %(asctime)s %(message)s') 8 h2.setFormatter(f2) 9 log2.addHandler(h2) 10 log2.info('log2----') 11 print('log2 formatter', h2.formatter) 12 print('h2', log2.handlers)
12、Filter 过滤
可以为handler增加过滤器,所以这种过滤器只影响某一个handler, 不会影响整个处理流程,但是,如果过滤器增加到logger上,就会影响流程。
1 import logging 2 3 FORMAT = '%(asctime)s %(name)s %(message)s' 4 logging.basicConfig(format=FORMAT, level=logging.INFO) 5 6 log1 = logging.getLogger('s') 7 log1.setLevel(30) 8 9 # h1 = logging.FileHandler('f:/0.log', 'w') 10 h1 = logging.StreamHandler() 11 h1.setLevel(logging.INFO) 12 13 fmt1 = logging.Formatter('------log1 - h1 %(message)s') 14 h1.setFormatter(fmt1) 15 log1.addHandler(h1) 16 17 ############# log 2 ######### 18 log2 = logging.getLogger('s.s1') 19 print(log2.getEffectiveLevel()) 20 21 h2 = logging.StreamHandler() 22 h2.setLevel(logging.INFO) 23 24 fmt2 = logging.Formatter('log2 -h2 %(message)s') 25 h2.setFormatter(fmt2) 26 27 f2 = logging.Filter('p') 28 h2.addFilter(f2) 29 30 log2.addHandler(h2) 31 32 log2.warning('-----log2 warning')
------log1 - h1 -----log2 warning 2018-10-30 17:53:22,143 s.s1 -----log2 warning 30