日志级别
日志级别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时的可读时间。默认情况下,它的格式为“2003-07-08 16:49:45,896”(逗号后面的数字是毫秒部分的时间) |
函数名 | %(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名 |
默认级别
#默认为WARRING
import logging
FORMAT="%(asctime)s\t Thread info: %(thread)d %(threadName)s %(message)s " \
"simple level: %(levelno)d %(levelname)s"
logging.basicConfig(format=FORMAT)
logging.info(20)
logging.warning(10)
构建消息
import logging
FORMAT="%(asctime)s\t Thread info: %(thread)d %(threadName)s %(message)s " \
"simple level: %(levelno)d %(levelname)s"
logging.basicConfig(format=FORMAT,level=logging.INFO)
logging.info(20)
logging.warning(10)
字符串扩展
如果需要输出其他内容可以使用extra扩展
import logging
FORMAT="%(asctime)s\t Thread info: %(thread)d %(threadName)s %(message)s " \
"simple level: %(levelno)d %(levelname)s~~~~~~~%(school)s"
logging.basicConfig(format=FORMAT,level=logging.INFO)
d={'school':'university'}
logging.info(20,extra=d)
logging.warning(10,extra=d)
修改日期和输出到文件
import logging
logging.basicConfig(format='%(asctime)s %(message)s',datefmt="%Y/%m/%d %H:%M:%S",filename='C:/Users/ASUS/temp/o.txt')
logging.warning('this')
logging.warning('is')
logging.warning('sssssss')
Logger类
在logging模块中,顶层代码中有
logging模块加载的时候,会创建一个全局对象root,它是一个RootLogger实例,即root logger。根Logger对象的默认等级为WARNING。
RootLogger类继承自Logger类
类Logger初始化方法签名是(name,level=0)
类RootLogger初始化方法签名(level),本质上调用的是logger.init(self,“root”,WARNING)
调用logging,basicConfig来调整级别,就是对这个根Logger的级别进行修改
构造
Logger实例的构建,使用Logger类也行,推荐使用getLogger函数
import logging
from logging import Manager,Logger
Logger.manager=Manager(Logger.root)
def getLogger(name=None):
if name:
return Logger.manager.getLogger(name)
else:
return root
使用工厂方法返回一个Logger实例
指定name,返回一个名称为name的Logger实例,如果再次使用相同的名字,返回同一个实例,背后使用一个字典保证同一个名称返回同一个Logger实例
未指定name,返回根Logger实例。
import logging
a=logging.Logger('hello',20)
b=logging.Logger('hello',30)
print(a,id(a))
print(b,id(b))
c=logging.getLogger('hello')
print(c,id(c))
d=logging.getLogger('hello')
print(d,id(d))
层次结构
Logger是层次结构的,使用.点号分割。
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),parent.parent,parent)
child=logging.getLogger("{}{}".format(__name__,'.child'))
print(child.name,type(child),id(child.parent),id(child))
Level级别设置
每一个logger实例都有级别设置
import logging
FORMAT="%(asctime)s\t Thread info: %(thread)d %(threadName)s %(message)s %(levelname)s"
logging.basicConfig(format=FORMAT,level=logging.INFO)
logger=logging.getLogger(__name__)
print(logger.name,type(logging),logger.level)
logger.info('1 info')
print(logger.getEffectiveLevel())
logger.setLevel(28)
print(logger.getEffectiveLevel(),logger.level,'~~~~~~~~~``')
# INFO的级别是20,所以不会输出
logger.info('2 info')
logger.setLevel(42)
logger.warning('3 warning')
logger.error('4 error')
logger.critical('5 critical')
root=logging.getLogger()
root.info('6 root info')
每一个logger实例,都有一个等效的level
logger对象可以在创建后动态的修改自己的level
等效level决定这logger实例能输出什么级别信息
Handler
Handler控制日志信息的输出目的地,可以是控制台、文件
- 可以单独设置level
- 可以单独设置格式
- 可以设置过滤器
Handler类层次
- Handler
- StreamHandler #不指定使用sys.stderr
- FileHandler #文件
- _StderrHandler#标准输出
- NullHandler#什么都不做
日志输出其实是Handler做的,也就是真正起作用的是Handler
在logging.basicConfig函数中:
- StreamHandler #不指定使用sys.stderr
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加一个输出到文件的FileHandler;如果没有设置文件名,则为根Logger加一个StreamHandler,默认输出到sys.stderr。
也就是说,根logger一定会至少有一个handler的。
import logging
FORMAT='%(asctime)s %(name)s %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)
logger=logging.getLogger('test')
print(logger.name,type(logger))
logger.info('line 1')
handler=logging.FileHandler('C:/Users/ASUS/temp/o.txt','w')
logger.addHandler(handler)
logger.info('line 2')
日志流
level继承
import logging
logging.basicConfig(format="%(asctime)s %(name)s [%(message)s]")
log1=logging.getLogger('s')
print(log1.level,log1.getEffectiveLevel())
log1.info('1 info')
log2=logging.getLogger('s.s1')
print(log2.level,log2.getEffectiveLevel())
log2.info('2.info')
print('~~~~~~~~~~~~')
log1.setLevel(20)
log1.info('3 info')
print(log1.level,log1.getEffectiveLevel())
print(log2.level,log2.getEffectiveLevel())
log2.info('4.info')
logger实例
如果不设置level,则初始level为0
如果设置了level,就优先使用自己的level,否则,继承最近的祖先的level。
信息是否能够从该logger实例上输出,就要看当前的函数的level是否大于等于logger的有效level。
继承关系及信息传递
-
每一个Logger实例会有一个level,如果输出等级小于这个level,无法输出
-
如果level没有设置,就用父logger的,如果父logger的level没有设置,继续找父的父,最终找到root上,如果root设置了就用root的,如果没有设置,采用默认值WARNING。
-
消息传递流程
- 如果消息在某一个logger对象上产生,这个对象就是当前logger,首先消息level要和当前logger的EffectiveLevel比较,如果低于当前logger的EffectiveLevel,则流程结束,否则生成log记录。
- 日志记录会交给当前logger的所有handler处理,记录还要和每一个handler的级别分别比较,低的不处理,否则按照handler输出日志记录
- 当前logger的所有handler处理完后,就要看自己的propagate属性,如果是True表示向父logger传递这个日志记录,否则流程到此结束
- 如果日志记录传递到了父logger,不需要和父logger的level比较,而是直接交给父的所有handler,父logger成为当前logger,直到当前logger的父logger是None退出。
-
logger实例初始的propagate属性为True,即允许向父logger传递消息
-
logging.basicConfig函数
如果root没有handler,就默认创建一个StreamHandler,如果设置了filename,就创建一个FileHandler。如果设置了format参数,就会用它生成一个Formatter对象,否则会生成缺省Formatter,并把这个formatter加入到刚才创建的handler,然后把这些handler加入到root.handlters列表上,level是设置给root loggre的,如果root.handler列表不为空,logging.basicConfig的调用什么都不做。