作者:E4b9a6, 创建:2020-09-22, 字数:7655, 已阅:76, 最后更新:2020-09-22
logging是Python的最常见的日志库,使用简单、兼容性强,支持yaml
配置,也支持代码创建
日志记录是程序部署到生产环境正常运行重要的一环,对于程序来说,一个号的日志库包括
logging是Python自带的日志库,在强大的日志功能下依旧兼具灵活性,不管是将日志写入文件,输出到到控制台,或者远程HTTP/FTP流服务器,都只需简单的配置即可实现
我们创建一个main.py
文件
import logging
import auxiliary_module
# create logger with 'spam_application'
logger = logging.getLogger('spam_application')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(fh)
logger.addHandler(ch)
logger.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
logger.info('created an instance of auxiliary_module.Auxiliary')
logger.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
logger.info('finished auxiliary_module.Auxiliary.do_something')
logger.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
logger.info('done with auxiliary_module.some_function()')
再创建一个auxiliary_module.py
import logging
# create logger
module_logger = logging.getLogger('spam_application.auxiliary')
class Auxiliary:
def __init__(self):
self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')
self.logger.info('creating an instance of Auxiliary')
def do_something(self):
self.logger.info('doing something')
a = 1 + 1
self.logger.info('done doing something')
def some_function():
module_logger.info('received a call to "some_function"')
运行 python3 main.py
,可以看到控制台打印如下输出
2020-07-21 00:39:24,997 - spam_application - INFO - creating an instance of auxiliary_module.Auxiliary
2020-07-21 00:39:24,997 - spam_application.auxiliary.Auxiliary - INFO - creating an instance of Auxiliary
2020-07-21 00:39:24,998 - spam_application - INFO - created an instance of auxiliary_module.Auxiliary
2020-07-21 00:39:24,998 - spam_application - INFO - calling auxiliary_module.Auxiliary.do_something
2020-07-21 00:39:24,998 - spam_application.auxiliary.Auxiliary - INFO - doing something
2020-07-21 00:39:24,998 - spam_application.auxiliary.Auxiliary - INFO - done doing something
2020-07-21 00:39:24,998 - spam_application - INFO - finished auxiliary_module.Auxiliary.do_something
2020-07-21 00:39:24,998 - spam_application - INFO - calling auxiliary_module.some_function()
2020-07-21 00:39:24,999 - spam_application.auxiliary - INFO - received a call to "some_function"
2020-07-21 00:39:24,999 - spam_application - INFO - done with auxiliary_module.some_function()
由上面的代码可以看出来,logging内置了几个对象
logging的工作流程官方示例图
在大部分情况下,我们只需要控制Logger
、Handler
、Formatter
三个对象即可
Logger是基础的日志对象,如示例代码使用 logging.getLogger('spam_application') 这个方式时,会获得一个名为spam_application的日志对象
然后调整这个日志对象对应的一些属性,我们就能得到一个自定义化的日志对象,在其他模块中再次使用这个日志对象时,之前所做的自定义化便会生效
接下来依次分析示例代码中的每一句话,看看具体实现了什么
setLevel(logging.DEBUG)
设置这个日志对象输出Debug以上级别的日志,日志级别官方文档摘录如下,如设置了Debug级别,则输出所有数值>DEBUG级别的日志
等级 | 数值 |
---|---|
CRITICAL | 50 |
FATAL | 50 |
ERROR | 40 |
WARNING | 30 |
WARN | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
fh = logging.FileHandler('spam.log')
创建了一个名为FH的handler日志处理格式,规定了DEBUG日志的输出目标输出格式,ch对象同理,其中 FileHandler 表示这是一个基于文件的输出处理器,会将日志输出到文件中,不同处理器有不同参数,常见处理器列表如下
处理器 | 说明 |
---|---|
StreamHandler | 日志输出到流,可以是 sys.stderr,sys.stdout或者自定义文件 |
FileHandler | 最简单的输出到文件日志处理器 |
BaseRotatingHandler | 最简单的日志回滚输出(切割日志文件) |
RotatingHandler | 支持自定义数量/日志文件的回滚 |
TimeRotatingHandler | 支持按时间分割日志文件的日志处理器 |
SocketHandler | 按sockets协议输出日志到指定的远程机器 |
SMTPHandler | 输出日志到远程邮件 |
SysLogHandler | 输出到sys日志文件 |
HTTPHandler | 输出到HTTP服务器 |
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
创建了一个格式输出器,其中的参数可自定义,参数列表如下
参数 | 说明 |
---|---|
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别的名称 |
%(pathname)s | 打印当前执行程序的路径,其实就是sys.argv[0] |
%(filename)s | 打印当前执行程序名 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间 |
%(thread)d | 打印线程ID |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程ID |
%(processName)s | 打印线程名称 |
%(module)s | 打印模块名称 |
%(message)s | 打印日志信息 |
剩下的四行分别设置对应的格式化对象/处理器对象到指定的对象中,从而完成输出,了解到这,Logging用来应付基本的使用已经没有问题了
如果对于异常捕获/文件配置有兴趣可以继续阅读下一节
对于异常输出,相信第一次写都会想到如下输出
import logging
try:
result = 10 / 0
except Exception as e:
logging.error('Error as %s', e)
事实上直接打印e显示出来的堆栈并不明显,也很难看清楚,更建议使用 logging.error('Error as',exc_info=True)/logging.exception('Error') 两种方式来打印堆栈信息
logging支持自定义日志配置文件,只需要使用如下代码来读取日志的即可配置文件
import logging.config
import yaml
with open('logging.yaml','r',encoding='utf-8') as f:
config = yaml.load(f)
logging.config.dictConfig(config)
配置文件支持格式非常广泛,包括config/ini/yaml/json等文件都可以作为logging的配置文件
下面是一个按照日期自动切割的yaml配置文件
version: 1
formatters:
common:
format: "%(asctime)s - %(levelname)s- %(threadName)s - %(message)s"
datefmt: "%Y/%m/%d %H:%M:%S"
console:
format: "%(asctime)s - %(levelname)s- %(pathname)s - %(message)s"
datefmt: "%Y/%m/%d %H:%M:%S"
handlers:
# 不同会输出大于等于此级别的信息 common:
class: logging.handlers.TimedRotatingFileHandler
formatter: common
level: INFO
when: D
interval: 1
encoding: utf8
filename: "app.log"
# suffix: "%Y-%m-%d.log"
# 日志保留个数
backupCount: 7
console:
class : logging.StreamHandler
formatter: brief
level : INFO
stream : ext://sys.stdout
loggers:
main.business:
level: INFO
handlers: [common]
# 如果模块中没有使用 如logging.getLogger('main.business') 这样的配置来获取loggers中对应的配置,则默认会使用下面的root配置
root:
level: DEBUG
handlers: [console]
参考资料