fix: 卸载日志逻辑,安装启动逻辑优化

增加:日志系统的前端显示,简略版本
This commit is contained in:
yyhuni
2025-12-17 16:27:58 +08:00
parent 2c45b3baa8
commit 3d20623b41
8 changed files with 164 additions and 17 deletions

View File

View File

@@ -0,0 +1,78 @@
import glob
import json
import logging
import subprocess
from datetime import datetime
logger = logging.getLogger(__name__)
class SystemLogService:
def __init__(self):
self.log_globs = [
"/app/backend/logs/*",
"/opt/xingrin/logs/*",
]
self.default_lines = 200
self.max_lines = 10000
self.timeout_seconds = 3
def get_logs_content(self, lines: int | None = None) -> str:
if lines is None:
lines = self.default_lines
lines = int(lines)
if lines < 1:
lines = 1
if lines > self.max_lines:
lines = self.max_lines
files: list[str] = []
for pattern in self.log_globs:
matched = sorted(glob.glob(pattern))
if matched:
files = matched
break
if not files:
return ""
cmd = ["tail", "-q", "-n", str(lines), *files]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=self.timeout_seconds,
check=False,
)
if result.returncode != 0:
logger.warning(
"tail command failed: returncode=%s stderr=%s",
result.returncode,
(result.stderr or "").strip(),
)
raw = result.stdout or ""
raw_lines = [ln for ln in raw.splitlines() if ln.strip()]
parsed: list[tuple[datetime | None, int, str]] = []
for idx, line in enumerate(raw_lines):
ts: datetime | None = None
if line.startswith("{") and line.endswith("}"):
try:
obj = json.loads(line)
asctime = obj.get("asctime")
if isinstance(asctime, str):
ts = datetime.strptime(asctime, "%Y-%m-%d %H:%M:%S")
except Exception:
ts = None
parsed.append((ts, idx, line))
parsed.sort(key=lambda x: (x[0] is None, x[0] or datetime.min, x[1]))
sorted_lines = [x[2] for x in parsed]
if len(sorted_lines) > lines:
sorted_lines = sorted_lines[-lines:]
return "\n".join(sorted_lines) + ("\n" if sorted_lines else "")

View File

