mirror of
https://github.com/rnvm9wjdtj-bot/myaps_api.git
synced 2026-06-02 05:54:40 +00:00
feat(monitor): 添加国际化支持(中文/英文/德语)
- 新增i18n框架和3个语言包(405个翻译条目/语言) - 修改监控模块3个HTML页面和JS添加国际化支持 - 支持浏览器语言自动检测、localStorage持久化、热切换 - 修复定时任务显示模板字符串语法错误 影响范围: - static/lib/i18n/: 新增i18n框架和语言包 - static/monitor/: 监控模块全面国际化 - 11 files changed, +2481/-502 lines
This commit is contained in:
+3
-1
@@ -102,4 +102,6 @@ offline_packages/
|
||||
offline_packages/**/
|
||||
|
||||
docker_images/
|
||||
docker_images/**/
|
||||
docker_images/**/
|
||||
|
||||
entrypoint.sh
|
||||
|
||||
+246
-81
@@ -1,31 +1,37 @@
|
||||
#!/bin/bash
|
||||
# =====================================================
|
||||
# 开发环境服务启停脚本
|
||||
# 开发环境服务启停脚本(智能版)
|
||||
# 用法:
|
||||
# ./dev_server.sh start - 启动服务
|
||||
# ./dev_server.sh start - 智能启动(自动检测并启动所需服务)
|
||||
# ./dev_server.sh stop - 停止服务
|
||||
# ./dev_server.sh restart - 重启服务
|
||||
# ./dev_server.sh status - 查看状态
|
||||
# ./dev_server.sh logs - 查看日志
|
||||
# =====================================================
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 查找Python解释器
|
||||
find_python() {
|
||||
# 优先使用虚拟环境
|
||||
if [ -f "$PROJECT_DIR/venv/bin/python" ]; then
|
||||
echo "$PROJECT_DIR/venv/bin/python"
|
||||
return
|
||||
fi
|
||||
# 尝试python3
|
||||
if command -v python3 &> /dev/null; then
|
||||
echo "python3"
|
||||
return
|
||||
fi
|
||||
# 尝试python
|
||||
if command -v python &> /dev/null; then
|
||||
echo "python"
|
||||
return
|
||||
@@ -42,20 +48,33 @@ LOG_FILE="$PROJECT_DIR/logs/dev_server.log"
|
||||
HOST="0.0.0.0"
|
||||
PORT="8001"
|
||||
|
||||
# 创建日志目录
|
||||
# 创建必要目录
|
||||
mkdir -p "$PROJECT_DIR/logs"
|
||||
mkdir -p "$PROJECT_DIR/storage"
|
||||
|
||||
# 获取进程ID
|
||||
get_pid() {
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
cat "$PID_FILE"
|
||||
else
|
||||
pgrep -f "python.*main\.py" | head -1 || pgrep -f "python3.*main\.py" | head -1
|
||||
# =====================================================
|
||||
# 智能服务检测与管理
|
||||
# =====================================================
|
||||
|
||||
check_redis() {
|
||||
if pgrep -x "redis-server" > /dev/null 2>&1; then
|
||||
if redis-cli ping > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# 检查服务是否运行
|
||||
is_running() {
|
||||
check_postgresql() {
|
||||
if pgrep -x "postgres" > /dev/null 2>&1; then
|
||||
if pg_isready > /dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
check_app() {
|
||||
local pid=$(get_pid)
|
||||
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
||||
return 0
|
||||
@@ -63,152 +82,298 @@ is_running() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start() {
|
||||
if is_running; then
|
||||
echo "服务已在运行中 (PID: $(get_pid))"
|
||||
start_redis() {
|
||||
echo -e "${BLUE}[Redis]${NC} 检查服务状态..."
|
||||
|
||||
if check_redis; then
|
||||
echo -e "${GREEN}[Redis]${NC} ✓ 已在运行"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}[Redis]${NC} 服务未运行,正在启动..."
|
||||
|
||||
if ! command -v redis-server &> /dev/null; then
|
||||
echo -e "${RED}[Redis]${NC} ✗ 未安装 redis-server"
|
||||
echo -e "${YELLOW}[Redis]${NC} 请执行: sudo apt install redis-server"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "正在启动服务..."
|
||||
echo "项目目录: $PROJECT_DIR"
|
||||
echo "Python解释器: $PYTHON_CMD"
|
||||
echo "访问地址: http://localhost:$PORT"
|
||||
echo "API文档: http://localhost:$PORT/docs"
|
||||
redis-server --daemonize yes 2>/dev/null
|
||||
|
||||
local count=0
|
||||
while ! check_redis && [ $count -lt 10 ]; do
|
||||
sleep 0.5
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
if check_redis; then
|
||||
echo -e "${GREEN}[Redis]${NC} ✓ 启动成功"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}[Redis]${NC} ✗ 启动失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_postgresql() {
|
||||
echo -e "${BLUE}[PostgreSQL]${NC} 检查服务状态..."
|
||||
|
||||
if check_postgresql; then
|
||||
echo -e "${GREEN}[PostgreSQL]${NC} ✓ 已在运行"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}[PostgreSQL]${NC} 服务未运行,正在启动..."
|
||||
|
||||
if ! command -v pg_isready &> /dev/null; then
|
||||
echo -e "${RED}[PostgreSQL]${NC} ✗ 未安装 PostgreSQL"
|
||||
echo -e "${YELLOW}[PostgreSQL]${NC} 请执行: sudo apt install postgresql postgresql-contrib"
|
||||
return 1
|
||||
fi
|
||||
|
||||
sudo service postgresql start 2>/dev/null || true
|
||||
|
||||
local count=0
|
||||
while ! check_postgresql && [ $count -lt 15 ]; do
|
||||
sleep 1
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
if check_postgresql; then
|
||||
echo -e "${GREEN}[PostgreSQL]${NC} ✓ 启动成功"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}[PostgreSQL]${NC} ✗ 启动失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_services() {
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}检查基础服务...${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
local redis_ok=true
|
||||
local pg_ok=true
|
||||
|
||||
start_redis || redis_ok=false
|
||||
start_postgresql || pg_ok=false
|
||||
|
||||
echo ""
|
||||
|
||||
if [ "$redis_ok" = false ] || [ "$pg_ok" = false ]; then
|
||||
echo -e "${RED}部分基础服务启动失败,应用可能无法正常工作${NC}"
|
||||
echo -e "${YELLOW}是否继续启动应用?[y/N]${NC}"
|
||||
read -r response
|
||||
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
||||
echo "已取消启动"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# =====================================================
|
||||
# 应用服务管理
|
||||
# =====================================================
|
||||
|
||||
get_pid() {
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
local pid=$(cat "$PID_FILE")
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "$pid"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
pgrep -f "python.*main\.py" | head -1 || true
|
||||
}
|
||||
|
||||
start_app() {
|
||||
if check_app; then
|
||||
echo -e "${GREEN}[应用]${NC} ✓ 已在运行 (PID: $(get_pid))"
|
||||
echo -e "${GREEN}[应用]${NC} 访问地址: http://localhost:$PORT"
|
||||
echo -e "${GREEN}[应用]${NC} API文档: http://localhost:$PORT/docs"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}启动应用服务...${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e " 项目目录: $PROJECT_DIR"
|
||||
echo -e " Python: $PYTHON_CMD"
|
||||
echo -e " 端口: $PORT"
|
||||
echo ""
|
||||
|
||||
# 启动服务
|
||||
nohup env PORT=$PORT $PYTHON_CMD main.py > "$LOG_FILE" 2>&1 &
|
||||
local pid=$!
|
||||
echo $pid > "$PID_FILE"
|
||||
|
||||
sleep 2
|
||||
|
||||
if is_running; then
|
||||
echo "✓ 服务启动成功 (PID: $pid)"
|
||||
echo "日志文件: $LOG_FILE"
|
||||
if check_app; then
|
||||
echo -e "${GREEN}[应用]${NC} ✓ 启动成功 (PID: $pid)"
|
||||
echo -e "${GREEN}[应用]${NC} 访问地址: http://localhost:$PORT"
|
||||
echo -e "${GREEN}[应用]${NC} API文档: http://localhost:$PORT/docs"
|
||||
echo -e "${GREEN}[应用]${NC} 日志文件: $LOG_FILE"
|
||||
return 0
|
||||
else
|
||||
echo "✗ 服务启动失败,请查看日志:"
|
||||
echo -e "${RED}[应用]${NC} ✗ 启动失败"
|
||||
echo -e "${YELLOW}[应用]${NC} 最近日志:"
|
||||
tail -20 "$LOG_FILE"
|
||||
rm -f "$PID_FILE"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
stop() {
|
||||
if ! is_running; then
|
||||
echo "服务未运行"
|
||||
stop_app() {
|
||||
if ! check_app; then
|
||||
echo -e "${YELLOW}[应用]${NC} 服务未运行"
|
||||
rm -f "$PID_FILE"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local pid=$(get_pid)
|
||||
echo "正在停止服务 (PID: $pid)..."
|
||||
echo -e "${YELLOW}[应用]${NC} 正在停止 (PID: $pid)..."
|
||||
|
||||
# 发送SIGTERM信号
|
||||
kill "$pid" 2>/dev/null
|
||||
|
||||
# 等待进程结束
|
||||
local count=0
|
||||
while kill -0 "$pid" 2>/dev/null && [ $count -lt 10 ]; do
|
||||
sleep 1
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
# 如果进程还在运行,强制结束
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "强制结束进程..."
|
||||
echo -e "${YELLOW}[应用]${NC} 强制结束..."
|
||||
kill -9 "$pid" 2>/dev/null
|
||||
fi
|
||||
|
||||
rm -f "$PID_FILE"
|
||||
echo "✓ 服务已停止"
|
||||
echo -e "${GREEN}[应用]${NC} ✓ 已停止"
|
||||
}
|
||||
|
||||
# 清除Python缓存
|
||||
clear_cache() {
|
||||
echo "清除Python缓存..."
|
||||
find "$PROJECT_DIR" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null
|
||||
find "$PROJECT_DIR" -name "*.pyc" -delete 2>/dev/null
|
||||
echo "✓ 缓存已清除"
|
||||
echo -e "${YELLOW}清除Python缓存...${NC}"
|
||||
find "$PROJECT_DIR" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find "$PROJECT_DIR" -name "*.pyc" -delete 2>/dev/null || true
|
||||
echo -e "${GREEN}✓ 缓存已清除${NC}"
|
||||
}
|
||||
|
||||
# 重启服务
|
||||
restart() {
|
||||
stop
|
||||
# =====================================================
|
||||
# 主命令
|
||||
# =====================================================
|
||||
|
||||
cmd_start() {
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}MyAPS API 开发服务器${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
ensure_services
|
||||
echo ""
|
||||
start_app
|
||||
}
|
||||
|
||||
cmd_stop() {
|
||||
echo -e "${BLUE}停止服务...${NC}"
|
||||
stop_app
|
||||
}
|
||||
|
||||
cmd_restart() {
|
||||
cmd_stop
|
||||
clear_cache
|
||||
sleep 1
|
||||
start
|
||||
cmd_start
|
||||
}
|
||||
|
||||
# 查看状态
|
||||
status() {
|
||||
if is_running; then
|
||||
cmd_status() {
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}服务状态${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}[Redis]${NC}"
|
||||
if check_redis; then
|
||||
echo -e " 状态: ${GREEN}运行中${NC}"
|
||||
redis-cli INFO server 2>/dev/null | grep "redis_version" | sed 's/^/ /' || true
|
||||
else
|
||||
echo -e " 状态: ${RED}未运行${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}[PostgreSQL]${NC}"
|
||||
if check_postgresql; then
|
||||
echo -e " 状态: ${GREEN}运行中${NC}"
|
||||
pg_isready 2>/dev/null | sed 's/^/ /' || true
|
||||
else
|
||||
echo -e " 状态: ${RED}未运行${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}[应用服务]${NC}"
|
||||
if check_app; then
|
||||
local pid=$(get_pid)
|
||||
echo "✓ 服务运行中"
|
||||
echo " PID: $pid"
|
||||
echo " 访问地址: http://localhost:$PORT"
|
||||
echo " API文档: http://localhost:$PORT/docs"
|
||||
|
||||
# 显示进程信息
|
||||
echo -e " 状态: ${GREEN}运行中${NC}"
|
||||
echo -e " PID: $pid"
|
||||
echo -e " 访问地址: http://localhost:$PORT"
|
||||
echo -e " API文档: http://localhost:$PORT/docs"
|
||||
if command -v ps &> /dev/null; then
|
||||
ps -p "$pid" -o pid,ppid,%cpu,%mem,etime,cmd 2>/dev/null || true
|
||||
ps -p "$pid" -o pid,ppid,%cpu,%mem,etime 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo "✗ 服务未运行"
|
||||
echo -e " 状态: ${RED}未运行${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 查看日志
|
||||
logs() {
|
||||
cmd_logs() {
|
||||
if [ ! -f "$LOG_FILE" ]; then
|
||||
echo "日志文件不存在: $LOG_FILE"
|
||||
echo -e "${YELLOW}日志文件不存在: $LOG_FILE${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "-f" ] || [ "$1" = "--follow" ]; then
|
||||
echo "实时查看日志 (Ctrl+C 退出)..."
|
||||
echo -e "${BLUE}实时查看日志 (Ctrl+C 退出)...${NC}"
|
||||
tail -f "$LOG_FILE"
|
||||
else
|
||||
echo "最近50行日志:"
|
||||
echo -e "${BLUE}最近50行日志:${NC}"
|
||||
tail -50 "$LOG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# 帮助信息
|
||||
help() {
|
||||
echo "用法: $0 {start|stop|restart|status|logs|clear_cache}"
|
||||
echo -e "${BLUE}用法:${NC} $0 {start|stop|restart|status|logs|clear_cache}"
|
||||
echo ""
|
||||
echo "命令:"
|
||||
echo " start - 启动服务"
|
||||
echo " stop - 停止服务"
|
||||
echo " restart - 重启服务(自动清除缓存)"
|
||||
echo " status - 查看服务状态"
|
||||
echo -e "${BLUE}命令:${NC}"
|
||||
echo " start - 智能启动(自动检测并启动 Redis/PostgreSQL/应用)"
|
||||
echo " stop - 停止应用服务"
|
||||
echo " restart - 重启应用服务(自动清除缓存)"
|
||||
echo " status - 查看所有服务状态"
|
||||
echo " logs - 查看日志 (添加 -f 参数实时查看)"
|
||||
echo " clear_cache - 清除Python缓存"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 start"
|
||||
echo " $0 restart"
|
||||
echo " $0 logs -f"
|
||||
echo -e "${BLUE}示例:${NC}"
|
||||
echo " $0 start # 一键启动所有服务"
|
||||
echo " $0 restart # 重启应用"
|
||||
echo " $0 logs -f # 实时查看日志"
|
||||
echo " $0 status # 查看所有服务状态"
|
||||
}
|
||||
|
||||
# 主入口
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
cmd_start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
cmd_stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
cmd_restart
|
||||
;;
|
||||
status)
|
||||
status
|
||||
cmd_status
|
||||
;;
|
||||
logs)
|
||||
logs "$2"
|
||||
cmd_logs "$2"
|
||||
;;
|
||||
clear_cache)
|
||||
clear_cache
|
||||
@@ -217,7 +382,7 @@ case "$1" in
|
||||
help
|
||||
;;
|
||||
*)
|
||||
echo "错误: 未知命令 '$1'"
|
||||
echo -e "${RED}错误: 未知命令 '$1'${NC}"
|
||||
help
|
||||
exit 1
|
||||
;;
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
/**
|
||||
* Deutsches Sprachpaket (Deutsch)
|
||||
* German Language Pack (German)
|
||||
*/
|
||||
window.__i18n_de_DE__ = {
|
||||
// ============ Seitentitel ============
|
||||
'page.title': 'MyAPI Systemüberwachung',
|
||||
'page.live_logs': 'Echtzeit-Logs - Systemüberwachung',
|
||||
'page.history_logs': 'Protokollverlauf - Systemüberwachung',
|
||||
|
||||
// ============ Navigation ============
|
||||
'nav.overview': '📊 Übersicht',
|
||||
'nav.database': '🗃️ Datenbank',
|
||||
'nav.events': '☎️ Ereignisse',
|
||||
'nav.scheduler': '⏰ Planer',
|
||||
'nav.http_requests': '📥 HTTP-Anfragen',
|
||||
'nav.outbound_requests': '📤 Ausgehend',
|
||||
'nav.logs': '📋 Protokolle',
|
||||
|
||||
// ============ Tabs ============
|
||||
'tab.overview': 'Übersicht',
|
||||
'tab.database': 'Datenbank',
|
||||
'tab.events': 'Ereignisse',
|
||||
'tab.scheduler': 'Planer',
|
||||
'tab.http': 'HTTP-Anfragen',
|
||||
'tab.outbound': 'Ausgehend',
|
||||
'tab.logs': 'Systemprotokolle',
|
||||
'tab.timeline': 'Zeitachse',
|
||||
'tab.chart': 'Diagramme',
|
||||
|
||||
// ============ Karten ============
|
||||
'card.resource': 'Ressourcennutzung',
|
||||
'card.db_status': 'Datenbankstatus',
|
||||
'card.db_connections': 'Datenbankverbindungen',
|
||||
'card.event_helpers': 'Ereignis-Helfer',
|
||||
'card.scheduler': 'Aufgabenplaner',
|
||||
'card.api_requests': 'HTTP-Anfragen',
|
||||
'card.outbound_requests': 'Ausgehende Anfragen',
|
||||
'card.redis': 'Redis-Status',
|
||||
'card.event_listener': 'Ereignis-Listener',
|
||||
'card.recent_alerts': 'Aktuelle Warnungen',
|
||||
'card.mysql': 'MySQL',
|
||||
'card.http_requests_log': 'HTTP-Anfragedatensätze',
|
||||
'card.scheduler_detail': 'Planer-Details',
|
||||
'card.outbound_requests_log': 'Ausgehende Anfragedatensätze',
|
||||
'card.callback_tracker': 'Callback-Tracker',
|
||||
'card.event_deduplicator': 'Ereignis-Deduplikator',
|
||||
|
||||
// ============ Kennzahlen ============
|
||||
'metric.cpu': 'CPU-Auslastung',
|
||||
'metric.memory': 'Speichernutzung',
|
||||
'metric.threads': 'Threads',
|
||||
'metric.uptime': 'Laufzeit',
|
||||
'metric.total_connections': 'Verbindungen',
|
||||
'metric.healthy': 'Gesund',
|
||||
'metric.unhealthy': 'Fehlerhaft',
|
||||
'metric.degraded': 'Eingeschränkt',
|
||||
'metric.total': 'Gesamt',
|
||||
'metric.success': 'Erfolg',
|
||||
'metric.failed': 'Fehler',
|
||||
'metric.pending': 'Ausstehend',
|
||||
'metric.active': 'Aktiv',
|
||||
'metric.idle': 'Inaktiv',
|
||||
'metric.unknown': 'Unbekannt',
|
||||
'metric.warning': 'Warnung',
|
||||
'metric.events_received': 'Empfangene Ereignisse',
|
||||
'metric.events_processed': 'Verarbeitete Ereignisse',
|
||||
'metric.events_failed': 'Fehlgeschlagene Ereignisse',
|
||||
'metric.jobs_running': 'Laufende Aufgaben',
|
||||
'metric.jobs_pending': 'Ausstehende Aufgaben',
|
||||
'metric.requests_total': 'Anfragen Gesamt',
|
||||
'metric.requests_slow': 'Langsame Anfragen',
|
||||
'metric.requests_error': 'Fehlerhafte Anfragen',
|
||||
'metric.error_rate': 'Fehlerquote',
|
||||
'metric.avg_time': 'Durchschn. Antwort',
|
||||
'metric.connection_status': 'Verbindungsstatus',
|
||||
'metric.host': 'Host',
|
||||
'metric.port': 'Port',
|
||||
'metric.database': 'Datenbank',
|
||||
'metric.used_connections': 'Verwendete Verbindungen',
|
||||
'metric.max_connections': 'Max. Verbindungen',
|
||||
'metric.connection_usage': 'Verbindungsnutzung',
|
||||
'metric.buffer_size': 'Puffergröße',
|
||||
'metric.buffer_threshold': 'Pufferschwellwert',
|
||||
'metric.buffer_usage': 'Puffernutzung',
|
||||
'metric.events_interrupted': 'Unterbrochen',
|
||||
'metric.overall_success_rate': 'Gesamterfolgsquote',
|
||||
'metric.active_event_types': 'Aktive Ereignistypen',
|
||||
'metric.backpressure_status': 'Rückstau-Status',
|
||||
'metric.backpressure_pending': 'Ausstehende Ereignisse',
|
||||
'metric.backpressure_usage': 'Rückstau-Nutzung',
|
||||
'metric.event_loop_status': 'Ereignisschleifen-Status',
|
||||
'metric.pending_callbacks': 'Ausstehende Callbacks',
|
||||
'metric.max_retries': 'Max. Wiederholungen',
|
||||
'metric.pending_retries': 'Ausstehende Wiederholungen',
|
||||
'metric.total_entries': 'Gesamteinträge',
|
||||
'metric.active_items': 'Aktive Elemente',
|
||||
'metric.ttl_seconds': 'TTL (Sekunden)',
|
||||
'metric.max_entries': 'Max. Einträge',
|
||||
'metric.dl_total': 'DL-Gesamt',
|
||||
'metric.recent_dl': 'Aktuelle DL',
|
||||
'metric.process_success_rate': 'Verarbeitungserfolgsquote',
|
||||
'metric.scheduler_status': 'Planer-Status',
|
||||
'metric.job_count': 'Aufgabenanzahl',
|
||||
'metric.pending_events': 'Ausstehende Ereignisse',
|
||||
'metric.total_events': 'Gesamtereignisse',
|
||||
'metric.file_size': 'Dateigröße',
|
||||
'metric.running_status': 'Betriebsstatus',
|
||||
|
||||
// ============ Status ============
|
||||
'status.healthy': '● System gesund',
|
||||
'status.unhealthy': '● System fehlerhaft',
|
||||
'status.degraded': '● System eingeschränkt',
|
||||
'status.running': 'Läuft',
|
||||
'status.checking': 'Prüfe',
|
||||
'status.stopped': 'Gestoppt',
|
||||
'status.loading': 'Laden...',
|
||||
'status.querying': 'Abfrage...',
|
||||
'status.exporting': 'Exportiere...',
|
||||
'status.no_data': 'Keine Daten',
|
||||
'status.connected': 'Verbunden',
|
||||
'status.connecting': 'Verbinde...',
|
||||
'status.disconnected': 'Getrennt',
|
||||
'status.reconnecting': 'Verbinde neu...',
|
||||
'status.paused': 'Pausiert',
|
||||
'status.enabled': 'Aktiviert',
|
||||
'status.disabled': 'Deaktiviert',
|
||||
'status.no_db_connections': 'Keine Datenbankverbindungen',
|
||||
'status.no_scheduler': 'Keine geplanten Aufgaben',
|
||||
'status.no_http_requests': 'Keine HTTP-Anfragen',
|
||||
'status.no_alerts': 'Keine Warnungen',
|
||||
'status.no_logs': 'Keine Protokolle',
|
||||
'status.no_events': 'Keine Ereignisstatistiken',
|
||||
'status.no_api_requests': 'Keine API-Anfragedatensätze',
|
||||
'status.no_outbound_requests': 'Keine ausgehenden Anfragedatensätze',
|
||||
'status.no_dead_letters': 'Keine DL',
|
||||
'status.partial_warnings': '● Teilweise Warnungen',
|
||||
'status.monitoring': 'Überwachung',
|
||||
|
||||
// ============ Schaltflächen ============
|
||||
'btn.query': 'Abfrage',
|
||||
'btn.reset': 'Zurücksetzen',
|
||||
'btn.refresh': 'Aktualisieren',
|
||||
'btn.export': 'Exportieren',
|
||||
'btn.export_csv': 'CSV Export',
|
||||
'btn.export_json': 'JSON Export',
|
||||
'btn.detail': 'Details',
|
||||
'btn.pause': 'Pause',
|
||||
'btn.resume': 'Fortsetzen',
|
||||
'btn.clear': 'Löschen',
|
||||
'btn.save': 'Speichern',
|
||||
'btn.delete': 'Löschen',
|
||||
'btn.close': 'Schließen',
|
||||
'btn.confirm': 'Bestätigen',
|
||||
'btn.cancel': 'Abbrechen',
|
||||
'btn.test': 'Testen',
|
||||
'btn.apply': 'Anwenden',
|
||||
'btn.filter': 'Filtern',
|
||||
'btn.clear_all': 'Alle löschen',
|
||||
'btn.realtime_on': 'Echtzeit: AN',
|
||||
'btn.realtime_off': 'Echtzeit: AUS',
|
||||
'btn.precise_locate': '± 60s',
|
||||
'btn.precise_cancel': '± 60s - Klick zum Abbrechen',
|
||||
'btn.reset_stats': 'Statistik zurücksetzen',
|
||||
'btn.flush_all': 'Alle leeren',
|
||||
'btn.new_window': 'Neues Fenster',
|
||||
'btn.auto_scroll': 'Auto-Scroll',
|
||||
'btn.live_logs': 'Echtzeit-Protokolle',
|
||||
'btn.history_query': 'Verlaufsanfrage',
|
||||
'btn.mark_all_read': 'Alle als gelesen markieren',
|
||||
'btn.clear_read_status': 'Lesestatus löschen',
|
||||
|
||||
// ============ Tabellenspalten ============
|
||||
'col.index': '#',
|
||||
'col.time': 'Zeit',
|
||||
'col.level': 'Stufe',
|
||||
'col.module': 'Modul',
|
||||
'col.message': 'Nachricht',
|
||||
'col.method': 'Methode',
|
||||
'col.path': 'Pfad',
|
||||
'col.url': 'URL',
|
||||
'col.status': 'Status',
|
||||
'col.duration': 'Dauer',
|
||||
'col.client_ip': 'Client-IP',
|
||||
'col.source': 'Quelle',
|
||||
'col.function': 'Funktion',
|
||||
'col.line': 'Zeile',
|
||||
'col.operation': 'Aktion',
|
||||
'col.count': 'Anzahl',
|
||||
'col.avg_time': 'Durchschn. Zeit',
|
||||
'col.max_time': 'Max. Zeit',
|
||||
'col.db_name': 'DB-Name',
|
||||
'col.last_check': 'Letzte Prüfung',
|
||||
'col.current_connections': 'Aktuell',
|
||||
'col.max_connections': 'Max',
|
||||
'col.min_connections': 'Min',
|
||||
'col.idle_connections': 'Inaktiv',
|
||||
'col.used_connections': 'Verwendet',
|
||||
'col.usage': 'Nutzung',
|
||||
'col.processed_records': 'Verarbeitet',
|
||||
'col.timestamp': 'Zeitstempel',
|
||||
'col.query_params': 'Abfrageparameter',
|
||||
'col.error_message': 'Fehlermeldung',
|
||||
'col.url': 'URL',
|
||||
'col.is_read': 'Gelesen',
|
||||
'col.description': 'Beschreibung',
|
||||
'col.total_received': 'Gesamt empfangen',
|
||||
'col.pending': 'Ausstehend',
|
||||
'col.processed': 'Verarbeitet',
|
||||
'col.interrupted': 'Unterbrochen',
|
||||
'col.completion_rate': 'Abschlussrate',
|
||||
'col.avg_latency': 'Durchschn. Latenz',
|
||||
'col.last_action': 'Letzte Aktion',
|
||||
'col.id': 'ID',
|
||||
'col.event_type': 'Ereignistyp',
|
||||
'col.table': 'Tabelle',
|
||||
|
||||
// ============ Schnellzeit ============
|
||||
'time.start_time': 'Startzeit',
|
||||
'time.end_time': 'Endzeit',
|
||||
'time.last_10m': 'Letzte 10 Min',
|
||||
'time.last_30m': 'Letzte 30 Min',
|
||||
'time.last_1h': 'Letzte Stunde',
|
||||
'time.last_6h': 'Letzte 6 Stunden',
|
||||
'time.last_24h': 'Letzte 24 Stunden',
|
||||
'time.range': 'Zeitbereich',
|
||||
|
||||
// ============ Filter ============
|
||||
'filter.level': 'Alle Stufen',
|
||||
'filter.type': 'Alle Typen',
|
||||
'filter.module': 'Modul',
|
||||
'filter.keyword': 'Schlüsselwort',
|
||||
'filter.method': 'Methode',
|
||||
'filter.client_ip': 'Client-IP',
|
||||
'filter.status_range': 'Statusbereich',
|
||||
'filter.duration_range': 'Dauerbereich',
|
||||
'filter.advanced': 'Erweiterte Filter',
|
||||
'filter.collapse': 'Einklappen',
|
||||
'filter.expand': 'Ausklappen',
|
||||
'filter.clear': 'Löschen',
|
||||
'filter.active': 'Aktive Filter',
|
||||
'filter.module_placeholder': 'Modulname eingeben',
|
||||
'filter.keyword_placeholder': 'Schlüsselwort eingeben',
|
||||
'filter.ip_placeholder': 'Client-IP eingeben',
|
||||
'filter.status_placeholder': 'z.B.: 400-499',
|
||||
'filter.duration_placeholder': 'z.B.: >1000',
|
||||
'filter.all_logs': 'Alle Protokolle',
|
||||
|
||||
// ============ Diagramme ============
|
||||
'chart.request_trend': '📊 Anfragentrend',
|
||||
'chart.level_distribution': '📊 Protokollstufen-Verteilung',
|
||||
'chart.status_distribution': '📈 Statuscode-Verteilung',
|
||||
'chart.slow_requests': '⏱️ Langsame Anfragen TOP10',
|
||||
'chart.total_requests': 'Anfragen Gesamt',
|
||||
'chart.error_count': 'Fehler',
|
||||
'chart.slow_count': 'Langsame Anfragen',
|
||||
'chart.log_count': 'Protokolle Gesamt',
|
||||
'chart.no_slow': '✅ Keine langsamen Anfragen',
|
||||
'chart.query_required': 'Bitte zuerst Abfrage ausführen',
|
||||
'chart.load_failed': '❌ Diagramm-Daten laden fehlgeschlagen, bitte erneut versuchen',
|
||||
'chart.redis_connections': 'Redis Verbindungspool-Nutzung',
|
||||
'chart.redis_buffer': 'Redis Puffergrößen-Änderungen',
|
||||
'chart.cpu': 'CPU',
|
||||
'chart.memory': 'Speicher',
|
||||
'chart.upload': 'Upload',
|
||||
'chart.download': 'Download',
|
||||
'chart.cpu_memory_axis': 'CPU / Speicher (%)',
|
||||
'chart.network_axis': 'Netzwerk-Upload / Download (KB/s)',
|
||||
'chart.used_connections': 'Verwendete Verbindungen',
|
||||
'chart.buffer_size_mb': 'Puffergröße (MB)',
|
||||
|
||||
// ============ Fehler ============
|
||||
'error.time_range_invalid': 'Startzeit kann nicht größer als Endzeit sein',
|
||||
'error.time_range_required': 'Bitte Startzeit und Endzeit auswählen',
|
||||
'error.query_failed': 'Abfrage fehlgeschlagen, bitte erneut versuchen',
|
||||
'error.export_failed': 'Export fehlgeschlagen, bitte erneut versuchen',
|
||||
'error.connection_failed': 'Verbindung fehlgeschlagen, bitte Seite aktualisieren',
|
||||
'error.max_templates': 'Maximal 10 Vorlagen erlaubt, bitte zuerst einige löschen',
|
||||
'error.auto_pause': 'Echtzeit-Verfolgung automatisch pausiert (über 10 Minuten)',
|
||||
'error.load_failed': 'Laden fehlgeschlagen, bitte erneut versuchen',
|
||||
'error.invalid_params': 'Ungültige Parameter',
|
||||
'error.clear_dl_failed': 'DeadLetter-Warteschlange löschen fehlgeschlagen',
|
||||
|
||||
// ============ Erfolg ============
|
||||
'success.query_complete': 'Abfrage abgeschlossen',
|
||||
'success.export_complete': 'Export abgeschlossen',
|
||||
'success.template_saved': 'Vorlage "{name}" gespeichert',
|
||||
'success.logs_cleared': 'Protokolle gelöscht',
|
||||
'success.operation_success': 'Operation erfolgreich',
|
||||
'success.dl_cleared': 'DeadLetter-Warteschlange gelöscht',
|
||||
|
||||
// ============ Zeitachse ============
|
||||
'timeline.title': 'Zeitachse',
|
||||
'timeline.no_data': 'Keine Daten, bitte zuerst Abfrage ausführen',
|
||||
'timeline.anomaly_detected': '⚠️ {count} Anomalien erkannt',
|
||||
'timeline.error_burst': '{count} aufeinanderfolgende FEHLER-Protokolle',
|
||||
'timeline.slow_anomaly': 'Langsame Anfrage Anomalie ({duration}ms > durchschn. {avg}ms×3)',
|
||||
'timeline.duplicate_error': 'Duplizierter Fehler "{msg}" {count} mal aufgetreten',
|
||||
'timeline.http_request': 'HTTP-Anfrage',
|
||||
'timeline.outbound_request': 'Ausgehende Anfrage',
|
||||
'timeline.system_log': 'Systemprotokoll',
|
||||
|
||||
// ============ Paginierung ============
|
||||
'pagination.page': 'Seite',
|
||||
'pagination.of': 'von',
|
||||
'pagination.items': 'Einträge',
|
||||
'pagination.showing': 'Anzeigen',
|
||||
'pagination.per_page': 'Pro Seite',
|
||||
'pagination.first': 'Erste',
|
||||
'pagination.last': 'Letzte',
|
||||
'pagination.prev': 'Vorherige',
|
||||
'pagination.next': 'Nächste',
|
||||
'pagination.go_to': 'Gehe zu',
|
||||
|
||||
// ============ Export ============
|
||||
'export.current_page': 'Aktuelle Seite exportieren',
|
||||
'export.all_data': 'Alle Daten exportieren',
|
||||
'export.format_csv': 'CSV-Format',
|
||||
'export.format_json': 'JSON-Format',
|
||||
'export.select_format': 'Format auswählen',
|
||||
|
||||
// ============ Vorlagen ============
|
||||
'template.saved_queries': 'Gespeicherte Abfragen...',
|
||||
'template.save': 'Abfrage speichern',
|
||||
'template.manage': 'Vorlagen verwalten',
|
||||
'template.name': 'Vorlagenname',
|
||||
'template.description': 'Beschreibung',
|
||||
'template.delete_confirm': 'Diese Vorlage löschen?',
|
||||
'template.empty': 'Keine gespeicherten Vorlagen',
|
||||
|
||||
// ============ Statistiken ============
|
||||
'stats.time_range': 'Abfragezeitbereich',
|
||||
'stats.http_requests': 'HTTP-Anfragen',
|
||||
'stats.outbound_requests': 'Ausgehende Anfragen',
|
||||
'stats.system_logs': 'Systemprotokolle',
|
||||
'stats.level_distribution': 'Stufenverteilung',
|
||||
'stats.all_time': 'Gesamte Zeit',
|
||||
|
||||
// ============ Sonstiges ============
|
||||
'other.last_update': 'Letzte Aktualisierung',
|
||||
'other.auto_reconnect': 'Neuverbindung in 5s',
|
||||
'other.waiting_logs': 'Warte auf Protokolldaten...',
|
||||
'other.no_matching_logs': 'Keine passenden Protokolle',
|
||||
'other.precise_mode': 'Präziser Modus',
|
||||
'other.saved_queries': 'Gespeicherte Abfragen...',
|
||||
'other.all_time': 'Gesamte Zeit',
|
||||
'other.linked_query': 'Verknüpfte Abfrage',
|
||||
'other.seconds': 'Sekunden',
|
||||
'other.minutes': 'Minuten',
|
||||
'other.hours': 'Stunden',
|
||||
'other.days': 'Tage',
|
||||
'other.ms': 'ms',
|
||||
'other.times': 'mal',
|
||||
'other.view_detail': 'Details anzeigen',
|
||||
'other.copy': 'Kopieren',
|
||||
'other.expand': 'Ausklappen',
|
||||
'other.collapse': 'Einklappen',
|
||||
'other.show_internal': 'Intern anzeigen',
|
||||
'other.show_read': 'Gelesen anzeigen',
|
||||
|
||||
// ============ Datum ============
|
||||
'date.today': 'Heute',
|
||||
'date.yesterday': 'Gestern',
|
||||
|
||||
// ============ Sprache ============
|
||||
'lang.select': 'Sprache auswählen',
|
||||
'lang.zh': '中文',
|
||||
'lang.en': 'English',
|
||||
'lang.de': 'Deutsch',
|
||||
|
||||
// ============ HTTP-Methoden ============
|
||||
'method.get': 'GET',
|
||||
'method.post': 'POST',
|
||||
'method.put': 'PUT',
|
||||
'method.delete': 'DELETE',
|
||||
'method.patch': 'PATCH',
|
||||
|
||||
// ============ Protokollstufen ============
|
||||
'level.debug': 'DEBUG',
|
||||
'level.info': 'INFO',
|
||||
'level.warning': 'WARNING',
|
||||
'level.error': 'ERROR',
|
||||
'level.critical': 'CRITICAL',
|
||||
|
||||
// ============ Planer ============
|
||||
'scheduler.rule': 'Planungsregel',
|
||||
'scheduler.last_run': 'Letzte Ausführung',
|
||||
'scheduler.max_time': 'Max. Zeit',
|
||||
'scheduler.never_run': 'Nie ausgeführt',
|
||||
'scheduler.default': 'Standard',
|
||||
'scheduler.running': 'Läuft',
|
||||
'scheduler.not_scheduled': 'Nicht geplant',
|
||||
|
||||
// ============ Relative Zeit ============
|
||||
'time.just_now': 'Gerade eben',
|
||||
'time.minutes_ago': 'vor {n} Min.',
|
||||
'time.hours_ago': 'vor {n} Stunden',
|
||||
'time.days_ago': 'vor {n} Tagen',
|
||||
'time.day_before_yesterday': 'Vorgestern',
|
||||
|
||||
// ============ Warnungstypen ============
|
||||
'alert.warning': 'Warnung',
|
||||
'alert.error': 'Fehler',
|
||||
'alert.critical': 'Kritisch',
|
||||
'alert.normal': 'Normal',
|
||||
|
||||
// ============ Verbindungsstatus ============
|
||||
'connection.failed': 'Verbindung fehlgeschlagen',
|
||||
'connection.reconnecting': 'Verbinde neu',
|
||||
'connection.connected': 'Verbunden',
|
||||
'connection.disconnected': 'Getrennt',
|
||||
|
||||
// ============ Überwachungsstatus ============
|
||||
'monitor.paused': '● Überwachung pausiert (inaktiv)',
|
||||
'monitor.panel': 'Überwachungstafel',
|
||||
'monitor.last_update': 'Letzte Aktualisierung',
|
||||
'monitor.reset_stats_confirm': 'Alle Ereignisstatistiken zurücksetzen?',
|
||||
|
||||
// ============ Hervorhebung ============
|
||||
'highlight.on': 'Hervorgehoben',
|
||||
'highlight.off': 'Hervorheben',
|
||||
|
||||
// ============ Kopieren ============
|
||||
'copy.success': 'Kopiert',
|
||||
'copy.failed': 'Kopieren fehlgeschlagen, bitte manuell kopieren',
|
||||
'copy.error': 'Kopieren fehlgeschlagen',
|
||||
|
||||
// ============ Eingabeaufforderungen ============
|
||||
'prompt.select_date': 'Bitte Datum auswählen',
|
||||
'prompt.fetch_failed': 'Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
|
||||
'prompt.fetch_outbound_failed': 'Ausgehende Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
|
||||
'prompt.reset_failed': 'Zurücksetzen fehlgeschlagen, bitte erneut versuchen',
|
||||
|
||||
// ============ Seitentitel ============
|
||||
'page.http_requests_log': 'HTTP-Anfragedatensätze',
|
||||
'page.outbound_requests_log': 'Ausgehende Anfragedatensätze',
|
||||
|
||||
// ============ Zeit ============
|
||||
'time.tomorrow': 'Morgen',
|
||||
'time.day_after_tomorrow': 'Übermorgen',
|
||||
'time.over_24h': 'Über 24 Stunden',
|
||||
'time.expired': 'Abgelaufen',
|
||||
'time.days_hours_later': 'In {days}T {hours}Std.',
|
||||
'time.unknown': 'Unbekannt',
|
||||
'time.month_day': '{day}.{month}.',
|
||||
|
||||
// ============ Aufgabentypen ============
|
||||
'task.system': 'System',
|
||||
'task.project': 'Projekt',
|
||||
'task.avg_time': 'Durchschn. Ausführungszeit',
|
||||
|
||||
// ============ Detail-Modal ============
|
||||
'modal.outbound_detail': 'Ausgehende Anfrage-Details',
|
||||
'modal.timestamp': 'Zeitstempel:',
|
||||
'modal.method': 'Methode:',
|
||||
'modal.status_code': 'Statuscode:',
|
||||
'modal.response_time': 'Antwortzeit:',
|
||||
'modal.module': 'Modul:',
|
||||
'modal.error_msg': 'Fehlermeldung:',
|
||||
|
||||
// ============ Anfrage/Antwort-Bereiche ============
|
||||
'section.request_headers': 'Anfrage-Header',
|
||||
'section.request_body': 'Anfrage-Body',
|
||||
'section.response_headers': 'Antwort-Header',
|
||||
'section.response_body': 'Antwort-Body',
|
||||
'section.no_response': 'Keine Antwort-Body-Daten',
|
||||
'section.response_failed': 'Antwort-Body-Verarbeitung fehlgeschlagen: ',
|
||||
'section.truncated': '[Inhalt gekürzt, exportieren für vollständigen Inhalt]',
|
||||
'section.large_data_warning': '⚠️ Große Datenmenge ({count} Zeichen), kann Leistung beeinträchtigen',
|
||||
'section.showing_chars': 'Zeige erste {count} Zeichen',
|
||||
'section.basic_info': 'Grundinformationen',
|
||||
'section.no_data': 'Keine Daten',
|
||||
|
||||
// ============ Ratenbegrenzung ============
|
||||
'status.rate_limited': 'Ratenbegrenzt',
|
||||
|
||||
// ============ Protokollstufen-Auswahl ============
|
||||
'log_level.all_with_debug': 'Alle Stufen (mit DEBUG)',
|
||||
'log_level.error': 'Fehlerprotokolle',
|
||||
'log_level.warning': 'Warnungsprotokolle',
|
||||
'log_level.info': 'Infoprotokolle',
|
||||
'log_level.debug': 'Debugprotokolle',
|
||||
|
||||
// ============ DeadLetter ============
|
||||
'dl.reprocess': 'Neu verarbeiten',
|
||||
'dl.reprocess_success': 'DeadLetter erfolgreich neu verarbeitet',
|
||||
'dl.reprocess_failed': 'DeadLetter Neuverarbeitung fehlgeschlagen'
|
||||
};
|
||||
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* English Language Pack (US English)
|
||||
*/
|
||||
window.__i18n_en_US__ = {
|
||||
// ============ Page Titles ============
|
||||
'page.title': 'MyAPI System Monitor',
|
||||
'page.live_logs': 'Live Logs - System Monitor',
|
||||
'page.history_logs': 'Log History Query - System Monitor',
|
||||
|
||||
// ============ Navigation ============
|
||||
'nav.overview': '📊 Overview',
|
||||
'nav.database': '🗃️ Database',
|
||||
'nav.events': '☎️ Events',
|
||||
'nav.scheduler': '⏰ Scheduler',
|
||||
'nav.http_requests': '📥 HTTP Requests',
|
||||
'nav.outbound_requests': '📤 Outbound',
|
||||
'nav.logs': '📋 Logs',
|
||||
|
||||
// ============ Tabs ============
|
||||
'tab.overview': 'Overview',
|
||||
'tab.database': 'Database',
|
||||
'tab.events': 'Events',
|
||||
'tab.scheduler': 'Scheduler',
|
||||
'tab.http': 'HTTP Requests',
|
||||
'tab.outbound': 'Outbound',
|
||||
'tab.logs': 'System Logs',
|
||||
'tab.timeline': 'Timeline',
|
||||
'tab.chart': 'Charts',
|
||||
|
||||
// ============ Cards ============
|
||||
'card.resource': 'Resource Usage',
|
||||
'card.db_status': 'Database Status',
|
||||
'card.db_connections': 'Database Connections',
|
||||
'card.event_helpers': 'Event Helpers',
|
||||
'card.scheduler': 'Job Scheduler',
|
||||
'card.api_requests': 'HTTP Requests',
|
||||
'card.outbound_requests': 'Outbound Requests',
|
||||
'card.redis': 'Redis Status',
|
||||
'card.event_listener': 'Event Listener',
|
||||
'card.recent_alerts': 'Recent Alerts',
|
||||
'card.mysql': 'MySQL',
|
||||
'card.http_requests_log': 'HTTP Request Records',
|
||||
'card.scheduler_detail': 'Scheduler Details',
|
||||
'card.outbound_requests_log': 'Outbound Request Records',
|
||||
'card.callback_tracker': 'Callback Tracker',
|
||||
'card.event_deduplicator': 'Event Deduplicator',
|
||||
|
||||
// ============ Metrics ============
|
||||
'metric.cpu': 'CPU Usage',
|
||||
'metric.memory': 'Memory Usage',
|
||||
'metric.threads': 'Threads',
|
||||
'metric.uptime': 'Uptime',
|
||||
'metric.total_connections': 'Total Connections',
|
||||
'metric.healthy': 'Healthy',
|
||||
'metric.unhealthy': 'Unhealthy',
|
||||
'metric.degraded': 'Degraded',
|
||||
'metric.total': 'Total',
|
||||
'metric.success': 'Success',
|
||||
'metric.failed': 'Failed',
|
||||
'metric.pending': 'Pending',
|
||||
'metric.active': 'Active',
|
||||
'metric.idle': 'Idle',
|
||||
'metric.unknown': 'Unknown',
|
||||
'metric.warning': 'Warning',
|
||||
'metric.events_received': 'Events Received',
|
||||
'metric.events_processed': 'Events Processed',
|
||||
'metric.events_failed': 'Events Failed',
|
||||
'metric.jobs_running': 'Running Jobs',
|
||||
'metric.jobs_pending': 'Pending Jobs',
|
||||
'metric.requests_total': 'Total Requests',
|
||||
'metric.requests_slow': 'Slow Requests',
|
||||
'metric.requests_error': 'Error Requests',
|
||||
'metric.error_rate': 'Error Rate',
|
||||
'metric.avg_time': 'Avg Response',
|
||||
'metric.connection_status': 'Connection Status',
|
||||
'metric.host': 'Host',
|
||||
'metric.port': 'Port',
|
||||
'metric.database': 'Database',
|
||||
'metric.used_connections': 'Used Connections',
|
||||
'metric.max_connections': 'Max Connections',
|
||||
'metric.connection_usage': 'Connection Usage',
|
||||
'metric.buffer_size': 'Buffer Size',
|
||||
'metric.buffer_threshold': 'Buffer Threshold',
|
||||
'metric.buffer_usage': 'Buffer Usage',
|
||||
'metric.events_interrupted': 'Interrupted',
|
||||
'metric.overall_success_rate': 'Overall Success Rate',
|
||||
'metric.active_event_types': 'Active Event Types',
|
||||
'metric.backpressure_status': 'Backpressure Status',
|
||||
'metric.backpressure_pending': 'Pending Events',
|
||||
'metric.backpressure_usage': 'Backpressure Usage',
|
||||
'metric.event_loop_status': 'Event Loop Status',
|
||||
'metric.pending_callbacks': 'Pending Callbacks',
|
||||
'metric.max_retries': 'Max Retries',
|
||||
'metric.pending_retries': 'Pending Retries',
|
||||
'metric.total_entries': 'Total Entries',
|
||||
'metric.active_items': 'Active Items',
|
||||
'metric.ttl_seconds': 'TTL (seconds)',
|
||||
'metric.max_entries': 'Max Entries',
|
||||
'metric.dl_total': 'DL Total',
|
||||
'metric.recent_dl': 'Recent DL',
|
||||
'metric.process_success_rate': 'Process Success Rate',
|
||||
'metric.scheduler_status': 'Scheduler Status',
|
||||
'metric.job_count': 'Job Count',
|
||||
'metric.pending_events': 'Pending Events',
|
||||
'metric.total_events': 'Total Events',
|
||||
'metric.file_size': 'File Size',
|
||||
'metric.running_status': 'Running Status',
|
||||
|
||||
// ============ Status ============
|
||||
'status.healthy': '● System Healthy',
|
||||
'status.unhealthy': '● System Unhealthy',
|
||||
'status.degraded': '● System Degraded',
|
||||
'status.running': 'Running',
|
||||
'status.checking': 'Checking',
|
||||
'status.stopped': 'Stopped',
|
||||
'status.loading': 'Loading...',
|
||||
'status.querying': 'Querying...',
|
||||
'status.exporting': 'Exporting...',
|
||||
'status.no_data': 'No data',
|
||||
'status.connected': 'Connected',
|
||||
'status.connecting': 'Connecting...',
|
||||
'status.disconnected': 'Disconnected',
|
||||
'status.reconnecting': 'Reconnecting...',
|
||||
'status.paused': 'Paused',
|
||||
'status.enabled': 'Enabled',
|
||||
'status.disabled': 'Disabled',
|
||||
'status.no_db_connections': 'No database connections',
|
||||
'status.no_scheduler': 'No scheduled tasks',
|
||||
'status.no_http_requests': 'No HTTP requests',
|
||||
'status.no_alerts': 'No alerts',
|
||||
'status.no_logs': 'No logs',
|
||||
'status.no_events': 'No event statistics',
|
||||
'status.no_api_requests': 'No API request records',
|
||||
'status.no_outbound_requests': 'No outbound request records',
|
||||
'status.no_dead_letters': 'No DL',
|
||||
'status.partial_warnings': '● Partial Warnings',
|
||||
'status.monitoring': 'Monitoring',
|
||||
|
||||
// ============ Buttons ============
|
||||
'btn.query': 'Query',
|
||||
'btn.reset': 'Reset',
|
||||
'btn.refresh': 'Refresh',
|
||||
'btn.export': 'Export',
|
||||
'btn.export_csv': 'Export CSV',
|
||||
'btn.export_json': 'Export JSON',
|
||||
'btn.detail': 'Detail',
|
||||
'btn.pause': 'Pause',
|
||||
'btn.resume': 'Resume',
|
||||
'btn.clear': 'Clear',
|
||||
'btn.save': 'Save',
|
||||
'btn.delete': 'Delete',
|
||||
'btn.close': 'Close',
|
||||
'btn.confirm': 'Confirm',
|
||||
'btn.cancel': 'Cancel',
|
||||
'btn.test': 'Test',
|
||||
'btn.apply': 'Apply',
|
||||
'btn.filter': 'Filter',
|
||||
'btn.clear_all': 'Clear All',
|
||||
'btn.realtime_on': 'Realtime: ON',
|
||||
'btn.realtime_off': 'Realtime: OFF',
|
||||
'btn.precise_locate': '± 60s',
|
||||
'btn.precise_cancel': '± 60s - Click to cancel',
|
||||
'btn.reset_stats': 'Reset Stats',
|
||||
'btn.flush_all': 'Flush All',
|
||||
'btn.new_window': 'New Window',
|
||||
'btn.auto_scroll': 'Auto Scroll',
|
||||
'btn.live_logs': 'Live Logs',
|
||||
'btn.history_query': 'History Query',
|
||||
'btn.mark_all_read': 'Mark All Read',
|
||||
'btn.clear_read_status': 'Clear Read Status',
|
||||
|
||||
// ============ Table Columns ============
|
||||
'col.index': '#',
|
||||
'col.time': 'Time',
|
||||
'col.level': 'Level',
|
||||
'col.module': 'Module',
|
||||
'col.message': 'Message',
|
||||
'col.method': 'Method',
|
||||
'col.path': 'Path',
|
||||
'col.url': 'URL',
|
||||
'col.status': 'Status',
|
||||
'col.duration': 'Duration',
|
||||
'col.client_ip': 'Client IP',
|
||||
'col.source': 'Source',
|
||||
'col.function': 'Function',
|
||||
'col.line': 'Line',
|
||||
'col.operation': 'Action',
|
||||
'col.count': 'Count',
|
||||
'col.avg_time': 'Avg Time',
|
||||
'col.max_time': 'Max Time',
|
||||
'col.db_name': 'DB Name',
|
||||
'col.last_check': 'Last Check',
|
||||
'col.current_connections': 'Current',
|
||||
'col.max_connections': 'Max',
|
||||
'col.min_connections': 'Min',
|
||||
'col.idle_connections': 'Idle',
|
||||
'col.used_connections': 'Used',
|
||||
'col.usage': 'Usage',
|
||||
'col.processed_records': 'Processed',
|
||||
'col.timestamp': 'Timestamp',
|
||||
'col.query_params': 'Query Params',
|
||||
'col.error_message': 'Error Message',
|
||||
'col.url': 'URL',
|
||||
'col.is_read': 'Read',
|
||||
'col.description': 'Description',
|
||||
'col.total_received': 'Total Received',
|
||||
'col.pending': 'Pending',
|
||||
'col.processed': 'Processed',
|
||||
'col.interrupted': 'Interrupted',
|
||||
'col.completion_rate': 'Completion Rate',
|
||||
'col.avg_latency': 'Avg Latency',
|
||||
'col.last_action': 'Last Action',
|
||||
'col.id': 'ID',
|
||||
'col.event_type': 'Event Type',
|
||||
'col.table': 'Table',
|
||||
|
||||
// ============ Quick Time ============
|
||||
'time.start_time': 'Start Time',
|
||||
'time.end_time': 'End Time',
|
||||
'time.last_10m': 'Last 10 min',
|
||||
'time.last_30m': 'Last 30 min',
|
||||
'time.last_1h': 'Last 1 hour',
|
||||
'time.last_6h': 'Last 6 hours',
|
||||
'time.last_24h': 'Last 24 hours',
|
||||
'time.range': 'Time Range',
|
||||
|
||||
// ============ Filters ============
|
||||
'filter.level': 'All Levels',
|
||||
'filter.type': 'All Types',
|
||||
'filter.module': 'Module',
|
||||
'filter.keyword': 'Keyword',
|
||||
'filter.method': 'Method',
|
||||
'filter.client_ip': 'Client IP',
|
||||
'filter.status_range': 'Status Range',
|
||||
'filter.duration_range': 'Duration Range',
|
||||
'filter.advanced': 'Advanced Filters',
|
||||
'filter.collapse': 'Collapse',
|
||||
'filter.expand': 'Expand',
|
||||
'filter.clear': 'Clear',
|
||||
'filter.active': 'Active Filters',
|
||||
'filter.module_placeholder': 'Enter module name',
|
||||
'filter.keyword_placeholder': 'Enter keyword',
|
||||
'filter.ip_placeholder': 'Enter client IP',
|
||||
'filter.status_placeholder': 'e.g.: 400-499',
|
||||
'filter.duration_placeholder': 'e.g.: >1000',
|
||||
'filter.all_logs': 'All Logs',
|
||||
|
||||
// ============ Charts ============
|
||||
'chart.request_trend': '📊 Request Trend',
|
||||
'chart.level_distribution': '📊 Log Level Distribution',
|
||||
'chart.status_distribution': '📈 Status Code Distribution',
|
||||
'chart.slow_requests': '⏱️ Slow Requests TOP10',
|
||||
'chart.total_requests': 'Total Requests',
|
||||
'chart.error_count': 'Errors',
|
||||
'chart.slow_count': 'Slow Requests',
|
||||
'chart.log_count': 'Total Logs',
|
||||
'chart.no_slow': '✅ No slow requests',
|
||||
'chart.query_required': 'Please execute query first',
|
||||
'chart.load_failed': '❌ Chart data load failed, please retry',
|
||||
'chart.redis_connections': 'Redis Connection Pool Usage',
|
||||
'chart.redis_buffer': 'Redis Buffer Size Changes',
|
||||
'chart.cpu': 'CPU',
|
||||
'chart.memory': 'Memory',
|
||||
'chart.upload': 'Upload',
|
||||
'chart.download': 'Download',
|
||||
'chart.cpu_memory_axis': 'CPU / Memory (%)',
|
||||
'chart.network_axis': 'Network Upload / Download (KB/s)',
|
||||
'chart.used_connections': 'Used Connections',
|
||||
'chart.buffer_size_mb': 'Buffer Size (MB)',
|
||||
|
||||
// ============ Errors ============
|
||||
'error.time_range_invalid': 'Start time cannot be greater than end time',
|
||||
'error.time_range_required': 'Please select start time and end time',
|
||||
'error.query_failed': 'Query failed, please try again',
|
||||
'error.export_failed': 'Export failed, please retry',
|
||||
'error.connection_failed': 'Connection failed, please refresh page',
|
||||
'error.max_templates': 'Maximum 10 templates allowed, please delete some first',
|
||||
'error.auto_pause': 'Realtime tracking auto-paused (over 10 minutes)',
|
||||
'error.load_failed': 'Load failed, please retry',
|
||||
'error.invalid_params': 'Invalid parameters',
|
||||
'error.clear_dl_failed': 'Failed to clear DeadLetter queue',
|
||||
|
||||
// ============ Success ============
|
||||
'success.query_complete': 'Query Complete',
|
||||
'success.export_complete': 'Export Complete',
|
||||
'success.template_saved': 'Template "{name}" saved',
|
||||
'success.logs_cleared': 'Logs cleared',
|
||||
'success.operation_success': 'Operation successful',
|
||||
'success.dl_cleared': 'DeadLetter queue cleared',
|
||||
|
||||
// ============ Timeline ============
|
||||
'timeline.title': 'Timeline',
|
||||
'timeline.no_data': 'No data, please execute query first',
|
||||
'timeline.anomaly_detected': '⚠️ {count} anomalies detected',
|
||||
'timeline.error_burst': '{count} consecutive ERROR logs',
|
||||
'timeline.slow_anomaly': 'Slow request anomaly ({duration}ms > avg {avg}ms×3)',
|
||||
'timeline.duplicate_error': 'Duplicate error "{msg}" appeared {count} times',
|
||||
'timeline.http_request': 'HTTP Request',
|
||||
'timeline.outbound_request': 'Outbound Request',
|
||||
'timeline.system_log': 'System Log',
|
||||
|
||||
// ============ Pagination ============
|
||||
'pagination.page': 'Page',
|
||||
'pagination.of': 'of',
|
||||
'pagination.items': 'items',
|
||||
'pagination.showing': 'Showing',
|
||||
'pagination.per_page': 'Per Page',
|
||||
'pagination.first': 'First',
|
||||
'pagination.last': 'Last',
|
||||
'pagination.prev': 'Previous',
|
||||
'pagination.next': 'Next',
|
||||
'pagination.go_to': 'Go to',
|
||||
|
||||
// ============ Export ============
|
||||
'export.current_page': 'Export Current Page',
|
||||
'export.all_data': 'Export All Data',
|
||||
'export.format_csv': 'CSV Format',
|
||||
'export.format_json': 'JSON Format',
|
||||
'export.select_format': 'Select Format',
|
||||
|
||||
// ============ Templates ============
|
||||
'template.saved_queries': 'Saved Queries...',
|
||||
'template.save': 'Save Query',
|
||||
'template.manage': 'Manage Templates',
|
||||
'template.name': 'Template Name',
|
||||
'template.description': 'Description',
|
||||
'template.delete_confirm': 'Delete this template?',
|
||||
'template.empty': 'No saved templates',
|
||||
|
||||
// ============ Statistics ============
|
||||
'stats.time_range': 'Query Time Range',
|
||||
'stats.http_requests': 'HTTP Requests',
|
||||
'stats.outbound_requests': 'Outbound Requests',
|
||||
'stats.system_logs': 'System Logs',
|
||||
'stats.level_distribution': 'Level Distribution',
|
||||
'stats.all_time': 'All Time',
|
||||
|
||||
// ============ Others ============
|
||||
'other.last_update': 'Last Update',
|
||||
'other.auto_reconnect': 'reconnect in 5s',
|
||||
'other.waiting_logs': 'Waiting for log data...',
|
||||
'other.no_matching_logs': 'No matching logs',
|
||||
'other.precise_mode': 'Precise Mode',
|
||||
'other.saved_queries': 'Saved Queries...',
|
||||
'other.all_time': 'All Time',
|
||||
'other.linked_query': 'Linked Query',
|
||||
'other.seconds': 'seconds',
|
||||
'other.minutes': 'minutes',
|
||||
'other.hours': 'hours',
|
||||
'other.days': 'days',
|
||||
'other.ms': 'ms',
|
||||
'other.times': 'times',
|
||||
'other.view_detail': 'View Detail',
|
||||
'other.copy': 'Copy',
|
||||
'other.expand': 'Expand',
|
||||
'other.collapse': 'Collapse',
|
||||
'other.show_internal': 'Show Internal',
|
||||
'other.show_read': 'Show Read',
|
||||
|
||||
// ============ Date ============
|
||||
'date.today': 'Today',
|
||||
'date.yesterday': 'Yesterday',
|
||||
|
||||
// ============ Language ============
|
||||
'lang.select': 'Select Language',
|
||||
'lang.zh': '中文',
|
||||
'lang.en': 'English',
|
||||
'lang.de': 'Deutsch',
|
||||
|
||||
// ============ HTTP Methods ============
|
||||
'method.get': 'GET',
|
||||
'method.post': 'POST',
|
||||
'method.put': 'PUT',
|
||||
'method.delete': 'DELETE',
|
||||
'method.patch': 'PATCH',
|
||||
|
||||
// ============ Log Levels ============
|
||||
'level.debug': 'DEBUG',
|
||||
'level.info': 'INFO',
|
||||
'level.warning': 'WARNING',
|
||||
'level.error': 'ERROR',
|
||||
'level.critical': 'CRITICAL',
|
||||
|
||||
// ============ Scheduler ============
|
||||
'scheduler.rule': 'Schedule Rule',
|
||||
'scheduler.last_run': 'Last Run',
|
||||
'scheduler.max_time': 'Max Time',
|
||||
'scheduler.never_run': 'Never Run',
|
||||
'scheduler.default': 'Default',
|
||||
'scheduler.running': 'Running',
|
||||
'scheduler.not_scheduled': 'Not Scheduled',
|
||||
|
||||
// ============ Relative Time ============
|
||||
'time.just_now': 'Just now',
|
||||
'time.minutes_ago': '{n} min ago',
|
||||
'time.hours_ago': '{n} hours ago',
|
||||
'time.days_ago': '{n} days ago',
|
||||
'time.day_before_yesterday': 'Day before yesterday',
|
||||
|
||||
// ============ Alert Types ============
|
||||
'alert.warning': 'Warning',
|
||||
'alert.error': 'Error',
|
||||
'alert.critical': 'Critical',
|
||||
'alert.normal': 'Normal',
|
||||
|
||||
// ============ Connection Status ============
|
||||
'connection.failed': 'Connection Failed',
|
||||
'connection.reconnecting': 'Reconnecting',
|
||||
'connection.connected': 'Connected',
|
||||
'connection.disconnected': 'Disconnected',
|
||||
|
||||
// ============ Monitor Status ============
|
||||
'monitor.paused': '● Monitor paused (inactive)',
|
||||
'monitor.panel': 'Monitor Panel',
|
||||
'monitor.last_update': 'Last Update',
|
||||
'monitor.reset_stats_confirm': 'Reset all event statistics?',
|
||||
|
||||
// ============ Highlight ============
|
||||
'highlight.on': 'Highlighted',
|
||||
'highlight.off': 'Highlight',
|
||||
|
||||
// ============ Copy ============
|
||||
'copy.success': 'Copied',
|
||||
'copy.failed': 'Copy failed, please copy manually',
|
||||
'copy.error': 'Copy failed',
|
||||
|
||||
// ============ Prompts ============
|
||||
'prompt.select_date': 'Please select a date',
|
||||
'prompt.fetch_failed': 'Failed to fetch request records, please try again',
|
||||
'prompt.fetch_outbound_failed': 'Failed to fetch outbound request records, please try again',
|
||||
'prompt.reset_failed': 'Reset failed, please try again',
|
||||
|
||||
// ============ Page Titles ============
|
||||
'page.http_requests_log': 'HTTP Request Records',
|
||||
'page.outbound_requests_log': 'Outbound Request Records',
|
||||
|
||||
// ============ Time ============
|
||||
'time.tomorrow': 'Tomorrow',
|
||||
'time.day_after_tomorrow': 'Day after tomorrow',
|
||||
'time.over_24h': 'Over 24 hours',
|
||||
'time.expired': 'Expired',
|
||||
'time.days_hours_later': '{days}d {hours}h later',
|
||||
'time.unknown': 'Unknown',
|
||||
'time.month_day': '{month}/{day}',
|
||||
|
||||
// ============ Task Types ============
|
||||
'task.system': 'System',
|
||||
'task.project': 'Project',
|
||||
'task.avg_time': 'Avg Execution Time',
|
||||
|
||||
// ============ Detail Modal ============
|
||||
'modal.outbound_detail': 'Outbound Request Detail',
|
||||
'modal.timestamp': 'Timestamp:',
|
||||
'modal.method': 'Method:',
|
||||
'modal.status_code': 'Status Code:',
|
||||
'modal.response_time': 'Response Time:',
|
||||
'modal.module': 'Module:',
|
||||
'modal.error_msg': 'Error Message:',
|
||||
|
||||
// ============ Request/Response Sections ============
|
||||
'section.request_headers': 'Request Headers',
|
||||
'section.request_body': 'Request Body',
|
||||
'section.response_headers': 'Response Headers',
|
||||
'section.response_body': 'Response Body',
|
||||
'section.no_response': 'No response body data',
|
||||
'section.response_failed': 'Response body processing failed: ',
|
||||
'section.truncated': '[Content truncated, export to view full content]',
|
||||
'section.large_data_warning': '⚠️ Large data ({count} chars), may affect performance',
|
||||
'section.showing_chars': 'Showing first {count} chars',
|
||||
'section.basic_info': 'Basic Information',
|
||||
'section.no_data': 'No data',
|
||||
|
||||
// ============ Rate Limiting ============
|
||||
'status.rate_limited': 'Rate Limited',
|
||||
|
||||
// ============ Log Level Selection ============
|
||||
'log_level.all_with_debug': 'All Levels (with DEBUG)',
|
||||
'log_level.error': 'Error Logs',
|
||||
'log_level.warning': 'Warning Logs',
|
||||
'log_level.info': 'Info Logs',
|
||||
'log_level.debug': 'Debug Logs',
|
||||
|
||||
// ============ DeadLetter ============
|
||||
'dl.reprocess': 'Reprocess',
|
||||
'dl.reprocess_success': 'DeadLetter reprocessed successfully',
|
||||
'dl.reprocess_failed': 'DeadLetter reprocess failed'
|
||||
};
|
||||
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* 轻量级国际化框架
|
||||
* 支持中文(zh-CN)、英语(en-US)、德语(de-DE)
|
||||
*
|
||||
* 特性:
|
||||
* - 无第三方依赖
|
||||
* - 自动检测浏览器语言
|
||||
* - 支持localStorage持久化
|
||||
* - 支持插值参数
|
||||
* - 支持热切换(无需刷新)
|
||||
*/
|
||||
class I18n {
|
||||
constructor() {
|
||||
this.currentLang = this.detectLanguage();
|
||||
this.messages = {};
|
||||
this.fallbackLang = 'zh-CN';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测用户语言
|
||||
* 优先级:localStorage > 浏览器语言 > 默认中文
|
||||
*/
|
||||
detectLanguage() {
|
||||
const supportedLangs = ['zh-CN', 'en-US', 'de-DE'];
|
||||
|
||||
const saved = localStorage.getItem('monitor-lang');
|
||||
if (saved && supportedLangs.includes(saved)) {
|
||||
return saved;
|
||||
}
|
||||
|
||||
const browserLang = navigator.language || navigator.userLanguage || 'zh-CN';
|
||||
|
||||
const langMap = {
|
||||
'zh': 'zh-CN',
|
||||
'zh-CN': 'zh-CN',
|
||||
'zh-Hans': 'zh-CN',
|
||||
'zh-Hans-CN': 'zh-CN',
|
||||
'zh-TW': 'zh-CN',
|
||||
'zh-HK': 'zh-CN',
|
||||
'zh-Hant': 'zh-CN',
|
||||
'en': 'en-US',
|
||||
'en-US': 'en-US',
|
||||
'en-GB': 'en-US',
|
||||
'en-AU': 'en-US',
|
||||
'en-CA': 'en-US',
|
||||
'de': 'de-DE',
|
||||
'de-DE': 'de-DE',
|
||||
'de-AT': 'de-DE',
|
||||
'de-CH': 'de-DE',
|
||||
'de-LI': 'de-DE'
|
||||
};
|
||||
|
||||
if (langMap[browserLang]) {
|
||||
return langMap[browserLang];
|
||||
}
|
||||
|
||||
const baseLang = browserLang.split('-')[0];
|
||||
if (langMap[baseLang]) {
|
||||
return langMap[baseLang];
|
||||
}
|
||||
|
||||
return 'zh-CN';
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化i18n
|
||||
*/
|
||||
async init() {
|
||||
try {
|
||||
await this.loadLanguage(this.currentLang);
|
||||
this.applyTranslations();
|
||||
this.updateLangSelector();
|
||||
this.updateHtmlLang();
|
||||
|
||||
console.log(`[i18n] Initialized with language: ${this.currentLang}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('[i18n] Initialization failed:', error);
|
||||
|
||||
if (this.currentLang !== this.fallbackLang) {
|
||||
console.log('[i18n] Falling back to:', this.fallbackLang);
|
||||
this.currentLang = this.fallbackLang;
|
||||
return await this.init();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言包
|
||||
*/
|
||||
async loadLanguage(lang) {
|
||||
const varName = `__i18n_${lang.replace('-', '_')}__`;
|
||||
|
||||
if (window[varName]) {
|
||||
this.messages = window[varName];
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = `/static/lib/i18n/${lang}.js`;
|
||||
script.onload = () => {
|
||||
if (window[varName]) {
|
||||
this.messages = window[varName];
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Language pack variable not found: ${varName}`));
|
||||
}
|
||||
};
|
||||
script.onerror = () => reject(new Error(`Failed to load language pack: ${lang}`));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用翻译到DOM
|
||||
*/
|
||||
applyTranslations() {
|
||||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
const text = this.t(key);
|
||||
if (text !== key) {
|
||||
el.textContent = text;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n-placeholder');
|
||||
const text = this.t(key);
|
||||
if (text !== key) {
|
||||
el.placeholder = text;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n-title');
|
||||
const text = this.t(key);
|
||||
if (text !== key) {
|
||||
el.title = text;
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-i18n-value]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n-value');
|
||||
const text = this.t(key);
|
||||
if (text !== key) {
|
||||
el.value = text;
|
||||
}
|
||||
});
|
||||
|
||||
const pageTitle = document.querySelector('[data-i18n-page-title]');
|
||||
if (pageTitle) {
|
||||
const key = pageTitle.getAttribute('data-i18n-page-title');
|
||||
const text = this.t(key);
|
||||
if (text !== key) {
|
||||
document.title = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取翻译文本
|
||||
* @param {string} key - 翻译键
|
||||
* @param {object} params - 插值参数(可选)
|
||||
*/
|
||||
t(key, params = {}) {
|
||||
let text = this.messages[key] || key;
|
||||
|
||||
Object.keys(params).forEach(k => {
|
||||
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), params[k]);
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换语言
|
||||
*/
|
||||
async switchLanguage(lang) {
|
||||
if (lang === this.currentLang) return;
|
||||
|
||||
const supportedLangs = ['zh-CN', 'en-US', 'de-DE'];
|
||||
if (!supportedLangs.includes(lang)) {
|
||||
console.error('[i18n] Unsupported language:', lang);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem('monitor-lang', lang);
|
||||
|
||||
this.currentLang = lang;
|
||||
await this.loadLanguage(lang);
|
||||
this.applyTranslations();
|
||||
this.updateLangSelector();
|
||||
this.updateHtmlLang();
|
||||
|
||||
window.dispatchEvent(new CustomEvent('langchange', {
|
||||
detail: { lang: lang }
|
||||
}));
|
||||
|
||||
console.log(`[i18n] Language switched to: ${lang}`);
|
||||
} catch (error) {
|
||||
console.error('[i18n] Language switch failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新语言选择器状态
|
||||
*/
|
||||
updateLangSelector() {
|
||||
const selector = document.getElementById('lang-selector');
|
||||
if (selector) {
|
||||
selector.value = this.currentLang;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新HTML lang属性
|
||||
*/
|
||||
updateHtmlLang() {
|
||||
const htmlEl = document.documentElement;
|
||||
if (htmlEl) {
|
||||
htmlEl.lang = this.currentLang;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言
|
||||
*/
|
||||
getCurrentLang() {
|
||||
return this.currentLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支持的语言列表
|
||||
*/
|
||||
getSupportedLanguages() {
|
||||
return [
|
||||
{ code: 'zh-CN', name: '中文', flag: '🇨🇳' },
|
||||
{ code: 'en-US', name: 'English', flag: '🇺🇸' },
|
||||
{ code: 'de-DE', name: 'Deutsch', flag: '🇩🇪' }
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有翻译
|
||||
*/
|
||||
has(key) {
|
||||
return !!this.messages[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有翻译键
|
||||
*/
|
||||
getKeys() {
|
||||
return Object.keys(this.messages);
|
||||
}
|
||||
}
|
||||
|
||||
const i18n = new I18n();
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => i18n.init());
|
||||
} else {
|
||||
i18n.init();
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
/**
|
||||
* 中文语言包(简体中文)
|
||||
* Chinese Language Pack (Simplified Chinese)
|
||||
*/
|
||||
window.__i18n_zh_CN__ = {
|
||||
// ============ 页面标题 ============
|
||||
'page.title': 'MyAPI 系统监控面板',
|
||||
'page.live_logs': '实时日志 - 系统监控',
|
||||
'page.history_logs': '日志历史查询 - 系统监控',
|
||||
|
||||
// ============ 导航菜单 ============
|
||||
'nav.overview': '📊 Overview',
|
||||
'nav.database': '🗃️ 数据库',
|
||||
'nav.events': '☎️ 事件处理',
|
||||
'nav.scheduler': '⏰ 定时任务',
|
||||
'nav.http_requests': '📥 接收请求',
|
||||
'nav.outbound_requests': '📤 发送请求',
|
||||
'nav.logs': '📋 日志',
|
||||
|
||||
// ============ 标签页 ============
|
||||
'tab.overview': '概览',
|
||||
'tab.database': '数据库',
|
||||
'tab.events': '事件处理',
|
||||
'tab.scheduler': '定时任务',
|
||||
'tab.http': '接收请求',
|
||||
'tab.outbound': '发送请求',
|
||||
'tab.logs': '系统日志',
|
||||
'tab.timeline': '时间线',
|
||||
'tab.chart': '图表分析',
|
||||
|
||||
// ============ 卡片标题 ============
|
||||
'card.resource': '资源使用',
|
||||
'card.db_status': '账套状态',
|
||||
'card.db_connections': '数据库连接状态',
|
||||
'card.event_helpers': '事件辅助模块',
|
||||
'card.scheduler': '定时任务调度器',
|
||||
'card.api_requests': 'HTTP请求统计',
|
||||
'card.outbound_requests': '对外HTTP请求',
|
||||
'card.redis': 'Redis状态',
|
||||
'card.event_listener': '事件监听',
|
||||
'card.recent_alerts': '最近告警',
|
||||
'card.mysql': 'MySQL',
|
||||
'card.http_requests_log': '接收请求记录',
|
||||
'card.scheduler_detail': '定时任务详情',
|
||||
'card.outbound_requests_log': '发送请求记录',
|
||||
'card.callback_tracker': '回调跟踪器',
|
||||
'card.event_deduplicator': '事件去重器',
|
||||
|
||||
// ============ 指标标签 ============
|
||||
'metric.cpu': 'CPU 使用率',
|
||||
'metric.memory': '内存使用',
|
||||
'metric.threads': '线程数',
|
||||
'metric.uptime': '运行时间',
|
||||
'metric.total_connections': '总连接数',
|
||||
'metric.healthy': '健康',
|
||||
'metric.unhealthy': '异常',
|
||||
'metric.degraded': '降级',
|
||||
'metric.total': '总数',
|
||||
'metric.success': '成功',
|
||||
'metric.failed': '失败',
|
||||
'metric.pending': '待处理',
|
||||
'metric.active': '活跃',
|
||||
'metric.idle': '空闲',
|
||||
'metric.unknown': '未知',
|
||||
'metric.warning': '警告',
|
||||
'metric.events_received': '接收事件',
|
||||
'metric.events_processed': '处理事件',
|
||||
'metric.events_failed': '失败事件',
|
||||
'metric.jobs_running': '运行中任务',
|
||||
'metric.jobs_pending': '待执行任务',
|
||||
'metric.requests_total': '总请求数',
|
||||
'metric.requests_slow': '慢请求',
|
||||
'metric.requests_error': '错误请求',
|
||||
'metric.error_rate': '错误率',
|
||||
'metric.avg_time': '平均响应',
|
||||
'metric.connection_status': '连接状态',
|
||||
'metric.host': '主机',
|
||||
'metric.port': '端口',
|
||||
'metric.database': '数据库',
|
||||
'metric.used_connections': '使用连接数',
|
||||
'metric.max_connections': '最大连接数',
|
||||
'metric.connection_usage': '连接使用率',
|
||||
'metric.buffer_size': '缓冲大小',
|
||||
'metric.buffer_threshold': '缓冲阈值',
|
||||
'metric.buffer_usage': '缓冲使用率',
|
||||
'metric.events_interrupted': '中断处理',
|
||||
'metric.overall_success_rate': '整体成功率',
|
||||
'metric.active_event_types': '活跃事件类型',
|
||||
'metric.backpressure_status': '背压状态',
|
||||
'metric.backpressure_pending': '待处理事件',
|
||||
'metric.backpressure_usage': '背压使用率',
|
||||
'metric.event_loop_status': '事件循环状态',
|
||||
'metric.pending_callbacks': '待处理回调',
|
||||
'metric.max_retries': '最大重试次数',
|
||||
'metric.pending_retries': '待处理重试',
|
||||
'metric.total_entries': '总存储条目',
|
||||
'metric.active_items': '活跃项目',
|
||||
'metric.ttl_seconds': 'TTL (秒)',
|
||||
'metric.max_entries': '最大条目',
|
||||
'metric.dl_total': 'DL总数',
|
||||
'metric.recent_dl': '最近DL',
|
||||
'metric.process_success_rate': '处理成功率',
|
||||
'metric.scheduler_status': '调度器状态',
|
||||
'metric.job_count': '任务数量',
|
||||
'metric.pending_events': '待写入事件',
|
||||
'metric.total_events': '总事件数',
|
||||
'metric.file_size': '文件大小',
|
||||
'metric.running_status': '运行状态',
|
||||
|
||||
// ============ 状态 ============
|
||||
'status.healthy': '● 系统正常',
|
||||
'status.unhealthy': '● 系统异常',
|
||||
'status.degraded': '● 系统降级',
|
||||
'status.running': '运行中',
|
||||
'status.checking': '检查中',
|
||||
'status.stopped': '已停止',
|
||||
'status.loading': '加载中...',
|
||||
'status.querying': '查询中...',
|
||||
'status.exporting': '导出中...',
|
||||
'status.no_data': '暂无数据',
|
||||
'status.connected': '已连接',
|
||||
'status.connecting': '连接中...',
|
||||
'status.disconnected': '已断开',
|
||||
'status.reconnecting': '重连中...',
|
||||
'status.paused': '已暂停',
|
||||
'status.enabled': '已启用',
|
||||
'status.disabled': '已禁用',
|
||||
'status.no_db_connections': '暂无数据库连接',
|
||||
'status.no_scheduler': '暂无定时任务',
|
||||
'status.no_http_requests': '暂无接收请求',
|
||||
'status.no_alerts': '暂无告警',
|
||||
'status.no_logs': '暂无日志',
|
||||
'status.no_events': '暂无事件统计',
|
||||
'status.no_api_requests': '暂无 API 请求记录',
|
||||
'status.no_outbound_requests': '暂无发送请求记录',
|
||||
'status.no_dead_letters': '暂无DL',
|
||||
'status.partial_warnings': '● 部分警告',
|
||||
'status.monitoring': '监控中',
|
||||
|
||||
// ============ 按钮 ============
|
||||
'btn.query': '查询',
|
||||
'btn.reset': '重置',
|
||||
'btn.refresh': '刷新',
|
||||
'btn.export': '导出',
|
||||
'btn.export_csv': '导出 CSV',
|
||||
'btn.export_json': '导出 JSON',
|
||||
'btn.detail': '详情',
|
||||
'btn.pause': '暂停',
|
||||
'btn.resume': '继续',
|
||||
'btn.clear': '清空',
|
||||
'btn.save': '保存',
|
||||
'btn.delete': '删除',
|
||||
'btn.close': '关闭',
|
||||
'btn.confirm': '确认',
|
||||
'btn.cancel': '取消',
|
||||
'btn.test': '测试',
|
||||
'btn.apply': '应用',
|
||||
'btn.filter': '过滤',
|
||||
'btn.clear_all': '清空全部',
|
||||
'btn.realtime_on': '实时追踪: 开',
|
||||
'btn.realtime_off': '实时追踪: 关',
|
||||
'btn.precise_locate': '± 60s',
|
||||
'btn.precise_cancel': '± 60s - 点击取消',
|
||||
'btn.reset_stats': '重置统计',
|
||||
'btn.flush_all': '立即刷新所有',
|
||||
'btn.new_window': '新窗口',
|
||||
'btn.auto_scroll': '自动滚动',
|
||||
'btn.live_logs': '实时日志',
|
||||
'btn.history_query': '历史查询',
|
||||
'btn.mark_all_read': '标记全部已读',
|
||||
'btn.clear_read_status': '清空已读状态',
|
||||
|
||||
// ============ 表格列名 ============
|
||||
'col.index': '序号',
|
||||
'col.time': '时间',
|
||||
'col.level': '级别',
|
||||
'col.module': '模块',
|
||||
'col.message': '消息',
|
||||
'col.method': '方法',
|
||||
'col.path': '端点',
|
||||
'col.url': 'URL',
|
||||
'col.status': '状态码',
|
||||
'col.duration': '响应时间',
|
||||
'col.client_ip': '客户端IP',
|
||||
'col.source': '来源',
|
||||
'col.function': '函数',
|
||||
'col.line': '行号',
|
||||
'col.operation': '操作',
|
||||
'col.count': '数量',
|
||||
'col.avg_time': '平均时间',
|
||||
'col.max_time': '最大时间',
|
||||
'col.db_name': '账套名称',
|
||||
'col.last_check': '最后检查',
|
||||
'col.current_connections': '当前连接',
|
||||
'col.max_connections': '最大连接',
|
||||
'col.min_connections': '最小连接',
|
||||
'col.idle_connections': '空闲连接',
|
||||
'col.used_connections': '使用中连接',
|
||||
'col.usage': '使用率',
|
||||
'col.processed_records': '处理记录',
|
||||
'col.timestamp': '时间戳',
|
||||
'col.query_params': '查询参数',
|
||||
'col.error_message': '错误信息',
|
||||
'col.url': 'URL',
|
||||
'col.is_read': '已读',
|
||||
'col.description': '描述',
|
||||
'col.total_received': '总接收',
|
||||
'col.pending': '待处理',
|
||||
'col.processed': '已处理',
|
||||
'col.interrupted': '中断',
|
||||
'col.completion_rate': '完成率',
|
||||
'col.avg_latency': '平均延迟',
|
||||
'col.last_action': '最后动作',
|
||||
'col.id': 'ID',
|
||||
'col.event_type': '事件类型',
|
||||
'col.table': '表',
|
||||
|
||||
// ============ 快捷时间 ============
|
||||
'time.start_time': '开始时间',
|
||||
'time.end_time': '结束时间',
|
||||
'time.last_10m': '最近10分钟',
|
||||
'time.last_30m': '最近30分钟',
|
||||
'time.last_1h': '最近1小时',
|
||||
'time.last_6h': '最近6小时',
|
||||
'time.last_24h': '最近24小时',
|
||||
'time.range': '时间范围',
|
||||
|
||||
// ============ 过滤条件 ============
|
||||
'filter.level': '全部级别',
|
||||
'filter.type': '全部数据',
|
||||
'filter.module': '模块',
|
||||
'filter.keyword': '关键词',
|
||||
'filter.method': '请求方法',
|
||||
'filter.client_ip': '客户端IP',
|
||||
'filter.status_range': '状态码范围',
|
||||
'filter.duration_range': '响应时间范围',
|
||||
'filter.advanced': '高级过滤',
|
||||
'filter.collapse': '收起',
|
||||
'filter.expand': '展开',
|
||||
'filter.clear': '清空',
|
||||
'filter.active': '已激活过滤条件',
|
||||
'filter.module_placeholder': '输入模块名',
|
||||
'filter.keyword_placeholder': '输入关键词搜索',
|
||||
'filter.ip_placeholder': '输入客户端IP',
|
||||
'filter.status_placeholder': '如: 400-499',
|
||||
'filter.duration_placeholder': '如: >1000',
|
||||
'filter.all_logs': '全部日志',
|
||||
|
||||
// ============ 图表 ============
|
||||
'chart.request_trend': '📊 请求量趋势',
|
||||
'chart.level_distribution': '📊 日志级别分布',
|
||||
'chart.status_distribution': '📈 状态码分布',
|
||||
'chart.slow_requests': '⏱️ 慢请求TOP10',
|
||||
'chart.total_requests': '总请求',
|
||||
'chart.error_count': '错误',
|
||||
'chart.slow_count': '慢请求',
|
||||
'chart.log_count': '日志总数',
|
||||
'chart.no_slow': '✅ 无慢请求',
|
||||
'chart.query_required': '请先执行查询',
|
||||
'chart.load_failed': '❌ 图表数据加载失败,请重试',
|
||||
'chart.redis_connections': 'Redis 连接池使用情况',
|
||||
'chart.redis_buffer': 'Redis 缓冲大小变化',
|
||||
'chart.cpu': 'CPU',
|
||||
'chart.memory': '内存',
|
||||
'chart.upload': '上传',
|
||||
'chart.download': '下载',
|
||||
'chart.cpu_memory_axis': 'CPU / 内存 (%)',
|
||||
'chart.network_axis': '网络上传 / 下载 (KB/s)',
|
||||
'chart.used_connections': '使用连接数',
|
||||
'chart.buffer_size_mb': '缓冲大小 (MB)',
|
||||
|
||||
// ============ 错误提示 ============
|
||||
'error.time_range_invalid': '开始时间不能大于结束时间',
|
||||
'error.time_range_required': '请选择开始时间和结束时间',
|
||||
'error.query_failed': '查询失败,请稍后重试',
|
||||
'error.export_failed': '导出失败,请重试',
|
||||
'error.connection_failed': '连接失败,请刷新页面',
|
||||
'error.max_templates': '最多保存10个模板,请先删除部分模板',
|
||||
'error.auto_pause': '实时追踪已自动暂停(超过10分钟)',
|
||||
'error.load_failed': '加载失败,请重试',
|
||||
'error.invalid_params': '参数错误',
|
||||
'error.clear_dl_failed': '清空DeadLetter队列失败',
|
||||
|
||||
// ============ 成功提示 ============
|
||||
'success.query_complete': '查询完成',
|
||||
'success.export_complete': '导出完成',
|
||||
'success.template_saved': '模板"{name}"已保存',
|
||||
'success.logs_cleared': '日志已清空',
|
||||
'success.operation_success': '操作成功',
|
||||
'success.dl_cleared': 'DeadLetter队列已清空',
|
||||
|
||||
// ============ 时间线 ============
|
||||
'timeline.title': '时间线',
|
||||
'timeline.no_data': '暂无数据,请先执行查询',
|
||||
'timeline.anomaly_detected': '⚠️ 发现 {count} 处异常',
|
||||
'timeline.error_burst': '连续{count}条ERROR日志',
|
||||
'timeline.slow_anomaly': '异常慢请求({duration}ms > 平均{avg}ms×3)',
|
||||
'timeline.duplicate_error': '重复错误"{msg}"出现{count}次',
|
||||
'timeline.http_request': 'HTTP请求',
|
||||
'timeline.outbound_request': '发送请求',
|
||||
'timeline.system_log': '系统日志',
|
||||
|
||||
// ============ 分页 ============
|
||||
'pagination.page': '页',
|
||||
'pagination.of': '共',
|
||||
'pagination.items': '条',
|
||||
'pagination.showing': '显示',
|
||||
'pagination.per_page': '每页',
|
||||
'pagination.first': '首页',
|
||||
'pagination.last': '末页',
|
||||
'pagination.prev': '上一页',
|
||||
'pagination.next': '下一页',
|
||||
'pagination.go_to': '跳转',
|
||||
|
||||
// ============ 导出 ============
|
||||
'export.current_page': '导出当前页',
|
||||
'export.all_data': '导出全部数据',
|
||||
'export.format_csv': 'CSV格式',
|
||||
'export.format_json': 'JSON格式',
|
||||
'export.select_format': '选择导出格式',
|
||||
|
||||
// ============ 查询模板 ============
|
||||
'template.saved_queries': '已保存查询...',
|
||||
'template.save': '保存查询条件',
|
||||
'template.manage': '管理模板',
|
||||
'template.name': '模板名称',
|
||||
'template.description': '描述',
|
||||
'template.delete_confirm': '确定删除该模板?',
|
||||
'template.empty': '暂无保存的模板',
|
||||
|
||||
// ============ 统计信息 ============
|
||||
'stats.time_range': '查询时间范围',
|
||||
'stats.http_requests': '接收请求',
|
||||
'stats.outbound_requests': '发送请求',
|
||||
'stats.system_logs': '系统日志',
|
||||
'stats.level_distribution': '级别分布',
|
||||
'stats.all_time': '全部时间',
|
||||
|
||||
// ============ 其他 ============
|
||||
'other.last_update': '最后更新',
|
||||
'other.auto_reconnect': '5秒后重连',
|
||||
'other.waiting_logs': '正在等待日志数据...',
|
||||
'other.no_matching_logs': '没有匹配的日志',
|
||||
'other.precise_mode': '精确定位',
|
||||
'other.saved_queries': '已保存查询...',
|
||||
'other.all_time': '全部时间',
|
||||
'other.linked_query': '联动查询',
|
||||
'other.seconds': '秒',
|
||||
'other.minutes': '分钟',
|
||||
'other.hours': '小时',
|
||||
'other.days': '天',
|
||||
'other.ms': '毫秒',
|
||||
'other.times': '次',
|
||||
'other.view_detail': '查看详情',
|
||||
'other.copy': '复制',
|
||||
'other.expand': '展开',
|
||||
'other.collapse': '收起',
|
||||
'other.show_internal': '显示内部请求',
|
||||
'other.show_read': '显示已读',
|
||||
|
||||
// ============ 日期 ============
|
||||
'date.today': '今天',
|
||||
'date.yesterday': '昨天',
|
||||
|
||||
// ============ 语言选择 ============
|
||||
'lang.select': '选择语言',
|
||||
'lang.zh': '中文',
|
||||
'lang.en': 'English',
|
||||
'lang.de': 'Deutsch',
|
||||
|
||||
// ============ HTTP方法 ============
|
||||
'method.get': 'GET',
|
||||
'method.post': 'POST',
|
||||
'method.put': 'PUT',
|
||||
'method.delete': 'DELETE',
|
||||
'method.patch': 'PATCH',
|
||||
|
||||
// ============ 日志级别 ============
|
||||
'level.debug': 'DEBUG',
|
||||
'level.info': 'INFO',
|
||||
'level.warning': 'WARNING',
|
||||
'level.error': 'ERROR',
|
||||
'level.critical': 'CRITICAL',
|
||||
|
||||
// ============ 定时任务 ============
|
||||
'scheduler.rule': '定时规则',
|
||||
'scheduler.last_run': '最近执行',
|
||||
'scheduler.max_time': '最大执行时间',
|
||||
'scheduler.never_run': '从未执行',
|
||||
'scheduler.default': '默认',
|
||||
'scheduler.running': '执行中',
|
||||
'scheduler.not_scheduled': '未计划',
|
||||
|
||||
// ============ 相对时间 ============
|
||||
'time.just_now': '刚刚',
|
||||
'time.minutes_ago': '{n}分钟前',
|
||||
'time.hours_ago': '{n}小时前',
|
||||
'time.days_ago': '{n}天前',
|
||||
'time.day_before_yesterday': '前天',
|
||||
|
||||
// ============ 告警类型 ============
|
||||
'alert.warning': '警告',
|
||||
'alert.error': '错误',
|
||||
'alert.critical': '严重',
|
||||
'alert.normal': '正常',
|
||||
|
||||
// ============ 连接状态 ============
|
||||
'connection.failed': '连接失败',
|
||||
'connection.reconnecting': '重新连接',
|
||||
'connection.connected': '已连接',
|
||||
'connection.disconnected': '断开',
|
||||
|
||||
// ============ 监控状态 ============
|
||||
'monitor.paused': '● 监控已暂停(长时间未活动)',
|
||||
'monitor.panel': '监控面板',
|
||||
'monitor.last_update': '最后更新',
|
||||
'monitor.reset_stats_confirm': '确定要重置所有事件统计吗?',
|
||||
|
||||
// ============ 高亮操作 ============
|
||||
'highlight.on': '已高亮',
|
||||
'highlight.off': '高亮',
|
||||
|
||||
// ============ 复制操作 ============
|
||||
'copy.success': '已复制',
|
||||
'copy.failed': '复制失败,请手动复制',
|
||||
'copy.error': '复制失败',
|
||||
|
||||
// ============ 选择提示 ============
|
||||
'prompt.select_date': '请选择日期',
|
||||
'prompt.fetch_failed': '获取请求记录失败,请稍后重试',
|
||||
'prompt.fetch_outbound_failed': '获取对外请求记录失败,请稍后重试',
|
||||
'prompt.reset_failed': '重置失败,请重试',
|
||||
|
||||
// ============ 页面标题 ============
|
||||
'page.http_requests_log': '接收请求记录',
|
||||
'page.outbound_requests_log': '发送请求记录',
|
||||
|
||||
// ============ 时间相关 ============
|
||||
'time.tomorrow': '明天',
|
||||
'time.day_after_tomorrow': '后天',
|
||||
'time.over_24h': '超过24小时',
|
||||
'time.expired': '已过期',
|
||||
'time.days_hours_later': '{days}天{hours}小时后',
|
||||
'time.unknown': '未知',
|
||||
'time.month_day': '{month}月{day}日',
|
||||
|
||||
// ============ 任务类型 ============
|
||||
'task.system': '系统',
|
||||
'task.project': '项目',
|
||||
'task.avg_time': '平均执行时间',
|
||||
|
||||
// ============ 详情模态框 ============
|
||||
'modal.outbound_detail': '发送请求详情',
|
||||
'modal.timestamp': '时间戳:',
|
||||
'modal.method': '方法:',
|
||||
'modal.status_code': '状态码:',
|
||||
'modal.response_time': '响应时间:',
|
||||
'modal.module': '模块:',
|
||||
'modal.error_msg': '错误信息:',
|
||||
|
||||
// ============ 请求/响应部分 ============
|
||||
'section.request_headers': '请求头',
|
||||
'section.request_body': '请求体',
|
||||
'section.response_headers': '响应头',
|
||||
'section.response_body': '响应体',
|
||||
'section.no_response': '无响应体数据',
|
||||
'section.response_failed': '响应体处理失败: ',
|
||||
'section.truncated': '[内容已截断,完整内容请导出查看]',
|
||||
'section.large_data_warning': '⚠️ 数据量较大({count}字符),可能影响显示性能',
|
||||
'section.showing_chars': '仅显示前{count}字符',
|
||||
'section.basic_info': '基本信息',
|
||||
'section.no_data': '无数据',
|
||||
|
||||
// ============ 限流状态 ============
|
||||
'status.rate_limited': '限流',
|
||||
|
||||
// ============ 日志级别选择 ============
|
||||
'log_level.all_with_debug': '全部级别 (含DEBUG)',
|
||||
'log_level.error': '错误日志',
|
||||
'log_level.warning': '警告日志',
|
||||
'log_level.info': '信息日志',
|
||||
'log_level.debug': '调试日志',
|
||||
|
||||
// ============ DeadLetter ============
|
||||
'dl.reprocess': '重新处理',
|
||||
'dl.reprocess_success': 'DeadLetter重新处理成功',
|
||||
'dl.reprocess_failed': 'DeadLetter重新处理失败'
|
||||
};
|
||||
@@ -2995,4 +2995,32 @@ td {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 语言选择器 */
|
||||
.lang-selector {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
background: white;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.lang-selector:hover {
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.lang-selector:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
|
||||
.lang-selector option {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>日志历史查询 - 系统监控</title>
|
||||
<title data-i18n-page-title="page.history_logs">日志历史查询 - 系统监控</title>
|
||||
<link rel="icon" href="/static/swagger/favicon.png" type="image/png">
|
||||
<link rel="stylesheet" href="/static/monitor/css/monitor.css">
|
||||
<script src="/static/lib/i18n/i18n.js"></script>
|
||||
<link rel="stylesheet" href="/static/mds/css/bootstrap-icons.css">
|
||||
<style>
|
||||
body {
|
||||
@@ -841,19 +842,23 @@
|
||||
<div class="history-logs-container">
|
||||
<div class="history-logs-header">
|
||||
<div class="history-logs-header-left">
|
||||
<h1>📊 日志历史查询</h1>
|
||||
<span class="badge" id="history-query-badge">联动查询</span>
|
||||
<h1 data-i18n="page.history_logs">📊 日志历史查询</h1>
|
||||
<span class="badge" id="history-query-badge" data-i18n="other.linked_query">联动查询</span>
|
||||
</div>
|
||||
<div class="history-logs-header-right">
|
||||
<select id="lang-selector" class="lang-selector" onchange="i18n.switchLanguage(this.value)">
|
||||
<option value="zh-CN">🇨🇳 中文</option>
|
||||
<option value="en-US">🇺🇸 English</option>
|
||||
<option value="de-DE">🇩🇪 Deutsch</option>
|
||||
</select>
|
||||
<div class="header-query-controls">
|
||||
<div class="time-range-selector">
|
||||
<!-- <label>起止时间:</label> -->
|
||||
<input type="datetime-local" id="history-start-time" class="datetime-input" step="1">
|
||||
<input type="datetime-local" id="history-end-time" class="datetime-input" step="1">
|
||||
</div>
|
||||
<div class="query-filters">
|
||||
<select id="history-log-level" class="filter-select">
|
||||
<option value="">全部级别</option>
|
||||
<option value="" data-i18n="filter.level">全部级别</option>
|
||||
<option value="DEBUG">≥ DEBUG</option>
|
||||
<option value="INFO">≥ INFO</option>
|
||||
<option value="WARNING">≥ WARNING</option>
|
||||
@@ -861,19 +866,19 @@
|
||||
<option value="CRITICAL">≥ CRITICAL</option>
|
||||
</select>
|
||||
<select id="history-query-type" class="filter-select">
|
||||
<option value="all">全部数据</option>
|
||||
<option value="http">接收请求</option>
|
||||
<option value="outbound">发送请求</option>
|
||||
<option value="logs">系统日志</option>
|
||||
<option value="all" data-i18n="filter.type">全部数据</option>
|
||||
<option value="http" data-i18n="tab.http">接收请求</option>
|
||||
<option value="outbound" data-i18n="tab.outbound">发送请求</option>
|
||||
<option value="logs" data-i18n="tab.logs">系统日志</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('10m')">10分钟</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('30m')">30分钟</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('1h')">1小时</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('6h')">6小时</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('24h')">24小时</button>
|
||||
<button class="btn btn-secondary header-btn header-btn-reset" onclick="resetHistoryQuery()">重置</button>
|
||||
<button class="btn btn-primary header-btn" id="query-btn" onclick="executeHistoryQuery()">查询</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('10m')" data-i18n="time.last_10m">10分钟</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('30m')" data-i18n="time.last_30m">30分钟</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('1h')" data-i18n="time.last_1h">1小时</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('6h')" data-i18n="time.last_6h">6小时</button>
|
||||
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('24h')" data-i18n="time.last_24h">24小时</button>
|
||||
<button class="btn btn-secondary header-btn header-btn-reset" onclick="resetHistoryQuery()" data-i18n="btn.reset">重置</button>
|
||||
<button class="btn btn-primary header-btn" id="query-btn" onclick="executeHistoryQuery()" data-i18n="btn.query">查询</button>
|
||||
</div>
|
||||
<div id="precise-mode-container"></div>
|
||||
</div>
|
||||
@@ -882,11 +887,11 @@
|
||||
<div class="history-logs-body">
|
||||
<div class="result-tabs" id="result-tabs" style="display: none;">
|
||||
<div class="result-tabs-left">
|
||||
<button class="tab-btn active" data-tab="logs" onclick="switchResultTab('logs')"><i class="bi bi-journal-text"></i> 系统日志</button>
|
||||
<button class="tab-btn" data-tab="http" onclick="switchResultTab('http')"><i class="bi bi-box-arrow-in-right"></i> 接收请求</button>
|
||||
<button class="tab-btn" data-tab="outbound" onclick="switchResultTab('outbound')"><i class="bi bi-box-arrow-right"></i> 发送请求</button>
|
||||
<button class="tab-btn" data-tab="timeline" onclick="switchResultTab('timeline')"><i class="bi bi-clock-history"></i> 时间线</button>
|
||||
<button class="tab-btn" data-tab="chart" onclick="switchResultTab('chart')"><i class="bi bi-bar-chart-line"></i> 图表分析</button>
|
||||
<button class="tab-btn active" data-tab="logs" onclick="switchResultTab('logs')"><i class="bi bi-journal-text"></i> <span data-i18n="tab.logs">系统日志</span></button>
|
||||
<button class="tab-btn" data-tab="http" onclick="switchResultTab('http')"><i class="bi bi-box-arrow-in-right"></i> <span data-i18n="tab.http">接收请求</span></button>
|
||||
<button class="tab-btn" data-tab="outbound" onclick="switchResultTab('outbound')"><i class="bi bi-box-arrow-right"></i> <span data-i18n="tab.outbound">发送请求</span></button>
|
||||
<button class="tab-btn" data-tab="timeline" onclick="switchResultTab('timeline')"><i class="bi bi-clock-history"></i> <span data-i18n="tab.timeline">时间线</span></button>
|
||||
<button class="tab-btn" data-tab="chart" onclick="switchResultTab('chart')"><i class="bi bi-bar-chart-line"></i> <span data-i18n="tab.chart">图表分析</span></button>
|
||||
</div>
|
||||
<div class="result-tabs-right">
|
||||
<div class="result-summary-inline">
|
||||
@@ -1000,7 +1005,7 @@
|
||||
</div>
|
||||
<div class="filter-item">
|
||||
<select id="saved-templates" class="filter-select" onchange="loadQueryTemplate()" style="max-width:120px;">
|
||||
<option value="">已保存查询...</option>
|
||||
<option value="" data-i18n="template.saved_queries">已保存查询...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1018,7 +1023,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="http-results-body">
|
||||
<tr><td colspan="8" class="no-data">暂无数据</td></tr>
|
||||
<tr><td colspan="8" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1071,7 +1076,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="outbound-results-body">
|
||||
<tr><td colspan="8" class="no-data">暂无数据</td></tr>
|
||||
<tr><td colspan="8" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1099,7 +1104,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logs-results-body">
|
||||
<tr><td colspan="6" class="no-data">暂无数据</td></tr>
|
||||
<tr><td colspan="6" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1107,7 +1112,7 @@
|
||||
<!-- 时间线标签页 -->
|
||||
<div class="result-tab-content" id="tab-timeline">
|
||||
<div class="timeline-container" id="timeline-container">
|
||||
<div class="timeline-empty">暂无数据,请先执行查询</div>
|
||||
<div class="timeline-empty" data-i18n="timeline.no_data">暂无数据,请先执行查询</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1176,7 +1181,7 @@
|
||||
const endDate = new Date(endTime);
|
||||
|
||||
if (startDate >= endDate) {
|
||||
alert('开始时间不能大于结束时间');
|
||||
alert(i18n.t('error.time_range_invalid'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1189,14 +1194,14 @@
|
||||
try {
|
||||
const badge = document.getElementById('history-query-badge');
|
||||
if (badge) {
|
||||
badge.textContent = '查询中...';
|
||||
badge.textContent = i18n.t('status.querying');
|
||||
badge.className = 'badge warning';
|
||||
}
|
||||
|
||||
const queryBtn = document.getElementById('query-btn');
|
||||
if (queryBtn) {
|
||||
queryBtn.disabled = true;
|
||||
queryBtn.textContent = '查询中...';
|
||||
queryBtn.textContent = i18n.t('status.querying');
|
||||
}
|
||||
|
||||
let url = `${API_BASE}/history/query?timezone_offset=${timezoneOffset}`;
|
||||
@@ -1301,29 +1306,29 @@
|
||||
switchResultTab(currentTab);
|
||||
|
||||
if (badge) {
|
||||
badge.textContent = '查询完成';
|
||||
badge.textContent = i18n.t('success.query_complete');
|
||||
badge.className = 'badge healthy';
|
||||
}
|
||||
|
||||
if (queryBtn) {
|
||||
queryBtn.disabled = false;
|
||||
queryBtn.textContent = '查询';
|
||||
queryBtn.textContent = i18n.t('btn.query');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('执行历史查询失败:', error);
|
||||
alert('查询失败,请稍后重试');
|
||||
alert(i18n.t('error.query_failed'));
|
||||
|
||||
const badge = document.getElementById('history-query-badge');
|
||||
if (badge) {
|
||||
badge.textContent = '查询失败';
|
||||
badge.textContent = i18n.t('error.query_failed');
|
||||
badge.className = 'badge error';
|
||||
}
|
||||
|
||||
const queryBtn = document.getElementById('query-btn');
|
||||
if (queryBtn) {
|
||||
queryBtn.disabled = false;
|
||||
queryBtn.textContent = '查询';
|
||||
queryBtn.textContent = i18n.t('btn.query');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1348,9 +1353,9 @@
|
||||
|
||||
document.getElementById('result-tabs').style.display = 'none';
|
||||
|
||||
document.getElementById('http-results-body').innerHTML = '<tr><td colspan="8" class="no-data">暂无数据</td></tr>';
|
||||
document.getElementById('outbound-results-body').innerHTML = '<tr><td colspan="8" class="no-data">暂无数据</td></tr>';
|
||||
document.getElementById('logs-results-body').innerHTML = '<tr><td colspan="6" class="no-data">暂无数据</td></tr>';
|
||||
document.getElementById('http-results-body').innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
document.getElementById('outbound-results-body').innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
document.getElementById('logs-results-body').innerHTML = '<tr><td colspan="6" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
|
||||
historyQueryData = { http: [], outbound: [], logs: [] };
|
||||
// 清理缓存的排序数据
|
||||
@@ -1360,7 +1365,7 @@
|
||||
|
||||
const badge = document.getElementById('history-query-badge');
|
||||
if (badge) {
|
||||
badge.textContent = '联动查询';
|
||||
badge.textContent = i18n.t('other.linked_query');
|
||||
badge.className = '';
|
||||
}
|
||||
}
|
||||
@@ -1548,7 +1553,7 @@
|
||||
const endTime = document.getElementById('history-end-time').value;
|
||||
|
||||
if (!startTime || !endTime) {
|
||||
alert('请先选择时间范围');
|
||||
alert(i18n.t('error.time_range_required'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1651,7 +1656,7 @@
|
||||
if (!container) return;
|
||||
|
||||
if (timelineData.length === 0) {
|
||||
container.innerHTML = '<div class="timeline-empty">暂无数据</div>';
|
||||
container.innerHTML = '<div class="timeline-empty">' + i18n.t('status.no_data') + '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1764,7 +1769,7 @@
|
||||
const endTime = document.getElementById('history-end-time').value;
|
||||
|
||||
if (!startTime || !endTime) {
|
||||
document.getElementById('tab-chart').innerHTML = '<div style="padding:40px;text-align:center;color:#999;">请先执行查询</div>';
|
||||
document.getElementById('tab-chart').innerHTML = '<div style="padding:40px;text-align:center;color:#999;">' + i18n.t('chart.query_required') + '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1861,7 +1866,7 @@
|
||||
console.error('渲染图表失败:', error);
|
||||
const chartContainer = document.querySelector('.chart-container');
|
||||
if (chartContainer) {
|
||||
chartContainer.innerHTML = '<div style="padding:40px;text-align:center;color:#ef4444;">❌ 图表数据加载失败,请重试</div>';
|
||||
chartContainer.innerHTML = '<div style="padding:40px;text-align:center;color:#ef4444;">' + i18n.t('chart.load_failed') + '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1877,11 +1882,11 @@
|
||||
const btn = document.getElementById('realtime-btn');
|
||||
|
||||
if (realtimeTracking) {
|
||||
btn.textContent = '实时追踪: 开';
|
||||
btn.textContent = i18n.t('btn.realtime_on');
|
||||
btn.className = 'btn btn-primary';
|
||||
startRealtimeTracking();
|
||||
} else {
|
||||
btn.textContent = '实时追踪: 关';
|
||||
btn.textContent = i18n.t('btn.realtime_off');
|
||||
btn.className = 'btn btn-secondary';
|
||||
stopRealtimeTracking();
|
||||
}
|
||||
@@ -1894,12 +1899,12 @@
|
||||
realtimeInterval = setInterval(async () => {
|
||||
trackingDuration += 5;
|
||||
|
||||
// 10分钟自动暂停
|
||||
if (trackingDuration >= 600) {
|
||||
stopRealtimeTracking();
|
||||
alert('实时追踪已自动暂停(超过10分钟)');
|
||||
return;
|
||||
}
|
||||
// 10分钟自动暂停
|
||||
if (trackingDuration >= 600) {
|
||||
stopRealtimeTracking();
|
||||
alert(i18n.t('error.auto_pause'));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `${API_BASE}/history/recent?since_timestamp=${encodeURIComponent(lastTimestamp)}&data_type=logs&limit=50`;
|
||||
@@ -1933,7 +1938,7 @@
|
||||
}
|
||||
realtimeTracking = false;
|
||||
const btn = document.getElementById('realtime-btn');
|
||||
btn.textContent = '实时追踪: 关';
|
||||
btn.textContent = i18n.t('btn.realtime_off');
|
||||
btn.className = 'btn btn-secondary';
|
||||
}
|
||||
|
||||
@@ -1959,14 +1964,14 @@
|
||||
if (!select) return;
|
||||
|
||||
const templates = getTemplates();
|
||||
select.innerHTML = '<option value="">已保存查询...</option>' +
|
||||
select.innerHTML = '<option value="">' + i18n.t('template.saved_queries') + '</option>' +
|
||||
templates.map((t, i) => `<option value="${i}">${t.name}</option>`).join('');
|
||||
}
|
||||
|
||||
function saveQueryTemplate() {
|
||||
const templates = getTemplates();
|
||||
if (templates.length >= 10) {
|
||||
alert('最多保存10个模板,请先删除部分模板');
|
||||
alert(i18n.t('error.max_templates'));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1992,7 +1997,7 @@
|
||||
|
||||
templates.push(template);
|
||||
saveTemplates(templates);
|
||||
alert(`模板"${name}"已保存`);
|
||||
alert(i18n.t('success.template_saved', {name: name}));
|
||||
}
|
||||
|
||||
function loadQueryTemplate() {
|
||||
@@ -2128,7 +2133,7 @@
|
||||
if (!tbody) return;
|
||||
|
||||
if (historyQueryData.http.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="no-data">暂无数据</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
sortedHttpData = [];
|
||||
return;
|
||||
}
|
||||
@@ -2165,7 +2170,7 @@
|
||||
if (!tbody) return;
|
||||
|
||||
if (historyQueryData.outbound.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="no-data">暂无数据</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
sortedOutboundData = [];
|
||||
return;
|
||||
}
|
||||
@@ -2202,7 +2207,7 @@
|
||||
if (!tbody) return;
|
||||
|
||||
if (historyQueryData.logs.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="no-data">暂无数据</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
|
||||
sortedLogsData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
+164
-158
@@ -3,30 +3,36 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MyAPI 系统监控面板</title>
|
||||
<title data-i18n-page-title="page.title">MyAPI 系统监控面板</title>
|
||||
<link rel="icon" href="/static/swagger/favicon.png" type="image/png">
|
||||
<link rel="stylesheet" href="/static/monitor/css/monitor.css">
|
||||
<link rel="stylesheet" href="/static/monitor/lib/prism.min.css">
|
||||
<script src="/static/monitor/lib/chart.min.js"></script>
|
||||
<script src="/static/monitor/lib/prism.min.js"></script>
|
||||
<script src="/static/monitor/lib/prism-json.min.js"></script>
|
||||
<script src="/static/lib/i18n/i18n.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<nav class="nav-menu">
|
||||
<a href="#" class="nav-item active" data-page="overview">📊 Overview</a>
|
||||
<a href="#" class="nav-item" data-page="database">🗃️ 数据库<span id="database-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="event-helpers">☎️ 事件处理</a>
|
||||
<a href="#" class="nav-item" data-page="scheduler">⏰ 定时任务</a>
|
||||
<a href="#" class="nav-item" data-page="api-requests">📥 接收请求<span id="api-requests-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="outbound-requests">📤 发送请求<span id="outbound-requests-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="logs">📋 日志<span id="logs-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item active" data-page="overview" data-i18n="nav.overview">📊 Overview</a>
|
||||
<a href="#" class="nav-item" data-page="database" data-i18n="nav.database">🗃️ 数据库<span id="database-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="event-helpers" data-i18n="nav.events">☎️ 事件处理</a>
|
||||
<a href="#" class="nav-item" data-page="scheduler" data-i18n="nav.scheduler">⏰ 定时任务</a>
|
||||
<a href="#" class="nav-item" data-page="api-requests" data-i18n="nav.http_requests">📥 接收请求<span id="api-requests-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="outbound-requests" data-i18n="nav.outbound_requests">📤 发送请求<span id="outbound-requests-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
<a href="#" class="nav-item" data-page="logs" data-i18n="nav.logs">📋 日志<span id="logs-badge" class="nav-badge" style="display: none;"></span></a>
|
||||
</nav>
|
||||
<div class="header-info">
|
||||
<span id="status-indicator" class="status healthy">● 系统正常</span>
|
||||
<span id="last-update">最后更新 --</span>
|
||||
<button id="refresh-btn" class="btn btn-primary" onclick="refreshAll()">刷新</button>
|
||||
<select id="lang-selector" class="lang-selector" onchange="i18n.switchLanguage(this.value)">
|
||||
<option value="zh-CN">🇨🇳 中文</option>
|
||||
<option value="en-US">🇺🇸 English</option>
|
||||
<option value="de-DE">🇩🇪 Deutsch</option>
|
||||
</select>
|
||||
<span id="status-indicator" class="status healthy" data-i18n="status.healthy">● 系统正常</span>
|
||||
<span id="last-update"><span data-i18n="other.last_update">最后更新</span> --</span>
|
||||
<button id="refresh-btn" class="btn btn-primary" onclick="refreshAll()" data-i18n="btn.refresh">刷新</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -35,20 +41,20 @@
|
||||
<!-- 资源监控卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>资源使用</h2>
|
||||
<span class="badge" id="resource-badge">运行中</span>
|
||||
<h2 data-i18n="card.resource">资源使用</h2>
|
||||
<span class="badge" id="resource-badge" data-i18n="status.running">运行中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">CPU 使用率</div>
|
||||
<div class="metric-label" data-i18n="metric.cpu">CPU 使用率</div>
|
||||
<div class="metric-value" id="cpu-value">--%</div>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="cpu-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">内存使用</div>
|
||||
<div class="metric-label" data-i18n="metric.memory">内存使用</div>
|
||||
<div class="metric-value" id="memory-value">
|
||||
<span id="memory-usage">-- MB</span>
|
||||
<span id="memory-percent" class="memory-percent">--%</span>
|
||||
@@ -58,11 +64,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">线程数</div>
|
||||
<div class="metric-label" data-i18n="metric.threads">线程数</div>
|
||||
<div class="metric-value" id="threads-value">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">运行时间</div>
|
||||
<div class="metric-label" data-i18n="metric.uptime">运行时间</div>
|
||||
<div class="metric-value" id="uptime-value">--</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,21 +81,21 @@
|
||||
<!-- 数据库状态卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>账套状态</h2>
|
||||
<span class="badge" id="db-badge">检查中</span>
|
||||
<h2 data-i18n="card.db_status">账套状态</h2>
|
||||
<span class="badge" id="db-badge" data-i18n="status.checking">检查中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="db-summary" id="db-summary">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">总连接数</span>
|
||||
<span class="summary-label" data-i18n="metric.total_connections">总连接数</span>
|
||||
<span class="summary-value" id="db-total">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">健康</span>
|
||||
<span class="summary-label" data-i18n="metric.healthy">健康</span>
|
||||
<span class="summary-value healthy" id="db-healthy">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">异常</span>
|
||||
<span class="summary-label" data-i18n="metric.unhealthy">异常</span>
|
||||
<span class="summary-value error" id="db-unhealthy">--</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,8 +108,8 @@
|
||||
<!-- 提醒信息卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>最近告警</h2>
|
||||
<button class="btn btn-small" onclick="clearAlerts()">清空</button>
|
||||
<h2 data-i18n="card.recent_alerts">最近告警</h2>
|
||||
<button class="btn btn-small" onclick="clearAlerts()" data-i18n="btn.clear">清空</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert-list" id="alert-list">
|
||||
@@ -115,21 +121,21 @@
|
||||
<!-- 接收请求卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>接收请求 <span style="font-size: 12px; color: #888; font-weight: normal;">(最近24小时)</span></h2>
|
||||
<span class="badge" id="http-badge">监控中</span>
|
||||
<h2 data-i18n="card.api_requests">接收请求</h2>
|
||||
<span class="badge" id="http-badge" data-i18n="status.running">监控中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="http-summary" id="http-summary">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">总请求数</span>
|
||||
<span class="summary-label" data-i18n="metric.requests_total">总请求数</span>
|
||||
<span class="summary-value" id="http-total">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">错误率</span>
|
||||
<span class="summary-label" data-i18n="metric.error_rate">错误率</span>
|
||||
<span class="summary-value" id="http-error-rate">--%</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">平均响应</span>
|
||||
<span class="summary-label" data-i18n="metric.avg_time">平均响应</span>
|
||||
<span class="summary-value" id="http-avg-time">--ms</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
@@ -143,21 +149,21 @@
|
||||
<!-- 发送请求卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>发送请求 <span style="font-size: 12px; color: #888; font-weight: normal;">(最近24小时)</span></h2>
|
||||
<span class="badge" id="outbound-badge">监控中</span>
|
||||
<h2 data-i18n="card.outbound_requests">发送请求</h2>
|
||||
<span class="badge" id="outbound-badge" data-i18n="status.running">监控中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="outbound-summary" id="outbound-summary">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">总请求数</span>
|
||||
<span class="summary-label" data-i18n="metric.requests_total">总请求数</span>
|
||||
<span class="summary-value" id="outbound-total">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">错误率</span>
|
||||
<span class="summary-label" data-i18n="metric.error_rate">错误率</span>
|
||||
<span class="summary-value" id="outbound-error-rate">--%</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">平均响应</span>
|
||||
<span class="summary-label" data-i18n="metric.avg_time">平均响应</span>
|
||||
<span class="summary-value" id="outbound-avg">--ms</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -167,17 +173,17 @@
|
||||
<!-- 定时任务监控卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>定时任务</h2>
|
||||
<span class="badge" id="scheduler-badge">检查中</span>
|
||||
<h2 data-i18n="card.scheduler">定时任务</h2>
|
||||
<span class="badge" id="scheduler-badge" data-i18n="status.checking">检查中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="scheduler-info" id="scheduler-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">调度器状态</span>
|
||||
<span class="info-label" data-i18n="metric.scheduler_status">调度器状态</span>
|
||||
<span class="info-value" id="scheduler-status">--</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">任务数量</span>
|
||||
<span class="info-label" data-i18n="metric.job_count">任务数量</span>
|
||||
<span class="info-value" id="job-count">--</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -190,8 +196,8 @@
|
||||
<!-- 数据库详细信息卡片 -->
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>MySQL</h2>
|
||||
<span class="badge" id="db-detail-badge">检查中</span>
|
||||
<h2 data-i18n="card.mysql">MySQL</h2>
|
||||
<span class="badge" id="db-detail-badge" data-i18n="status.checking">检查中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- 数据库详细信息 -->
|
||||
@@ -199,17 +205,17 @@
|
||||
<table class="db-detail-table" id="db-detail-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>账套名称</th>
|
||||
<th>连接状态</th>
|
||||
<th>最后检查</th>
|
||||
<th>当前连接</th>
|
||||
<th>最大连接</th>
|
||||
<th>最小连接</th>
|
||||
<th>空闲连接</th>
|
||||
<th>使用中连接</th>
|
||||
<th>使用率</th>
|
||||
<th>处理记录</th>
|
||||
<th>次数</th>
|
||||
<th data-i18n="col.db_name">账套名称</th>
|
||||
<th data-i18n="metric.connection_status">连接状态</th>
|
||||
<th data-i18n="col.last_check">最后检查</th>
|
||||
<th data-i18n="col.current_connections">当前连接</th>
|
||||
<th data-i18n="col.max_connections">最大连接</th>
|
||||
<th data-i18n="col.min_connections">最小连接</th>
|
||||
<th data-i18n="col.idle_connections">空闲连接</th>
|
||||
<th data-i18n="col.used_connections">使用中连接</th>
|
||||
<th data-i18n="col.usage">使用率</th>
|
||||
<th data-i18n="col.processed_records">处理记录</th>
|
||||
<th data-i18n="col.count">次数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="db-detail-tbody">
|
||||
@@ -223,62 +229,62 @@
|
||||
<!-- Redis 监控卡片 -->
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>Redis</h2>
|
||||
<span class="badge" id="redis-badge">检查中</span>
|
||||
<h2 data-i18n="card.redis">Redis</h2>
|
||||
<span class="badge" id="redis-badge" data-i18n="status.checking">检查中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="redis-metrics-grid">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">连接状态</div>
|
||||
<div class="metric-label" data-i18n="metric.connection_status">连接状态</div>
|
||||
<div class="metric-value" id="redis-status">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">主机</div>
|
||||
<div class="metric-label" data-i18n="metric.host">主机</div>
|
||||
<div class="metric-value" id="redis-host">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">端口</div>
|
||||
<div class="metric-label" data-i18n="metric.port">端口</div>
|
||||
<div class="metric-value" id="redis-port">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">数据库</div>
|
||||
<div class="metric-label" data-i18n="metric.database">数据库</div>
|
||||
<div class="metric-value" id="redis-db">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="redis-pool-metrics">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">使用连接数</div>
|
||||
<div class="metric-label" data-i18n="metric.used_connections">使用连接数</div>
|
||||
<div class="metric-value" id="redis-connections-used">0</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">最大连接数</div>
|
||||
<div class="metric-label" data-i18n="metric.max_connections">最大连接数</div>
|
||||
<div class="metric-value" id="redis-connections-max">0</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">连接使用率</div>
|
||||
<div class="metric-label" data-i18n="metric.connection_usage">连接使用率</div>
|
||||
<div class="metric-value" id="redis-connection-usage">0%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="redis-buffer-metrics">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">缓冲大小</div>
|
||||
<div class="metric-label" data-i18n="metric.buffer_size">缓冲大小</div>
|
||||
<div class="metric-value" id="redis-buffer-size">0</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">缓冲阈值</div>
|
||||
<div class="metric-label" data-i18n="metric.buffer_threshold">缓冲阈值</div>
|
||||
<div class="metric-value" id="redis-buffer-threshold">0</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">缓冲使用率</div>
|
||||
<div class="metric-label" data-i18n="metric.buffer_usage">缓冲使用率</div>
|
||||
<div class="metric-value" id="redis-buffer-usage">0%</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<h3>Redis 连接池使用情况</h3>
|
||||
<h3 data-i18n="chart.redis_connections">Redis 连接池使用情况</h3>
|
||||
<canvas id="redis-connections-chart"></canvas>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<h3>Redis 缓冲大小变化</h3>
|
||||
<h3 data-i18n="chart.redis_buffer">Redis 缓冲大小变化</h3>
|
||||
<canvas id="redis-buffer-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
@@ -289,7 +295,7 @@
|
||||
<main class="main-content page-content" id="page-api-requests">
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>接收请求记录</h2>
|
||||
<h2 data-i18n="card.http_requests_log">接收请求记录</h2>
|
||||
<div class="card-actions">
|
||||
<div class="date-selector" style="display: none;">
|
||||
<label for="api-date-picker">选择日期:</label>
|
||||
@@ -300,11 +306,11 @@
|
||||
<label>
|
||||
<input type="checkbox" id="show-api-internal-requests" onchange="toggleAPIIntternalRequests()">
|
||||
<span class="toggle-slider"></span>
|
||||
显示内部请求
|
||||
<span data-i18n="other.show_internal">显示内部请求</span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-sm" onclick="refreshAPIRequests()">刷新</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="resetAPIStats()">重置统计</button>
|
||||
<button class="btn btn-sm" onclick="refreshAPIRequests()" data-i18n="btn.refresh">刷新</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="resetAPIStats()" data-i18n="btn.reset_stats">重置统计</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -312,15 +318,15 @@
|
||||
<table class="api-requests-table" id="api-requests-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>时间戳</th>
|
||||
<th>方法</th>
|
||||
<th>端点</th>
|
||||
<th>查询参数</th>
|
||||
<th>状态码</th>
|
||||
<th>响应时间</th>
|
||||
<th>客户端IP</th>
|
||||
<th>错误信息</th>
|
||||
<th data-i18n="col.index">序号</th>
|
||||
<th data-i18n="col.timestamp">时间戳</th>
|
||||
<th data-i18n="col.method">方法</th>
|
||||
<th data-i18n="col.path">端点</th>
|
||||
<th data-i18n="col.query_params">查询参数</th>
|
||||
<th data-i18n="col.status">状态码</th>
|
||||
<th data-i18n="col.duration">响应时间</th>
|
||||
<th data-i18n="col.client_ip">客户端IP</th>
|
||||
<th data-i18n="col.error_message">错误信息</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="api-requests-tbody">
|
||||
@@ -375,9 +381,9 @@
|
||||
<main class="main-content page-content" id="page-scheduler">
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>定时任务详情</h2>
|
||||
<span class="badge" id="scheduler-detail-badge">检查中</span>
|
||||
<button class="btn btn-small" onclick="refreshSchedulerPage()">刷新</button>
|
||||
<h2 data-i18n="card.scheduler_detail">定时任务详情</h2>
|
||||
<span class="badge" id="scheduler-detail-badge" data-i18n="status.checking">检查中</span>
|
||||
<button class="btn btn-small" onclick="refreshSchedulerPage()" data-i18n="btn.refresh">刷新</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="scheduler-detail-grid" id="scheduler-detail-grid">
|
||||
@@ -391,7 +397,7 @@
|
||||
<main class="main-content page-content" id="page-outbound-requests">
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>发送请求记录</h2>
|
||||
<h2 data-i18n="card.outbound_requests_log">发送请求记录</h2>
|
||||
<div class="card-actions">
|
||||
<div class="date-selector" style="display: none;">
|
||||
<label for="outbound-date-picker">选择日期:</label>
|
||||
@@ -402,11 +408,11 @@
|
||||
<label>
|
||||
<input type="checkbox" id="show-internal-requests" onchange="toggleInternalRequests()">
|
||||
<span class="toggle-slider"></span>
|
||||
显示内部请求
|
||||
<span data-i18n="other.show_internal">显示内部请求</span>
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-sm" onclick="refreshOutboundRequests()">刷新</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="resetOutboundStats()">重置统计</button>
|
||||
<button class="btn btn-sm" onclick="refreshOutboundRequests()" data-i18n="btn.refresh">刷新</button>
|
||||
<button class="btn btn-sm btn-secondary" onclick="resetOutboundStats()" data-i18n="btn.reset_stats">重置统计</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -415,14 +421,14 @@
|
||||
<table class="outbound-requests-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>时间戳</th>
|
||||
<th>方法</th>
|
||||
<th>URL</th>
|
||||
<th>状态码</th>
|
||||
<th>响应时间</th>
|
||||
<th>模块</th>
|
||||
<th>错误信息</th>
|
||||
<th data-i18n="col.index">序号</th>
|
||||
<th data-i18n="col.timestamp">时间戳</th>
|
||||
<th data-i18n="col.method">方法</th>
|
||||
<th data-i18n="col.url">URL</th>
|
||||
<th data-i18n="col.status">状态码</th>
|
||||
<th data-i18n="col.duration">响应时间</th>
|
||||
<th data-i18n="col.module">模块</th>
|
||||
<th data-i18n="col.error_message">错误信息</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="outbound-requests-table">
|
||||
@@ -441,23 +447,23 @@
|
||||
<div class="card-header">
|
||||
<h2>Warning & Error</h2>
|
||||
<div class="log-page-controls">
|
||||
<button class="btn btn-primary" onclick="window.open('/monitor/live-logs', '_blank')">实时日志</button>
|
||||
<button class="btn btn-primary" onclick="window.open('/monitor/history-logs', '_blank')">历史查询</button>
|
||||
<button class="btn btn-primary" onclick="window.open('/monitor/live-logs', '_blank')" data-i18n="btn.live_logs">实时日志</button>
|
||||
<button class="btn btn-primary" onclick="window.open('/monitor/history-logs', '_blank')" data-i18n="btn.history_query">历史查询</button>
|
||||
<div class="toggle-switch">
|
||||
<label>
|
||||
<input type="checkbox" id="show-read-logs" onchange="toggleReadLogs()">
|
||||
<span class="toggle-slider"></span>
|
||||
显示已读
|
||||
<span data-i18n="other.show_read">显示已读</span>
|
||||
</label>
|
||||
</div>
|
||||
<select id="log-page-level" onchange="fetchLogsPage()">
|
||||
<option value="">全部日志</option>
|
||||
<option value="" data-i18n="filter.all_logs">全部日志</option>
|
||||
<option value="error">错误日志</option>
|
||||
<option value="warning">警告日志</option>
|
||||
</select>
|
||||
<button class="btn btn-small" onclick="markAllLogsAsRead()">标记全部已读</button>
|
||||
<button class="btn btn-small" onclick="clearReadStatus()">清空已读状态</button>
|
||||
<button class="btn btn-small" onclick="refreshLogsPage()">刷新</button>
|
||||
<button class="btn btn-small" onclick="markAllLogsAsRead()" data-i18n="btn.mark_all_read">标记全部已读</button>
|
||||
<button class="btn btn-small" onclick="clearReadStatus()" data-i18n="btn.clear_read_status">清空已读状态</button>
|
||||
<button class="btn btn-small" onclick="refreshLogsPage()" data-i18n="btn.refresh">刷新</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -465,12 +471,12 @@
|
||||
<table class="logs-table" id="logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>时间</th>
|
||||
<th>级别</th>
|
||||
<th>模块</th>
|
||||
<th>消息</th>
|
||||
<th>已读</th>
|
||||
<th data-i18n="col.index">序号</th>
|
||||
<th data-i18n="col.time">时间</th>
|
||||
<th data-i18n="col.level">级别</th>
|
||||
<th data-i18n="col.module">模块</th>
|
||||
<th data-i18n="col.message">消息</th>
|
||||
<th data-i18n="col.is_read">已读</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logs-tbody">
|
||||
@@ -487,55 +493,55 @@
|
||||
<!-- 事件监听卡片 -->
|
||||
<section class="card full-width">
|
||||
<div class="card-header">
|
||||
<h2>事件监听</h2>
|
||||
<h2 data-i18n="card.event_listener">事件监听</h2>
|
||||
<div class="events-actions">
|
||||
<button class="btn btn-small" onclick="refreshEventStats()">刷新</button>
|
||||
<button class="btn btn-small" onclick="flushAllEvents()">立即刷新所有</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="resetEventStats()">重置统计</button>
|
||||
<button class="btn btn-small" onclick="refreshEventStats()" data-i18n="btn.refresh">刷新</button>
|
||||
<button class="btn btn-small" onclick="flushAllEvents()" data-i18n="btn.flush_all">立即刷新所有</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="resetEventStats()" data-i18n="btn.reset_stats">重置统计</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="events-summary" id="events-summary">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">总接收事件</span>
|
||||
<span class="summary-label" data-i18n="metric.events_received">总接收事件</span>
|
||||
<span class="summary-value" id="events-total-received">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">已处理事件</span>
|
||||
<span class="summary-label" data-i18n="metric.events_processed">已处理事件</span>
|
||||
<span class="summary-value healthy" id="events-total-processed">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">中断处理</span>
|
||||
<span class="summary-label" data-i18n="metric.events_interrupted">中断处理</span>
|
||||
<span class="summary-value error" id="events-total-failed">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">待处理</span>
|
||||
<span class="summary-label" data-i18n="metric.pending">待处理</span>
|
||||
<span class="summary-value" id="events-total-pending">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">整体成功率</span>
|
||||
<span class="summary-label" data-i18n="metric.overall_success_rate">整体成功率</span>
|
||||
<span class="summary-value" id="events-success-rate">--%</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">活跃事件类型</span>
|
||||
<span class="summary-label" data-i18n="metric.active_event_types">活跃事件类型</span>
|
||||
<span class="summary-value" id="events-active-types">--</span>
|
||||
</div>
|
||||
<!-- 背压监控 -->
|
||||
<div class="summary-item" id="backpressure-summary" style="display: none;">
|
||||
<span class="summary-label">背压状态</span>
|
||||
<span class="summary-label" data-i18n="metric.backpressure_status">背压状态</span>
|
||||
<span class="summary-value" id="backpressure-status">正常</span>
|
||||
</div>
|
||||
<div class="summary-item" id="backpressure-pending" style="display: none;">
|
||||
<span class="summary-label">待处理事件</span>
|
||||
<span class="summary-label" data-i18n="metric.backpressure_pending">待处理事件</span>
|
||||
<span class="summary-value" id="backpressure-pending-count">--</span>
|
||||
</div>
|
||||
<div class="summary-item" id="backpressure-percent" style="display: none;">
|
||||
<span class="summary-label">背压使用率</span>
|
||||
<span class="summary-label" data-i18n="metric.backpressure_usage">背压使用率</span>
|
||||
<span class="summary-value" id="backpressure-usage">--%</span>
|
||||
</div>
|
||||
<!-- 事件循环健康状态 -->
|
||||
<div class="summary-item" id="event-loop-status" style="display: none;">
|
||||
<span class="summary-label">事件循环状态</span>
|
||||
<span class="summary-label" data-i18n="metric.event_loop_status">事件循环状态</span>
|
||||
<span class="summary-value" id="event-loop-health">正常</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -543,17 +549,17 @@
|
||||
<table class="events-table" id="events-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>描述</th>
|
||||
<th>总接收</th>
|
||||
<th>待处理</th>
|
||||
<th>已处理</th>
|
||||
<th>中断</th>
|
||||
<th>完成率</th>
|
||||
<th>平均延迟</th>
|
||||
<th>操作</th>
|
||||
<th>最后动作</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
<th data-i18n="col.description">描述</th>
|
||||
<th data-i18n="col.total_received">总接收</th>
|
||||
<th data-i18n="col.pending">待处理</th>
|
||||
<th data-i18n="col.processed">已处理</th>
|
||||
<th data-i18n="col.interrupted">中断</th>
|
||||
<th data-i18n="col.completion_rate">完成率</th>
|
||||
<th data-i18n="col.avg_latency">平均延迟</th>
|
||||
<th data-i18n="col.operation">操作</th>
|
||||
<th data-i18n="col.last_action">最后动作</th>
|
||||
<th data-i18n="col.status">状态</th>
|
||||
<th data-i18n="col.operation">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="events-tbody">
|
||||
@@ -571,21 +577,21 @@
|
||||
<!-- 回调跟踪器卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>回调跟踪器</h2>
|
||||
<span class="badge" id="callback-badge">监控中</span>
|
||||
<h2 data-i18n="card.callback_tracker">回调跟踪器</h2>
|
||||
<span class="badge" id="callback-badge" data-i18n="status.monitoring">监控中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">待处理回调</div>
|
||||
<div class="metric-label" data-i18n="metric.pending_callbacks">待处理回调</div>
|
||||
<div class="metric-value" id="callback-pending">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">最大重试次数</div>
|
||||
<div class="metric-label" data-i18n="metric.max_retries">最大重试次数</div>
|
||||
<div class="metric-value" id="callback-max-retries">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">待处理重试</div>
|
||||
<div class="metric-label" data-i18n="metric.pending_retries">待处理重试</div>
|
||||
<div class="metric-value" id="callback-pending-retries">--</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -595,25 +601,25 @@
|
||||
<!-- 事件去重器卡片 -->
|
||||
<section class="card">
|
||||
<div class="card-header">
|
||||
<h2>事件去重器</h2>
|
||||
<span class="badge" id="deduplicator-badge">监控中</span>
|
||||
<h2 data-i18n="card.event_deduplicator">事件去重器</h2>
|
||||
<span class="badge" id="deduplicator-badge" data-i18n="status.monitoring">监控中</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">总存储条目</div>
|
||||
<div class="metric-label" data-i18n="metric.total_entries">总存储条目</div>
|
||||
<div class="metric-value" id="deduplicator-total">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">活跃项目</div>
|
||||
<div class="metric-label" data-i18n="metric.active_items">活跃项目</div>
|
||||
<div class="metric-value" id="deduplicator-active">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">TTL (秒)</div>
|
||||
<div class="metric-label" data-i18n="metric.ttl_seconds">TTL (秒)</div>
|
||||
<div class="metric-value" id="deduplicator-ttl">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">最大条目</div>
|
||||
<div class="metric-label" data-i18n="metric.max_entries">最大条目</div>
|
||||
<div class="metric-value" id="deduplicator-max">--</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -627,40 +633,40 @@
|
||||
<div class="card-header">
|
||||
<h2>Dead Letter Queue</h2>
|
||||
<div class="dead-letter-actions">
|
||||
<button class="btn btn-small" onclick="refreshDeadLetterStats()">刷新</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="clearDeadLetters()">清空</button>
|
||||
<button class="btn btn-small" onclick="refreshDeadLetterStats()" data-i18n="btn.refresh">刷新</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="clearDeadLetters()" data-i18n="btn.clear">清空</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">待写入事件</div>
|
||||
<div class="metric-label" data-i18n="metric.pending_events">待写入事件</div>
|
||||
<div class="metric-value" id="dlq-pending">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">总事件数</div>
|
||||
<div class="metric-label" data-i18n="metric.total_events">总事件数</div>
|
||||
<div class="metric-value" id="dlq-total">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">文件大小</div>
|
||||
<div class="metric-label" data-i18n="metric.file_size">文件大小</div>
|
||||
<div class="metric-value" id="dlq-file-size">--</div>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<div class="metric-label">运行状态</div>
|
||||
<div class="metric-label" data-i18n="metric.running_status">运行状态</div>
|
||||
<div class="metric-value" id="dlq-running">--</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dead-letter-summary" id="dead-letter-summary" style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #e2e8f0;">
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">DL总数</span>
|
||||
<span class="summary-label" data-i18n="metric.dl_total">DL总数</span>
|
||||
<span class="summary-value" id="dead-letter-total">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">最近DL</span>
|
||||
<span class="summary-label" data-i18n="metric.recent_dl">最近DL</span>
|
||||
<span class="summary-value" id="dead-letter-recent">--</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span class="summary-label">处理成功率</span>
|
||||
<span class="summary-label" data-i18n="metric.process_success_rate">处理成功率</span>
|
||||
<span class="summary-value" id="dead-letter-success-rate">--%</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -668,13 +674,13 @@
|
||||
<table class="dead-letter-table" id="dead-letter-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>事件类型</th>
|
||||
<th>时间</th>
|
||||
<th>数据库</th>
|
||||
<th>表</th>
|
||||
<th>错误信息</th>
|
||||
<th>操作</th>
|
||||
<th data-i18n="col.id">ID</th>
|
||||
<th data-i18n="col.event_type">事件类型</th>
|
||||
<th data-i18n="col.time">时间</th>
|
||||
<th data-i18n="col.database">数据库</th>
|
||||
<th data-i18n="col.table">表</th>
|
||||
<th data-i18n="col.error_message">错误信息</th>
|
||||
<th data-i18n="col.operation">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dead-letter-tbody">
|
||||
|
||||
+221
-184
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>实时日志 - 系统监控</title>
|
||||
<title data-i18n-page-title="page.live_logs">实时日志 - 系统监控</title>
|
||||
<link rel="icon" href="/static/swagger/favicon.png" type="image/png">
|
||||
<link rel="stylesheet" href="/static/monitor/css/monitor.css">
|
||||
<script src="/static/lib/i18n/i18n.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
@@ -330,19 +331,23 @@
|
||||
<div class="live-logs-container">
|
||||
<div class="live-logs-header">
|
||||
<div class="live-logs-header-left">
|
||||
<h3>实时日志流</h3>
|
||||
<h3 data-i18n="page.live_logs">实时日志流</h3>
|
||||
<div class="connection-status">
|
||||
<div class="status-dot connecting" id="statusDot"></div>
|
||||
<span id="statusText">连接中...</span>
|
||||
<span id="statusText" data-i18n="status.connecting">连接中...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-controls">
|
||||
<select id="lang-selector" class="lang-selector" onchange="i18n.switchLanguage(this.value)" style="margin-right: 12px;">
|
||||
<option value="zh-CN">🇨🇳 中文</option>
|
||||
<option value="en-US">🇺🇸 English</option>
|
||||
<option value="de-DE">🇩🇪 Deutsch</option>
|
||||
</select>
|
||||
<div class="header-control-group">
|
||||
<label>级别:</label>
|
||||
<label data-i18n="filter.level">级别:</label>
|
||||
<select id="levelFilter" onchange="applyFilters()">
|
||||
<option value="">全部</option>
|
||||
<!-- <option value="DEBUG">DEBUG</option> -->
|
||||
<option value="" data-i18n="filter.level">全部</option>
|
||||
<option value="INFO" selected>INFO</option>
|
||||
<option value="WARNING">WARNING</option>
|
||||
<option value="ERROR">ERROR</option>
|
||||
@@ -351,11 +356,11 @@
|
||||
</div>
|
||||
|
||||
<div class="header-control-group">
|
||||
<input type="text" id="searchInput" placeholder="搜索日志..." onkeyup="handleSearch(event)">
|
||||
<input type="text" id="searchInput" data-i18n-placeholder="filter.keyword_placeholder" placeholder="搜索日志..." onkeyup="handleSearch(event)">
|
||||
</div>
|
||||
|
||||
<div class="header-control-group">
|
||||
<label>自动滚动</label>
|
||||
<label data-i18n="other.auto_scroll">自动滚动</label>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="autoScroll" checked onchange="toggleAutoScroll()">
|
||||
<span class="toggle-slider"></span>
|
||||
@@ -363,17 +368,17 @@
|
||||
</div>
|
||||
|
||||
<button class="btn btn-secondary" onclick="togglePause()">
|
||||
<span id="pauseBtnText">暂停</span>
|
||||
<span id="pauseBtnText" data-i18n="btn.pause">暂停</span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-danger" onclick="clearLogs()">清空</button>
|
||||
<button class="btn btn-danger" onclick="clearLogs()" data-i18n="btn.clear">清空</button>
|
||||
|
||||
<button class="btn btn-primary" onclick="openInNewWindow()">新窗口</button>
|
||||
<button class="btn btn-primary" onclick="openInNewWindow()" data-i18n="btn.new_window">新窗口</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="log-stats">
|
||||
<span>总日志: <strong id="totalCount">0</strong></span>
|
||||
<span data-i18n="metric.total">总日志: <strong id="totalCount">0</strong></span>
|
||||
<span>DEBUG: <span class="level-count DEBUG" id="debugCount">0</span></span>
|
||||
<span>INFO: <span class="level-count INFO" id="infoCount">0</span></span>
|
||||
<span>WARNING: <span class="level-count WARNING" id="warningCount">0</span></span>
|
||||
@@ -382,7 +387,7 @@
|
||||
</div>
|
||||
|
||||
<div class="logs-container" id="logsContainer">
|
||||
<div class="no-logs" id="noLogs">
|
||||
<div class="no-logs" id="noLogs" data-i18n="other.waiting_logs">
|
||||
正在等待日志数据...
|
||||
</div>
|
||||
</div>
|
||||
@@ -544,7 +549,7 @@
|
||||
if (isDomReady) {
|
||||
const statusText = document.getElementById('statusText');
|
||||
if (statusText) {
|
||||
statusText.textContent = '连接失败,请刷新页面';
|
||||
statusText.textContent = i18n.t('error.connection_failed');
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -600,14 +605,14 @@
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
dot.classList.add('connected');
|
||||
text.textContent = '已连接';
|
||||
text.textContent = i18n.t('status.connected');
|
||||
break;
|
||||
case 'connecting':
|
||||
dot.classList.add('connecting');
|
||||
text.textContent = '连接中...';
|
||||
text.textContent = i18n.t('status.connecting');
|
||||
break;
|
||||
case 'disconnected':
|
||||
text.textContent = '已断开 (5秒后重连)';
|
||||
text.textContent = i18n.t('status.disconnected') + ' (' + i18n.t('other.auto_reconnect') + ')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -722,7 +727,7 @@
|
||||
}
|
||||
|
||||
if (logs.length === 0) {
|
||||
container.innerHTML = '<div class="no-logs" id="noLogs">正在等待日志数据...</div>';
|
||||
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('other.waiting_logs') + '</div>';
|
||||
lastRenderIndex = 0;
|
||||
return;
|
||||
}
|
||||
@@ -738,7 +743,7 @@
|
||||
});
|
||||
|
||||
if (filteredLogs.length === 0) {
|
||||
container.innerHTML = '<div class="no-logs" id="noLogs">没有匹配的日志</div>';
|
||||
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('other.no_matching_logs') + '</div>';
|
||||
lastRenderIndex = 0;
|
||||
return;
|
||||
}
|
||||
@@ -811,7 +816,7 @@
|
||||
const autoScrollIndicator = document.getElementById('autoScrollIndicator');
|
||||
|
||||
if (pauseBtnText) {
|
||||
pauseBtnText.textContent = isPaused ? '继续' : '暂停';
|
||||
pauseBtnText.textContent = isPaused ? i18n.t('btn.resume') : i18n.t('btn.pause');
|
||||
}
|
||||
if (autoScrollIndicator) {
|
||||
autoScrollIndicator.classList.toggle('show', isPaused);
|
||||
@@ -856,7 +861,7 @@
|
||||
updateStats();
|
||||
const logsContainer = document.getElementById('logsContainer');
|
||||
if (logsContainer) {
|
||||
logsContainer.innerHTML = '<div class="no-logs">日志已清空</div>';
|
||||
logsContainer.innerHTML = '<div class="no-logs">' + i18n.t('success.logs_cleared') + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user