首页 > 技术文章 > 第十一篇 logging模块

zhuzhaoli 2019-03-21 09:20 原文

logging模块是Python中内置的很强大的一个日志模块,它可以帮我们记录程序运行的情况,对于后续排错有很好的帮助。

logging模块定义了下表所示的日志级别,按照严重程度由低到高排列:

级别 使用时机
DEBUG 详细信息,常用来打印
INFO 程序正常运行过程中产生的信息
WARNING 告诉用户,虽然程序正常运行,但是可能会产生错误
ERROR 由于很严重的问题,程序不能执行功能
CRITICAL 严重错误,程序已经不能继续运行

说明:默认情况下的级别是WARNING,表示只有WARNING和比WARNING严重的事件才会被记录。

1.1基本用法

import logging

logging.info("info message")
logging.debug("debug message")
logging.warning("warn message")
logging.error("error message")
logging.critical("critical message")

------输出内容-------
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

从上面的结果中可以看出,默认情况下,logging模块是将输出的结果直接打印到屏幕上的,而且info和debug级别的日志都没有打印,即默认情况下,只有级别大于debug的日志才能输出,输出日志的格式“日志级别:实例名:日志信息”。

那么如何将日志输出到文件中呢?

如果需要将日志输出到文件中,在调用logging模块记录日志前,需要做个简单的配置:

# -*- coding:utf-8 -*-

import logging

# 设置日志输出的文件和日志的等级
logging.basicConfig(filename="logger.log", level=logging.INFO)
logging.info("info message")
logging.debug("debug message")
logging.warning("warn message")
logging.error("error message")
logging.critical("critical message")

查看生成的logger.log文件内容,大于等于info级别的日志均输出:

INFO:root:info message
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

1.2更加完善的日志功能

如果只是简单地使用logging,那么使用上面介绍的方法就可以了,如果要深度定制logging,那么就需要对它有更深入的了解。下面的内容才是基本的logging模块的使用方法。

logging模块采用了模块化设计,主要包含四种组件:

  • Loggers:记录器,提供应用程序代码能直接使用的接口;
  • Handlers:处理器,将记录器产生的日志发送至目的地;
  • Filters:过滤器,提供更好的粒度控制,决定哪些日志会被输出;
  • Formatters:格式化器,设置日志内容的组成结构和消息字段。

1.2.1Loggers记录器

logging模块的日志功能是基于Logger类实现的。我们可以通过下面的方法获取一个Logger类的实例(建议以模块名命名logger实例)。

logger = logging.getLogger(__name__)

Logger是一个树形层级结构,在使用debug(),info(),warn(),error(),critical()等方法之前必须先创建一个Logger的实例,即创建一个记录器,如果没有显式的进行创建,则默认创建一个root logger,并应用默认的日志级别(WARN),默认的处理器Handler(StreamHandler,即将日志信息打印在标准输出上),和默认的格式化器Formatter,就像我们在前面举的那些例子一样。

logger对象有三重功能。首先,提供应用程序调用的接口;其次,决定日志记录的级别;最后,将日志内容传递到相关联的handlers中。

总结logger对象的用法,可以分成两类:配置和消息发送。

下面是最常用的配置方法:

  • Logger.setLevel():设置日志记录级别
  • Logger.addHandler()Logger.removeHandler():为logger对象添加或删除handler处理器对象。
  • Logger.addFilter()Logger.removeFilter():为为logger对象添加或删除filter过滤器对象。

配置好logger对象后,就可以使用下面的方法创建日志消息了:

  • Logger.debug()Logger.info()Logger.warning()Logger.error()and Logger.critical():创建对应级别的日志,但不一定会被记录。
  • Logger.exception():创建一个类似Logger.error()的日志消息。不同的是Logger.exception()保存有一个追踪栈。该方法只能在异常handler中调用。
  • Logger.log():显式的创建一条日志,是前面几种方法的通用方法。

注意,getLogger()方法返回一个logger对象的引用,并以你提供的name参数命名,如果未提供名字,那么默认为‘root’。使用同样的name参数,多次调用getLogger(),将返回同样的logger对象。

1.2.2Handlers处理器

Handlers对象是日志信息的处理器、分发器。它们将日志分发到不同的目的地。比如有时候我们希望将所有的日志都记录在本地文件内,将error及其以上级别的日志发送到标准输出stdout,将critical级别的日志以邮件的方法发送给管理员。这就需要同时有三个独立的handler,分别负责一个方向的日志处理。

logging模块使用较多的handlers有两个,StreamHandlerFileHandler

  • StreamHandler
    • 标准输出stdout(如显示器)分发器。
    • 创建方法: sh = logging.StreamHandler(stream=None)
  • FileHandler
    • 将日志保存到磁盘文件的处理器。
    • 创建方法: fh = logging.FileHandler(filename, mode='a', encoding=None, delay=False)

handlers对象有下面的方法:

  • setLevel():和logger对象的一样,设置日志记录级别。那为什么要设置两层日志级别呢?logger对象的日志级别是全局性的,对所有handler都有效,相当于默认等级。而handlers的日志级别只对自己接收到的logger传来的日志有效,进行了更深一层的过滤。
  • setFormatter():设置当前handler对象使用的消息格式。
  • addFilter() 和 removeFilter():配置或删除一个filter过滤对象

