diff --git a/site/app.py b/site/app.py index a3e2807..28b113a 100644 --- a/site/app.py +++ b/site/app.py @@ -6,7 +6,7 @@ from routes.main import bp as main_bp from routes.api import bp as api_bp from routes.api_test import bp as api_test_bp -VERSION = "1.0.78" +VERSION = "1.0.79" app = Flask(__name__, template_folder="templates", static_folder="static") app.config["NUBES_API_ENDPOINT"] = os.getenv("NUBES_API_ENDPOINT", "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc") diff --git a/site/routes/api_test.py b/site/routes/api_test.py index 85b2206..c0ff371 100644 --- a/site/routes/api_test.py +++ b/site/routes/api_test.py @@ -1,6 +1,7 @@ from flask import Blueprint, current_app, jsonify, request import uuid -import collections +import os +import fcntl from api.http_client import HttpClient, detect_endpoint from operations.get_services import get_services, get_service_detail @@ -12,14 +13,33 @@ bp = Blueprint("api_test", __name__) AUTOTEST_PREFIX = "autotest-" -# Кольцевой буфер логов (показываются в UI) -_log_lines = collections.deque(maxlen=200) +LOG_FILE = "/tmp/app-autotest.log" +MAX_LOG_SIZE = 512 * 1024 # 512 KB — ротация +MAX_LOG_LINES = 200 # сколько отдавать в /api/log def _log(msg): - """Пишет и в stdout (для gunicorn), и в буфер (для UI).""" + """Пишет в stdout (gunicorn) и в файл (UI-панель, общий для всех воркеров).""" print(msg, flush=True) - _log_lines.append(msg) + try: + with open(LOG_FILE, "a") as f: + fcntl.flock(f, fcntl.LOCK_EX) + f.write(msg + "\n") + fcntl.flock(f, fcntl.LOCK_UN) + # Ротация если разросся + st = os.stat(LOG_FILE) + if st.st_size > MAX_LOG_SIZE: + with open(LOG_FILE, "r+") as f: + fcntl.flock(f, fcntl.LOCK_EX) + f.seek(st.st_size // 2) + f.readline() # доесть строку + rest = f.read() + f.seek(0) + f.truncate() + f.write(rest) + fcntl.flock(f, fcntl.LOCK_UN) + except Exception: + pass # молча — не ронять запрос из-за лога def _with_prefix(name): @@ -338,8 +358,17 @@ def api_test_status(op_uid): @bp.route("/api/log") def api_log(): - """Отдать последние строки лога для UI-панели.""" - return jsonify(list(_log_lines)) + """Отдать последние MAX_LOG_LINES строк лога для UI-панели.""" + try: + with open(LOG_FILE, "r") as f: + fcntl.flock(f, fcntl.LOCK_SH) + lines = f.readlines() + fcntl.flock(f, fcntl.LOCK_UN) + return jsonify([l.rstrip("\n") for l in lines[-MAX_LOG_LINES:]]) + except FileNotFoundError: + return jsonify([]) + except Exception: + return jsonify([]) def _find_uid(resp):