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 国际许可协议进行许可