Python 中的日志(logging)模块基础与实战【第105篇—logging模块】

Python 中的日志(logging)模块基础与实战

在软件开发过程中,日志是一项至关重要的功能,它可以帮助我们追踪程序的执行过程、排查问题并记录关键信息。Python 提供了一个强大且灵活的日志模块,即 logging 模块,它允许我们以结构化的方式管理和记录日志信息。在本文中,我们将深入了解 logging 模块的基础知识,并通过实际代码示例演示其用法。

在这里插入图片描述

1. 日志模块基础

1.1 导入日志模块

首先,我们需要导入 logging 模块:

import logging
1.2 配置日志

在使用日志模块之前,我们可以配置日志的基本设置,例如设置日志级别、输出格式等。以下是一个简单的配置示例:

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
  • level: 设置日志级别,可以选择 DEBUGINFOWARNINGERRORCRITICAL
  • format: 设置日志输出格式,上述格式中包含了时间、级别和消息。
1.3 记录日志

现在我们可以使用 logging 模块记录日志了。例如:

logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")

2. 代码实战

现在,让我们通过一个实际的例子来演示如何在代码中使用 logging 模块。

import logging# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')def divide(x, y):try:result = x / yexcept ZeroDivisionError:logging.error("Attempted to divide by zero")else:logging.info(f"The result of {x} / {y} is {result}")if __name__ == "__main__":# 记录日志logging.debug("Program starts")# 调用函数divide(10, 2)divide(8, 0)# 记录日志logging.debug("Program ends")

在这个例子中,我们定义了一个 divide 函数用于执行除法操作。通过配置日志,我们可以在函数中记录相关信息,包括成功执行和异常情况。通过查看日志,我们能够更容易地追踪程序的执行流程,发现潜在问题。

3. 代码解析

3.1 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
  • level=logging.DEBUG: 设置日志级别为 DEBUG,表示所有级别的日志都会被记录。
  • format='%(asctime)s - %(levelname)s - %(message)s': 设置日志输出格式,其中 %asctime 表示时间,%levelname 表示级别,%message 表示消息。
3.2 记录日志
logging.debug("Program starts")
  • 使用 logging.debug 记录 DEBUG 级别的日志。
logging.error("Attempted to divide by zero")
  • 使用 logging.error 记录 ERROR 级别的日志,并输出错误消息。
logging.info(f"The result of {x} / {y} is {result}")
  • 使用 logging.info 记录 INFO 级别的日志,并输出计算结果。

通过这些记录,我们可以清晰地了解程序的执行过程,包括开始、结束以及可能发生的异常情况。

通过本文的介绍,你已经学会了如何在 Python 中使用 logging 模块进行基础配置和实际应用。合理的日志记录有助于提高代码的可维护性和可调试性,是每个开发者在项目中不可或缺的一项技能。

4. 高级日志处理

4.1 输出到文件

除了在控制台输出日志信息,logging 模块还允许将日志记录到文件中。我们可以通过配置 FileHandler 来实现:

file_handler = logging.FileHandler('logfile.log')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))logger = logging.getLogger()
logger.addHandler(file_handler)

这段代码创建一个文件处理器 FileHandler,将日志记录到名为 logfile.log 的文件中,级别为 DEBUG

4.2 多模块共享日志配置

如果你的应用程序包含多个模块,可以通过以下方式实现日志的共享配置:

# main.py
import logging
import mymoduledef main():logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')mymodule.run()if __name__ == "__main__":main()# mymodule.py
import loggingdef run():logger = logging.getLogger(__name__)logger.debug("This message is from mymodule")

在这个例子中,main.py 配置了基本的日志设置,而 mymodule.py 中通过 getLogger(__name__) 获取到了相同的日志实例,使得两者共享相同的日志配置。

6. 自定义日志处理器

logging 模块允许用户自定义日志处理器,以满足特定的需求。我们可以通过创建一个继承自 logging.Handler 的类来实现自定义处理器。

以下是一个简单的例子,展示如何创建一个将日志记录到数据库的处理器:

import logging
import sqlite3class DatabaseHandler(logging.Handler):def __init__(self, db_path):super().__init__()self.db_path = db_pathself.connection = sqlite3.connect(self.db_path)self.cursor = self.connection.cursor()def emit(self, record):log_message = self.format(record)self.cursor.execute("INSERT INTO logs (message) VALUES (?)", (log_message,))self.connection.commit()# 使用自定义处理器
db_handler = DatabaseHandler('mydatabase.db')
db_handler.setLevel(logging.INFO)
db_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))logger = logging.getLogger()
logger.addHandler(db_handler)# 记录日志
logger.info("This message will be stored in the database")

