You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:40:24