日志
如果你曾经在代码中加入 print() 语句,在程序运行时输出某些变量的值,你就使用了记日志的方式来调试代码。记日志是一种很好的方式,可以理解程序中发生的事,以及事情发生的顺序。 Python 的 logging 模块使得你很容易创建自定义的消息记录。这些日志消息将描述程序执行何时到达日志函数调用,并列出你指定的任何变量当时的值。另一方面,缺失日志信息表明有一部分代码被跳过,从未执行。
使用日志模块
要启用
logging 模块
,在程序运行时将日志信息显示在屏幕上,请将下面的代码复制到程序顶部(但在 Python 的#!行之下):
import
logging
logging
.
basicConfig
(
level
=
logging
.
DEBUG
,
format
=
' %(asctime)s - %(levelname)s- %(message)s'
# 日志打印格式
)
基本上,当 Python 记录一个事件的日志时,它会创建一个 LogRecord 对象,保存关于该事件的信息。 logging 模块的函数让你指定想看到的这个 LogRecord 对象的细节,以及希望的细节展示方式。
假如你编写了一个函数,计算一个数的阶乘。在数学上, 4 的阶乘是1 × 2 × 3 × 4,即 24。 7 的阶乘是 1 × 2 × 3 × 4 × 5 × 6 × 7,即 5040。打开一个新的文件编辑器窗口,输入以下代码。其中有一个缺陷,但你也会输入一些日志信息,帮助你弄清楚哪里出了问题。
import
logging
logging
.
basicConfig
(
level
=
logging
.
DEBUG
,
format
=
' %(asctime)s - %(levelname)s- %(message)s'
)
logging
.
debug
(
'Start of program'
)
def
factorial
(
n
)
:
logging
.
debug
(
'Start of factorial(%s%%)'
%
(
n
)
)
total
=
1
for
i
in
range
(
n
+
1
)
:
total
*=
i
logging
.
debug
(
'i is '
+
str
(
i
)
+
', total is '
+
str
(
total
)
)
logging
.
debug
(
'End of factorial(%s%%)'
%
(
n
)
)
return
total
print
(
factorial
(
5
)
)
logging
.
debug
(
'End of program'
)
输出:
0
2019-06-17 14:17:35,350 - DEBUG- Start of program
2019-06-17 14:17:35,350 - DEBUG- Start of factorial(5%)
2019-06-17 14:17:35,350 - DEBUG- i is 0, total is 0
2019-06-17 14:17:35,350 - DEBUG- i is 1, total is 0
2019-06-17 14:17:35,350 - DEBUG- i is 2, total is 0
2019-06-17 14:17:35,350 - DEBUG- i is 3, total is 0
2019-06-17 14:17:35,350 - DEBUG- i is 4, total is 0
2019-06-17 14:17:35,350 - DEBUG- i is 5, total is 0
2019-06-17 14:17:35,350 - DEBUG- End of factorial(5%)
2019-06-17 14:17:35,350 - DEBUG- End of program
打印日志信息时,使用 logging.debug() 函数。这个 debug() 函数将调用 basicConfig(),打印一行信息。这行信息的格式是我们在 basicConfig()函数中指定的,并且包括我们传递给 debug() 的消息。
factorial() 函数返回 0 作为 5 的阶乘,这是不对的。 for 循环应该用从 1 到 5的数,乘以 total 的值。但 logging.debug() 显示的日志信息表明, i 变量从 0 开始,而不是 1。因为 0 乘任何数都是 0,所以接下来的迭代中, total 的值都是错的。日志消息提供了可以追踪的痕迹,帮助你弄清楚何时事情开始不对。将代码行
for i in range(n + 1)
:改为
for i in range(1, n + 1):
,再次运行程序。
输出看起来像这样:
输出:
120
2019-06-17 14:22:30,612 - DEBUG- Start of program
2019-06-17 14:22:30,612 - DEBUG- Start of factorial(5%)
2019-06-17 14:22:30,612 - DEBUG- i is 1, total is 1
2019-06-17 14:22:30,612 - DEBUG- i is 2, total is 2
2019-06-17 14:22:30,612 - DEBUG- i is 3, total is 6
2019-06-17 14:22:30,612 - DEBUG- i is 4, total is 24
2019-06-17 14:22:30,612 - DEBUG- i is 5, total is 120
2019-06-17 14:22:30,612 - DEBUG- End of factorial(5%)
2019-06-17 14:22:30,612 - DEBUG- End of program
日志级别
“日志级别”提供了一种方式,按重要性对日志消息进行分类。 5 个日志级别如表所示,从最不重要到最重要。利用不同的日志函数,消息可以按某个级别记入日志。
日志的级别
import
logging
logging
.
debug
(
'debug message'
)
logging
.
info
(
'info message'
)
logging
.
warning
(
'warning message'
)
logging
.
error
(
'error message'
)
logging
.
critical
(
'critical message'
)
灵活配置日志级别,日志格式,输出位置
除了将日志消息显示在屏幕上,还可以将它们写入文本文件。
logging.basicConfig() 函数
接受 filename 关键字参数,
import
logging
logging
.
basicConfig
(
filename
=
'myProgramLog.txt'
,
level
=
logging
.
DEBUG
,
format
=
'%(asctime)s - %(levelname)s - %(message)s'
)
日志信息将被保存到 myProgramLog.txt 文件中。虽然日志消息很有用,但它们可能塞满屏幕,让你很难读到程序的输出。将日志信息写入到文件,让屏幕保持干净,又能保存信息,这样在运行程序后,可以阅读这些信息。可以用任何文件编辑器打开这个文本文件,诸如 Notepad 或 TextEdit。
日志文本默认的是
追加方式进行写入的
,可以更改写入的方式
filemode=‘w
logging
.
basicConfig
(
filename
=
'myProgramLog.txt'
,
level
=
logging
.
DEBUG
,
format
=
'%(asctime)s - %(levelname)s - %(message)s'
,
filemode
=
'w'
)
logging.basicConfig()函数
中可通过具体参数来更改logging模块默认行为,可用参数有
-
filename
:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。 -
filemode
:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 -
format
:指定handler使用的日志显示格式。 -
datefmt
:指定日期时间格式。 -
level
:设置rootlogger的日志级别 - stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,‘w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
import
logging
logging
.
basicConfig
(
level
=
logging
.
DEBUG
,
format
=
'%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s'
,
datefmt
=
'%a, %d %b %Y %H:%M:%S'
,
filename
=
'test.log'
,
filemode
=
'w'
)
logging
.
debug
(
'debug message'
)
logging
.
info
(
'info message'
)
logging
.
warning
(
'warning message'
)
logging
.
error
(
'error message'
)
logging
.
critical
(
'critical message'
)
输出:test.log
format参数中可能用到的格式化串:
- %(name)s Logger的名字
- %(levelno)s 数字形式的日志级别
- %(levelname)s 文本形式的日志级别
- %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
- %(filename)s 调用日志输出函数的模块的文件名
- %(module)s 调用日志输出函数的模块名
- %(funcName)s 调用日志输出函数的函数名
- %(lineno)d 调用日志输出函数的语句所在的代码行
- %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
- %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
- %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
- %(thread)d 线程ID。可能没有
- %(threadName)s 线程名。可能没有
- %(process)d 进程ID。可能没有
-
%(message)s用户输出的消息
logger对象
上述几个例子中我们了解到了logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()(分别用以记录不同级别的日志信息),logging.basicConfig()(用默认日志格式(Formatter)为日志系统建立一个默认的流处理器(StreamHandler),设置基础配置(如日志级别等)并加到root logger(根Logger)中)这几个logging模块级别的函数,另外还有一个模块级别的函数是logging.getLogger([name])(返回一个logger对象,如果没有指定名字将返回root logger)
一个最简单的过程:
import
logging
logger
=
logging
.
getLogger
(
)
# 创建一个handler,用于写入日志文件
fh
=
logging
.
FileHandler
(
'test11.log'
,
mode
=
'w'
)
# 再创建一个handler,用于输出到控制台
ch
=
logging
.
StreamHandler
(
)
formatter
=
logging
.
Formatter
(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
fh
.
setFormatter
(
formatter
)
ch
.
setFormatter
(
formatter
)
logger
.
addHandler
(
fh
)
#logger对象可以添加多个fh和ch对象
logger
.
addHandler
(
ch
)
logger
.
setLevel
(
logging
.
DEBUG
)
#设置日志的级别。对于低于该级别的日志消息将被忽略.
logger
.
debug
(
'logger debug message'
)
logger
.
info
(
'logger info message'
)
logger
.
warning
(
'logger warning message'
)
logger
.
error
(
'logger error message'
)
logger
.
critical
(
'logger critical message'
)
-
FileHandler(StreamHandler):
A handler class which writes formatted logging records to disk files.
def init (self, filename, mode=‘a’, encoding=None, delay=False): -
logging库提供了多个组件:
Logge
r、Handler
、Filter
、Formatter
。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。 -
Logger是一个树形层级结构,输出信息之前都要获得一个Logger(如果没有显示的获取则自动创建并使用root Logger,)。 logger = logging.getLogger()返回一个默认的Logger也即root Logger,并应用默认的日志级别、Handler和Formatter设置。
当然也可以通过Logger.setLevel(lel)
指定最低的日志级别,可用的日志级别有logging.DEBUG、``logging.INFO
、logging.WARNING
、logging.ERROR
、logging.CRITICAL
。 -
Logger.debug()
、Logger.info()
、Logger.warning()
、Logger.error()
、Logger.critical()
输出不同级别的日志,只有日志等级大于或等于设置的日志级别的日志才会被输出
可以将上述logging进行函数化
import
logging
def
logger
(
)
:
logger
=
logging
.
getLogger
(
)
# 创建一个handler,用于写入日志文件
fh
=
logging
.
FileHandler
(
'test10.log'
,
mode
=
'w'
)
# 再创建一个handler,用于输出到控制台
ch
=
logging
.
StreamHandler
(
)
formatter
=
logging
.
Formatter
(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
fh
.
setFormatter
(
formatter
)
ch
.
setFormatter
(
formatter
)
logger
.
addHandler
(
fh
)
#logger对象可以添加多个fh和ch对象
logger
.
addHandler
(
ch
)
logger
.
setLevel
(
logging
.
DEBUG
)
#设置日志的级别。对于低于该级别的日志消息将被忽略.
return
logger
logger
=
logger
(
)
logger
.
debug
(
'logger debug message'
)
logger
.
info
(
'logger info message'
)
logger
.
warning
(
'logger warning message'
)
logger
.
error
(
'logger error message'
)
logger
.
critical
(
'logger critical message'
)