@@ -2,11 +2,12 @@
通用模块 URL 配置
"""
from django.urls import path
from .views import LoginView, LogoutView, MeView, ChangePasswordView
from .views import LoginView, LogoutView, MeView, ChangePasswordView, SystemLogsView
urlpatterns = [
path('auth/login/', LoginView.as_view(), name='auth-login'),
path('auth/logout/', LogoutView.as_view(), name='auth-logout'),
path('auth/me/', MeView.as_view(), name='auth-me'),
path('auth/change-password/', ChangePasswordView.as_view(), name='auth-change-password'),
path('system/logs/', SystemLogsView.as_view(), name='system-logs'),
]

View File

@@ -1,3 +1,4 @@
from .auth_views import LoginView, LogoutView, MeView, ChangePasswordView
from .system_log_views import SystemLogsView
__all__ = ['LoginView', 'LogoutView', 'MeView', 'ChangePasswordView']
__all__ = ['LoginView', 'LogoutView', 'MeView', 'ChangePasswordView', 'SystemLogsView']

View File

@@ -0,0 +1,36 @@
import logging
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.common.services.system_log_service import SystemLogService
logger = logging.getLogger(__name__)
@method_decorator(csrf_exempt, name="dispatch")
class SystemLogsView(APIView):
authentication_classes = []
permission_classes = [AllowAny]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.service = SystemLogService()
def get(self, request):
try:
lines_raw = request.query_params.get("lines")
lines = int(lines_raw) if lines_raw is not None else None
content = self.service.get_logs_content(lines=lines)
return Response({"content": content})
except ValueError:
return Response({"error": "lines 参数必须是整数"}, status=status.HTTP_400_BAD_REQUEST)
except Exception:
logger.exception("获取系统日志失败")
return Response({"error": "获取系统日志失败"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

View File

@@ -85,38 +85,68 @@ export BUILDKIT_INLINE_CACHE=1
# 使用指定的 compose 文件
COMPOSE_ARGS="-f ${COMPOSE_FILE} ${PROFILE_ARG}"
SERVICES="$(${COMPOSE_CMD} ${COMPOSE_ARGS} config --services)"
service_exists() {
echo "$SERVICES" | grep -qx "$1"
}
BACKEND_SERVICES=()
for s in redis server agent; do
if service_exists "$s"; then
BACKEND_SERVICES+=("$s")
fi
done
# 如果使用本地数据库,先启动 postgres 并等待健康
start_postgres_first() {
if [ -n "$PROFILE_ARG" ]; then
echo -e "${CYAN}[DB]${NC} 启动 PostgreSQL 容器..."
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d postgres
echo -e "${CYAN}[DB]${NC} 等待 PostgreSQL 就绪..."
local max_wait=30
local count=0
while [ $count -lt $max_wait ]; do
if ${COMPOSE_CMD} ${COMPOSE_ARGS} exec -T postgres pg_isready -U postgres >/dev/null 2>&1; then
echo -e "${GREEN}[DB]${NC} PostgreSQL 已就绪"
return 0
fi
sleep 1
count=$((count + 1))
done
echo -e "${YELLOW}[WARN]${NC} PostgreSQL 等待超时,继续启动其他服务..."
fi
}
echo ""
if [ "$DEV_MODE" = true ]; then
# 开发模式:本地构建
if [ "$WITH_FRONTEND" = true ]; then
echo -e "${CYAN}[BUILD]${NC} 并行构建镜像..."
${COMPOSE_CMD} ${COMPOSE_ARGS} build --parallel
start_postgres_first
echo -e "${CYAN}[START]${NC} 启动全部服务..."
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d
else
echo -e "${CYAN}[BUILD]${NC} 并行构建后端镜像..."
${COMPOSE_CMD} ${COMPOSE_ARGS} build --parallel server scan-worker maintenance-worker
${COMPOSE_CMD} ${COMPOSE_ARGS} build --parallel "${BACKEND_SERVICES[@]}"
start_postgres_first
echo -e "${CYAN}[START]${NC} 启动后端服务..."
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d redis server scan-worker maintenance-worker
if [ -n "$PROFILE_ARG" ]; then
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d postgres
fi
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d "${BACKEND_SERVICES[@]}"
fi
else
# 生产模式:拉取 Docker Hub 镜像
if [ "$WITH_FRONTEND" = true ]; then
echo -e "${CYAN}[PULL]${NC} 拉取最新镜像..."
${COMPOSE_CMD} ${COMPOSE_ARGS} pull
start_postgres_first
echo -e "${CYAN}[START]${NC} 启动全部服务..."
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d
else
echo -e "${CYAN}[PULL]${NC} 拉取后端镜像..."
${COMPOSE_CMD} ${COMPOSE_ARGS} pull redis server scan-worker maintenance-worker
${COMPOSE_CMD} ${COMPOSE_ARGS} pull "${BACKEND_SERVICES[@]}"
start_postgres_first
echo -e "${CYAN}[START]${NC} 启动后端服务..."
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d redis server scan-worker maintenance-worker
if [ -n "$PROFILE_ARG" ]; then
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d postgres
fi
${COMPOSE_CMD} ${COMPOSE_ARGS} up -d "${BACKEND_SERVICES[@]}"
fi
fi
echo -e "${GREEN}[OK]${NC} 服务已启动"

View File

@@ -9,7 +9,7 @@ import { useSystemLogs } from "@/hooks/use-system-logs"
export function SystemLogsView() {
const { theme } = useTheme()
const { data } = useSystemLogs({ lines: 2000 })
const { data } = useSystemLogs({ lines: 200 })
const content = useMemo(() => data?.content ?? "", [data?.content])

View File

@@ -95,16 +95,17 @@ fi
# ==============================================================================
# 2. 删除扫描日志和结果目录
# ==============================================================================
LOGS_DIR="$ROOT_DIR/backend/logs"
RESULTS_DIR="$ROOT_DIR/backend/results"
step "[2/6] 是否删除扫描日志和结果目录 ($LOGS_DIR, $RESULTS_DIR)(Y/n)"
OPT_LOGS_DIR="/opt/xingrin/logs"
OPT_RESULTS_DIR="/opt/xingrin/results"
step "[2/6] 是否删除扫描日志和结果目录 ($OPT_LOGS_DIR, $OPT_RESULTS_DIR)(Y/n)"
read -r ans_logs
ans_logs=${ans_logs:-Y}
if [[ $ans_logs =~ ^[Yy]$ ]]; then
info "正在删除日志和结果目录..."
rm -rf "$LOGS_DIR" "$RESULTS_DIR"
rm -rf "$OPT_LOGS_DIR" "$OPT_RESULTS_DIR"
success "已删除日志和结果目录。"
else
warn "已保留日志和结果目录。"