日志一共分成5个等级,从低到高分别是:DEBUG、INFO、 WARNING、 ERROR 、CRITICAL。
在输出log的时候,不能所有日志都输出出来,而是有所选择,比如有时候我们只希望看到warning以上严重程度的,如果有太多info、debug会让log可读性变得很差。
级别 | 值 |
---|---|
CRITICAL/FATAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
说明:
DEBUG: 详细的信息,通常只出现在诊断问题上
INFO: 确认一切按预期运行
WARNING: 一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如。磁盘空间低”)。这个软件还能按预期工作。
ERROR: 更严重的问题,软件没能执行一些功能
CRITICAL: 一个严重的错误,这表明程序本身可能无法继续运行
注意:
默认只打印WARNING及其以上等级的log,要想打印WARNING以下等级的log,此时就可以引入NOTSET级别来显示。
简单示例:
import logging # 引入日志模块
logging.debug("I am message." )
logging.info("I am message." )
logging.warning("I am message." )
logging.error("I am message." )
logging.critical("I am message." )
import logging
logging.basicConfig(level=logging.NOTSET) # 设置日志级别
logging.debug("I am message." )
logging.info("I am message." )
logging.warning("I am message." )
logging.error("I am message." )
logging.critical("I am message." )
运行结果:
说明:
logging.basicconfig() 函数
该函数可以用来修改日志的输出格式和方式,作用是将修改后的日志内容,写入到一个文件中。
可选参数如下:
参数 | 含义 |
---|---|
%(name)s | Logger的名字 |
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别名称 |
%(pathname)s | 打印当前执行程序的路径,其实就是sys.argv[0] |
%(filename)s | 打印当前执行程序名 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间,默认格式是 “2001-08-08 16:49:45,896”。逗号后面的是毫秒 |
%(thread)d | 打印线程ID |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程ID |
%(message)s | 打印日志信息 |
例如:
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
logging.debug("I am message." )
logging.info("I am message." )
logging.warning("I am message." )
logging.error("I am message." )
logging.critical("I am message." )
上面我们看到,日志都是输出在控制台的,name如何将日志输出到日志文件呢?
直接看如下代码:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
import os
logfile_dir = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
logfile_name = 'log.txt' # log文件名
logfile_path = os.path.join(logfile_dir, logfile_name) # log文件的全路径
logging.basicConfig(
level=logging.WARNING,
format='%(asctime)s %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
datefmt = '%Y-%m-%d %H:%M:%S %a ',
filename=logfile_path,
filemode='w'
)
logging.debug("I am message." )
logging.info("I am message." )
logging.warning("I am message." )
logging.error("I am message." )
logging.critical("I am message." )
执行代码后,看到脚本目录下多了一个log.txt文件,打开后看到有如下日志:
组件名 | 类名 | 功能描述 |
---|---|---|
记录器 | Logger | 提供日志接口,供应用代码使用 |
处理器 | Handler | 将(记录器产生的)日志记录发送至合适的目的地输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式化器 | Formatter | 决定日志记录的最终输出格式 |
这些组件之间的关系描述:
**简单点说就是:**日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
日志器Logger以工厂化的形式返回一个Logger类实例。一般而言,大多使用下面的方法获得Logger类实例:logging.getLogger(name)
说明: 参数name表示将要返回的日志器的名称标识。
在使用logging模块时,系统会自动实例化一个名为 root 的日志器(根日志器),当未指定name属性时,事实上就是将变量名指向根日志器。
另外, Logger实例具有层级继承的特点,层级之间已“.”连接,例如:“a.b”,“a.b.c”,a是父日志器,b是子日志器,在未对子日志器进行配置情况下,子日志器默认继承父日志器的配置,对子日志器重新配置不会影响父日志器。
Logger类有以下的常用方法:
方法 | 描述 |
---|---|
Logger.setLevel() | 设置日志器将会处理的日志消息的最低严重级别 |
Logger.addHandler() | 为该logger对象添加一个handler对象 |
Logger.removeHandler() | 为该logger对象添加移除一个handler对象 |
Logger.addFilter() | 为该logger对象添加一个filter对象 |
Logger.removeFilter() | 为该logger对象移除一个filter对象 |
Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() | 创建一个与它们的方法名对应等级的日志记录 |
Logger.exception() | 创建一个类似于Logger.error()的日志消息 |
Logger.log() | 需要获取一个明确的日志level参数来创建一个日志记录 |
Handler实例用于将日志记录发送到指定的位置进行输出。
一个logger对象可以添加多个handler(例如既要在控制台输出日志,又要将日志写入到文件A,还要讲日志写入文件B,这就可以配置3个handler),每个handler又可以定义不同日志级别,以实现日志分级过滤显示。
Handler类有以下的常用方法:
方法 | 描述 |
---|---|
handler.setLevel() | 设置handler处理的日志信息最低级别 |
handler.setFormatter( | 给这个handler选择一个格式 |
handler.addFilter() | 为handler添加一个过滤器对象 |
handler.removeFilter() | 为handler删除一个过滤器对象 |
要注意的是,在实际开发中,最好不要直接使用Handler类,应根据实际的功能需要,实例化Handler类的子类。(因为Handler是一个基类,它只定义了素有handlers都应该有的接口,同时提供了一些子类可以直接使用或覆盖的默认行为)
Handler类的子类包括:
Handler | 描述 |
---|---|
logging.StreamHandler | 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。 |
logging.FileHandler | 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handlers.HTTPHandler | 将日志消息以GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler | 将日志消息发送给一个指定的email地址 |
logging.NullHandler | 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。 |
下面来看一下代码示例,目的:(1)在控制台输出日志(日志级别为info);(2)同时将日志写入到文件a.log文件(日志级别为debug);(3)还要将日志写入文件b.log文件(日志级别为warning)
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 控制台输出,日志级别为info
con_handler = logging.StreamHandler()
con_handler.setLevel(logging.INFO)
logger.addHandler(con_handler)
# 输出到文件a.log,日志级别为debug
file_a_handler = logging.FileHandler('./a.log', encoding='UTF-8')
file_a_handler.setLevel(logging.DEBUG)
logger.addHandler(file_a_handler)
# 输出到文件b.log,日志级别为warning
file_b_handler = logging.FileHandler('./b.log', encoding='UTF-8')
file_b_handler.setLevel(logging.WARNING)
logger.addHandler(file_b_handler)
if __name__ == '__main__':
logger.debug('I am a debug msg')
logger.info('I am a info msg')
logger.warning('I am a warning msg')
运行结果:
(1)控制台输出如下:
(2)a.log输出如下:
(3)b.log输出如下:
注意: 在一个日志器中添加多个handler时,最好通过logger.setLevel(logging.DEBUG)先设置一下logger本身的日志级别,如果某个handler的级别比logger的日志级别低,那么该handler的日志级别无效,handler会以logger的级别来处理。
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。
Formatter类的构造方法定义如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
可见,该构造方法接收3个可选参数:
在我们已经知道的logging使用方法中,都是通过日志级别来控制日志是否输出,Filter能够实现更加强大的过滤功能,控制日志输出。自定义的过滤器中必须覆写filter方法,当filter的返回值判断为True则允许输出,反之不允许输出。例如过滤包含敏感信息的日志,
import logging
class CountryFilter(logging.Filter):
def filter(self, record):
return "America" not in record.getMessage()
logger = logging.getLogger()
handler = logging.StreamHandler()
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.addFilter(CountryFilter())
logger.critical('I love America')
logger.debug('I love China')
运行结果:
从上面的运行结果可以看到,虽然第一条日志记录的日志等级更高,但是因为包含了过滤器中设置的敏感信息,所以不被允许输出。
下面根据上面的流程来写一个示例代码,如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
# 第1步:创建一个名为KD的日志器
logger = logging.getLogger("KD")
# 第2步:设置日志器的日志等级
logger.setLevel(logging.DEBUG)
# 第3步:创建适合的Handler(一个控制台,一个文件)
con_handler = logging.StreamHandler()
file_handler= logging.FileHandler('./test.log', encoding='UTF-8')
# 第4步:设置Handler的日志级别
con_handler.setLevel(logging.ERROR)
file_handler.setLevel(logging.INFO)
# 第5步:创建日志输出格式
formatter = logging.Formatter(
fmt = "%(asctime)s %(name)s %(filename)s %(message)s",
datefmt = '%Y-%m-%d %H:%M:%S %a '
)
# 第6步:向Handler中添加上面创建的格式
con_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 第7步:将上面创建的Handler添加到logger中
logger.addHandler(con_handler)
logger.addHandler(file_handler)
# 第8步:输出不同级别的log
logger.debug("调试信息")
logger.info("提示信息")
logger.warning("警告信息")
logger.error("错误信息")
(1)控制台输出如下:
(2)test.log文件输出如下:
在日志中,如果只是记录发生了异常,那其实作用不大,还是不知道为什么发生了异常。如果我们在日志中能看到异常信息是什么,那么记录的日志就完美了。
Python的logging模块确实也提供了这一功能,代码示例如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
# 第1步:创建一个名为KD的日志器
logger = logging.getLogger("KD")
# 第2步:设置日志器的日志等级
logger.setLevel(logging.DEBUG)
# 第3步:创建适合的Handler(一个控制台,一个文件)
con_handler = logging.StreamHandler()
file_handler= logging.FileHandler('./test.log', encoding='UTF-8')
# 第4步:设置Handler的日志级别
con_handler.setLevel(logging.ERROR)
file_handler.setLevel(logging.INFO)
# 第5步:创建日志输出格式
formatter = logging.Formatter(
fmt = "%(asctime)s %(name)s %(filename)s %(message)s",
datefmt = '%Y-%m-%d %H:%M:%S %a '
)
# 第6步:向Handler中添加上面创建的格式
con_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 第7步:将上面创建的Handler添加到logger中
logger.addHandler(con_handler)
logger.addHandler(file_handler)
# 第8步:输出不同级别的log
logger.debug("调试信息")
logger.info("提示信息")
logger.warning("警告信息")
try:
logger.error("错误信息: 除法运算异常....")
1/0
except Exception as e:
logger.exception(e)
运行结果:
(1)控制台输出如下:
(2)test.log文件输出如下:
参考链接:
[1] https://www.cnblogs.com/Nicholas0707/p/9021672.html
[2] http://www.likecs.com/show-57627.html