Python-Logging
文章目录
Python
提供了功能强大且灵活的日志系统模块logging
。根据业务需求可以根据自定义的日志事件等级做对应的处理。
基础模式
只是向控制台打印信息,我们可以直接调用日志记录方法:
等级 | 使用场景 |
---|---|
DEBUG | 详细信息,通常只有在诊断问题时才有意义 |
INFO | 确认事情按预期工作 |
WARNING | 表示发生了意想不到的事情,或者指示在不久的将来出现问题(例如,“磁盘空间不足”)。该软件仍在按预期工作 |
ERROR | 由于更严重的问题,软件还没有能够执行一些功能 |
CRITICAL | 一个严重的错误,表明程序本身可能无法继续运行 |
这些方法名代表日志等级严重程度。最低严重等级是debug
,最严重等级是critical
。
1 | import logging |
你将看到:
1 | WARNING:root:It is a warn message! |
logging.warning()
方法的信息被打印了,但logging.info()
方法信息没有被打印出来。
这是因为在logging
模块默认等级是WARNING
。低于这个等级的日志记录方法信息不会被跟踪。
logging.basicConfig
可以通过logging.basicConfig(**kwargs)
对logging`进行配置。
1 | import logging |
上面的代码会将日志信息写入到example.log 文件中,里面内容如下:
1 | DEBUG:root:This message should go to the log file |
我们通过basicConfig()
方法指定了logging
的等级为DEBUG
,并且将信息写入example.log 文件中。
你多次调用上面的脚本,日志会向example.log 文件尾部添加。如果你希望每调用一次脚本,日志能覆盖原因文件内容,应该指定文件操作模式filemode
。配置应该如下:
1 | logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG) |
默认日志输出格式是:[levelname]:[name]:[message]
。通过basicConfig
我们可以指定日志输出格式。
1 | import logging |
将日志记录时间也记录下来了。
记录变量数据
记录变量有三种写法%
,$
,{
:
1 | import logging |
Python3.2以后才支持{
和$
写法。使用规则是str.format()
和string.Template
高级模式
logging
库使用模块方式提供四种组件用于定制日志系统:
loggers
- 记录器,暴露给应用程序直接调用的接口。handler
- 处理器,将记录器日志发送给合适的目的地。filter
- 过滤器,提供更好的粒度设施来确定要输出的日志记录。formatters
- 格式化器,指定最终输出中日志记录的布局。
日志信息是在LogRecord
实例的loggers
,handlers
,filters
,formatters
之间传递的。
logger
记录器是日志记录系统中执行开始单元。记录器是Logger
类的实例化。通过logging.getLogger(name)
获得。
Logging工作流
Logging工作流程图如下:
使用Logging
模块的全局作用域中的getLogger
函数获得Logger
实例。使用Logger
对象中的debug
,info
,error
,warn
,critical
等方法记录日志信息。
当调用日志记录方法后:
- 判断日志的等级是否大于Logger对象设置的等级,如果大于则继续执行,否则结束。
- 产生日志。
- 记录器连接的过滤器是否拒绝记录,如果
yes
则结束,否则继续。 - 将日志传递给当前logger绑定的处理器,如果当前logger有处理器就交给处理器处理。
- 当前logger是否允许将日志传播给父级,如果
yes
则查找父级logger,如果找到将日志传递给父级处理器处理。 - 处理器接收到日志,先判断日志等级是否大于处理器设置的等级,如果大于则继续否则结束掉。
- 处理器将可以处理的日志交给于自己绑定的过滤器,如果过滤器拒绝则结束。
- 处理器的过滤器通过日志筛选,则日志最后按照处理器设定的打印格式输出到设定的目的地。
Loggers
Loggers实例是通过logging.getLogger(name)
获得。它主要做三件事情:
- 向应用代码提供运行时记录日志信息的方法。
- 根据严重性或通过过滤器确定采取的日志消息。
- 将日志信息传递给感兴趣的处理器
logger对象的方法中分为两类:配置和发送信息。
configuration
Logger.setLevel()
指定记录器处理日志的最低严重级别。Logger.addHandler()
和Logger.removeHandler
给记录器添加或移除处理器。通过过滤的日志信息会交给处理器处理。处理器可以有多个,会依次执行处理日志信息。- [
Logger.addFilter()
]和[Logger.removeFilter()
]添加或移除过滤器。通过记录器等级设置的日志信息会交给过滤器处理。过滤器可以有多个,会依次进行日志过滤。
以上方法不是必须设定的。
记录日志方法有:
Logger.debug()
,Logger.info()
,Logger.warning()
,Logger.error()
,Logger.critical()
。创建日志记录信息,根据它们名称代码严重等级。它们接收变量赋值。Logger.exception()
创建一个类似Logger.error()
错误信息。但是它只能用于exception
处理方法中。Logger.log(lvl,msg)
接收整数参数(lvl )作为信息等级。然后接收日志信息。一般用于记录大量日志信息。
日志记录方法等级:
Level | Numeric value |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
getLogger()
方法返回一个logger实例,如果不指定名称则默认时root。同一个名字调用多次getLogger()
方法返回是同一个logger实例。
每个记录器有个名字,如果没有赋值默认是root
。它们是.
做为名字分隔符拥有上下级关系的空间结构。例如:一个记录器名字是scan
,则它是scan.html
,scan.css
的父级。子级没有指定等级它会继承父级的等级。默认记录等级是(WARING)。
Logger
名字可以是任意形式的,这个记录器的名字就是创建它这块应用区域在日志系统中的名字。
记录器的名字用于追踪包或模块的层级,并直观的显示日志记录发生的位置。
记录器会将信息传播给父级记录器的处理器进行处理。你可以将propagate
设为Flase
关闭掉。
Handlers
Handler对象目的是将日志信息(基于日志严重等级)分配到指定的目的地。Logger
可以添加0个或多个Handler
,通过addHandler()
方法。例子:
一个脚本,需要将应用所有的日志信息写入日志文件,并将所有严重等级大于higher
的日志使用标准方式输出,最后将critical
等级的日志信息用邮件发送给指定邮箱。则这个脚本需要创建三个handler
。
handler处理器拥有下这些方法:
setLevel()
处理器处理等级setFormatter()
处理器输出格式addFilter()
和removeFilter()
添加或移除绑定的过滤器。
标准库已经包含了一些常用的处理类型(StreamHandler 和 FileHandler)
Formatters
Formatter
对象负责配置日志消息的结构,顺序,内容。例子:
1 | import logging |
上面代码指定了日志最后输出信息格式是:当前时间-记录器名字-日志等级-日志信息
。
formatter支持变量如下:
Format | Description |
---|---|
%(asctime)s |
LogRecord创建时的可读时间 |
%(filename)s |
文件名字 |
%(funcName)s |
方法名 |
%(levelname)s |
日志等级 |
%(levelno)s |
日志等级数 |
%(lineno)d |
记录调用发出的源行号 |
%(message)s |
日志信息 |
%(module)s |
模块名 |
%(name)s |
记录器名字 |
%(pathname)s |
发出日志记录调用的源文件的完整路径名(如果可用)。 |
%(process)d |
进程ID(如果可用) |
%(processName)s |
进程名字 |
Configuring Logging
配置logging
有三种方式:
- 通过在Python 代码创建 loggers,handlers,formatters,使用配合方法将它们联系起来。
- 通过logging配置文件,然后通过
fileConfig()
方法读取。 - 创建一个字典类型的配置项,传给
dictConfig()
方法。
在Python代码配置Logging规则:
1 | import logging |
运行自个模块,在命令行会输出这些:
1 | python simple_logging_module.py |
通过fileConfig()
加载配置规则:
1 | import logging |
配置文件:
[loggers] # 定义loggers
keys=root,simpleExample[handlers] # 定义handlers
keys=consoleHandler[formatters] # 定义formatters
keys=simpleFormatter[logger_root] # root logger
level=DEBUG
handlers=consoleHandler[logger_simpleExample] # simpleExample logger
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0[handler_consoleHandler] # consoleHandler handler
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,) # 处理器参数[formatter_simpleFormatter] # simpleFormatter formatter
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
它的输出和非文件配置几乎一样:
1 | python simple_logging_config.py |
配置文件格式规则是configparser
。
配置文件将配置项从python代码中抽离出来,更便于管理和修改。
fileConfig()
有个默认参数disable_existing_loggers
默认为True
。它会导致在fileConfig()
之前存在的loggers
失效。将它设为False
就可以了。
重复打印
在日志系统中因为我们代码写法不慎,经常会导致日志重复打印。
basicConfig()
basicConfig()方法时系统会默认创建一个handler,如果你再添加一个控制台handler时就会出现重复日志。
1 | import logging |
handler
1 | import logging |
每次调用get_logger()方法时都会给它加一个新的handler。则一天信息就会经过累加的处理器。全局只配置logger
一次。
root
1 | import logging |
为嘛最后的App shutdown打印了两次?
只要你在程序中使用过root logger,那么默认你打印的所有日志都算它一份。
作者: Fynn
链接: https://fynn90.github.io/2017/12/15/python-logging%20/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可