日志
如果你曾经在代码中加入 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')