python的标准日志模块logging
4个主要的组件:
- logger:日志类,应用程序往往通过调用它提供的api来记录日志
- handler:对日志信息处理,可以将日志发送(保存)到不同的目标域中
- filter:对日志信息进行过滤
- formatter:日志的格式化
日志的作用:
在代码中,日志的作用是记录信息,便于查看问题,定位问题
简单完整实例
- 日志收集器:初始化一个logger(类似一个笔记本)
- 设置日志收集器级别level
- 日志处理器准备handler:类似不同的笔(每只笔的允许书写等级不一样,默认级别是warning)
- 日志处理器级别设置(最终日志记录处理器和handler的较高的等级)
- 设置日志格式 format:格式
- 添加日志处理器
import logging
logger = logging.getLogger("tester")#初始化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)s
- module %(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")
- 将设置的日志格式传递给handler:handler.setFormatter(fmt)
利用log的lineno可以迅速确定哪行代码出错
日志输出与print有什么区别呢?
- 可以控制消息的级别,过滤掉一些不那么重要的消息。
- 可以决定输出到什么地方,以及怎么输出
- 而且用户不会看到日志的输出
日志级别:
在记录日志时,日志消息都会关联一个级别(‘级别’本质上是一个非负整数)。默认有六个级别,分别是:
级别 | 对应的值 | 表示的含义 |
CRITICAL | 50 | 崩溃,我的天,把程序猿祭天了吧 |
ERROR | 40 | 错误,大家快来看,这里有BUG |
WARNING | 30 | 警告,这次没事,下次可能会出错,比如版本可能要更新啥的 |
INFO | 20 | 主体功能的信息,类似日报,做了些啥 |
DEBUG | 10 | 调试,一些额外信息,备注,和主体功能无关 |
NOTSET | 0 | 废话,等于没写 |
可以给日志对象(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文件同级目录下生成一个新的日志文件,不方便同一管理,因此这里应该用绝对路径