logging模块
日志级别
日志级别Level | 数值 |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30,默认级别 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
日志级别指的是生产日志的事件的严重程度,设置一个级别后,严重程度低于设置的值的日志消息将被忽略
debug(), info(), warning(), error()和critical()方法
格式字符串
属性名 | 格式 | 描述 |
---|---|---|
日志消息内容 | %(message)s | The logged message,computed as msg% args.当调用Formatter.format()时设置 |
asctime | %(asctime)s | 创建LogRecord时的可读时间。默认情况下,它的格式为’年-月-日 时:分:秒,x’(逗号后面的x数字是毫秒部分的时间) |
函数名 | %(funcName)s | 日志调用所在的函数名 |
日志级别名称 | %(levelname)s | 消息级别名称,‘DEBUG’,‘INFO’,‘WARNING’,‘ERROR’,‘CRITICAL’ |
日志级别的数值 | %(levelno)s | 消息的级别数字,对应DEBUG, INFO, WARNING, ERROR, CRITICAL |
行号 | %(lineno)d | 日志调用所在的源码行号 |
模块 | %(module)s | 模块(filename的名字部分) |
进程ID | %(process)d | 进程 ID |
线程ID | %(thread)d | 线程ID |
进程名称 | %(processName)s | 进程名 |
线程名称% | (threadName)s | 线程名字 |
logger名称 | %(name)s | logger名字 |
注意:funcName、threadName、processName都是小驼峰。
例如,默认级别是 warning 值为30,低于30的级别不能输出
import logging
FORMAT = '%(asctime)s,Thread info: %(thread)d, %(threadName)s,%(message)s'
logging.basicConfig(format=FORMAT)
logging.info('ABCD') #info级别的值为20,低于默认级别30 ,所有不能打印
logging.warning('abcd')
--------------------------------------------------------------
2019-06-11 17:39:30,757,Thread info: 13092, MainThread,abcd
python 格式化构建消息
import logging
FORMAT = '%(asctime)s,Thread info: %(thread)d, %(threadName)s,%(message)s,%(country)s'
logging.basicConfig(format=FORMAT,level=logging.INFO) #level = logging.INFO 设置级别
d = {'country': 'China'} #python格式化构建消息
logging.info('I am {}'.format(20),extra=d)
---------------------------------------------------------------
2019-06-11 18:51:47,695,Thread info: 2724, MainThread,I am 20,China
修改日期格式
import logging
logging.basicConfig(format='%(asctime)s,%(message)s',datefmt='%Y/%m/%d %I:%M:%S')
logging.warning('I am {}'.format(20))
-----------------------------------------------------
2019/06/11 07:07:23,I am 20
输出到文件
import logging
logging.basicConfig(format='%(asctime)s,%(message)s',filename='f:/work/001/a.log') #输出文件的路径必须存在
logging.warning('I am {},I am Chinese'.format(20))
logger 类
在logging模块中,顶层代码有如下代码:
root = RootLogger(WRANING)
Logger.root = root
- logging模块加载的时候,会创建一个全局对象root,它是一个RootLogger实例,即rootlogger. RootLogger对象的默认级别是WRANING
- RootLogger类继承自Logger类(Logger的级别是0)
- 类Logger初始化方法签名是(name,level=NOTSET)
- 类RootLogger而初始化签名(level),本质上调用的是Logger.__ init__(self,“root”,WRANING),调用logging.basicConfig来调整级别,就是对这个RootLogger的级别进行修改
构造
Logger实例的构建,使用Logger类也行,但推荐getLogger函数
#源码
def getLogger(name=None):
"""
Return a logger with the specified name, creating it if necessary.
If no name is specified, return the root logger.
"""
if name:
return Logger.manager.getLogger(name)
else:
return root #此处的root指的是root = RootLogger(WARNING)的root
import logging
a = logging.Logger('A',20)
b = logging.Logger('B',30)
print(a,id(a),a.level)
print(b,id(b),b.level)
c = logging.getLogger('A') #如果不启名字,返回的都是根logger,如果启了名字,只要名字相同,返回的都是同一个对象
print(c,id(c),c.level)
d = logging.getLogger('A')
print(d,id(d),d.level)
-----------------------------------
<Logger A (INFO)> 1988127554864 20
<Logger B (WARNING)> 1988127581296 30
<Logger A (WARNING)> 1988127581632 0
<Logger A (WARNING)> 1988127581632 0
- 使用工厂方法返回一个Logger实例
- 指定name,返回一个名称为name的Logger实例,如果再次使用相同的名字,返回同一个实例,背后使用一个字典保证同一个名称防护同一个Logger实例
- 未指定name,返回Logger实例
层次结构
Logger是层次结构的使用.点号 分割,例如’a’,‘a.b’,a是a.b的parent,a.b是a的子child,对于foo来说,名字为foo.bar,foo.bar.baz,foo.bam都是foo的后代.
import logging
root = logging.root
print(root,id(root))
root=logging.getLogger()
print(root,id(root))
print(root.name,type(root),root.parent)
parent = logging.getLogger(__name__)
print(parent.name,type(parent),id(parent.parent),id(parent))
child = logging.getLogger("{}{}".format(__name__,'.child'))
print(child.name,type(child),id(child),id(child.parent))
---------------------------------------------------------
<RootLogger root (WARNING)> 2466000351016
<RootLogger root (WARNING)> 2466000351016
root <class 'logging.RootLogger'> None
__main__ <class 'logging.Logger'> 2466000351016 2465998202080
__main__.child <class 'logging.Logger'> 2465998202472 2465998202080
level级别的设置
每一个logger实例都有个级别
每一个logger实例都有一个等效的level
logger对象可以在创建后动态的修改自己的level,等效的level决定这logger实例能输出什么级别的信息
import logging
FORMAT = '%(asctime)-15s\tThread info: %(thread)d %(threadName)s [%(message)s]'
logging.basicConfig(format=FORMAT, level=logging.INFO )
logger = logging.getLogger('NAME') #此处不写名字,则创建的实例就是root实例,有效级别就是默认的(level设置的级别,level不设置,就是默认的WARNING)
print('A',logger.name, type(logger), logger.level) # 新的logger实例的level是0
logger.info('1 info')
print('B',logger.getEffectiveLevel()) # 有效级别,从哪里来的
logger.setLevel(28) # 设置logger的level
print('C',logger.getEffectiveLevel(), logger.level)
logger.info('2 info')
logger.setLevel(42)
logger.warning('3 warning')
logger.error('4 error')
logger.critical('5 critical')
root = logging.getLogger() # Rootlogge
root.info('6 root info')
--------------------------------------------------------
2019-06-11 21:59:24,611 Thread info: 18196 MainThread [1 info]
2019-06-11 21:59:24,611 Thread info: 18196 MainThread [5 critical]
A NAME <class 'logging.Logger'> 0
2019-06-11 21:59:24,611 Thread info: 18196 MainThread [6 root info]
B 20
C 28 28
日志流
level的继承
import logging
logging.basicConfig(format="%(asctime)s %(name)s [%(message)s]")
log1 = logging.getLogger('s') # getLogger() 的好处,可以动态设置有效级别
print(log1.level,log1.getEffectiveLevel(),'------1') #0,30
# log1.info('1 info')
print(log1.parent)
log2 = logging.getLogger('s.s1')
print(log2.level,log2.getEffectiveLevel(),'------2') #0,30
# log2.info('2 info')
log1.setLevel(20)
# log1.info('3 info')
print(log1.level,log1.getEffectiveLevel(),'------3') #20,20
print(log2.level,log2.getEffectiveLevel(),'------4') #20,20
# log2.info('4 info')
log2.setLevel(30)
print(log2.level,log2.getEffectiveLevel(),'------5') #30,30
# log2.info('5 info')
print(log1.level,log1.getEffectiveLevel(),'------6') #20,20