Flask解决日志重复打印的原理与方案详解

 更新时间:2025年05月12日 08:21:29   作者:码农阿豪@新空间  
在Flask应用开发中,日志管理是一个容易被忽视但极其重要的环节,本文将详细分析日志重复的根本原因,并提供一套完整的解决方案,希望对大家有一定的帮助

引言

在Flask应用开发中,日志管理是一个容易被忽视但极其重要的环节。许多开发者会遇到日志重复打印的问题,尤其是在多线程、多进程或模块化项目中。本文将详细分析日志重复的根本原因,并提供一套完整的解决方案,帮助开发者彻底解决这一问题。

问题背景

在开发一个电话号码匹配服务时,我们发现日志中每条消息都被重复打印两次,例如:

2025-05-11 15:38:46,291 - app - INFO - 文件上传请求 - 全国匹配: 否, 接收邮箱: fffffhemo@qq.com
2025-05-11 15:38:46,291 - app - INFO - 文件上传请求 - 全国匹配: 否, 接收邮箱: fffffhemo@qq.com

这种重复日志不仅干扰调试,还会占用额外的存储资源。经过排查,我们发现问题的根源在于 日志处理器被多次添加 和 混用 logging 与 app.logger。

日志重复的根本原因

1. 日志处理器重复添加

Flask的日志系统默认会添加一个处理器(如控制台输出),而开发者可能手动添加了额外的处理器(如文件日志),导致每条日志被多个处理器处理。

错误示例:

def create_app():
    app = Flask(__name__)
    
    # 添加文件处理器
    file_handler = TimedRotatingFileHandler('app.log')
    app.logger.addHandler(file_handler)
    
    # 默认已有一个处理器,此时共有两个处理器
    return app

2. 混用 logging 和 app.logger

在Flask中,app.logger 是对Python标准库 logging 的封装。如果同时使用两者,会导致日志被重复记录。

错误示例:

import logging
from flask import current_app

def some_function():
    logging.info("使用标准库logging")  # 记录一次
    current_app.logger.info("使用Flask logger")  # 记录第二次

3. 多线程或多进程初始化

多线程:后台线程可能重复初始化日志。

多进程:使用 gunicorn --workers=2 时,每个进程会独立初始化日志。

解决方案

1. 统一使用 app.logger

完全移除 logging 的直接调用,改用 app.logger 或 current_app.logger。

修复后代码:

from flask import current_app

def process_data():
    current_app.logger.info("处理数据")  # ✅ 统一使用Flask logger

2. 确保日志只初始化一次

在 create_app 中,通过标记防止重复初始化:

def create_flask_app_with_configs() -> Flask:
    phone_app = PhoneApp(__name__)

    if hasattr(phone_app, "_logger_initialized"):
        return phone_app
    phone_app._logger_initialized = True  # 标记已初始化

    # 清空默认处理器
    phone_app.logger.handlers.clear()

    # 添加自定义处理器
    file_handler = TimedRotatingFileHandler("app.log")
    phone_app.logger.addHandler(file_handler)

    return phone_app

3. 修复后台线程的日志

在后台线程中,必须绑定应用上下文才能使用 current_app.logger:

def background_task():
    from flask import current_app
    with current_app.app_context():
        current_app.logger.info("后台任务执行中")  # ✅ 正确方式

4. 禁用Flask默认日志(可选)

禁用Werkzeug的默认访问日志,减少干扰:

# 禁用Werkzeug日志
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.handlers.clear()
werkzeug_logger.setLevel(logging.WARNING)

完整修复后的代码

以下是彻底修复后的 app.py 核心部分:

import os
import threading
from flask import Flask, current_app
from logging.handlers import TimedRotatingFileHandler

class PhoneApp(Flask):
    pass

def create_flask_app_with_configs() -> Flask:
    phone_app = PhoneApp(__name__)

    if hasattr(phone_app, "_logger_initialized"):
        return phone_app
    phone_app._logger_initialized = True

    # 清空默认处理器
    phone_app.logger.handlers.clear()

    # 文件日志处理器
    file_handler = TimedRotatingFileHandler(
        "app.log", when="midnight", backupCount=7
    )
    phone_app.logger.addHandler(file_handler)

    return phone_app

def create_app() -> Flask:
    app = create_flask_app_with_configs()
    app.logger.info("应用初始化完成")  # ✅ 统一使用app.logger
    return app

if __name__ == "__main__":
    app = create_app()
    app.run(use_reloader=False)  # 关闭调试重载器

验证日志是否修复

检查日志文件:确认每条日志只出现一次。

测试多线程:启动后台任务,观察日志是否正常。

生产环境测试:用 gunicorn 多worker测试,确保无重复。

总结

问题原因解决方案
日志重复打印处理器被多次添加清空默认处理器,确保只初始化一次
混用 logging 和 app.logger日志被两种方式记录统一使用 app.logger
多线程/多进程问题每个线程/进程独立初始化标记初始化状态,绑定上下文

通过以上方法,你可以彻底解决Flask日志重复问题,让日志系统清晰高效!

到此这篇关于Flask解决日志重复打印的原理与方案详解的文章就介绍到这了,更多相关Flask解决日志重复打印内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python随机生成8位密码的示例详解

    Python随机生成8位密码的示例详解

    这篇文章主要为大家详细介绍了基于Python实现随机生成8位密码的相关方法,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-02-02
  • Python基于tkinter canvas实现图片裁剪功能

    Python基于tkinter canvas实现图片裁剪功能

    这篇文章主要介绍了Python基于tkinter canvas实现图片裁剪功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • python实现求纯色彩图像的边框

    python实现求纯色彩图像的边框

    这篇文章主要为大家详细介绍了python实现求纯色彩图像的边框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • python3设计模式之简单工厂模式

    python3设计模式之简单工厂模式

    这篇文章主要为大家详细介绍了python3设计模式之简单工厂模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Python中的with关键字和文件操作方法

    Python中的with关键字和文件操作方法

    在Python编程中,with关键字用于简化文件操作流程,包括文件的打开、读取、写入和关闭,它是一个上下文管理器,确保即使在发生异常的情况下,文件也能被正确关闭,释放系统资源,本文给大家介绍Python中的with关键字和文件操作方法,感兴趣的朋友一起看看吧
    2024-10-10
  • 在keras 中获取张量 tensor 的维度大小实例

    在keras 中获取张量 tensor 的维度大小实例

    这篇文章主要介绍了在keras 中获取张量 tensor 的维度大小实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • certifi轻松地管理Python证书信任链保障网络安全

    certifi轻松地管理Python证书信任链保障网络安全

    在使用Python进行网络通信时,我们通常需要使用第三方库来处理HTTPS连接,其中,certifi库是一个非常实用的库,可以帮助我们轻松地管理Python的证书信任链
    2024-01-01
  • Python光学仿真理解Jones矩阵学习

    Python光学仿真理解Jones矩阵学习

    这篇文章主要为大家介绍了Python光学仿真理解Jones矩阵的学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2021-10-10
  • python中的None与NULL用法说明

    python中的None与NULL用法说明

    这篇文章主要介绍了python中的None与NULL用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-05-05
  • Python使用PIL.image保存图片

    Python使用PIL.image保存图片

    PIL库支持图像存储、显示和处理,它能够处理几乎所有图片格式,可以完成对图像的缩放、剪裁、叠加以及向图像添加线条、图像和文字等操作,下面这篇文章主要给大家介绍了关于Python使用PIL.image保存图片的相关资料,需要的朋友可以参考下
    2022-12-12

最新评论