在这个例子中,我们创建了一个 DatabaseHandler 类,继承自 logging.Handler,并重写了 emit 方法来定义日志的处理逻辑。在使用时,我们实例化这个处理器,并将其添加到日志记录器中。

7. 异常处理与日志记录

在实际开发中,异常处理和日志记录经常结合使用,以便及时捕获和记录程序运行时的错误信息。考虑以下代码:

import logginglogging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')def process_data(data):try:result = data / 0except Exception as e:logging.exception("Error occurred while processing data: %s", str(e))# 测试异常处理与日志记录
if __name__ == "__main__":process_data(42)

在这个例子中,process_data 函数故意引发了一个除以零的异常。通过 logging.exception 方法,我们记录了异常信息,包括异常的堆栈信息,这对于调试和定位问题非常有帮助。

9. 配置文件管理日志

在实际应用中,通常会使用配置文件来管理日志记录器的配置,而不是在代码中硬编码。这样可以使配置更加灵活,便于在不同环境中调整日志设置。

首先,创建一个日志配置文件 logging_config.ini

[loggers]
keys=root,sampleLogger[handlers]
keys=consoleHandler,fileHandler[formatters]
keys=sampleFormatter[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
propagate=0[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=sampleFormatter
args=(sys.stdout,)[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=sampleFormatter
args=('mylogfile.log', 'a')[formatter_sampleFormatter]
format=%(asctime)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S

接下来,使用以下代码来读取配置文件并配置日志记录器:

import logging.config
import sysdef configure_logging():try:logging.config.fileConfig('logging_config.ini')logger = logging.getLogger()logger.info("Logging configuration successful")except Exception as e:print(f"Error configuring logging: {str(e)}")sys.exit(1)if __name__ == "__main__":configure_logging()# 测试日志记录器logging.debug("This is a debug message")logging.info("This is an info message")logging.warning("This is a warning message")logging.error("This is an error message")logging.critical("This is a critical message")

在这个例子中,通过 logging.config.fileConfig('logging_config.ini') 从配置文件中读取配置并应用到日志记录器。这使得我们可以在不修改代码的情况下,通过修改配置文件来调整日志设置。

10. 日志轮换

在实际应用中,日志文件可能会变得非常大。为了避免占用过多磁盘空间,可以使用日志轮换进行管理。logging 模块内置了 RotatingFileHandlerTimedRotatingFileHandler 来支持日志轮换。

from logging.handlers import RotatingFileHandler# 创建 RotatingFileHandler
rotating_handler = RotatingFileHandler('mylogfile.log', maxBytes=1024, backupCount=3)
rotating_handler.setLevel(logging.DEBUG)
rotating_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))# 添加到日志记录器
logger = logging.getLogger()
logger.addHandler(rotating_handler)

在这个例子中,RotatingFileHandler 会在日志文件大小达到 maxBytes 时创建一个新文件,并保留 backupCount 个旧文件。这有助于控制日志文件的大小,并且可以防止它们无限增长。

12. 异步日志记录

在高并发和性能要求较高的应用中,同步的日志记录可能成为性能瓶颈。为了解决这个问题,Python 3.7 及以上版本引入了 asyncio 模块,支持异步日志记录。异步日志记录允许日志消息在后台线程或进程中异步处理,从而减少对主线程的阻塞。

以下是一个简单的异步日志记录的示例:

import logging
import asyncio# 配置异步日志记录器
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')async def log_message(message):await asyncio.sleep(1)logging.info(message)async def main():tasks = [log_message(f"Message {i}") for i in range(5)]await asyncio.gather(*tasks)if __name__ == "__main__":loop = asyncio.get_event_loop()loop.run_until_complete(main())

在这个例子中,通过 async def log_message 定义了一个异步函数来模拟异步的日志记录操作。在 main 函数中,使用 asyncio.gather 同时运行多个异步任务。通过这种方式,可以在后台异步处理日志记录,而不会阻塞主线程的执行。

13. 集成日志框架

在大型项目中,可能会使用其他日志框架,例如 logurustructlog 等,它们提供了更多的功能和灵活性。以下是一个使用 loguru 的简单示例:

from loguru import logger# 配置日志记录器
logger.add("mylogfile.log", level="DEBUG", format="{time} - {level} - {message}")# 记录日志
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

loguru 提供了更直观的 API 和更灵活的配置方式,支持异步日志记录,以及丰富的日志格式和过滤器等功能。

15. 日志安全性和最佳实践

在开发中,确保日志记录的安全性和遵循最佳实践是至关重要的。以下是一些关于日志安全性和最佳实践的建议:

15.1 日志敏感信息

避免在日志中记录敏感信息,例如密码、API 密钥等。确保你的日志中不包含任何可能导致安全问题的信息。

# 不安全的例子
logger.info(f"User logged in with password: {password}")# 更安全的例子
logger.info("User logged in")
15.2 日志级别选择

根据需求选择适当的日志级别。在生产环境中,可以将日志级别设置为 INFO 或更高级别,以避免记录大量冗余信息。

# 生产环境中避免过多冗余信息
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
15.3 异常信息记录

在记录异常信息时,使用 exception 方法而不是 error 方法,以便记录完整的异常信息,包括堆栈跟踪。

try:# some code that may raise an exception
except Exception as e:logging.exception("An error occurred: %s", str(e))
15.4 日志审计

在需要进行审计的应用中,记录关键操作和事件,以便追踪用户行为和系统状态。

# 记录关键操作
logger.info("User {username} performed action {action}")# 记录系统状态
logger.info("System is running with {resource} available")
15.5 定期清理日志

定期清理日志文件,以避免磁盘空间过度占用。使用合适的轮换策略和定期清理任务。

15.6 日志记录性能

在高性能要求的应用中,考虑使用轻量级的日志记录器,并避免不必要的日志记录。异步日志记录、日志级别过滤等都是提高性能的方式。

17. 实例演练:Flask 应用中的日志记录

让我们通过一个具体的实例演练,如何在一个简单的 Flask 应用中使用日志记录来追踪请求、错误以及应用的运行状态。

首先,安装 Flask 和 loguru(如果你选择使用):

pip install Flask loguru

然后,创建一个名为 app.py 的 Flask 应用:

from flask import Flask, request
from loguru import loggerapp = Flask(__name__)# 配置日志记录器
logger.add("app.log", level="INFO", format="{time} - {level} - {message}")# 请求日志中间件
@app.before_request
def log_request_info():logger.info(f"Request - {request.method} {request.url}")# 错误日志中间件
@app.errorhandler(500)
def log_server_error(e):logger.error(f"Internal Server Error - {e}")return "Internal Server Error", 500# 路由
@app.route('/')
def index():return "Hello, World!"@app.route('/error')
def simulate_error():raise Exception("Simulated Internal Server Error")if __name__ == "__main__":app.run(debug=True)

在这个示例中,我们使用 Flask 框架创建了一个简单的应用。同时,我们使用 loguru 配置了一个日志记录器,并在请求处理前后、出现错误时记录相应的日志信息。

  • logger.add("app.log", level="INFO", format="{time} - {level} - {message}"):配置了一个将日志记录到文件 app.log 的处理器。

  • @app.before_request:在每个请求到达应用之前,记录请求的方法和 URL。

  • @app.errorhandler(500):当应用遇到内部服务器错误(500)时,记录错误信息。

  • / 路由:返回简单的 “Hello, World!”。

  • /error 路由:模拟一个内部服务器错误,触发错误处理中间件。

通过运行这个应用,你可以在终端和 app.log 文件中查看相应的日志记录。这样的日志记录方式有助于实时监控应用的运行状态,追踪请求处理过程,以及快速定位和解决错误。

总结:

通过本篇技术博客,我们全面深入地探讨了 Python 中日志处理的各个方面,涵盖了基础知识、代码实战、高级技术以及实际应用场景。以下是文章的主要亮点:

  1. 基础知识: 我们从导入模块、配置日志和记录日志的基础知识入手,详细介绍了 logging 模块的基本用法,包括日志级别、格式化输出等。

  2. 代码实战: 通过实际的代码示例,我们展示了如何在 Python 中使用 logging 模块进行日志记录。从简单的配置到实际的函数调用,让读者通过实战了解日志的基本应用。

  3. 高级技术: 我们深入研究了一些高级的日志处理技术,包括自定义日志处理器、异步日志记录、配置文件管理日志、日志轮换等。这些技术提供了更灵活、更高效的日志处理方式。

  4. 实际应用: 通过在 Flask 应用中的实例演练,展示了如何将日志记录应用到实际的 Web 开发中,追踪请求、处理错误以及记录应用的运行状态。

  5. 安全性和最佳实践: 强调了日志记录中的安全性问题,如避免记录敏感信息,选择适当的日志级别,审计关键操作等。同时,提供了一些建议,如定期清理日志、考虑日志记录性能等。

通过这篇技术博客,读者不仅了解了日志处理的基础知识和实际应用,还深入了解了一些高级技术和最佳实践。这些知识将有助于读者更好地利用日志记录功能,提高代码的可读性、可维护性,以及系统的安全性和稳定性。希望读者通过本文的学习,能够在实际项目中更加高效地应用日志处理技术。

通过本文的实例演练,我们深入了解了在 Flask 应用中如何使用日志记录来追踪请求、错误和应用的运行状态。这个实例演练展示了如何在实际项目中应用日志记录的最佳实践,以提高系统的可维护性和运行稳定性。希望这个例子对你理解日志记录在 Web 应用中的应用有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/498510.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【方法】带密码的7Z分卷文件如何解压?

7Z是很多人工作中经常用到的压缩文件格式,那如果压缩成7Z分卷压缩文件,并且设置了密码,后续要如何解压呢?不清楚的小伙伴一起来看看吧! 首先,在解压7Z分卷压缩文件前,需要先满足以下两个条件&a…

Android T 远程动画显示流程其三——桌面侧动画启动到系统侧结束流程

前言 接着前文分析Android T 远程动画显示流程其二 我们通过IRemoteAnimationRunner跨进程通信从系统进程来到了桌面进程,这里是真正动画播放的逻辑。 之后又通过IRemoteAnimationFinishedCallback跨进程通信回到系统进程,处理动画结束时的逻辑。 进入…

YOLO V3学习(3)——基于darknet ros的目标检测 基于gpu版本

本机已经安装GPU显卡,并下载了Cuda、Cudnn。GPU配置为1660Ti 1.darknet编译 下载darknet: git clone https://github.com/AlexeyAB/darknet.git 修改相应的makefile文件 主要的修改部分: GPU1 CUDNN1 CUDNN_HALF0 OPENCV1 AVX0 OPENMP0 LIB…

表格图片太大怎么批量压缩?快速处理图片大小的方法

在工作或者学习中制作表格的时候,经常需要插入一些图片来修饰内容,当遇到图片太大无法导入的情况就比较麻烦,尤其是多张图片处理的时候,那么表格图片太大怎么批量压缩呢?接下来小编就分享给大家一个快速图片压缩的方法…

Python进阶学习:Pickle模块--dump()和load()的用法

Python进阶学习:Pickle模块–dump()和load()的用法 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希望得到您…

【三维重建】【slam】【分块重建】LocalRF:逐步优化的局部辐射场的鲁棒视图合成

项目地址:https://localrf.github.io/ 题目:Progressively Optimized Local Radiance Fields for Robust View Synthesis 来源:KAIST、National Taiwan University、Meta 、University of Maryland, College Park 提示:文章用了s…

Springboot中ApplicationContextInitializer的使用及源码分析

文章目录 一、认识ApplicationContextInitializer1、ApplicationContextInitializer的作用2、认识ApplicationContextInitializer接口3、ApplicationContextInitializer的常用用法(1)注册BeanFactoryPostProcessor(2)注册Applicat…

用OpenArk查看Windows 11电脑中全部快捷键并解决热键冲突问题

本文介绍在Windows电脑中,基于OpenArk工具,查看电脑操作系统与所有软件的快捷键,并对快捷键冲突加以处理的方法。 最近,发现有道词典的双击Ctrl功能失效了,不能很方便地翻译界面中的英语;所以,就…

从 0 到 1 搭建亿级商品 ES 搜索引擎

建设并维护一个亿级的搜索引擎并非易事,也不存在一劳永逸的最优治理方法。本文是在实践中不断学习和总结的成果,介绍了如何搭建一个可支持从千万级到亿级商品量级的搜索系统,并实现查询总 QPS 从百级增长到千级,写入总 QPS 从百级…

15:00面试,15:08就出来了,问的问题有点变态。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到9月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…

js获取地理位置并记录

正常情况下,一般都是通过请求时附带的ip给后端,然后再通过ip解析出客户端访问所在的地理位置,那么,如果不通过ip呢。让用户手动授权允许访问客户端的位置,以此获取地址位置精度更高。 不知你们在访问一些网站时&#x…

Leetcode : 215. 数组中的第 K 个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 思路:最开始排序算法&…