logging模块内置了下面的handler处理器,从字面上你就能看出它们的大概用途:

  • StreamHandler
  • FileHandler
  • BaseRotatingHandler
  • RotatingFileHandler
  • TimedRotatingFileHandler
  • SocketHandler
  • DatagramHandler
  • SMTPHandler
  • SysLogHandler
  • NTEventLogHandler
  • HTTPHandler
  • WatchedFileHandler
  • QueueHandler
  • NullHandler

1.2.3Formatters

Formatter对象用来最终设置日志信息的顺序、结构和内容。其构造方法为:

ft = logging.Formatter.__init__(fmt=None, datefmt=None, style=’%’)

如果不指定datefmt,那么它默认是%Y-%m-%d %H:%M:%S样式的。

style参数默认为百分符%,这表示前面的fmt参数应该是一个%(<dictionary key>)s格式的字符串,而可以使用的logging内置的keys,如下表所示:

属性格式描述
asctime %(asctime)s 日志产生的时间,默认格式为2003-07-08 16:49:45,896
created %(created)f time.time()生成的日志创建时间戳
filename %(filename)s 生成日志的程序名
funcName %(funcName)s 调用日志的函数名
levelname %(levelname)s 日志级别 ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
levelno %(levelno)s 日志级别对应的数值
lineno %(lineno)d 日志所针对的代码行号(如果可用的话)
module %(module)s 生成日志的模块名
msecs %(msecs)d 日志生成时间的毫秒部分
message %(message)s 具体的日志信息
name %(name)s 日志调用者
pathname %(pathname)s 生成日志的文件的完整路径
process %(process)d 生成日志的进程ID(如果可用)
processName %(processName)s 进程名(如果可用)
thread %(thread)d 生成日志的线程ID(如果可用)
threadName %(threadName)s 线程名(如果可用)

1.2.4Filter过滤器

Handlers和Loggers可以使用Filters来完成比日志级别更复杂的过滤。比如我们定义了filter = logging.Filter('a.b.c'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带a.b.c前缀的Logger才能输出其日志。

创建方法: filter = logging.Filter(name='')

例如:

filter = logging.Filter('mylogger.child1.child2')  
fh.addFilter(filter)

则只会输出下面格式的日志,注意其用户名:

2017-09-27 16:27:46,227 - mylogger.child1.child2 - DEBUG - logger1 debug message
2017-09-27 16:27:46,227 - mylogger.child1.child2 - DEBUG - logger1 debug message
2017-09-27 16:27:46,227 - mylogger.child1.child2 - DEBUG - logger1 debug message
2017-09-27 16:27:46,227 - mylogger.child1.child2 - DEBUG - logger1 debug message

1.2.5配置日志模块

有三种配置logging的方法:

  • 创建loggers、handlers和formatters,然后使用Python的代码调用上面介绍过的配置函数。
  • 创建一个logging配置文件,然后使用fileConfig()方法读取它。
  • 创建一个配置信息字典然后将它传递给dictConfig()方法。

下面的例子采用了第一种方法:

#simple_logging_module.py

import logging

# 创建logger记录器
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# 创建一个控制台处理器,并将日志级别设置为debug。
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# 创建formatter格式化器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 将formatter添加到ch处理器
ch.setFormatter(formatter)

# 将ch添加到logger
logger.addHandler(ch)

# 然后就可以开始使用了!
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

在命令行中运行上面的代码,输出结果如下:

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

下面是使用第二种方法,logging配置文件的方式:

# simple_logging_config.py

import logging
import logging.config

logging.config.fileConfig('logging.conf') # 读取config文件

# 创建logger记录器
logger = logging.getLogger('simpleExample')

# 使用日志功能
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

其中的logging.conf配置文件内容如下:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

在命令行中执行代码,结果如下:

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

Python官方更推荐第三种新的配置方法,类字典形式的配置信息,因为Python的字典运用形式多样,操作灵活。比如,你可以通过JSON格式保存字典,或者YAML格式保存信息,然后读取成字典。当然,你也可以直接在Python代码里编写传统的带有配置信息的字典。一切都是基于键值对形式的就OK。

下面的例子就是基于YAML配置文件的日志。logging.conf.yaml配置文件内容如下:

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
 root:
    level: DEBUG
    handlers: [console]

这里要先通过pip安装yaml模块:

pip install pyyaml

yaml模块的使用很简单,使用open()方法打开一个yaml文件对象,然后使用yaml的load()方法将文件内容读成一个Python的字典对象。最后我们根据这个字典对象,使用logging.conf的dictConfig()方法,获取配置信息。如下代码所示:

import logging
import logging.config
import yaml

# 通过yaml文件配置logging
f = open("logging.conf.yaml")
dic = yaml.load(f)
f.close()
logging.config.dictConfig(dic)

# 创建logger
logger = logging.getLogger('simpleExample')

# 输出日志
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

输出结果:

2017-09-27 17:41:09,241 - simpleExample - DEBUG - debug message
2017-09-27 17:41:09,242 - simpleExample - INFO - info message
2017-09-27 17:41:09,242 - simpleExample - WARNING - warn message
2017-09-27 17:41:09,242 - simpleExample - ERROR - error message
2017-09-27 17:41:09,242 - simpleExample - CRITICAL - critical message

推荐阅读