python的标准日志模块logging
4个主要的组件:
logger:日志类,应用程序往往通过调用它提供的api来记录日志handler:对日志信息处理,可以将日志发送(保存)到不同的目标域中filter:对日志信息进行过滤formatter:日志的格式化
日志的作用:
在代码中,日志的作用是记录信息,便于查看问题,定位问题
简单完整实例
日志收集器:初始化一个logger(类似一个笔记本)设置日志收集器级别level日志处理器准备handler:类似不同的笔(每只笔的允许书写等级不一样,默认级别是warning)日志处理器级别设置(最终日志记录处理器和handler的较高的等级)设置日志格式 format:格式添加日志处理器
import logging
logger = logging.getLogger("python25")#初始化logger收集器,并取名
logger.setLevel('DEBUG')#设置级别,要记录的最低等级,要用大写
handler = logging.FileHandler("log.txt") #将logging输出到哪个文件
#sthandler = logging.StreamHandler() #与filehandler对应,这个是直接输出到控制台
handler.setLevel("WARNING")#handler和logger都要设置级别,取最高的那个
handler.setLevel("ERROR")
logger.addHandler(handler)#添加handler(将handler与logger绑定),一个logger可同时绑定两个
#handler
logger.addHandler(handler)
fmt = logging.Formatter("%(name)s-%(levelname)s-%(message)s")#设置格式,格式固定的用法,里面还可以加其他数据
handler.setFormatter(fmt)#将格式添加到handler中
logger.debug("hello")
logger.warning("world")
上面代码中的第10行,Formatter中的数据常用的有如下几个:
levelname 用%(levelname)s表示,表示logging级别levelno %(levelno)s 用数字来表示级别lineno %(lineno)d 行号(这里有个坑,视频42的36分左右会讲)message %(message)smodule %(module)s 模块名(不带后缀)filename %(filename)s 模块名+后缀(.py)name %(name)s 收集器的名字asctime %(asctime)s (什么时候运行的)时间funcName %(funcName)s 函数名(封装在函数内时可以用)
上面的代码简单来说就是:
初始化一个日志收集器: logger = logging.getLogger("python25")设置日志收集器级别:logger.setLevel('DEBUG')初始化一个日志处理器handler:handler = logging.FileHandler("log.txt")设置handler级别:handler.setLevel("WARNING")将logger和handler绑定:logger.addHandler(handler)设置日志格式:fmt = logging.Formatter("%(name)s-%(levelname)s-%(message)s")将设置的日志格式传递给logger:handler.setFormatter(fmt)
利用log的lineno可以迅速确定哪行代码出错
日志输出与print有什么区别呢?
可以控制消息的级别,过滤掉一些不那么重要的消息。可以决定输出到什么地方,以及怎么输出而且用户不会看到日志的输出
日志级别:
在记录日志时,日志消息都会关联一个级别(‘级别’本质上是一个非负整数)。默认有六个级别,分别是:
级别
对应的值
表示的含义
CRITICAL50崩溃,我的天,把程序猿祭天了吧ERROR40错误,大家快来看,这里有BUGWARNING30警告,这次没事,下次可能会出错,比如版本可能要更新啥的INFO20主体功能的信息,类似日报,做了些啥DEBUG10调试,一些额外信息,备注,和主体功能无关NOTSET0废话,等于没写
可以给日志对象(logger Instance)设置日志级别,低于该级别的日志消息将会被忽略,也可以给Handler设置日志级别,对低于该级别的日志消息,Handler也会忽略
可能存在的问题:
如果在一个模块中将l日志的各个方法封装到了一个类中,且你封装时对日志输出的格式包含了lineno,然后在其他的文件调用,会发现日志中记录的行数不封装log的文件调用log类中方法的函数那一行。原因是你在封装类时,生成了logger,logger已经记录了那些信息。
因此,最好不要直接封装日志方法,而是直接继承logging.Logger
import logging
class Log(logging.Logger):
def __init__(self,name = 'root',
level = 'DEBUG'):
super().__init__(name)
# self.logger =logging.getLogger('mylog')
self.handler = logging.FileHandler("newlog1.txt")
self.handler.setLevel(level)
self.addHandler(self.handler)
fmt= logging.Formatter("%(asctime)s-%(lineno)d-%(message)s")
self.handler.setFormatter(fmt)
上面的代码也存在一个问题,由于FileHandler中的文件路径是相对路径,每一个py文件调用log时,都会在该py文件同级目录下生成一个新的日志文件,不方便同一管理,因此这里应该用绝对路径