5分钟入门一个python库——logging

今天给大家介绍的是 Pthon 标准库中的 logging 库,
用来在项目代码中进行故障分析与问题定位。小伙伴可能要发问了,平常我
用 print 就可以打印错误的呀,为什么还要专门引入一个模块?

是的,通常当你只有少量的代码,可以使用 print() 打印出你想要调试的信息,
但一旦你要处理多个模块的或大型项目时,就需要一个更加灵活、打印更加清晰的方式来定位、
追踪你想了解的信息。

大部分时候,你的代码需要经历开发、调试、审查、测试、上线等不同阶段,
在开发时你想要打印的信息和上线后你想看到的信息类型往往是不同的,
也就是说,在发布上线后时,你可能只想看警告和错误信息,然而在开发调试阶段时,
你需要看到更多跟调试有关的信息,这就涉及到日志级别的控制,仅仅靠 print 是很难满足需求的啦。

我们先来看一个最基本的例子

import logging
logging.basicConfig(level=logging.DEBUG,
                    format='[%(asctime)s] %(levelname)-6s: %(message)s',
                    )

logging.debug('这是一个 debug 级别的消息')
# 占位符可以用 %s、%d 等
logging.info('这是一个 %s 级别的消息', 'info')
logging.warning('这是一个 warning 级别的消息')


是不是 so easy!

我们把日志级别设置高一点,只让它输出 info 及以上的信息

import logging
logging.basicConfig(level=logging.INFO,
                    format='[%(asctime)s] %(levelname)-6s: %(message)s',
                    )

logging.debug('这是一个 debug 级别的消息')
logging.info('这是一个 info 级别的消息')
logging.warning('这是一个 warning 级别的消息')


看,虽然我们代码中有写打印 debug 信息,但根据设定的日志级别,它是会忽略 debug 信息的

好了,你大概明白了日志是怎么设置和控制输出级别的了, 然而实际中我们在写项目时可不会要求这么简单。
为了能让多个模块共用统一的配置,且不与某些框架自带的日志记录器配置冲突,我们还要完善下。

logging 实战

假设我们在项目中有个 logger_base.py 的文件用来统一配置

# @File   : logger_base.py

import logging
import sys

# 获取一个日志管理器实例对象
# 注意 getLogger 一定要给个名字, 可以用 __name__ 哦
# 不然获取到的日志就是 根日志器了,容易与其他框架日志器冲突
logger = logging.getLogger('my-logger')

# 统一设置日志级别
logger.setLevel(logging.DEBUG)

# 设置日志打印格式
log_format = logging.Formatter(
    '[%(asctime)s] %(name)s %(levelname)s: %(message)s', 
    datefmt='%Y/%m/%d %H:%M:%S'
)

# 实例化一个日志流处理器 stream 可以指定输出流类型(默认是 sys.stderr)
# 这里指定了标准输出流,即跟 `print`一样直接输出到 console
stream_handler = logging.StreamHandler(stream=sys.stdout)

# 为日志流处理器添加上面定义好的输出格式
stream_handler.setFormatter(log_format)

# 为 `logger` 添加流处理器
logger.addHandler(stream_handler)

总结下: 获取 logger → 设置级别 → 添加流处理器

我们在同级目录新建一个py文件来调用下

from logger_base import logger

logger.debug('这是一个 debug 级别的消息')
logger.info('这是一个 info 级别的消息')
logger.error('这是一个 error 级别的消息')

我们再来看看同时配置多个日志流处理器栗子

logger = logging.getLogger('my-logger')

# 设置日志打印格式
log_format = logging.Formatter(
    '[%(asctime)s] %(name)s %(levelname)s: %(message)s',
    datefmt='%Y/%m/%d %H:%M:%S'
)

# 实例化多个日志流处理器
stream_handler = logging.StreamHandler(stream=sys.stdout)
stream_handler.setFormatter(log_format)
# 单独设置日志级别
stream_handler.setLevel(logging.INFO)

file_hander = logging.FileHandler('./mylog.log', encoding='utf-8')
file_hander.setFormatter(log_format)
file_hander.setLevel(logging.DEBUG)

# 为 `logger` 配置多个流处理器
# 注意当多个handler设置不同的日志级别时
# 日志器级别应设置成最低的那个,如这里 DEBUG(文末解释为啥不是 NOSET)
# handler 会在此级别进行二次过滤
logger.setLevel(logging.DEBUG)
logger.handlers = [stream_handler, file_hander]

console 输出的日志(stream_handler)

./mylog.log 中记录的日志(file_hander)

更多配置

format: 指定日志信息的输出格式,部分参数如下所示

%(levelno)s:打印日志级别的数值
%(levelname)s:打印日志级别的名称
%(pathname)s:打印当前执行程序的路径
%(filename)s:打印当前执行程序名
%(funcName)s:打印日志的当前函数
%(lineno)d:打印日志的当前行号
%(asctime)s:打印日志的时间
%(thread)d:打印线程ID
%(threadName)s:打印线程名称
%(process)d:打印进程ID
%(processName)s:打印进程名称
%(module)s:打印模块名称
%(message)s:打印日志信息

stream: 在没有指定 filename 的时候会默认使用 StreamHandler
handlers: 可以指定日志处理时所使用的 Handlers,必须是可迭代的

logging.StreamHandler 可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息
logging.FileHandler 用于向一个文件输出日志信息
logging.handlers.RotatingFileHandler 类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出
logging.handlers.TimedRotatingFileHandler 和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就自动创建新的日志文件
logging.handlers.SocketHandler 使用TCP协议,将日志信息发送到网络。
logging.handlers.DatagramHandler 使用UDP协议,将日志信息发送到网络。
logging.handlers.SysLogHandler 日志输出到syslog
logging.handlers.NTEventLogHandler 远程输出日志到Windows NT/2000/XP的事件日志 
logging.handlers.SMTPHandler 远程输出日志到邮件地址
logging.handlers.MemoryHandler 日志输出到内存中的制定buffer
logging.handlers.HTTPHandler 通过"GET"或"POST"远程输出到HTTP服务器
日志等级(level) 描述
DEBUG 最详细的日志信息,典型应用场景是 问题诊断
INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING 当某些不期望的事情发生时记录的信息,但是此时应用程序还是正常运行的
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息

logging 模块还有很多配置,想了解更多的可以直接看看官方文档

logger最低级别为什么要设置 DEBUG 而不是 NOSET 的解释

创建一个 logger 时,设置级别为 NOTSET (当 logger 是根 logger 时,将处理所有消息;
当 logger 是非根 logger 时,所有消息会委派给父级,一般是根 logger)
而根 logger 创建时使用的是 WARNING 级别, 官网详情

所以上面所提到的最低级别要想用 NOTSET 时,可以改为设置 root 的级别
logger.setLevel(logging.DEBUG)
logging.root.setLevel(logging.NOTSET)

The end, See you!

posted @ 2020-11-22 17:56  mocobk  阅读(9)  评论(0)    收藏  举报