Python 标准库模块 - logging
logging 模块是 Python 内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等。
相比 print
函数,具备如下优点:
- 可以通过设置不同的日志等级,在 release 版本中只输出重要信息,而不必显示大量的调试信息。
-
print
将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging 则可以由开发者决定将信息输出到什么地方,以及怎么输出。
下面,我们就来学习一下标准库模块 logging
的基本用法吧~
一. 日志级别
logging 模块提供的日志级别如下:
日志等级 | 描述 |
---|---|
DEBUG | 最详细的日志信息,典型应用场景是问题诊断。 |
INFO | 记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作。 |
WARNING | 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的。 |
ERROR | 由于一个更严重的问题导致某些功能不能正常运行时记录的信息。 |
CRITICAL | 当发生严重错误,导致应用程序不能继续运行时记录的信息。 |
上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的。当为某个应用程序指定一个日志级别后,应用程序会记录所有日志级别大于或等于指定日志级别的日志信息。
默认情况下,logging 模块将日志打印到屏幕上,日志级别为 WARNING,即只有日志级别高于 WARNING 的日志才会被输出。下面是一个简单的例子说明:

从运行结果中,我们可以看到默认只有 WARNING、ERROR 及 CRITICAL 级别的日志打印到了标准输出。这里需要补充说明一下默认输出的日志格式:
>> logging.BASIC_FORMAT
'%(levelname)s:%(name)s:%(message)s'
日志格式默认由三部分组成,中间以冒号分隔,分别为:日志级别、logger
实例的名称以及日志消息内容。
想要个性化设置日志级别、日志输出的目的地以及日志格式等,下面就来展开学习 logging
模块把!
二. logging 模块的使用
2.1 使用 basciConfig
进行简单配置
logging 模块级别的函数 basciConfig
用于对日志系统进行基本配置。通常用于一次性配置简单脚本的日志。
logging.basicConfig
函数最常用的几个关键字参数如下:
关键字参数 | 释义 |
---|---|
filename | 指定日志文件名; |
filemode | 指定日志文件的打开格式:w 或者 a ,默认为追加模式; |
format | 指定日志输出内容和格式; |
datefmt | 指定 %(asctime)s 显示的时间日期格式; |
level | 设置根记录器输出日志的级别; |
stream | 使用指定的流初始化 StreamHandler ; |
handlers | 由日志处理器 Handler 组成的可迭代对象,这些 Handler 都将被添加到根日志器; |
注:
stream
参数与filename
不兼容,如果两个都有,stream
将被忽略。
默认创建的StreamHandler
向sys.stderr
输出。
下面是一个简单的例子:
import logging
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s')
logger = logging.getLogger(__name__)
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log')
运行结果:

在使用 2>/dev/null
将标准错误重定向之后,屏幕没有任何打印。下面我们指定 stream=sys.stdout
:
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s',
stream=sys.stdout)
logger = logging.getLogger(__name__)
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log')
运行结果:

我们也可以不指定 stream
,而是使用 filename
参数将日志输出到一个文件:
import logging
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] [%(levelname)s] [%(name)s] - %(message)s',
filename='test_log.log')
logger = logging.getLogger(__name__)
logger.debug('debug message log.')
logger.info('info message log')
logger.warn('warn message log')
logger.error('error message log')
logger.critical('critical message log')
运行结果:

