Python中logging模块
1、日志级别
日志级别 |
数值 |
Critical |
50 |
Error |
40 |
Warning |
30 |
Info |
20 |
Debug |
10 |
Notset |
0 |
日志级别指的是产生日志的事件的严重程度。
设置一个级别后,严重程度第一设置值得日志消息将被忽略。
Debug(),info(),warning(),error(),critical()方法;
2、格式字符串
属性 |
格式 |
描述 |
日志消息内容 |
%(message)s |
The logged message,computed as msg %args,当调用 formatter.format()时候会被调用 |
Asctime |
%(asctime)s |
创建logrecord的可读时间,默认情况下,格式为2003-07-08 00:00:00,000(逗号后面的数字是毫秒部分的时间) |
函数名 |
%(funcname)s |
日志调用所在的函数名 |
日志级别名称 |
%(levelname)s |
消息的级别名称‘debug,info,warning,error,critical’ |
日志级别数值 |
%(levelno)s |
消息的级别数字,debug,info,warning,error,critical |
行号 |
%(lineno)s |
日志调用所在的源码行号 |
模块 |
%(module)s |
模块(费了那么的名字部分) |
进程ID |
%(process)s |
进程ID |
线程ID |
%(thread)d |
线程ID |
进程名 |
%(processname)s |
进程名 |
线程名 |
%(threadname)s |
线程名称 |
funName、threadName、processName都是小驼峰。
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT)
logging.info('{}'.format(20)) #info不显示
logging.warning('{}'.format(20)) #默认级别的
2018-06-13 11:01:56,992 Thread info:420 MainThread 20
上栗info不显示的原因是因为logging.basicConfig(format=FORMAT) 后面的默认值level的值是logging.WARNING 低于此级别的不进行显示处理。
改造以后
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.DEBUG)
logging.info('1{}'.format(0)) #info不显示
logging.warning('2{}'.format(0)) #默认级别的
logging.debug('3{}'.format(0))
logging.error('4{}'.format(0))
logging.critical('5{}'.format(0))
2018-06-13 11:39:28,053 Thread info:6212 MainThread 10
2018-06-13 11:39:28,053 Thread info:6212 MainThread 20
2018-06-13 11:39:28,053 Thread info:6212 MainThread 30
2018-06-13 11:39:28,053 Thread info:6212 MainThread 40
2018-06-13 11:39:28,053 Thread info:6212 MainThread 50
格式化字符串是需要定义的。Schools自定义的。打印输出的时候利用extra显示定义的新定义的消息等。
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s %(school)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)
d = {'school':'baidu.com'}
logging.info('i am %s %s',20,'years old.',extra = d)
logging.warning('i am %s %s',20,'years old.',extra = d)
2018-06-13 14:57:40,631 Thread info:8908 MainThread i am 20 years old. baidu.com
2018-06-13 14:57:40,631 Thread info:8908 MainThread i am 20 years old. baidu.com
3、修改日期格式
import logging
logging.basicConfig(format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S')
logging.warning('this event was logged')
2018/06/13 03:04:38 this event was logged
可以自己定义时间的格式,利用datefmt=新的时间格式即可以。
4、输出到文件
import logging
logging.basicConfig(format='%(asctime)s %(message)s',filename='test1.log')
for _ in range(5):
logging.warning('this is event logged')
还可以输出日志到文件,只是需要filename指定文件存放地址
5、Logger类:
Loggin模块加载的时候,会创建一个root logger,跟logger对象的默认级别是WARNING,调用logging.basicConfig来调整级别,就是对这个跟logger的级别进行修改。
1)构造
Logging.getlogger([name=None])
使用工厂方法返回一个logger实例。
指定name,返回一个名称为name的logger的实例,如果再次使用相同的名字,就是实例化一个对象,未指定none,返回根logger实例。
2)层次结构
Logger是层次结构的,使用.点号分割,如a,a.b或者a.b.c.d,a是a.d的父parent,a.b是a的子child,对于foo来说,名字为foo.bar/foo.bar.bar/foo.bam都是foo的后代。
import logging
root = logging.getLogger()
print(root.name,type(root),root.parent,id(root))
logger = logging.getLogger(__name__)
print(logger.name,type(logger),id(logger.parent),id(logger))
loggerchild = logging.getLogger(__name__+'.child')
print(loggerchild.name,type(loggerchild),id(loggerchild.parent),id(loggerchild))
root <class 'logging.RootLogger'> None 283120699768
__main__ <class 'logging.Logger'> 283120699768 283118806968
__main__.child <class 'logging.Logger'> 283118806968 283118808872
3)level级别设置
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.DEBUG)
logger = logging.getLogger(__name__)
print(logger.name,type(logger))
print(logger.getEffectiveLevel())
logger.info('hello')
logger.setLevel(28)
print(logger.getEffectiveLevel())
logger.info('hello2')
logger.warning('hello3 warning')
root = logging.getLogger()
root.info('hello4 info root')
2018-06-13 15:49:51,076 Thread info:8952 MainThread hello
__main__ <class 'logging.Logger'>
2018-06-13 15:49:51,076 Thread info:8952 MainThread hello3 warning
10
2018-06-13 15:49:51,076 Thread info:8952 MainThread hello4 info root
28
每一个logger创建后,都有一个等效的level。
Logger对象可以在创建后动态的修改自己的level。
不指定的话就是标准错误输出。
6、handler
Handler:主要工作的。
Handler控制日志信息的输出的目的地,可以是控制台、文件。
可以单独设置level Handler都有独立的level。
可以单独设置格式
可以设置过滤器
Handler类继承。
Handler
Streamhandler不指定使用sys.stderr
filehandler文件
_stderrhandler 标准输出
Nullhandler 什么都不做
输出打印全部靠的是handler,真正干活的也是handler。
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。
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.DEBUG)
logger = logging.getLogger('test')
print(logger.name,type(logger))
logging.info('line 1')
handler = logging.FileHandler('ll.log','w')
logger.addHandler(handler)
logger.info('line 2')
文件内的内容为line 2 控制台打印输出的内容为line1和line2,原因是因为设置的等级问题,没指定在控制台输出是因为继承的root的类,输出在控制台中。
7、日志流
Level的继承
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)
root = logging.getLogger()
log1 = logging.getLogger('s')
log1.setLevel(logging.INFO)
log2 = logging.getLogger('s.s1')
log2.warning('log2 warning')
2018-06-13 16:37:50,650 Thread info:8464 MainThread log2 warning
logger实例,如果设置了level,就用它和信息的级别比较,否则,继承最近的祖先的level。
继承关系及信息传递:
每一个logger实例的level如同入口。让水流进来,如果门槛太高,信息就无法进入,例如log3.warning(‘log3’),如果log3定义的级别高,就不会有信息通过log3
如果level没有设置,就用父logger的,如果父logger的level没有设置,继续找父的父,最终找到root上面,如果设置了就用他的,如果root没有设置,root的默认值是WARNING,
消息传递流程:
1)、如果消息在某个logger对象上产生,这个logger就是当前的logger,首先消息level要和当前的logger的effective比较,如果第一当前的logger的effectivelevel,则流程结束。
2)、日志记录会交给当前logger的所有handler处理,记录还要和每一个handler的级别分别比较,低的不处理,否则按照handler输出日志记录。
3)、当前logger的所有handler处理完后,就要看自己的propagate属性,如果是True表示向父logger传达这个日志,否则到此结束流程。
4)、如果日志记录仪传递到了父logger,不需要和logger的level比较,而是直接交给父的所有handler,父logger成为当前的logger,重复2,3步骤,直到当前的父logger是None退出,也就是说当前logger最后一般是root logger(是否能到root logger更看中间的logger是否允许propagate)。
Logger实话初始化的propagate属性为True,即允许向父logger传递消息。
Logger.basicConfig
如果root没有handler,就默认创建一个streamhandler,如果设置了filename,就创建一个filehandler,如果设置了format参数,就会用它生成一个formatter对象,并把这个formatter加入到刚刚创建的handler上,然后把这些handler加入到root.handlers列表上,level是设置给root.logger的
如果root.handlers 列表不能为空,loggingbasicConfig的调用什么都不做。
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('h1.log')
h1.setLevel(logging.WARNING)
log1.addHandler(h1)
print('log1',log1.handlers)
log2 = logging.getLogger('s.s1')
log2.setLevel(logging.CRITICAL)
h2 = logging.FileHandler('h2.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)
root [<logging.StreamHandler object at 0x00000088203E9748>]
root [<logging.StreamHandler object at 0x00000088203E9748>, <logging.StreamHandler object at 0x00000088203FE5F8>]
root handler = <logging.StreamHandler object at 0x00000088203E9748>,formatter=<logging.Formatter object at 0x00000088203E9780>
root handler = <logging.StreamHandler object at 0x00000088203FE5F8>,formatter=None
log1 [<logging.FileHandler object at 0x0000008820E2DDA0>]
log2 [<logging.FileHandler object at 0x0000008820E2DF28>]
20
log3 []
s.s1.s2 2018-06-13 17:17:43,695 <logging.Logger object at 0x0000008820E2DF60>
<logging.Logger object at 0x0000008820E2DF60>
8、Formatter
Logging的formatter类,允许指定某个格式的字符串,如果提供None,name%(message)s将会作为默认值。
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('h1.log')
h1.setLevel(logging.WARNING)
log1.addHandler(h1)
print('log1',log1.handlers)
log2 = logging.getLogger('s.s1')
log2.setLevel(logging.CRITICAL)
h2 = logging.FileHandler('h2.log')
h2.setLevel(logging.WARNING)
log2.addHandler(h2)
print('log2',log2.handlers)
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)
s.s1.s2 2018-06-13 17:24:13,392 <logging.Logger object at 0x00000033721EF208>
root [<logging.StreamHandler object at 0x0000003372019780>]
<logging.Logger object at 0x00000033721EF208>
root [<logging.StreamHandler object at 0x0000003372019780>, <logging.StreamHandler object at 0x0000003372130710>]
root handler = <logging.StreamHandler object at 0x0000003372019780>,formatter=<logging.Formatter object at 0x00000033720197B8>
root handler = <logging.StreamHandler object at 0x0000003372130710>,formatter=None
log1 [<logging.FileHandler object at 0x00000033721DDEB8>]
log2 [<logging.FileHandler object at 0x00000033721EF080>]
log2 formatter <logging.Formatter object at 0x00000033721EF1D0>
log2 [<logging.FileHandler object at 0x00000033721EF080>]
20
log3 []
9、filter是过滤
可以为handler增加过滤器,所哟这种过滤器只是影响某一个handler,不会影响整个处理流程,但是,如果过滤器增加到logger上,就会影响整个流程。
import logging
FORMAT = '%(asctime)-15s\tThread info:%(thread)d %(threadName)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.DEBUG)
log1 = logging.getLogger('s')
log1.setLevel(logging.WARNING)
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')
print(log2.getEffectiveLevel())
h2 = logging.StreamHandler()
h2.setLevel(logging.INFO)
fmt2 = logging.Formatter('log2-h2 %(message)s')
h2.setFormatter(fmt2)
f2 = logging.Filter('s')
h2.addFilter(f2)
log2.addFilter(h2)
log2.warning('log2 waring ')
30
log1-h1 log2 waring
2018-06-13 17:36:59,798 Thread info:8360 MainThread log2 waring
消息log2的,他的名字是s.s1,所以过滤器的名字设置为s或者s.s1,消息就可以通过,但是如果使其他的就不能通过,不设置过滤器的名字,所以消息通过。
总结:过滤器的核心就一句,在logging.filter类的filter方法中
Record.namem.find(self.name,0,self.nlen) != 0
本质上是等价于record.name.startswith(filter.name)