Python日志捕获与断言那些你不知道的实用技巧。。。
作为一名程序员,日志对我来说就像一个黑匣子。调试难搞的问题?翻翻日志准没错。出bug了不想背锅?日志也能帮你洗清冤屈。
不过,光有日志还不够,有时候我们需要更高级的操作,比如动态捕获日志内容、在测试中对日志断言,甚至把日志记录到文件里方便后续分析。今天我就带大家全面了解一下Python的logging模块,以及它在实际场景中的高效用法。
Python的logging模块是一个强大的内置工具,支持灵活的日志级别和处理方式。每当提到日志,绕不开一个概念:日志级别。
DEBUG是最基础的调试信息,INFO记录正常的运行状态,WARNING提示可能出现的问题,ERROR说明程序出错了,而CRITICAL表示严重问题,程序可能直接“嗝屁”。这些级别从低到高,重要性逐步递增。
我们来看一个简单的例子:
import logging# 配置日志格式和级别
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
# 生成不同级别的日志信息
logging.debug('调试信息:变量x的值为42')
logging.info('程序启动成功')
logging.warning('警告:磁盘空间不足')
logging.error('错误:文件未找到')
logging.critical('严重错误:系统崩溃!')
运行后,日志会直接打印到控制台,包括时间、日志级别和具体的消息。比如:
2024-11-18 14:30:00,000 - DEBUG - 调试信息:变量x的值为42
2024-11-18 14:30:00,001 - INFO - 程序启动成功
2024-11-18 14:30:00,002 - WARNING - 警告:磁盘空间不足
2024-11-18 14:30:00,003 - ERROR - 错误:文件未找到
2024-11-18 14:30:00,004 - CRITICAL - 严重错误:系统崩溃!
这时候你可能会问:“日志打印的内容如果太多,我能不能只看重要的?”当然可以!只需修改日志级别,比如设置为WARNING,这样就只会显示WARNING及以上级别的日志,清爽多了。
不过,有时候日志内容不仅要打印出来,还得捕获下来进行后续操作,比如在测试中验证日志是否符合预期。这里有个小技巧:使用StringIO捕获日志内容。
import logging
from io import StringIO# 使用 StringIO 作为日志捕获容器
log_capture_string = StringIO()
ch = logging.StreamHandler(log_capture_string)
ch.setLevel(logging.DEBUG)
# 配置日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# 设置 logger
logger = logging.getLogger()
logger.addHandler(ch)
logger.setLevel(logging.DEBUG)
# 写入一些日志
logger.info('这是一个普通日志')
logger.error('出现了一个错误')
# 获取日志内容
log_contents = log_capture_string.getvalue()
print("捕获的日志内容:")
print(log_contents)
# 清理处理器
logger.removeHandler(ch)
log_capture_string.close()
运行后,你会发现日志内容被存储到了StringIO对象中,想怎么处理就怎么处理。这种方式特别适合在测试场景下使用,比如断言日志内容是否正确。
在自动化测试中,日志捕获和断言是标配。用unittest结合logging,就能轻松验证日志输出是否符合预期。看下面的例子:
import logging
from io import StringIO
import unittestclass TestLogging(unittest.TestCase):
def test_logging_output(self):
log_capture_string = StringIO()
ch = logging.StreamHandler(log_capture_string)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(ch)
logger.setLevel(logging.DEBUG)
# 写入日志
logger.info('测试日志内容')
logger.error('模拟错误日志')
# 获取日志内容
log_contents = log_capture_string.getvalue()
# 验证日志内容是否包含特定消息
self.assertIn('测试日志内容', log_contents)
self.assertIn('模拟错误日志', log_contents)
logger.removeHandler(ch)
log_capture_string.close()
if __name__ == '__main__':
unittest.main()
这个例子中,assertIn用来检查日志内容是否包含特定的字符串。如果日志内容匹配,测试通过;如果不匹配,测试直接失败,方便排查问题。
当然,日志不仅要实时捕获,往往还需要写入文件以便日后分析。Python的FileHandler可以轻松实现这一点:
import logging# 配置日志文件
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# 写入日志
logging.debug('这是调试日志')
logging.info('启动成功')
logging.warning('磁盘空间不足')
logging.error('文件未找到')
logging.critical('系统崩溃')
运行代码后,你会发现所有日志都被写入了app.log文件中。通过这种方式,你可以轻松实现日志的长期保存。
结合捕获、断言和文件记录,我们可以设计一个综合的测试框架。以下是一个综合实例,展示如何在测试中同时捕获日志内容并写入文件:
import logging
from io import StringIO
import unittestclass TestLoggingWithFile(unittest.TestCase):
def setUp(self):
self.log_capture_string = StringIO()
ch = logging.StreamHandler(self.log_capture_string)
ch.setLevel(logging.DEBUG)
fh = logging.FileHandler('test_log.log')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
fh.setFormatter(formatter)
self.logger = logging.getLogger()
self.logger.addHandler(ch)
self.logger.addHandler(fh)
self.logger.setLevel(logging.DEBUG)
def tearDown(self):
self.logger.handlers.clear()
def test_logging_capture_and_file(self):
self.logger.info('测试日志写入')
self.logger.error('这是一个错误日志')
log_contents = self.log_capture_string.getvalue()
self.assertIn('测试日志写入', log_contents)
self.assertIn('这是一个错误日志', log_contents)
if __name__ == '__main__':
unittest.main()
这个实例演示了如何捕获日志内容进行断言,并同时将日志写入到文件中。这种方法非常适合用于复杂的测试场景。
日志记录不仅是调试和监控的利器,还能通过捕获和断言的方式进一步提升测试质量。Python的logging模块功能强大且灵活,无论是开发还是测试,都值得深入掌握。
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。