如何用Flask持续上传实时更新的日志文件至服务器
当前方案存在的问题及修正方案
一、现有代码的核心问题
后端(app.py)问题
- 每次请求读取整个日志文件,大文件会占用大量内存,且无法仅返回新增内容
- 文件读取后未执行关闭操作,存在资源泄漏风险
- 异常处理逻辑错误,
raise "Error %s" % e不是标准的HTTP错误响应方式,应该返回对应状态码的响应 - 无法主动感知日志文件变化,只能被动响应请求,无法实现“有新日志就推送”的需求
前端(index.js)问题
- GET请求设置
multipart/form-data请求头完全无效,GET请求不需要该类型头 - 仅执行单次请求,无法持续获取新增日志内容
url和text未用var/let/const声明,会成为全局变量,易引发变量冲突
二、修正后的基础实现方案
后端(app.py)
通过记录文件读取位置,每次请求仅返回新增内容,同时修复异常处理和资源管理:
import os from flask import Flask, session, Response, abort app = Flask(__name__) app.secret_key = 'your_custom_secret_key' # 必须配置session密钥才能使用session # 存储不同客户端的日志读取位置,用session标识区分 log_read_positions = {} @app.route("/logs", methods=['GET']) def logs(): if 'api_key' not in session: abort(401) # 未授权返回401状态码 log_path = "logs.log" client_id = session.get('client_id', 'default') # 用客户端唯一标识区分读取位置 try: file_size = os.path.getsize(log_path) # 初始化当前客户端的读取位置 if client_id not in log_read_positions: log_read_positions[client_id] = file_size # 只读新增内容 with open(log_path, "r") as f: f.seek(log_read_positions[client_id]) new_content = f.read() log_read_positions[client_id] = f.tell() # 更新读取位置 return Response(new_content, content_type='text/plain') except FileNotFoundError: abort(404, description="日志文件不存在") except Exception as e: abort(500, description=f"读取日志失败: {str(e)}")
前端(index.js)
通过定时轮询持续获取新增日志,修复请求头和变量声明问题:
function fetchNewLogs() { const xhr = new XMLHttpRequest(); const url = '/logs'; xhr.open('GET', url, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { const newLogs = xhr.responseText; if (newLogs) { // 仅当有新增内容时输出 console.log(newLogs); // 可将内容追加到页面元素,例如: // document.getElementById('log-display').textContent += newLogs; } } else { console.error('获取日志失败:', xhr.statusText); } } }; xhr.send(null); } // 每2秒轮询一次,可根据需求调整间隔 setInterval(fetchNewLogs, 2000);
三、更优实时方案:Server-Sent Events(SSE)
如果需要更高的实时性,SSE是更合适的选择,后端可主动推送新日志:
后端SSE实现
@app.route("/logs/sse") def logs_sse(): if 'api_key' not in session: abort(401) def log_stream(): log_path = "logs.log" last_file_size = os.path.getsize(log_path) while True: current_size = os.path.getsize(log_path) if current_size > last_file_size: with open(log_path, "r") as f: f.seek(last_file_size) new_content = f.read() last_file_size = current_size yield f"data: {new_content}\n\n" # 每秒检查一次文件变化 import time time.sleep(1) return Response(log_stream(), content_type='text/event-stream')
前端SSE实现
const logSource = new EventSource('/logs/sse'); logSource.onmessage = function(event) { console.log(event.data); // 追加到页面显示 // document.getElementById('log-display').textContent += event.data; }; logSource.onerror = function(error) { console.error('SSE连接异常:', error); logSource.close(); };
内容的提问来源于stack exchange,提问作者Oxy Op
相关产品推荐
相关产品推荐