上面的例子中,参数 format
用到了很多占位符,下面是常用的格式占位符与含义:
格式占位符 | 释义 |
---|---|
%(levelno)s | 打印日志级别的数值。 |
%(levelname)s | 打印日志级别名称。 |
%(name)s | 日志记录器 logger 的名称,通常设置为:__name__ 。 |
%(pathname)s | 打印当前执行程序的路径,其实就是 sys.argv[0] 。 |
%(filename)s | 打印当前执行程序名。 |
%(funcName)s | 打印日志的当前函数。 |
%(lineno)d | 打印日志的当前行号。 |
%(asctime)s | 打印日志的时间。 |
%(threadName)s | 打印线程名称。 |
%(process)d | 打印进程 id 。 |
%(message)s | 打印日志信息。 |
在介绍 basicConfig
的参数时,我们提到了日志记录器,处理器以及格式化参数 format
,这就涉及到了 logging
模块中非常重要的几个概念:
- 日志记录器
Logger
:提供应用程序代码可以直接访问的接口。 - 日志处理器
Handler
:将日志记录器产生的记录输出到指定位置。 - 日志格式化器
Formatter
:指明日志输出的内容和格式。
三者的对应关系为:一个日志记录器可以添加多个处理器 Handler
,每个 Handler
可以指定一个日志格式化器 Formatter
。下面,我们就来一一学习他们~
2.2 日志处理器 Handler
import logging
import sys
def log():
logger = logging.getLogger('log_test')
logger.setLevel(logging.DEBUG)
if not logger.handlers:
file_handler = logging.FileHandler("test.log",encoding="utf-8")
logger.addHandler(file_handler)
stdout_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stdout_handler)
return logger
logger = log()
logger.debug("查错")
logger.info("提示")
logger.warning("警告")
logger.error("错误")
运行结果:

由于我们为记录器配置了两个处理器:file_handler
和 stdout_handler
,因此在标准输出和 test.log
文件中都记录了程序中的日志。这里由于我们运行了两次程序,因此 test.log
中产生了两组输出。
使用 logging.getLogger
获取记录器,不传递任何参数时,默认返回根记录器:
>> logger = logging.getLogger()
>> logger
<RootLogger root (WARNING)>
默认情况下 logger.handlers
为空列表:
>> logger.handlers
[]
使用 logger.addHandler
为记录器添加日志处理器:
>> stdout_handler = logging.StreamHandler(sys.stdout)
>> logger.addHandler(stdout_handler)
>> logger.handlers
[<StreamHandler stdout (NOTSET)>]
2.3 日志格式化器 Formatter
import logging
def log():
logger = logging.getLogger("log_test")
logger.setLevel(logging.DEBUG)
if not logger.handlers:
file_handler = logging.FileHandler("test.log",encoding="utf-8")
formatter = logging.Formatter(
fmt="%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s",
datefmt="%Y/%m/%d %X")
# 为handler指定输出格式
file_handler.setFormatter(formatter)
# 为logger添加的日志处理器
logger.addHandler(file_handler)
return logger
logger = log()
logger.debug("查错")
logger.info("提示")
logger.warning("警告")
logger.error("错误")
运行结果:

使用 logging.Formatter
即可创建日志格式化器:formatter = logging.Formatter(fmt=None, datefmt=None)
。其中,fmt
是消息的格式化字符串,datefmt
是日期字符串。如果不指明 fmt
,将使用 '%(message)s';如果不指明 datefmt
,将使用 %Y-%m-%d %H:%M:%S
日期格式。
2.4 捕获 traceback
Python 中的 traceback 模块用于捕获异常信息,可以在 logging 中记录下 traceback 。
import logging
import sys
LOG_FORMAT = "[%(asctime)s]-[%(levelname)s] %(message)s"
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
LOG_FILE = sys.argv[0].split('.')[0]
def log():
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setFormatter(logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT))
file_handler.setLevel(logging.ERROR)
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT))
stream_handler.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
return logger
if __name__ == "__main__":
logger = log()
logger.info('################# {} #################'.format(LOG_FILE))
try:
print(6/0)
except ZeroDivisionError as e:
# logger.error("ZeroDivisionError occured in my proceger.", exc_info=True)
logger.exception("ZeroDivisionError occured in my proceger.")
运行结果:

使用 logger.error(xxxxx, exc_info=True)
即可在日志中捕获 traceback 信息,更为方便地可以直接使用 logger.exception
方法。查阅日志文件 test_log 可以清晰看到 traceback 信息:
mia@ubuntu:~/Desktop$ cat test_log
[2021-07-03 01:21:22]-[ERROR] ZeroDivisionError occured in my proceger.
Traceback (most recent call last):
File "test_log.py", line 29, in <module>
print(6/0)
ZeroDivisionError: division by zero
[2021-07-03 01:21:33]-[ERROR] ZeroDivisionError occured in my proceger.
Traceback (most recent call last):
File "test_log.py", line 29, in <module>
print(6/0)
ZeroDivisionError: division by zero