示例代码
import logging
LOG_FORMAT = "%(asctime)s====%(levelname)s++++%(message)s"
logging.basicConfig(filename="tulingxueyuan.log", level=logging.DEBUG, format=LOG_FORMAT)
logging.debug("This is a debug log .")
logging.info("This is a info log .")
logging.warning("This is a warning log .")
logging.error("This is a error log .")
logging.critical("This is a critical log .")
# 另一种写法
logging.log(logging.DEBUG, "This is a debug log .")
logging.log(logging.INFO, "This is a info log .")
logging.log(logging.WARNING, "This is a warning log .")
logging.log(logging.ERROR, "This is a error log .")
logging.log(logging.CRITICAL, "This is a critical log .")
logging模块的使用方式
logging模块提供两种记录日志的方式:
- 第一种 使用logging提供的模块级别的函数
-
第二种 使用logging日志系统的四大组件
其中logging.basicConfig(**kwargs)函数用于指定“要记录的日志级别”、“日志格式”、“日志输出位置”、“日志文件的打开模式”等信息,其他几个都是用于记录各个级别日志的函数。
logging.basicConfig函数说明
关键字参数:
- filename,指定日志输目标文件的文件名,指定后日志不会在控制台输出。
- filemode,指定日志文件的打开模式,默认为‘a’,此项必须在filename指定才有效。
- format,指定日志格式字符串。
- datefmt,指定日期时间格式。
- level,指定日志器的日志级别。
- stream,指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常。
- style,指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'。
- handlers,该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。
logging模块定义的格式字符串字段
logging模块的处理流程
四大组件
- 日志器(Logger):产生日志的一个接口
- 处理器(Handler):把产生的日志发送到相应的目的地
- 过滤器(Filter):更精细的控制那些日志输出
- 格式器(Formatter):对输出信息进行格式化
组件直接的关系如下:
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
- 不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
logging模块相关类及常用方法
下面介绍下与logging四大组件相关的类:Logger, Handler, Filter, Formatter。
Logger类
Logger对象有3个任务:
- 使应用程序可以在运行时记录日志消息
- 基于日志严重等级或filter对象决定对哪些日志进行后续处理
- 将日志消息传给所有感兴趣的handlers
logger对象最常用的方法分为两类:
配置方法和消息发送方法。
常用配置方法:
方法 | 描述 |
---|---|
Logger.setLevel() | 设置日志器将会处理的日志消息的最低严重级别 |
Logger.addHandler() 和 Logger.removeHandler() | 为该logger对象添加 和 移除一个handler对象 |
Logger.addFilter() 和 Logger.removeFilter() | 为该logger对象添加 和 移除一个filter对象 |
创建日志方法:
方法 | 描述 |
---|---|
Logger.debug(), Logger.info()等 | 用来创建指定等级的日志记录 |
Logger.exception() | 创建一个类似于Logger.error()的日志消息 |
Logger.log() | 需要获取一个明确的日志level参数来创建一个日志记录 |
- Logger.exception()与Logger.error()的区别在于:Logger.exception()将会输出堆栈追踪信息,另外通常只是在一个exception handler中调用该方法。
- Logger.log()与Logger.debug()、Logger.info()等方法相比,虽然需要多传一个level参数,显得不是那么方便,但是当需要记录自定义level的日志时还是需要该方法来完成。
通过logging.getLogger方法来得到Logger对象,logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值为'root'。若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。
Handler类
Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:
- 把所有日志都发送到一个日志文件中;
- 把所有严重级别大于等于error的日志发送到stdout(标准输出);
- 把所有严重级别为critical的日志发送到一个email邮件地址。这种场景就需要3个不同的handlers,每个handler复杂发送一个特定严重级别的日志到一个特定的位置。
配置方法:
方法 | 描述 |
---|---|
Handler.setLevel() | 设置handler将会处理的日志消息的最低严重级别 |
Handler.setFormatter() | 为handler设置一个格式器对象 |
Handler.addFilter() 和 Handler.removeFilter() | 为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'信息的出现。 |
Formater类
Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。
Formatter类构造方法如下:
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
Filter类
定义如下:
class logging.Filter(name=“”)
filter(record)
比如,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。
filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。
使用logging四大组件记录日志
1.需求
- 将所有级别的所有日志写入磁盘文件
- all.log中记录所有的日志信息,格式为:日期时间-级别-内容
- error.log单独记录error及以上的日志信息,日期和时间 - 日志级别 - 文件名[:行号] - 日志信息
- all.log在每天凌晨进行日志切割
2.分析
- 记录所有级别的日志,level设置为DEBUG
- 日志被发送给两个不同的目的地,需要设置两个handler,都需要写入磁盘,都跟filehandler有关
- all.log要求按照时间进行日志分割,需要用到logging.handlers.TimedRotatingFileHandler,而error.log没有要求日志分割,可以使用FileHandler
- 两个日志文件格式不同,设置不同的格式器
3.代码实现
import logging
import logging.handlers
import datetime
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)
rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight',
interval=1, backupCount=7,
atTime=datetime.time(0, 0, 0, 0))
rf_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
f_handler = logging.FileHandler('error.log')
f_handler.setLevel(logging.ERROR)
f_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s"))
logger.addHandler(rf_handler)
logger.addHandler(f_handler)
logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
配置logging的几种方式
作为开发者,我们可以通过以下3中方式来配置logging:
- 使用python代码显式的创建loggers,handlers和formatters并分别调用他们的配置函数。
- 创建日志配置文件,然后使用fileConfig函数来读取该文件的内容
- 创建一个包含配置信息的dict,然后把它传递给dictConfig函数
可以参考博客《Python之配置日志的几种方式》
向日志输出中添加上下文信息
除了传递给日志记录函数的参数外,有时候我们还想在日志输出中包含一些额外的上下文信息。比如,在一个网络应用中,可能希望在日志中记录客户端的特定信息,如:远程客户端的IP地址和用户名。这里我们来介绍以下几种实现方式:
- 通过日志记录函数传递一个extra参数引入上下文信息
- 使LoggerAdapter引入上下文信息
- 使用Filters引入上下文信息
具体说明请参考另一篇博文《Python之向日志输出中添加上下文信息》