简介
在Python中,提供了Logging模块用来记录日志,可以实现多种日志输出的形式,比如:控制台·、文件以及邮件等。
日志级别
级别 | 何时使用 |
---|---|
DEBUG | 细节信息,仅在诊断问题时适用 |
INFO | 确认程序按预期执行 |
WARNING | 表明有已经发生或即将发生的意外(如:磁盘空间不足)。程序仍然按照预期执行。 |
ERROR | 由于严重的问题,程序的某些功能已经不能正常执行 |
CRITICAL | 严重的错误,表明程序已不能继续执行 |
直接使用Logging模块的方法
简单的例子
import logging
logging.warning('Wathch outQ')
# 默认级别是WARNING,所以info不会输出
logging.info('i told you so')
默认日志级别是WARNING,所以下面的info不会打印
记录日志到文件
import logging
logging.basicConfig(filename='example.log', level=logging.DEBUG)
logging.debug('a debug log')
logging.info('a info log')
logging.warning('a warning log')
# example.log
DEBUG:root:a debug log
INFO:root:a info log
WARNING:root:a warning log
logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)
可以通过指定filemode属性来控制日志打印是追加还是重新开始
记录变量数据
要记录变量数据的话,使用格式字符串作为占位符,并将数据作为参数
import logging
logging.warning('%s before you %s', 'Look', 'leap!')
日志记录格式
通过format参数可以指定要使用的格式
import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')
format中可以使用的所有字符串,参考:LogRecord 属性
在日志中显示时间
可以在format字符串里用'%(asctime)s'
import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
# result
2010-12-12 11:41:42,612 is when this event was logged.
自定义时间显示格式,可以使用baseConfig
的datafmt
参数
import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
# result
12/12/2010 11:46:36 AM is when this event was logged.
进阶使用
Logging模块分为四大组件:
- Logger(记录器):暴露了代码直接调用的接口。
- Handler(处理器):将日志记录发送到适当的目标(文件、控制台等)。
- Filter(过滤器):提供更精细的附加功能,用于确定要输出的日志记录。
- Formatter(格式化器):指定最终输出的日志样式。
日志信息存储在LogRecord
实例中,并在Logger、Handler、Filter和Formatter之间传递
每个Logger都有一个名字,而且通过名字中包含.
,可以实现Logger的继承关系,比如oss
就是oss.env
的父级,而且oss.env
这个logger可以继承oss
这个logger的Level
、Handler
等
default_logger = logging.getLogger('oss')
...
logger = logging.getLogger('oss.env')
在每个使用日志记录的模块中使用模块级记录器,命名如下:
logger = logging.getLogger(__name__)
Logger存在一个根记录器,Logging.debug()
、Logging.info()
这种直接调用Logging模块的方法使用的就是根记录器,根记录器的name
是root
可以将日志记录到不同的地方,目标由handler
类提供,一个logger可以添加多个handler
记录流程
Logger的流程:
首先在调用logger记录日志的时候,就会根据level决定是否生成LogRecord对象实例
创建实例后,再根据Filter过滤log信息
之后先将实例传给handler,同时判断是否需要传递给父级的logger
Handler的流程:
判断Handler的日志级别
根据Handler的Filter过滤信息
最后格式化输出
Logger 记录器
Logger有三大任务,首先是暴露给代码一些方法,在需要记录日志的时候进行调用,其次是根据严重性(默认过滤工具)或Filter对象确定要处理的日志消息。第三,记录器将日志消息传给所有感兴趣的Handler
记录器上使用最多的方法分为两类,配置和日志记录
配置方法:
Logger.setLevel()
指定记录器处理的最低日志级别。Logger.addHandler()
和Logger.removeHandler()
从记录器对象中添加或删除处理器对象Logger.addFilter()
和Logger.removeFilter()
,从记录器中添加或删除过滤器对象
不用在每个Logger上进行配置,可以通过记录器的继承关系来减少配置
日志记录方法:
Logger.debug()
、Logger.info()
等方法都创建日志记录,包含消息和各自对应的级别。该消息实际上是一个格式化字符串,它可能包含标题字符串替换语法 %s 、 %d 、 %f 等等。其余参数是与消息中的替换字段对应的对象列表。Logger.exception()
创建和Logger.error()
类似的异常信息。不同的是,Logger.exception()
同时记录当前堆栈信息。仅在异常处理程序中调用此方法Logger.log()
将日志级别作为显式参数,是自定义日志级别的方法
getLogger()
方法返回指定名称的Logger实例,如果未指定的话返回root
。名称是以句点分隔的层次结构。有名字的Logger是单例的,所以多次调用getLogger()返回的是对同一Logger对象的引用。在继承关系中,低级别的logger是高级别logger的子级,比如有一个名为oss
的Logger,那么oss.env
、oss.publish.sit
都是oss
的子级
Logger有有效等级
的概念。如果没有在logger上显式指定level,那么将用其父级的level。如果父级也没有,那么接着向上找。最终的root
是有默认级别WARNING
的
子logger将消息传播到与其上级logger关联的handler。因此,不必为应用程序使用的所有记录器定义和配置处理程序。为顶级记录器配置处理程序并根据需要创建子记录器就足够了。(但是,你可以通过将记录器的 propagate 属性设置 False 来关闭传播。)
# 举例
import logging
# 创建父级Logger,设置级别为DEBUG
default_logger = logging.getLogger('parent')
default_logger.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(asctime)s - %(name)20s - %(levelname)8s - %(message)s')
# 创建一个用于console的handler,设置级别为INFO
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
default_logger.addHandler(ch)
# 创建子Logger,增加FileHandler
child_logger = logging.getLogger('parent.child')
fh = logging.FileHandler('aaa.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
child_logger.addHandler(fh)
child_logger.debug('a debug log')
child_logger.info('a info log')
child_logger.warning('a warning log')
child_logger.error('a error log')
child_logger.critical('a critical log')
# result
# 控制台和aaa.log内都记录了日志,因为子Logger会将消息传到父Logger的Handler,所以也能在控制台输出日志
Handler 处理程序
Handler根据日志级别将日志消息发送给指定目标(文件,控制台等)。Logger可以添加多个Handler,来实现将日志同时输出到控制台和文件,以及将严重错误日志发送到邮箱等。
主要使用的两个Handler是StreamHandler
和FileHandler
配置方法:
setLevel()
方法,就像在记录器对象中一样,指定将被分派到适当目标的最低严重性。为什么有两个setLevel()
方法?Logger
中设置的级别确定将传递给其处理程序的消息的严重性。每个Handler
中设置的级别确定处理程序将发送哪些消息。[setFormatter()](<https://docs.python.org/zh-cn/3.8/library/logging.html#logging.Handler.setFormatter>)
选择一个该处理程序使用的 Formatter 对象。[addFilter()](<https://docs.python.org/zh-cn/3.8/library/logging.html#logging.Handler.addFilter>)
和[removeFilter()](<https://docs.python.org/zh-cn/3.8/library/logging.html#logging.Handler.removeFilter>)
分别在处理程序上配置和取消配置过滤器对象。
Formatter 格式化程序
Formatter对象配置日志最终顺序、结构和内容。可以实例化Handler类,如果需要特殊行为,可能需要继承Handler类。构造函数有三个可选参数:—— 消息格式字符串、日期格式字符串和样式指示符。
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
如果没有消息格式字符串,则默认使用原始消息。如果没有日期格式字符串,则默认日期格式为:
%Y-%m-%d %H:%M:%S
最后加上毫秒数。 style
是 %
,'{
' 或 '$
' 之一。 如果未指定其中一个,则将使用 '%
'。
如果 style
是 '%',则消息格式字符串使用 %(<dictionary key>)s
样式字符串替换;可能的键值在 LogRecord 属性 中。 如果样式为 '{',则假定消息格式字符串与 str.format()
(使用关键字参数)兼容,而如果样式为 '$' ,则消息格式字符串应符合 string.Template.substitute()
。
以下消息格式字符串将以人类可读的格式记录时间、消息的严重性以及消息的内容,按此顺序:
'%(asctime)s - %(levelname)s - %(message)s'
格式化程序使用用户可配置的函数将记录的创建时间转换为元组。 默认情况下,使用 time.localtime() ;要为特定格式化程序实例更改此项,请将实例的 converter 属性设置为具有相同签名的函数 time.localtime() 或 time.gmtime() 。 要为所有格式化程序更改它,例如,如果你希望所有记录时间都以 GMT 显示,请在格式化程序类中设置 converter 属性(对于 GMT 显示,设置为 time.gmtime )。