优化语言补丁的命名空间,优化历史日志的查询性能

This commit is contained in:
2026-05-24 21:29:34 +08:00
parent ab2641876d
commit d6e4a6a089
10 changed files with 5183 additions and 1811 deletions
+154 -30
View File
@@ -4,6 +4,20 @@
提供监控相关的 API 端点
"""
import time
import json
import asyncio
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, HTTPException, WebSocket
from fastapi.responses import JSONResponse, StreamingResponse
from typing import Dict, Any, List, Optional
from tortoise import Tortoise
from .service import monitor_service
from .log_stream_service import log_stream_service
from .storage import request_storage, outbound_request_storage, system_log_storage
from .models import APIRequest, OutboundAPIRequest, SystemLog
from core.settings import TIMEZONE
from globalobjects import logger as log_config
import time
import json
import asyncio
@@ -1030,6 +1044,8 @@ async def get_history_by_time_range(
http_requests = await request_storage.get_requests_with_filters(
utc_start, utc_end, limit, filter_params
)
print(f"[DEBUG] HTTP查询: page={page}, page_size={page_size}, 返回{len(http_requests)}")
# 列表查询只返回基本字段,不返回request_body/response_body大字段(详情时懒加载)
result["http_requests"] = [{
"id": req.id,
"timestamp": req.timestamp.isoformat(),
@@ -1042,11 +1058,8 @@ async def get_history_by_time_range(
"user_agent": req.user_agent,
"is_slow": req.is_slow,
"is_error": req.is_error,
"error_message": req.error_message,
"request_body": req.request_body,
"response_body": req.response_body,
"request_headers": getattr(req, 'request_headers', None),
"response_headers": getattr(req, 'response_headers', None)
"error_message": req.error_message
# 不返回: request_body, response_body, request_headers, response_headers
} for req in http_requests]
# 查询发送请求
@@ -1054,6 +1067,8 @@ async def get_history_by_time_range(
outbound_requests = await outbound_request_storage.get_requests_with_filters(
utc_start, utc_end, limit, filter_params
)
print(f"[DEBUG] Outbound查询: page={page}, page_size={page_size}, 返回{len(outbound_requests)}")
# 列表查询只返回基本字段,不返回request_body/response_body大字段
result["outbound_requests"] = [{
"id": req.id,
"timestamp": req.timestamp.isoformat(),
@@ -1064,11 +1079,8 @@ async def get_history_by_time_range(
"module": req.module,
"is_slow": req.is_slow,
"is_error": req.is_error,
"error_message": req.error_message,
"request_body": req.request_body,
"response_body": req.response_body,
"request_headers": req.request_headers,
"response_headers": req.response_headers
"error_message": req.error_message
# 不返回: request_body, response_body, request_headers, response_headers
} for req in outbound_requests]
# 查询系统日志
@@ -1076,6 +1088,8 @@ async def get_history_by_time_range(
logs = await system_log_storage.get_logs_with_filters(
utc_start, utc_end, level, limit, filter_params
)
print(f"[DEBUG] Logs查询: page={page}, page_size={page_size}, 返回{len(logs)}")
# 列表查询不返回stack_trace大字段(详情时懒加载)
result["logs"] = [{
"id": log.id,
"timestamp": log.timestamp.isoformat(),
@@ -1083,18 +1097,25 @@ async def get_history_by_time_range(
"module": log.module,
"function": log.function,
"line_number": log.line_number,
"message": log.message,
"stack_trace": log.stack_trace
"message": log.message[:500] if log.message else None # 截断message,避免超长日志
# 不返回: stack_trace
} for log in logs]
# 计算分页元数据
# 统计总数(用于结果摘要显示)- 只执行一次count
result["stats"] = {
"http_count": await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_http else 0,
"outbound_count": await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_outbound else 0,
"logs_count": await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params) if should_query_logs else 0
}
# 计算分页元数据(复用stats的count结果)
total_count = 0
if should_query_http:
total_count = await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params)
total_count = result["stats"]["http_count"]
elif should_query_outbound:
total_count = await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params)
total_count = result["stats"]["outbound_count"]
elif should_query_logs:
total_count = await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params)
total_count = result["stats"]["logs_count"]
if total_count > 0:
total_pages = (total_count + page_size - 1) // page_size
@@ -1109,21 +1130,36 @@ async def get_history_by_time_range(
"end_index": min(page * page_size, total_count)
}
# 统计总数(用于结果摘要显示)
result["stats"] = {
"http_count": await request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_http else 0,
"outbound_count": await outbound_request_storage.count_requests_with_filters(utc_start, utc_end, filter_params) if should_query_outbound else 0,
"logs_count": await system_log_storage.count_logs_with_filters(utc_start, utc_end, level, filter_params) if should_query_logs else 0
}
# 统计日志级别分布(仅查询系统日志时)
# 统计日志级别分布(仅查询系统日志时)- 优化:使用GROUP BY聚合,避免查询所有记录
if should_query_logs and result["stats"]["logs_count"] > 0:
level_stats = {}
for lv in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
count = await system_log_storage.count_logs_with_filters(utc_start, utc_end, lv, filter_params)
if count > 0:
level_stats[lv] = count
result["stats"]["level_distribution"] = level_stats
try:
# 使用原生SQL的GROUP BY聚合,性能最优
from .models import SystemLog
from tortoise import connections
conn = connections.get("default")
# 构建SQL查询
sql = "SELECT level, COUNT(*) as count FROM system_logs WHERE 1=1"
params = []
if utc_start and utc_end:
sql += " AND timestamp >= $1 AND timestamp <= $2"
params.extend([utc_start, utc_end])
if filter_params and filter_params.get('module'):
sql += f" AND module IN ({','.join(['$'+str(i+3) for i in range(len(filter_params['module']))])})"
params.extend(filter_params['module'])
sql += " GROUP BY level"
# 执行查询
rows = await conn.execute_query(sql, params)
level_stats = {row['level']: row['count'] for row in rows[1] if row.get('level')}
result["stats"]["level_distribution"] = level_stats
except Exception as e:
print(f"统计级别分布失败: {e}")
result["stats"]["level_distribution"] = {}
return result
@@ -1576,3 +1612,91 @@ async def export_history_data(
media_type="application/json",
headers={"Content-Disposition": f"attachment; filename={filename}"}
)
# ========== 详情懒加载端点 ==========
@router.get("/history/outbound/{request_id}")
async def get_outbound_request_detail(request_id: int):
"""
获取发送请求详情(懒加载,包含request_body、response_body等大字段)
"""
try:
req = await OutboundAPIRequest.get_or_none(id=request_id)
if not req:
raise HTTPException(status_code=404, detail="请求不存在")
return {
"id": req.id,
"timestamp": req.timestamp.isoformat(),
"method": req.method,
"url": req.url,
"status_code": req.status_code,
"duration": req.duration * 1000,
"module": req.module,
"is_slow": req.is_slow,
"is_error": req.is_error,
"error_message": req.error_message,
"request_body": req.request_body,
"response_body": req.response_body,
"request_headers": req.request_headers,
"response_headers": req.response_headers
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
@router.get("/history/http/{request_id}")
async def get_http_request_detail(request_id: int):
"""
获取HTTP请求详情(懒加载,包含request_body、response_body等大字段)
"""
try:
req = await APIRequest.get_or_none(id=request_id)
if not req:
raise HTTPException(status_code=404, detail="请求不存在")
return {
"id": req.id,
"timestamp": req.timestamp.isoformat(),
"method": req.method,
"path": req.path,
"query_params": req.query_params,
"status_code": req.status_code,
"duration": req.response_time,
"client_ip": req.client_ip,
"user_agent": req.user_agent,
"is_slow": req.is_slow,
"is_error": req.is_error,
"error_message": req.error_message,
"request_body": req.request_body,
"response_body": req.response_body,
"request_headers": getattr(req, 'request_headers', None),
"response_headers": getattr(req, 'response_headers', None)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
@router.get("/history/log/{log_id}")
async def get_log_detail(log_id: int):
"""
获取系统日志详情(懒加载,包含stack_trace等大字段)
"""
try:
log = await SystemLog.get_or_none(id=log_id)
if not log:
raise HTTPException(status_code=404, detail="日志不存在")
return {
"id": log.id,
"timestamp": log.timestamp.isoformat(),
"level": log.level,
"module": log.module,
"function": log.function,
"line_number": log.line_number,
"message": log.message,
"stack_trace": log.stack_trace
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"查询详情失败: {str(e)}")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+431 -414
View File
@@ -4,497 +4,507 @@
*/
window.__i18n_de_DE__ = {
// ============ Seitentitel ============
'page.title': 'MyAPI Systemüberwachung',
'page.live_logs': 'Echtzeit-Logs - Systemüberwachung',
'page.history_logs': 'Protokollverlauf - Systemüberwachung',
'monitor.page.title': 'MyAPI Systemüberwachung',
'monitor.page.live_logs': 'Echtzeit-Logs - Systemüberwachung',
'monitor.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',
'monitor.nav.overview': '📊 Übersicht',
'monitor.nav.database': '🗃️ Datenbank',
'monitor.nav.events': '☎️ Ereignisse',
'monitor.nav.scheduler': '⏰ Planer',
'monitor.nav.http_requests': '📥 HTTP-Anfragen',
'monitor.nav.outbound_requests': '📤 Ausgehend',
'monitor.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',
'monitor.tab.overview': 'Übersicht',
'monitor.tab.database': 'Datenbank',
'monitor.tab.events': 'Ereignisse',
'monitor.tab.scheduler': 'Planer',
'monitor.tab.http': 'HTTP-Anfragen',
'monitor.tab.outbound': 'Ausgehend',
'monitor.tab.logs': 'Systemprotokolle',
'monitor.tab.timeline': 'Zeitachse',
'monitor.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',
'monitor.card.resource': 'Ressourcennutzung',
'monitor.card.db_status': 'Datenbankstatus',
'monitor.card.db_connections': 'Datenbankverbindungen',
'monitor.card.event_helpers': 'Ereignis-Helfer',
'monitor.card.scheduler': 'Aufgabenplaner',
'monitor.card.api_requests': 'HTTP-Anfragen',
'monitor.card.outbound_requests': 'Ausgehende Anfragen',
'monitor.card.redis': 'Redis-Status',
'monitor.card.event_listener': 'Ereignis-Listener',
'monitor.card.recent_alerts': 'Aktuelle Warnungen',
'monitor.card.mysql': 'MySQL',
'monitor.card.http_requests_log': 'HTTP-Anfragedatensätze',
'monitor.card.scheduler_detail': 'Planer-Details',
'monitor.card.outbound_requests_log': 'Ausgehende Anfragedatensätze',
'monitor.card.callback_tracker': 'Callback-Tracker',
'monitor.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',
'monitor.metric.cpu': 'CPU-Auslastung',
'monitor.metric.memory': 'Speichernutzung',
'monitor.metric.threads': 'Threads',
'monitor.metric.uptime': 'Laufzeit',
'monitor.metric.total_connections': 'Verbindungen',
'monitor.metric.healthy': 'Gesund',
'monitor.metric.unhealthy': 'Fehlerhaft',
'monitor.metric.degraded': 'Eingeschränkt',
'monitor.metric.total': 'Gesamt',
'monitor.metric.success': 'Erfolg',
'monitor.metric.failed': 'Fehler',
'monitor.metric.pending': 'Ausstehend',
'monitor.metric.active': 'Aktiv',
'monitor.metric.idle': 'Inaktiv',
'monitor.metric.unknown': 'Unbekannt',
'monitor.metric.warning': 'Warnung',
'monitor.metric.events_received': 'Empfangene Ereignisse',
'monitor.metric.events_processed': 'Verarbeitete Ereignisse',
'monitor.metric.events_failed': 'Fehlgeschlagene Ereignisse',
'monitor.metric.jobs_running': 'Laufende Aufgaben',
'monitor.metric.jobs_pending': 'Ausstehende Aufgaben',
'monitor.metric.requests_total': 'Anfragen Gesamt',
'monitor.metric.requests_slow': 'Langsame Anfragen',
'monitor.metric.requests_error': 'Fehlerhafte Anfragen',
'monitor.metric.error_rate': 'Fehlerquote',
'monitor.metric.avg_time': 'Durchschn. Antwort',
'monitor.metric.connection_status': 'Verbindungsstatus',
'monitor.metric.host': 'Host',
'monitor.metric.port': 'Port',
'monitor.metric.database': 'Datenbank',
'monitor.metric.used_connections': 'Verwendete Verbindungen',
'monitor.metric.max_connections': 'Max. Verbindungen',
'monitor.metric.connection_usage': 'Verbindungsnutzung',
'monitor.metric.buffer_size': 'Puffergröße',
'monitor.metric.buffer_threshold': 'Pufferschwellwert',
'monitor.metric.buffer_usage': 'Puffernutzung',
'monitor.metric.events_interrupted': 'Unterbrochen',
'monitor.metric.overall_success_rate': 'Gesamterfolgsquote',
'monitor.metric.active_event_types': 'Aktive Ereignistypen',
'monitor.metric.backpressure_status': 'Rückstau-Status',
'monitor.metric.backpressure_pending': 'Ausstehende Ereignisse',
'monitor.metric.backpressure_usage': 'Rückstau-Nutzung',
'monitor.metric.event_loop_status': 'Ereignisschleifen-Status',
'monitor.metric.pending_callbacks': 'Ausstehende Callbacks',
'monitor.metric.max_retries': 'Max. Wiederholungen',
'monitor.metric.pending_retries': 'Ausstehende Wiederholungen',
'monitor.metric.total_entries': 'Gesamteinträge',
'monitor.metric.active_items': 'Aktive Elemente',
'monitor.metric.ttl_seconds': 'TTL (Sekunden)',
'monitor.metric.max_entries': 'Max. Einträge',
'monitor.metric.dl_total': 'DL-Gesamt',
'monitor.metric.recent_dl': 'Aktuelle DL',
'monitor.metric.process_success_rate': 'Verarbeitungserfolgsquote',
'monitor.metric.scheduler_status': 'Planer-Status',
'monitor.metric.job_count': 'Aufgabenanzahl',
'monitor.metric.pending_events': 'Ausstehende Ereignisse',
'monitor.metric.total_events': 'Gesamtereignisse',
'monitor.metric.file_size': 'Dateigröße',
'monitor.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',
'monitor.status.healthy': '● System gesund',
'monitor.status.unhealthy': '● System fehlerhaft',
'monitor.status.degraded': '● System eingeschränkt',
'monitor.status.running': 'Läuft',
'monitor.status.checking': 'Prüfe',
'monitor.status.stopped': 'Gestoppt',
'monitor.status.loading': 'Laden...',
'monitor.status.querying': 'Abfrage...',
'monitor.status.exporting': 'Exportiere...',
'monitor.status.no_data': 'Keine Daten',
'monitor.status.connected': 'Verbunden',
'monitor.status.connecting': 'Verbinde...',
'monitor.status.disconnected': 'Getrennt',
'monitor.status.reconnecting': 'Verbinde neu...',
'monitor.status.paused': 'Pausiert',
'monitor.status.enabled': 'Aktiviert',
'monitor.status.disabled': 'Deaktiviert',
'monitor.status.no_db_connections': 'Keine Datenbankverbindungen',
'monitor.status.no_scheduler': 'Keine geplanten Aufgaben',
'monitor.status.no_http_requests': 'Keine HTTP-Anfragen',
'monitor.status.no_alerts': 'Keine Warnungen',
'monitor.status.no_logs': 'Keine Protokolle',
'monitor.status.no_events': 'Keine Ereignisstatistiken',
'monitor.status.no_api_requests': 'Keine API-Anfragedatensätze',
'monitor.status.no_outbound_requests': 'Keine ausgehenden Anfragedatensätze',
'monitor.status.no_dead_letters': 'Keine DL',
'monitor.status.partial_warnings': '● Teilweise Warnungen',
'monitor.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',
'monitor.btn.query': 'Abfrage',
'monitor.btn.reset': 'Zurücksetzen',
'monitor.btn.refresh': 'Aktualisieren',
'monitor.btn.export': 'Exportieren',
'monitor.btn.export_csv': 'CSV Export',
'monitor.btn.export_json': 'JSON Export',
'monitor.btn.detail': 'Details',
'monitor.btn.pause': 'Pause',
'monitor.btn.resume': 'Fortsetzen',
'monitor.btn.clear': 'Löschen',
'monitor.btn.save': 'Speichern',
'monitor.btn.delete': 'Löschen',
'monitor.btn.close': 'Schließen',
'monitor.btn.confirm': 'Bestätigen',
'monitor.btn.cancel': 'Abbrechen',
'monitor.btn.test': 'Testen',
'monitor.btn.apply': 'Anwenden',
'monitor.btn.filter': 'Filtern',
'monitor.btn.clear_all': 'Alle löschen',
'monitor.btn.realtime_on': 'Echtzeit: AN',
'monitor.btn.realtime_off': 'Echtzeit: AUS',
'monitor.btn.precise_locate': '± 60s',
'monitor.btn.precise_cancel': '± 60s - Klick zum Abbrechen',
'monitor.btn.reset_stats': 'Statistik zurücksetzen',
'monitor.btn.flush_all': 'Alle leeren',
'monitor.btn.new_window': 'Neues Fenster',
'monitor.btn.auto_scroll': 'Auto-Scroll',
'monitor.btn.live_logs': 'Echtzeit-Protokolle',
'monitor.btn.history_query': 'Verlaufsanfrage',
'monitor.btn.mark_all_read': 'Alle als gelesen markieren',
'monitor.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',
'monitor.col.index': '#',
'monitor.col.time': 'Zeit',
'monitor.col.level': 'Stufe',
'monitor.col.module': 'Modul',
'monitor.col.message': 'Nachricht',
'monitor.col.method': 'Methode',
'monitor.col.path': 'Pfad',
'monitor.col.url': 'URL',
'monitor.col.status': 'Status',
'monitor.col.duration': 'Dauer',
'monitor.col.client_ip': 'Client-IP',
'monitor.col.source': 'Quelle',
'monitor.col.function': 'Funktion',
'monitor.col.line': 'Zeile',
'monitor.col.operation': 'Aktion',
'monitor.col.count': 'Anzahl',
'monitor.col.avg_time': 'Durchschn. Zeit',
'monitor.col.max_time': 'Max. Zeit',
'monitor.col.db_name': 'DB-Name',
'monitor.col.last_check': 'Letzte Prüfung',
'monitor.col.current_connections': 'Aktuell',
'monitor.col.max_connections': 'Max',
'monitor.col.min_connections': 'Min',
'monitor.col.idle_connections': 'Inaktiv',
'monitor.col.used_connections': 'Verwendet',
'monitor.col.usage': 'Nutzung',
'monitor.col.processed_records': 'Verarbeitet',
'monitor.col.timestamp': 'Zeitstempel',
'monitor.col.query_params': 'Abfrageparameter',
'monitor.col.error_message': 'Fehlermeldung',
'monitor.col.url': 'URL',
'monitor.col.is_read': 'Gelesen',
'monitor.col.description': 'Beschreibung',
'monitor.col.total_received': 'Gesamt empfangen',
'monitor.col.pending': 'Ausstehend',
'monitor.col.processed': 'Verarbeitet',
'monitor.col.interrupted': 'Unterbrochen',
'monitor.col.completion_rate': 'Abschlussrate',
'monitor.col.avg_latency': 'Durchschn. Latenz',
'monitor.col.last_action': 'Letzte Aktion',
'monitor.col.id': 'ID',
'monitor.col.event_type': 'Ereignistyp',
'monitor.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',
'monitor.time.start_time': 'Startzeit',
'monitor.time.end_time': 'Endzeit',
'monitor.time.last_10m': 'Letzte 10 Min',
'monitor.time.last_30m': 'Letzte 30 Min',
'monitor.time.last_1h': 'Letzte Stunde',
'monitor.time.last_6h': 'Letzte 6 Stunden',
'monitor.time.last_24h': 'Letzte 24 Stunden',
'monitor.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',
'monitor.filter.level': 'Alle Stufen',
'monitor.filter.type': 'Alle Typen',
'monitor.filter.module': 'Modul',
'monitor.filter.keyword': 'Schlüsselwort',
'monitor.filter.method': 'Methode',
'monitor.filter.client_ip': 'Client-IP',
'monitor.filter.status_range': 'Statusbereich',
'monitor.filter.duration_range': 'Dauerbereich',
'monitor.filter.advanced': 'Erweiterte Filter',
'monitor.filter.collapse': 'Einklappen',
'monitor.filter.expand': 'Ausklappen',
'monitor.filter.clear': 'Löschen',
'monitor.filter.active': 'Aktive Filter',
'monitor.filter.module_placeholder': 'Modulname eingeben',
'monitor.filter.keyword_placeholder': 'Schlüsselwort eingeben',
'monitor.filter.ip_placeholder': 'Client-IP eingeben',
'monitor.filter.status_placeholder': 'z.B.: 400-499',
'monitor.filter.duration_placeholder': 'z.B.: >1000',
'monitor.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)',
'monitor.chart.request_trend': '📊 Anfragentrend',
'monitor.chart.level_distribution': '📊 Protokollstufen-Verteilung',
'monitor.chart.status_distribution': '📈 Statuscode-Verteilung',
'monitor.chart.slow_requests': '⏱️ Langsame Anfragen TOP10',
'monitor.chart.total_requests': 'Anfragen Gesamt',
'monitor.chart.error_count': 'Fehler',
'monitor.chart.slow_count': 'Langsame Anfragen',
'monitor.chart.log_count': 'Protokolle Gesamt',
'monitor.chart.no_slow': '✅ Keine langsamen Anfragen',
'monitor.chart.query_required': 'Bitte zuerst Abfrage ausführen',
'monitor.chart.load_failed': '❌ Diagramm-Daten laden fehlgeschlagen, bitte erneut versuchen',
'monitor.chart.no_data': 'Keine Daten',
'monitor.chart.no_trend': 'Keine Trenddaten',
'monitor.chart.redis_connections': 'Redis Verbindungspool-Nutzung',
'monitor.chart.redis_buffer': 'Redis Puffergrößen-Änderungen',
'monitor.chart.cpu': 'CPU',
'monitor.chart.memory': 'Speicher',
'monitor.chart.upload': 'Upload',
'monitor.chart.download': 'Download',
'monitor.chart.cpu_memory_axis': 'CPU / Speicher (%)',
'monitor.chart.network_axis': 'Netzwerk-Upload / Download (KB/s)',
'monitor.chart.used_connections': 'Verwendete Verbindungen',
'monitor.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',
'monitor.error.time_range_invalid': 'Startzeit kann nicht größer als Endzeit sein',
'monitor.error.time_range_required': 'Bitte Startzeit und Endzeit auswählen',
'monitor.error.query_failed': 'Abfrage fehlgeschlagen, bitte erneut versuchen',
'monitor.error.export_failed': 'Export fehlgeschlagen, bitte erneut versuchen',
'monitor.error.connection_failed': 'Verbindung fehlgeschlagen, bitte Seite aktualisieren',
'monitor.error.max_templates': 'Maximal 10 Vorlagen erlaubt, bitte zuerst einige löschen',
'monitor.error.auto_pause': 'Echtzeit-Verfolgung automatisch pausiert (über 10 Minuten)',
'monitor.error.load_failed': 'Laden fehlgeschlagen, bitte erneut versuchen',
'monitor.error.invalid_params': 'Ungültige Parameter',
'monitor.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',
'monitor.success.query_complete': 'Abfrage abgeschlossen',
'monitor.success.export_complete': 'Export abgeschlossen',
'monitor.success.template_saved': 'Vorlage "{name}" gespeichert',
'monitor.success.logs_cleared': 'Protokolle gelöscht',
'monitor.success.operation_success': 'Operation erfolgreich',
'monitor.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',
'monitor.timeline.title': 'Zeitachse',
'monitor.timeline.no_data': 'Keine Daten, bitte zuerst Abfrage ausführen',
'monitor.timeline.anomaly_detected': '⚠️ {count} Anomalien erkannt',
'monitor.timeline.error_burst': '{count} aufeinanderfolgende FEHLER-Protokolle',
'monitor.timeline.slow_anomaly': 'Langsame Anfrage Anomalie ({duration}ms > durchschn. {avg}ms×3)',
'monitor.timeline.duplicate_error': 'Duplizierter Fehler "{msg}" {count} mal aufgetreten',
'monitor.timeline.http_request': 'HTTP-Anfrage',
'monitor.timeline.outbound_request': 'Ausgehende Anfrage',
'monitor.timeline.system_log': 'Systemprotokoll',
'monitor.timeline.summary_title': '📊 Timeline-Statistiken',
'monitor.timeline.total_events': 'Gesamtereignisse',
'monitor.timeline.http_requests': 'HTTP-Anfragen',
'monitor.timeline.outbound_requests': 'Ausgehende Anfragen',
'monitor.timeline.system_logs': 'Systemprotokolle',
'monitor.timeline.errors': 'Fehler',
'monitor.timeline.warnings': 'Warnungen',
'monitor.timeline.slow_requests': 'Langsame Anfragen',
// ============ 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',
'monitor.pagination.page': 'Seite',
'monitor.pagination.of': 'von',
'monitor.pagination.items': 'Einträge',
'monitor.pagination.showing': 'Anzeigen',
'monitor.pagination.per_page': 'Pro Seite',
'monitor.pagination.first': 'Erste',
'monitor.pagination.last': 'Letzte',
'monitor.pagination.prev': 'Vorherige',
'monitor.pagination.next': 'Nächste',
'monitor.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',
'monitor.export.current_page': 'Aktuelle Seite exportieren',
'monitor.export.all_data': 'Alle Daten exportieren',
'monitor.export.format_csv': 'CSV-Format',
'monitor.export.format_json': 'JSON-Format',
'monitor.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',
'monitor.template.saved_queries': 'Gespeicherte Abfragen...',
'monitor.template.save': 'Abfrage speichern',
'monitor.template.manage': 'Vorlagen verwalten',
'monitor.template.name': 'Vorlagenname',
'monitor.template.description': 'Beschreibung',
'monitor.template.delete_confirm': 'Diese Vorlage löschen?',
'monitor.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',
'monitor.stats.time_range': 'Abfragezeitbereich',
'monitor.stats.http_requests': 'HTTP-Anfragen',
'monitor.stats.outbound_requests': 'Ausgehende Anfragen',
'monitor.stats.system_logs': 'Systemprotokolle',
'monitor.stats.level_distribution': 'Stufenverteilung',
'monitor.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',
'monitor.other.last_update': 'Letzte Aktualisierung',
'monitor.other.auto_reconnect': 'Neuverbindung in 5s',
'monitor.other.waiting_logs': 'Warte auf Protokolldaten...',
'monitor.other.no_matching_logs': 'Keine passenden Protokolle',
'monitor.other.precise_mode': 'Präziser Modus',
'monitor.other.saved_queries': 'Gespeicherte Abfragen...',
'monitor.other.all_time': 'Gesamte Zeit',
'monitor.other.linked_query': 'Verknüpfte Abfrage',
'monitor.other.seconds': 'Sekunden',
'monitor.other.minutes': 'Minuten',
'monitor.other.hours': 'Stunden',
'monitor.other.days': 'Tage',
'monitor.other.ms': 'ms',
'monitor.other.times': 'mal',
'monitor.other.view_detail': 'Details anzeigen',
'monitor.other.copy': 'Kopieren',
'monitor.other.expand': 'Ausklappen',
'monitor.other.collapse': 'Einklappen',
'monitor.other.show_internal': 'Intern anzeigen',
'monitor.other.show_read': 'Gelesen anzeigen',
// ============ Datum ============
'date.today': 'Heute',
'date.yesterday': 'Gestern',
'monitor.date.today': 'Heute',
'monitor.date.yesterday': 'Gestern',
// ============ Sprache ============
'lang.select': 'Sprache auswählen',
'lang.zh': '中文',
'lang.en': 'English',
'lang.de': 'Deutsch',
'monitor.lang.select': 'Sprache auswählen',
'monitor.lang.zh': '中文',
'monitor.lang.en': 'English',
'monitor.lang.de': 'Deutsch',
// ============ HTTP-Methoden ============
'method.get': 'GET',
'method.post': 'POST',
'method.put': 'PUT',
'method.delete': 'DELETE',
'method.patch': 'PATCH',
'monitor.method.get': 'GET',
'monitor.method.post': 'POST',
'monitor.method.put': 'PUT',
'monitor.method.delete': 'DELETE',
'monitor.method.patch': 'PATCH',
// ============ Protokollstufen ============
'level.debug': 'DEBUG',
'level.info': 'INFO',
'level.warning': 'WARNING',
'level.error': 'ERROR',
'level.critical': 'CRITICAL',
'monitor.level.debug': 'DEBUG',
'monitor.level.info': 'INFO',
'monitor.level.warning': 'WARNING',
'monitor.level.error': 'ERROR',
'monitor.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',
'monitor.scheduler.rule': 'Planungsregel',
'monitor.scheduler.last_run': 'Letzte Ausführung',
'monitor.scheduler.max_time': 'Max. Zeit',
'monitor.scheduler.never_run': 'Nie ausgeführt',
'monitor.scheduler.default': 'Standard',
'monitor.scheduler.running': 'Läuft',
'monitor.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',
'monitor.time.just_now': 'Gerade eben',
'monitor.time.minutes_ago': 'vor {n} Min.',
'monitor.time.hours_ago': 'vor {n} Stunden',
'monitor.time.days_ago': 'vor {n} Tagen',
'monitor.time.day_before_yesterday': 'Vorgestern',
// ============ Warnungstypen ============
'alert.warning': 'Warnung',
'alert.error': 'Fehler',
'alert.critical': 'Kritisch',
'alert.normal': 'Normal',
'monitor.alert.warning': 'Warnung',
'monitor.alert.error': 'Fehler',
'monitor.alert.critical': 'Kritisch',
'monitor.alert.normal': 'Normal',
// ============ Verbindungsstatus ============
'connection.failed': 'Verbindung fehlgeschlagen',
'connection.reconnecting': 'Verbinde neu',
'connection.connected': 'Verbunden',
'connection.disconnected': 'Getrennt',
'monitor.connection.failed': 'Verbindung fehlgeschlagen',
'monitor.connection.reconnecting': 'Verbinde neu',
'monitor.connection.connected': 'Verbunden',
'monitor.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?',
'monitor.monitor.paused': '● Überwachung pausiert (inaktiv)',
'monitor.monitor.panel': 'Überwachungstafel',
'monitor.monitor.last_update': 'Letzte Aktualisierung',
'monitor.monitor.reset_stats_confirm': 'Alle Ereignisstatistiken zurücksetzen?',
// ============ Hervorhebung ============
'highlight.on': 'Hervorgehoben',
'highlight.off': 'Hervorheben',
'monitor.highlight.on': 'Hervorgehoben',
'monitor.highlight.off': 'Hervorheben',
// ============ Kopieren ============
'copy.success': 'Kopiert',
'copy.failed': 'Kopieren fehlgeschlagen, bitte manuell kopieren',
'copy.error': 'Kopieren fehlgeschlagen',
'monitor.copy.success': 'Kopiert',
'monitor.copy.failed': 'Kopieren fehlgeschlagen, bitte manuell kopieren',
'monitor.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',
'monitor.prompt.select_date': 'Bitte Datum auswählen',
'monitor.prompt.fetch_failed': 'Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
'monitor.prompt.fetch_outbound_failed': 'Ausgehende Anfragedatensätze abrufen fehlgeschlagen, bitte erneut versuchen',
'monitor.prompt.reset_failed': 'Zurücksetzen fehlgeschlagen, bitte erneut versuchen',
// ============ Seitentitel ============
'page.http_requests_log': 'HTTP-Anfragedatensätze',
'page.outbound_requests_log': 'Ausgehende Anfragedatensätze',
'monitor.page.http_requests_log': 'HTTP-Anfragedatensätze',
'monitor.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}.',
'monitor.time.tomorrow': 'Morgen',
'monitor.time.day_after_tomorrow': 'Übermorgen',
'monitor.time.over_24h': 'Über 24 Stunden',
'monitor.time.expired': 'Abgelaufen',
'monitor.time.days_hours_later': 'In {days}T {hours}Std.',
'monitor.time.unknown': 'Unbekannt',
'monitor.time.month_day': '{day}.{month}.',
// ============ Aufgabentypen ============
'task.system': 'System',
'task.project': 'Projekt',
'task.avg_time': 'Durchschn. Ausführungszeit',
'monitor.task.system': 'System',
'monitor.task.project': 'Projekt',
'monitor.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:',
'monitor.modal.outbound_detail': 'Ausgehende Anfrage-Details',
'monitor.modal.timestamp': 'Zeitstempel:',
'monitor.modal.method': 'Methode:',
'monitor.modal.status_code': 'Statuscode:',
'monitor.modal.response_time': 'Antwortzeit:',
'monitor.modal.module': 'Modul:',
'monitor.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',
'monitor.section.request_headers': 'Anfrage-Header',
'monitor.section.request_body': 'Anfrage-Body',
'monitor.section.response_headers': 'Antwort-Header',
'monitor.section.response_body': 'Antwort-Body',
'monitor.section.no_response': 'Keine Antwort-Body-Daten',
'monitor.section.response_failed': 'Antwort-Body-Verarbeitung fehlgeschlagen: ',
'monitor.section.truncated': '[Inhalt gekürzt, exportieren für vollständigen Inhalt]',
'monitor.section.large_data_warning': '⚠️ Große Datenmenge ({count} Zeichen), kann Leistung beeinträchtigen',
'monitor.section.showing_chars': 'Zeige erste {count} Zeichen',
'monitor.section.basic_info': 'Grundinformationen',
'monitor.section.no_data': 'Keine Daten',
// ============ Ratenbegrenzung ============
'status.rate_limited': 'Ratenbegrenzt',
'monitor.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',
'monitor.log_level.all_with_debug': 'Alle Stufen (mit DEBUG)',
'monitor.log_level.error': 'Fehlerprotokolle',
'monitor.log_level.warning': 'Warnungsprotokolle',
'monitor.log_level.info': 'Infoprotokolle',
'monitor.log_level.debug': 'Debugprotokolle',
// ============ DeadLetter ============
'dl.reprocess': 'Neu verarbeiten',
'dl.reprocess_success': 'DeadLetter erfolgreich neu verarbeitet',
'dl.reprocess_failed': 'DeadLetter Neuverarbeitung fehlgeschlagen',
'monitor.dl.reprocess': 'Neu verarbeiten',
'monitor.dl.reprocess_success': 'DeadLetter erfolgreich neu verarbeitet',
'monitor.dl.reprocess_failed': 'DeadLetter Neuverarbeitung fehlgeschlagen',
// ============ Backend-Warnmeldungen-Zuordnung ============
'alert.scheduler_not_running': 'Planer läuft nicht',
'alert.db_connection_failed': 'Datenbankverbindung fehlgeschlagen',
'alert.redis_connection_failed': 'Redis-Verbindung fehlgeschlagen',
'alert.event_listener_stopped': 'Ereignis-Listener gestoppt',
'alert.binlog_listener_stopped': 'Binlog-Listener gestoppt',
'alert.high_error_rate': 'Hohe Fehlerrate',
'alert.memory_warning': 'Speichernutzung-Warnung',
'alert.cpu_warning': 'CPU-Nutzung-Warnung',
'monitor.alert.scheduler_not_running': 'Planer läuft nicht',
'monitor.alert.db_connection_failed': 'Datenbankverbindung fehlgeschlagen',
'monitor.alert.redis_connection_failed': 'Redis-Verbindung fehlgeschlagen',
'monitor.alert.event_listener_stopped': 'Ereignis-Listener gestoppt',
'monitor.alert.binlog_listener_stopped': 'Binlog-Listener gestoppt',
'monitor.alert.high_error_rate': 'Hohe Fehlerrate',
'monitor.alert.memory_warning': 'Speichernutzung-Warnung',
'monitor.alert.cpu_warning': 'CPU-Nutzung-Warnung',
// ================================================================================
// MDS-Modul (Daten-Bereitstellungssystem)
@@ -659,5 +669,12 @@ window.__i18n_de_DE__ = {
'mds.other.tip': 'Hinweis',
'mds.other.warning': 'Warnung',
'mds.other.error': 'Fehler',
'mds.other.success': 'Erfolg'
'mds.other.success': 'Erfolg',
// ============ 补充翻译键 ============
'monitor.other.yes': 'Ja',
'monitor.other.no': 'Nein',
'monitor.other.data_points': 'Datenpunkte',
'monitor.col.is_slow': 'Ist langsame Anfrage',
'monitor.col.is_error': 'Ist Fehler'
};
+431 -414
View File
@@ -3,497 +3,507 @@
*/
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',
'monitor.page.title': 'MyAPI System Monitor',
'monitor.page.live_logs': 'Live Logs - System Monitor',
'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',
'monitor.nav.overview': '📊 Overview',
'monitor.nav.database': '🗃️ Database',
'monitor.nav.events': '☎️ Events',
'monitor.nav.scheduler': '⏰ Scheduler',
'monitor.nav.http_requests': '📥 HTTP Requests',
'monitor.nav.outbound_requests': '📤 Outbound',
'monitor.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',
'monitor.tab.overview': 'Overview',
'monitor.tab.database': 'Database',
'monitor.tab.events': 'Events',
'monitor.tab.scheduler': 'Scheduler',
'monitor.tab.http': 'HTTP Requests',
'monitor.tab.outbound': 'Outbound',
'monitor.tab.logs': 'System Logs',
'monitor.tab.timeline': 'Timeline',
'monitor.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',
'monitor.card.resource': 'Resource Usage',
'monitor.card.db_status': 'Database Status',
'monitor.card.db_connections': 'Database Connections',
'monitor.card.event_helpers': 'Event Helpers',
'monitor.card.scheduler': 'Job Scheduler',
'monitor.card.api_requests': 'HTTP Requests',
'monitor.card.outbound_requests': 'Outbound Requests',
'monitor.card.redis': 'Redis Status',
'monitor.card.event_listener': 'Event Listener',
'monitor.card.recent_alerts': 'Recent Alerts',
'monitor.card.mysql': 'MySQL',
'monitor.card.http_requests_log': 'HTTP Request Records',
'monitor.card.scheduler_detail': 'Scheduler Details',
'monitor.card.outbound_requests_log': 'Outbound Request Records',
'monitor.card.callback_tracker': 'Callback Tracker',
'monitor.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',
'monitor.metric.cpu': 'CPU Usage',
'monitor.metric.memory': 'Memory Usage',
'monitor.metric.threads': 'Threads',
'monitor.metric.uptime': 'Uptime',
'monitor.metric.total_connections': 'Total Connections',
'monitor.metric.healthy': 'Healthy',
'monitor.metric.unhealthy': 'Unhealthy',
'monitor.metric.degraded': 'Degraded',
'monitor.metric.total': 'Total',
'monitor.metric.success': 'Success',
'monitor.metric.failed': 'Failed',
'monitor.metric.pending': 'Pending',
'monitor.metric.active': 'Active',
'monitor.metric.idle': 'Idle',
'monitor.metric.unknown': 'Unknown',
'monitor.metric.warning': 'Warning',
'monitor.metric.events_received': 'Events Received',
'monitor.metric.events_processed': 'Events Processed',
'monitor.metric.events_failed': 'Events Failed',
'monitor.metric.jobs_running': 'Running Jobs',
'monitor.metric.jobs_pending': 'Pending Jobs',
'monitor.metric.requests_total': 'Total Requests',
'monitor.metric.requests_slow': 'Slow Requests',
'monitor.metric.requests_error': 'Error Requests',
'monitor.metric.error_rate': 'Error Rate',
'monitor.metric.avg_time': 'Avg Response',
'monitor.metric.connection_status': 'Connection Status',
'monitor.metric.host': 'Host',
'monitor.metric.port': 'Port',
'monitor.metric.database': 'Database',
'monitor.metric.used_connections': 'Used Connections',
'monitor.metric.max_connections': 'Max Connections',
'monitor.metric.connection_usage': 'Connection Usage',
'monitor.metric.buffer_size': 'Buffer Size',
'monitor.metric.buffer_threshold': 'Buffer Threshold',
'monitor.metric.buffer_usage': 'Buffer Usage',
'monitor.metric.events_interrupted': 'Interrupted',
'monitor.metric.overall_success_rate': 'Overall Success Rate',
'monitor.metric.active_event_types': 'Active Event Types',
'monitor.metric.backpressure_status': 'Backpressure Status',
'monitor.metric.backpressure_pending': 'Pending Events',
'monitor.metric.backpressure_usage': 'Backpressure Usage',
'monitor.metric.event_loop_status': 'Event Loop Status',
'monitor.metric.pending_callbacks': 'Pending Callbacks',
'monitor.metric.max_retries': 'Max Retries',
'monitor.metric.pending_retries': 'Pending Retries',
'monitor.metric.total_entries': 'Total Entries',
'monitor.metric.active_items': 'Active Items',
'monitor.metric.ttl_seconds': 'TTL (seconds)',
'monitor.metric.max_entries': 'Max Entries',
'monitor.metric.dl_total': 'DL Total',
'monitor.metric.recent_dl': 'Recent DL',
'monitor.metric.process_success_rate': 'Process Success Rate',
'monitor.metric.scheduler_status': 'Scheduler Status',
'monitor.metric.job_count': 'Job Count',
'monitor.metric.pending_events': 'Pending Events',
'monitor.metric.total_events': 'Total Events',
'monitor.metric.file_size': 'File Size',
'monitor.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',
'monitor.status.healthy': '● System Healthy',
'monitor.status.unhealthy': '● System Unhealthy',
'monitor.status.degraded': '● System Degraded',
'monitor.status.running': 'Running',
'monitor.status.checking': 'Checking',
'monitor.status.stopped': 'Stopped',
'monitor.status.loading': 'Loading...',
'monitor.status.querying': 'Querying...',
'monitor.status.exporting': 'Exporting...',
'monitor.status.no_data': 'No data',
'monitor.status.connected': 'Connected',
'monitor.status.connecting': 'Connecting...',
'monitor.status.disconnected': 'Disconnected',
'monitor.status.reconnecting': 'Reconnecting...',
'monitor.status.paused': 'Paused',
'monitor.status.enabled': 'Enabled',
'monitor.status.disabled': 'Disabled',
'monitor.status.no_db_connections': 'No database connections',
'monitor.status.no_scheduler': 'No scheduled tasks',
'monitor.status.no_http_requests': 'No HTTP requests',
'monitor.status.no_alerts': 'No alerts',
'monitor.status.no_logs': 'No logs',
'monitor.status.no_events': 'No event statistics',
'monitor.status.no_api_requests': 'No API request records',
'monitor.status.no_outbound_requests': 'No outbound request records',
'monitor.status.no_dead_letters': 'No DL',
'monitor.status.partial_warnings': '● Partial Warnings',
'monitor.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',
'monitor.btn.query': 'Query',
'monitor.btn.reset': 'Reset',
'monitor.btn.refresh': 'Refresh',
'monitor.btn.export': 'Export',
'monitor.btn.export_csv': 'Export CSV',
'monitor.btn.export_json': 'Export JSON',
'monitor.btn.detail': 'Detail',
'monitor.btn.pause': 'Pause',
'monitor.btn.resume': 'Resume',
'monitor.btn.clear': 'Clear',
'monitor.btn.save': 'Save',
'monitor.btn.delete': 'Delete',
'monitor.btn.close': 'Close',
'monitor.btn.confirm': 'Confirm',
'monitor.btn.cancel': 'Cancel',
'monitor.btn.test': 'Test',
'monitor.btn.apply': 'Apply',
'monitor.btn.filter': 'Filter',
'monitor.btn.clear_all': 'Clear All',
'monitor.btn.realtime_on': 'Realtime: ON',
'monitor.btn.realtime_off': 'Realtime: OFF',
'monitor.btn.precise_locate': '± 60s',
'monitor.btn.precise_cancel': '± 60s - Click to cancel',
'monitor.btn.reset_stats': 'Reset Stats',
'monitor.btn.flush_all': 'Flush All',
'monitor.btn.new_window': 'New Window',
'monitor.btn.auto_scroll': 'Auto Scroll',
'monitor.btn.live_logs': 'Live Logs',
'monitor.btn.history_query': 'History Query',
'monitor.btn.mark_all_read': 'Mark All Read',
'monitor.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',
'monitor.col.index': '#',
'monitor.col.time': 'Time',
'monitor.col.level': 'Level',
'monitor.col.module': 'Module',
'monitor.col.message': 'Message',
'monitor.col.method': 'Method',
'monitor.col.path': 'Path',
'monitor.col.url': 'URL',
'monitor.col.status': 'Status',
'monitor.col.duration': 'Duration',
'monitor.col.client_ip': 'Client IP',
'monitor.col.source': 'Source',
'monitor.col.function': 'Function',
'monitor.col.line': 'Line',
'monitor.col.operation': 'Action',
'monitor.col.count': 'Count',
'monitor.col.avg_time': 'Avg Time',
'monitor.col.max_time': 'Max Time',
'monitor.col.db_name': 'DB Name',
'monitor.col.last_check': 'Last Check',
'monitor.col.current_connections': 'Current',
'monitor.col.max_connections': 'Max',
'monitor.col.min_connections': 'Min',
'monitor.col.idle_connections': 'Idle',
'monitor.col.used_connections': 'Used',
'monitor.col.usage': 'Usage',
'monitor.col.processed_records': 'Processed',
'monitor.col.timestamp': 'Timestamp',
'monitor.col.query_params': 'Query Params',
'monitor.col.error_message': 'Error Message',
'monitor.col.url': 'URL',
'monitor.col.is_read': 'Read',
'monitor.col.description': 'Description',
'monitor.col.total_received': 'Total Received',
'monitor.col.pending': 'Pending',
'monitor.col.processed': 'Processed',
'monitor.col.interrupted': 'Interrupted',
'monitor.col.completion_rate': 'Completion Rate',
'monitor.col.avg_latency': 'Avg Latency',
'monitor.col.last_action': 'Last Action',
'monitor.col.id': 'ID',
'monitor.col.event_type': 'Event Type',
'monitor.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',
'monitor.time.start_time': 'Start Time',
'monitor.time.end_time': 'End Time',
'monitor.time.last_10m': 'Last 10 min',
'monitor.time.last_30m': 'Last 30 min',
'monitor.time.last_1h': 'Last 1 hour',
'monitor.time.last_6h': 'Last 6 hours',
'monitor.time.last_24h': 'Last 24 hours',
'monitor.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',
'monitor.filter.level': 'All Levels',
'monitor.filter.type': 'All Types',
'monitor.filter.module': 'Module',
'monitor.filter.keyword': 'Keyword',
'monitor.filter.method': 'Method',
'monitor.filter.client_ip': 'Client IP',
'monitor.filter.status_range': 'Status Range',
'monitor.filter.duration_range': 'Duration Range',
'monitor.filter.advanced': 'Advanced Filters',
'monitor.filter.collapse': 'Collapse',
'monitor.filter.expand': 'Expand',
'monitor.filter.clear': 'Clear',
'monitor.filter.active': 'Active Filters',
'monitor.filter.module_placeholder': 'Enter module name',
'monitor.filter.keyword_placeholder': 'Enter keyword',
'monitor.filter.ip_placeholder': 'Enter client IP',
'monitor.filter.status_placeholder': 'e.g.: 400-499',
'monitor.filter.duration_placeholder': 'e.g.: >1000',
'monitor.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)',
'monitor.chart.request_trend': '📊 Request Trend',
'monitor.chart.level_distribution': '📊 Log Level Distribution',
'monitor.chart.status_distribution': '📈 Status Code Distribution',
'monitor.chart.slow_requests': '⏱️ Slow Requests TOP10',
'monitor.chart.total_requests': 'Total Requests',
'monitor.chart.error_count': 'Errors',
'monitor.chart.slow_count': 'Slow Requests',
'monitor.chart.log_count': 'Total Logs',
'monitor.chart.no_slow': '✅ No slow requests',
'monitor.chart.query_required': 'Please execute query first',
'monitor.chart.load_failed': '❌ Chart data load failed, please retry',
'monitor.chart.no_data': 'No data',
'monitor.chart.no_trend': 'No trend data',
'monitor.chart.redis_connections': 'Redis Connection Pool Usage',
'monitor.chart.redis_buffer': 'Redis Buffer Size Changes',
'monitor.chart.cpu': 'CPU',
'monitor.chart.memory': 'Memory',
'monitor.chart.upload': 'Upload',
'monitor.chart.download': 'Download',
'monitor.chart.cpu_memory_axis': 'CPU / Memory (%)',
'monitor.chart.network_axis': 'Network Upload / Download (KB/s)',
'monitor.chart.used_connections': 'Used Connections',
'monitor.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',
'monitor.error.time_range_invalid': 'Start time cannot be greater than end time',
'monitor.error.time_range_required': 'Please select start time and end time',
'monitor.error.query_failed': 'Query failed, please try again',
'monitor.error.export_failed': 'Export failed, please retry',
'monitor.error.connection_failed': 'Connection failed, please refresh page',
'monitor.error.max_templates': 'Maximum 10 templates allowed, please delete some first',
'monitor.error.auto_pause': 'Realtime tracking auto-paused (over 10 minutes)',
'monitor.error.load_failed': 'Load failed, please retry',
'monitor.error.invalid_params': 'Invalid parameters',
'monitor.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',
'monitor.success.query_complete': 'Query Complete',
'monitor.success.export_complete': 'Export Complete',
'monitor.success.template_saved': 'Template "{name}" saved',
'monitor.success.logs_cleared': 'Logs cleared',
'monitor.success.operation_success': 'Operation successful',
'monitor.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',
'monitor.timeline.title': 'Timeline',
'monitor.timeline.no_data': 'No data, please execute query first',
'monitor.timeline.anomaly_detected': '⚠️ {count} anomalies detected',
'monitor.timeline.error_burst': '{count} consecutive ERROR logs',
'monitor.timeline.slow_anomaly': 'Slow request anomaly ({duration}ms > avg {avg}ms×3)',
'monitor.timeline.duplicate_error': 'Duplicate error "{msg}" appeared {count} times',
'monitor.timeline.http_request': 'HTTP Request',
'monitor.timeline.outbound_request': 'Outbound Request',
'monitor.timeline.system_log': 'System Log',
'monitor.timeline.summary_title': '📊 Timeline Statistics',
'monitor.timeline.total_events': 'Total Events',
'monitor.timeline.http_requests': 'HTTP Requests',
'monitor.timeline.outbound_requests': 'Outbound Requests',
'monitor.timeline.system_logs': 'System Logs',
'monitor.timeline.errors': 'Errors',
'monitor.timeline.warnings': 'Warnings',
'monitor.timeline.slow_requests': 'Slow Requests',
// ============ 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',
'monitor.pagination.page': 'Page',
'monitor.pagination.of': 'of',
'monitor.pagination.items': 'items',
'monitor.pagination.showing': 'Showing',
'monitor.pagination.per_page': 'Per Page',
'monitor.pagination.first': 'First',
'monitor.pagination.last': 'Last',
'monitor.pagination.prev': 'Previous',
'monitor.pagination.next': 'Next',
'monitor.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',
'monitor.export.current_page': 'Export Current Page',
'monitor.export.all_data': 'Export All Data',
'monitor.export.format_csv': 'CSV Format',
'monitor.export.format_json': 'JSON Format',
'monitor.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',
'monitor.template.saved_queries': 'Saved Queries...',
'monitor.template.save': 'Save Query',
'monitor.template.manage': 'Manage Templates',
'monitor.template.name': 'Template Name',
'monitor.template.description': 'Description',
'monitor.template.delete_confirm': 'Delete this template?',
'monitor.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',
'monitor.stats.time_range': 'Query Time Range',
'monitor.stats.http_requests': 'HTTP Requests',
'monitor.stats.outbound_requests': 'Outbound Requests',
'monitor.stats.system_logs': 'System Logs',
'monitor.stats.level_distribution': 'Level Distribution',
'monitor.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',
'monitor.other.last_update': 'Last Update',
'monitor.other.auto_reconnect': 'reconnect in 5s',
'monitor.other.waiting_logs': 'Waiting for log data...',
'monitor.other.no_matching_logs': 'No matching logs',
'monitor.other.precise_mode': 'Precise Mode',
'monitor.other.saved_queries': 'Saved Queries...',
'monitor.other.all_time': 'All Time',
'monitor.other.linked_query': 'Linked Query',
'monitor.other.seconds': 'seconds',
'monitor.other.minutes': 'minutes',
'monitor.other.hours': 'hours',
'monitor.other.days': 'days',
'monitor.other.ms': 'ms',
'monitor.other.times': 'times',
'monitor.other.view_detail': 'View Detail',
'monitor.other.copy': 'Copy',
'monitor.other.expand': 'Expand',
'monitor.other.collapse': 'Collapse',
'monitor.other.show_internal': 'Show Internal',
'monitor.other.show_read': 'Show Read',
// ============ Date ============
'date.today': 'Today',
'date.yesterday': 'Yesterday',
'monitor.date.today': 'Today',
'monitor.date.yesterday': 'Yesterday',
// ============ Language ============
'lang.select': 'Select Language',
'lang.zh': '中文',
'lang.en': 'English',
'lang.de': 'Deutsch',
'monitor.lang.select': 'Select Language',
'monitor.lang.zh': '中文',
'monitor.lang.en': 'English',
'monitor.lang.de': 'Deutsch',
// ============ HTTP Methods ============
'method.get': 'GET',
'method.post': 'POST',
'method.put': 'PUT',
'method.delete': 'DELETE',
'method.patch': 'PATCH',
'monitor.method.get': 'GET',
'monitor.method.post': 'POST',
'monitor.method.put': 'PUT',
'monitor.method.delete': 'DELETE',
'monitor.method.patch': 'PATCH',
// ============ Log Levels ============
'level.debug': 'DEBUG',
'level.info': 'INFO',
'level.warning': 'WARNING',
'level.error': 'ERROR',
'level.critical': 'CRITICAL',
'monitor.level.debug': 'DEBUG',
'monitor.level.info': 'INFO',
'monitor.level.warning': 'WARNING',
'monitor.level.error': 'ERROR',
'monitor.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',
'monitor.scheduler.rule': 'Schedule Rule',
'monitor.scheduler.last_run': 'Last Run',
'monitor.scheduler.max_time': 'Max Time',
'monitor.scheduler.never_run': 'Never Run',
'monitor.scheduler.default': 'Default',
'monitor.scheduler.running': 'Running',
'monitor.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',
'monitor.time.just_now': 'Just now',
'monitor.time.minutes_ago': '{n} min ago',
'monitor.time.hours_ago': '{n} hours ago',
'monitor.time.days_ago': '{n} days ago',
'monitor.time.day_before_yesterday': 'Day before yesterday',
// ============ Alert Types ============
'alert.warning': 'Warning',
'alert.error': 'Error',
'alert.critical': 'Critical',
'alert.normal': 'Normal',
'monitor.alert.warning': 'Warning',
'monitor.alert.error': 'Error',
'monitor.alert.critical': 'Critical',
'monitor.alert.normal': 'Normal',
// ============ Connection Status ============
'connection.failed': 'Connection Failed',
'connection.reconnecting': 'Reconnecting',
'connection.connected': 'Connected',
'connection.disconnected': 'Disconnected',
'monitor.connection.failed': 'Connection Failed',
'monitor.connection.reconnecting': 'Reconnecting',
'monitor.connection.connected': 'Connected',
'monitor.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?',
'monitor.monitor.paused': '● Monitor paused (inactive)',
'monitor.monitor.panel': 'Monitor Panel',
'monitor.monitor.last_update': 'Last Update',
'monitor.monitor.reset_stats_confirm': 'Reset all event statistics?',
// ============ Highlight ============
'highlight.on': 'Highlighted',
'highlight.off': 'Highlight',
'monitor.highlight.on': 'Highlighted',
'monitor.highlight.off': 'Highlight',
// ============ Copy ============
'copy.success': 'Copied',
'copy.failed': 'Copy failed, please copy manually',
'copy.error': 'Copy failed',
'monitor.copy.success': 'Copied',
'monitor.copy.failed': 'Copy failed, please copy manually',
'monitor.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',
'monitor.prompt.select_date': 'Please select a date',
'monitor.prompt.fetch_failed': 'Failed to fetch request records, please try again',
'monitor.prompt.fetch_outbound_failed': 'Failed to fetch outbound request records, please try again',
'monitor.prompt.reset_failed': 'Reset failed, please try again',
// ============ Page Titles ============
'page.http_requests_log': 'HTTP Request Records',
'page.outbound_requests_log': 'Outbound Request Records',
'monitor.page.http_requests_log': 'HTTP Request Records',
'monitor.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}',
'monitor.time.tomorrow': 'Tomorrow',
'monitor.time.day_after_tomorrow': 'Day after tomorrow',
'monitor.time.over_24h': 'Over 24 hours',
'monitor.time.expired': 'Expired',
'monitor.time.days_hours_later': '{days}d {hours}h later',
'monitor.time.unknown': 'Unknown',
'monitor.time.month_day': '{month}/{day}',
// ============ Task Types ============
'task.system': 'System',
'task.project': 'Project',
'task.avg_time': 'Avg Execution Time',
'monitor.task.system': 'System',
'monitor.task.project': 'Project',
'monitor.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:',
'monitor.modal.outbound_detail': 'Outbound Request Detail',
'monitor.modal.timestamp': 'Timestamp:',
'monitor.modal.method': 'Method:',
'monitor.modal.status_code': 'Status Code:',
'monitor.modal.response_time': 'Response Time:',
'monitor.modal.module': 'Module:',
'monitor.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',
'monitor.section.request_headers': 'Request Headers',
'monitor.section.request_body': 'Request Body',
'monitor.section.response_headers': 'Response Headers',
'monitor.section.response_body': 'Response Body',
'monitor.section.no_response': 'No response body data',
'monitor.section.response_failed': 'Response body processing failed: ',
'monitor.section.truncated': '[Content truncated, export to view full content]',
'monitor.section.large_data_warning': '⚠️ Large data ({count} chars), may affect performance',
'monitor.section.showing_chars': 'Showing first {count} chars',
'monitor.section.basic_info': 'Basic Information',
'monitor.section.no_data': 'No data',
// ============ Rate Limiting ============
'status.rate_limited': 'Rate Limited',
'monitor.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',
'monitor.log_level.all_with_debug': 'All Levels (with DEBUG)',
'monitor.log_level.error': 'Error Logs',
'monitor.log_level.warning': 'Warning Logs',
'monitor.log_level.info': 'Info Logs',
'monitor.log_level.debug': 'Debug Logs',
// ============ DeadLetter ============
'dl.reprocess': 'Reprocess',
'dl.reprocess_success': 'DeadLetter reprocessed successfully',
'dl.reprocess_failed': 'DeadLetter reprocess failed',
'monitor.dl.reprocess': 'Reprocess',
'monitor.dl.reprocess_success': 'DeadLetter reprocessed successfully',
'monitor.dl.reprocess_failed': 'DeadLetter reprocess failed',
// ============ Backend Alert Message Mapping ============
'alert.scheduler_not_running': 'Scheduler not running',
'alert.db_connection_failed': 'Database connection failed',
'alert.redis_connection_failed': 'Redis connection failed',
'alert.event_listener_stopped': 'Event listener stopped',
'alert.binlog_listener_stopped': 'Binlog listener stopped',
'alert.high_error_rate': 'High error rate',
'alert.memory_warning': 'Memory usage warning',
'alert.cpu_warning': 'CPU usage warning',
'monitor.alert.scheduler_not_running': 'Scheduler not running',
'monitor.alert.db_connection_failed': 'Database connection failed',
'monitor.alert.redis_connection_failed': 'Redis connection failed',
'monitor.alert.event_listener_stopped': 'Event listener stopped',
'monitor.alert.binlog_listener_stopped': 'Binlog listener stopped',
'monitor.alert.high_error_rate': 'High error rate',
'monitor.alert.memory_warning': 'Memory usage warning',
'monitor.alert.cpu_warning': 'CPU usage warning',
// ================================================================================
// MDS Module (Data Staging System)
@@ -658,5 +668,12 @@ window.__i18n_en_US__ = {
'mds.other.tip': 'Tip',
'mds.other.warning': 'Warning',
'mds.other.error': 'Error',
'mds.other.success': 'Success'
'mds.other.success': 'Success',
// ============ 补充翻译键 ============
'monitor.other.yes': 'Yes',
'monitor.other.no': 'No',
'monitor.other.data_points': 'data points',
'monitor.col.is_slow': 'Is Slow Request',
'monitor.col.is_error': 'Is Error'
};
+431 -414
View File
@@ -4,497 +4,507 @@
*/
window.__i18n_zh_CN__ = {
// ============ 页面标题 ============
'page.title': 'MyAPI 系统监控面板',
'page.live_logs': '实时日志 - 系统监控',
'page.history_logs': '日志历史查询 - 系统监控',
'monitor.page.title': 'MyAPI 系统监控面板',
'monitor.page.live_logs': '实时日志 - 系统监控',
'monitor.page.history_logs': '日志历史查询 - 系统监控',
// ============ 导航菜单 ============
'nav.overview': '📊 Overview',
'nav.database': '🗃️ 数据库',
'nav.events': '☎️ 事件处理',
'nav.scheduler': '⏰ 定时任务',
'nav.http_requests': '📥 接收请求',
'nav.outbound_requests': '📤 发送请求',
'nav.logs': '📋 日志',
'monitor.nav.overview': '📊 Overview',
'monitor.nav.database': '🗃️ 数据库',
'monitor.nav.events': '☎️ 事件处理',
'monitor.nav.scheduler': '⏰ 定时任务',
'monitor.nav.http_requests': '📥 接收请求',
'monitor.nav.outbound_requests': '📤 发送请求',
'monitor.nav.logs': '📋 日志',
// ============ 标签页 ============
'tab.overview': '概览',
'tab.database': '数据库',
'tab.events': '事件处理',
'tab.scheduler': '定时任务',
'tab.http': '接收请求',
'tab.outbound': '发送请求',
'tab.logs': '系统日志',
'tab.timeline': '时间线',
'tab.chart': '图表分析',
'monitor.tab.overview': '概览',
'monitor.tab.database': '数据库',
'monitor.tab.events': '事件处理',
'monitor.tab.scheduler': '定时任务',
'monitor.tab.http': '接收请求',
'monitor.tab.outbound': '发送请求',
'monitor.tab.logs': '系统日志',
'monitor.tab.timeline': '时间线',
'monitor.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': '事件去重器',
'monitor.card.resource': '资源使用',
'monitor.card.db_status': '账套状态',
'monitor.card.db_connections': '数据库连接状态',
'monitor.card.event_helpers': '事件辅助模块',
'monitor.card.scheduler': '定时任务调度器',
'monitor.card.api_requests': 'HTTP请求统计',
'monitor.card.outbound_requests': '对外HTTP请求',
'monitor.card.redis': 'Redis状态',
'monitor.card.event_listener': '事件监听',
'monitor.card.recent_alerts': '最近告警',
'monitor.card.mysql': 'MySQL',
'monitor.card.http_requests_log': '接收请求记录',
'monitor.card.scheduler_detail': '定时任务详情',
'monitor.card.outbound_requests_log': '发送请求记录',
'monitor.card.callback_tracker': '回调跟踪器',
'monitor.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': '运行状态',
'monitor.metric.cpu': 'CPU 使用率',
'monitor.metric.memory': '内存使用',
'monitor.metric.threads': '线程数',
'monitor.metric.uptime': '运行时间',
'monitor.metric.total_connections': '总连接数',
'monitor.metric.healthy': '健康',
'monitor.metric.unhealthy': '异常',
'monitor.metric.degraded': '降级',
'monitor.metric.total': '总数',
'monitor.metric.success': '成功',
'monitor.metric.failed': '失败',
'monitor.metric.pending': '待处理',
'monitor.metric.active': '活跃',
'monitor.metric.idle': '空闲',
'monitor.metric.unknown': '未知',
'monitor.metric.warning': '警告',
'monitor.metric.events_received': '接收事件',
'monitor.metric.events_processed': '处理事件',
'monitor.metric.events_failed': '失败事件',
'monitor.metric.jobs_running': '运行中任务',
'monitor.metric.jobs_pending': '待执行任务',
'monitor.metric.requests_total': '总请求数',
'monitor.metric.requests_slow': '慢请求',
'monitor.metric.requests_error': '错误请求',
'monitor.metric.error_rate': '错误率',
'monitor.metric.avg_time': '平均响应',
'monitor.metric.connection_status': '连接状态',
'monitor.metric.host': '主机',
'monitor.metric.port': '端口',
'monitor.metric.database': '数据库',
'monitor.metric.used_connections': '使用连接数',
'monitor.metric.max_connections': '最大连接数',
'monitor.metric.connection_usage': '连接使用率',
'monitor.metric.buffer_size': '缓冲大小',
'monitor.metric.buffer_threshold': '缓冲阈值',
'monitor.metric.buffer_usage': '缓冲使用率',
'monitor.metric.events_interrupted': '中断处理',
'monitor.metric.overall_success_rate': '整体成功率',
'monitor.metric.active_event_types': '活跃事件类型',
'monitor.metric.backpressure_status': '背压状态',
'monitor.metric.backpressure_pending': '待处理事件',
'monitor.metric.backpressure_usage': '背压使用率',
'monitor.metric.event_loop_status': '事件循环状态',
'monitor.metric.pending_callbacks': '待处理回调',
'monitor.metric.max_retries': '最大重试次数',
'monitor.metric.pending_retries': '待处理重试',
'monitor.metric.total_entries': '总存储条目',
'monitor.metric.active_items': '活跃项目',
'monitor.metric.ttl_seconds': 'TTL (秒)',
'monitor.metric.max_entries': '最大条目',
'monitor.metric.dl_total': 'DL总数',
'monitor.metric.recent_dl': '最近DL',
'monitor.metric.process_success_rate': '处理成功率',
'monitor.metric.scheduler_status': '调度器状态',
'monitor.metric.job_count': '任务数量',
'monitor.metric.pending_events': '待写入事件',
'monitor.metric.total_events': '总事件数',
'monitor.metric.file_size': '文件大小',
'monitor.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': '监控中',
'monitor.status.healthy': '● 系统正常',
'monitor.status.unhealthy': '● 系统异常',
'monitor.status.degraded': '● 系统降级',
'monitor.status.running': '运行中',
'monitor.status.checking': '检查中',
'monitor.status.stopped': '已停止',
'monitor.status.loading': '加载中...',
'monitor.status.querying': '查询中...',
'monitor.status.exporting': '导出中...',
'monitor.status.no_data': '暂无数据',
'monitor.status.connected': '已连接',
'monitor.status.connecting': '连接中...',
'monitor.status.disconnected': '已断开',
'monitor.status.reconnecting': '重连中...',
'monitor.status.paused': '已暂停',
'monitor.status.enabled': '已启用',
'monitor.status.disabled': '已禁用',
'monitor.status.no_db_connections': '暂无数据库连接',
'monitor.status.no_scheduler': '暂无定时任务',
'monitor.status.no_http_requests': '暂无接收请求',
'monitor.status.no_alerts': '暂无告警',
'monitor.status.no_logs': '暂无日志',
'monitor.status.no_events': '暂无事件统计',
'monitor.status.no_api_requests': '暂无 API 请求记录',
'monitor.status.no_outbound_requests': '暂无发送请求记录',
'monitor.status.no_dead_letters': '暂无DL',
'monitor.status.partial_warnings': '● 部分警告',
'monitor.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': '清空已读状态',
'monitor.btn.query': '查询',
'monitor.btn.reset': '重置',
'monitor.btn.refresh': '刷新',
'monitor.btn.export': '导出',
'monitor.btn.export_csv': '导出 CSV',
'monitor.btn.export_json': '导出 JSON',
'monitor.btn.detail': '详情',
'monitor.btn.pause': '暂停',
'monitor.btn.resume': '继续',
'monitor.btn.clear': '清空',
'monitor.btn.save': '保存',
'monitor.btn.delete': '删除',
'monitor.btn.close': '关闭',
'monitor.btn.confirm': '确认',
'monitor.btn.cancel': '取消',
'monitor.btn.test': '测试',
'monitor.btn.apply': '应用',
'monitor.btn.filter': '过滤',
'monitor.btn.clear_all': '清空全部',
'monitor.btn.realtime_on': '实时追踪: 开',
'monitor.btn.realtime_off': '实时追踪: 关',
'monitor.btn.precise_locate': '± 60s',
'monitor.btn.precise_cancel': '± 60s - 点击取消',
'monitor.btn.reset_stats': '重置统计',
'monitor.btn.flush_all': '立即刷新所有',
'monitor.btn.new_window': '新窗口',
'monitor.btn.auto_scroll': '自动滚动',
'monitor.btn.live_logs': '实时日志',
'monitor.btn.history_query': '历史查询',
'monitor.btn.mark_all_read': '标记全部已读',
'monitor.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': '表',
'monitor.col.index': '序号',
'monitor.col.time': '时间',
'monitor.col.level': '级别',
'monitor.col.module': '模块',
'monitor.col.message': '消息',
'monitor.col.method': '方法',
'monitor.col.path': '端点',
'monitor.col.url': 'URL',
'monitor.col.status': '状态码',
'monitor.col.duration': '响应时间',
'monitor.col.client_ip': '客户端IP',
'monitor.col.source': '来源',
'monitor.col.function': '函数',
'monitor.col.line': '行号',
'monitor.col.operation': '操作',
'monitor.col.count': '数量',
'monitor.col.avg_time': '平均时间',
'monitor.col.max_time': '最大时间',
'monitor.col.db_name': '账套名称',
'monitor.col.last_check': '最后检查',
'monitor.col.current_connections': '当前连接',
'monitor.col.max_connections': '最大连接',
'monitor.col.min_connections': '最小连接',
'monitor.col.idle_connections': '空闲连接',
'monitor.col.used_connections': '使用中连接',
'monitor.col.usage': '使用率',
'monitor.col.processed_records': '处理记录',
'monitor.col.timestamp': '时间戳',
'monitor.col.query_params': '查询参数',
'monitor.col.error_message': '错误信息',
'monitor.col.url': 'URL',
'monitor.col.is_read': '已读',
'monitor.col.description': '描述',
'monitor.col.total_received': '总接收',
'monitor.col.pending': '待处理',
'monitor.col.processed': '已处理',
'monitor.col.interrupted': '中断',
'monitor.col.completion_rate': '完成率',
'monitor.col.avg_latency': '平均延迟',
'monitor.col.last_action': '最后动作',
'monitor.col.id': 'ID',
'monitor.col.event_type': '事件类型',
'monitor.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': '时间范围',
'monitor.time.start_time': '开始时间',
'monitor.time.end_time': '结束时间',
'monitor.time.last_10m': '最近10分钟',
'monitor.time.last_30m': '最近30分钟',
'monitor.time.last_1h': '最近1小时',
'monitor.time.last_6h': '最近6小时',
'monitor.time.last_24h': '最近24小时',
'monitor.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': '全部日志',
'monitor.filter.level': '全部级别',
'monitor.filter.type': '全部数据',
'monitor.filter.module': '模块',
'monitor.filter.keyword': '关键词',
'monitor.filter.method': '请求方法',
'monitor.filter.client_ip': '客户端IP',
'monitor.filter.status_range': '状态码范围',
'monitor.filter.duration_range': '响应时间范围',
'monitor.filter.advanced': '高级过滤',
'monitor.filter.collapse': '收起',
'monitor.filter.expand': '展开',
'monitor.filter.clear': '清空',
'monitor.filter.active': '已激活过滤条件',
'monitor.filter.module_placeholder': '输入模块名',
'monitor.filter.keyword_placeholder': '输入关键词搜索',
'monitor.filter.ip_placeholder': '输入客户端IP',
'monitor.filter.status_placeholder': '如: 400-499',
'monitor.filter.duration_placeholder': '如: >1000',
'monitor.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)',
'monitor.chart.request_trend': '📊 请求量趋势',
'monitor.chart.level_distribution': '📊 日志级别分布',
'monitor.chart.status_distribution': '📈 状态码分布',
'monitor.chart.slow_requests': '⏱️ 慢请求TOP10',
'monitor.chart.total_requests': '总请求',
'monitor.chart.error_count': '错误',
'monitor.chart.slow_count': '慢请求',
'monitor.chart.log_count': '日志总数',
'monitor.chart.no_slow': '✅ 无慢请求',
'monitor.chart.query_required': '请先执行查询',
'monitor.chart.load_failed': '❌ 图表数据加载失败,请重试',
'monitor.chart.no_data': '无数据',
'monitor.chart.no_trend': '无趋势数据',
'monitor.chart.redis_connections': 'Redis 连接池使用情况',
'monitor.chart.redis_buffer': 'Redis 缓冲大小变化',
'monitor.chart.cpu': 'CPU',
'monitor.chart.memory': '内存',
'monitor.chart.upload': '上传',
'monitor.chart.download': '下载',
'monitor.chart.cpu_memory_axis': 'CPU / 内存 (%)',
'monitor.chart.network_axis': '网络上传 / 下载 (KB/s)',
'monitor.chart.used_connections': '使用连接数',
'monitor.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队列失败',
'monitor.error.time_range_invalid': '开始时间不能大于结束时间',
'monitor.error.time_range_required': '请选择开始时间和结束时间',
'monitor.error.query_failed': '查询失败,请稍后重试',
'monitor.error.export_failed': '导出失败,请重试',
'monitor.error.connection_failed': '连接失败,请刷新页面',
'monitor.error.max_templates': '最多保存10个模板,请先删除部分模板',
'monitor.error.auto_pause': '实时追踪已自动暂停(超过10分钟)',
'monitor.error.load_failed': '加载失败,请重试',
'monitor.error.invalid_params': '参数错误',
'monitor.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队列已清空',
'monitor.success.query_complete': '查询完成',
'monitor.success.export_complete': '导出完成',
'monitor.success.template_saved': '模板"{name}"已保存',
'monitor.success.logs_cleared': '日志已清空',
'monitor.success.operation_success': '操作成功',
'monitor.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': '系统日志',
'monitor.timeline.title': '时间线',
'monitor.timeline.no_data': '暂无数据,请先执行查询',
'monitor.timeline.anomaly_detected': '⚠️ 发现 {count} 处异常',
'monitor.timeline.error_burst': '连续{count}条ERROR日志',
'monitor.timeline.slow_anomaly': '异常慢请求({duration}ms > 平均{avg}ms×3)',
'monitor.timeline.duplicate_error': '重复错误"{msg}"出现{count}次',
'monitor.timeline.http_request': 'HTTP请求',
'monitor.timeline.outbound_request': '发送请求',
'monitor.timeline.system_log': '系统日志',
'monitor.timeline.summary_title': '📊 时间线统计',
'monitor.timeline.total_events': '总事件',
'monitor.timeline.http_requests': 'HTTP请求',
'monitor.timeline.outbound_requests': '发送请求',
'monitor.timeline.system_logs': '系统日志',
'monitor.timeline.errors': '错误',
'monitor.timeline.warnings': '警告',
'monitor.timeline.slow_requests': '慢请求',
// ============ 分页 ============
'pagination.page': '页',
'pagination.of': '共',
'pagination.items': '条',
'pagination.showing': '显示',
'pagination.per_page': '每页',
'pagination.first': '首页',
'pagination.last': '末页',
'pagination.prev': '上一页',
'pagination.next': '下一页',
'pagination.go_to': '跳转',
'monitor.pagination.page': '页',
'monitor.pagination.of': '共',
'monitor.pagination.items': '条',
'monitor.pagination.showing': '显示',
'monitor.pagination.per_page': '每页',
'monitor.pagination.first': '首页',
'monitor.pagination.last': '末页',
'monitor.pagination.prev': '上一页',
'monitor.pagination.next': '下一页',
'monitor.pagination.go_to': '跳转',
// ============ 导出 ============
'export.current_page': '导出当前页',
'export.all_data': '导出全部数据',
'export.format_csv': 'CSV格式',
'export.format_json': 'JSON格式',
'export.select_format': '选择导出格式',
'monitor.export.current_page': '导出当前页',
'monitor.export.all_data': '导出全部数据',
'monitor.export.format_csv': 'CSV格式',
'monitor.export.format_json': 'JSON格式',
'monitor.export.select_format': '选择导出格式',
// ============ 查询模板 ============
'template.saved_queries': '已保存查询...',
'template.save': '保存查询条件',
'template.manage': '管理模板',
'template.name': '模板名称',
'template.description': '描述',
'template.delete_confirm': '确定删除该模板?',
'template.empty': '暂无保存的模板',
'monitor.template.saved_queries': '已保存查询...',
'monitor.template.save': '保存查询条件',
'monitor.template.manage': '管理模板',
'monitor.template.name': '模板名称',
'monitor.template.description': '描述',
'monitor.template.delete_confirm': '确定删除该模板?',
'monitor.template.empty': '暂无保存的模板',
// ============ 统计信息 ============
'stats.time_range': '查询时间范围',
'stats.http_requests': '接收请求',
'stats.outbound_requests': '发送请求',
'stats.system_logs': '系统日志',
'stats.level_distribution': '级别分布',
'stats.all_time': '全部时间',
'monitor.stats.time_range': '查询时间范围',
'monitor.stats.http_requests': '接收请求',
'monitor.stats.outbound_requests': '发送请求',
'monitor.stats.system_logs': '系统日志',
'monitor.stats.level_distribution': '级别分布',
'monitor.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': '显示已读',
'monitor.other.last_update': '最后更新',
'monitor.other.auto_reconnect': '5秒后重连',
'monitor.other.waiting_logs': '正在等待日志数据...',
'monitor.other.no_matching_logs': '没有匹配的日志',
'monitor.other.precise_mode': '精确定位',
'monitor.other.saved_queries': '已保存查询...',
'monitor.other.all_time': '全部时间',
'monitor.other.linked_query': '联动查询',
'monitor.other.seconds': '秒',
'monitor.other.minutes': '分钟',
'monitor.other.hours': '小时',
'monitor.other.days': '天',
'monitor.other.ms': '毫秒',
'monitor.other.times': '次',
'monitor.other.view_detail': '查看详情',
'monitor.other.copy': '复制',
'monitor.other.expand': '展开',
'monitor.other.collapse': '收起',
'monitor.other.show_internal': '显示内部请求',
'monitor.other.show_read': '显示已读',
// ============ 日期 ============
'date.today': '今天',
'date.yesterday': '昨天',
'monitor.date.today': '今天',
'monitor.date.yesterday': '昨天',
// ============ 语言选择 ============
'lang.select': '选择语言',
'lang.zh': '中文',
'lang.en': 'English',
'lang.de': 'Deutsch',
'monitor.lang.select': '选择语言',
'monitor.lang.zh': '中文',
'monitor.lang.en': 'English',
'monitor.lang.de': 'Deutsch',
// ============ HTTP方法 ============
'method.get': 'GET',
'method.post': 'POST',
'method.put': 'PUT',
'method.delete': 'DELETE',
'method.patch': 'PATCH',
'monitor.method.get': 'GET',
'monitor.method.post': 'POST',
'monitor.method.put': 'PUT',
'monitor.method.delete': 'DELETE',
'monitor.method.patch': 'PATCH',
// ============ 日志级别 ============
'level.debug': 'DEBUG',
'level.info': 'INFO',
'level.warning': 'WARNING',
'level.error': 'ERROR',
'level.critical': 'CRITICAL',
'monitor.level.debug': 'DEBUG',
'monitor.level.info': 'INFO',
'monitor.level.warning': 'WARNING',
'monitor.level.error': 'ERROR',
'monitor.level.critical': 'CRITICAL',
// ============ 定时任务 ============
'scheduler.rule': '定时规则',
'scheduler.last_run': '最近执行',
'scheduler.max_time': '最大执行时间',
'scheduler.never_run': '从未执行',
'scheduler.default': '默认',
'scheduler.running': '执行中',
'scheduler.not_scheduled': '未计划',
'monitor.scheduler.rule': '定时规则',
'monitor.scheduler.last_run': '最近执行',
'monitor.scheduler.max_time': '最大执行时间',
'monitor.scheduler.never_run': '从未执行',
'monitor.scheduler.default': '默认',
'monitor.scheduler.running': '执行中',
'monitor.scheduler.not_scheduled': '未计划',
// ============ 相对时间 ============
'time.just_now': '刚刚',
'time.minutes_ago': '{n}分钟前',
'time.hours_ago': '{n}小时前',
'time.days_ago': '{n}天前',
'time.day_before_yesterday': '前天',
'monitor.time.just_now': '刚刚',
'monitor.time.minutes_ago': '{n}分钟前',
'monitor.time.hours_ago': '{n}小时前',
'monitor.time.days_ago': '{n}天前',
'monitor.time.day_before_yesterday': '前天',
// ============ 告警类型 ============
'alert.warning': '警告',
'alert.error': '错误',
'alert.critical': '严重',
'alert.normal': '正常',
'monitor.alert.warning': '警告',
'monitor.alert.error': '错误',
'monitor.alert.critical': '严重',
'monitor.alert.normal': '正常',
// ============ 连接状态 ============
'connection.failed': '连接失败',
'connection.reconnecting': '重新连接',
'connection.connected': '已连接',
'connection.disconnected': '断开',
'monitor.connection.failed': '连接失败',
'monitor.connection.reconnecting': '重新连接',
'monitor.connection.connected': '已连接',
'monitor.connection.disconnected': '断开',
// ============ 监控状态 ============
'monitor.paused': '● 监控已暂停(长时间未活动)',
'monitor.panel': '监控面板',
'monitor.last_update': '最后更新',
'monitor.reset_stats_confirm': '确定要重置所有事件统计吗?',
'monitor.monitor.paused': '● 监控已暂停(长时间未活动)',
'monitor.monitor.panel': '监控面板',
'monitor.monitor.last_update': '最后更新',
'monitor.monitor.reset_stats_confirm': '确定要重置所有事件统计吗?',
// ============ 高亮操作 ============
'highlight.on': '已高亮',
'highlight.off': '高亮',
'monitor.highlight.on': '已高亮',
'monitor.highlight.off': '高亮',
// ============ 复制操作 ============
'copy.success': '已复制',
'copy.failed': '复制失败,请手动复制',
'copy.error': '复制失败',
'monitor.copy.success': '已复制',
'monitor.copy.failed': '复制失败,请手动复制',
'monitor.copy.error': '复制失败',
// ============ 选择提示 ============
'prompt.select_date': '请选择日期',
'prompt.fetch_failed': '获取请求记录失败,请稍后重试',
'prompt.fetch_outbound_failed': '获取对外请求记录失败,请稍后重试',
'prompt.reset_failed': '重置失败,请重试',
'monitor.prompt.select_date': '请选择日期',
'monitor.prompt.fetch_failed': '获取请求记录失败,请稍后重试',
'monitor.prompt.fetch_outbound_failed': '获取对外请求记录失败,请稍后重试',
'monitor.prompt.reset_failed': '重置失败,请重试',
// ============ 页面标题 ============
'page.http_requests_log': '接收请求记录',
'page.outbound_requests_log': '发送请求记录',
'monitor.page.http_requests_log': '接收请求记录',
'monitor.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}日',
'monitor.time.tomorrow': '明天',
'monitor.time.day_after_tomorrow': '后天',
'monitor.time.over_24h': '超过24小时',
'monitor.time.expired': '已过期',
'monitor.time.days_hours_later': '{days}天{hours}小时后',
'monitor.time.unknown': '未知',
'monitor.time.month_day': '{month}月{day}日',
// ============ 任务类型 ============
'task.system': '系统',
'task.project': '项目',
'task.avg_time': '平均执行时间',
'monitor.task.system': '系统',
'monitor.task.project': '项目',
'monitor.task.avg_time': '平均执行时间',
// ============ 详情模态框 ============
'modal.outbound_detail': '发送请求详情',
'modal.timestamp': '时间戳:',
'modal.method': '方法:',
'modal.status_code': '状态码:',
'modal.response_time': '响应时间:',
'modal.module': '模块:',
'modal.error_msg': '错误信息:',
'monitor.modal.outbound_detail': '发送请求详情',
'monitor.modal.timestamp': '时间戳:',
'monitor.modal.method': '方法:',
'monitor.modal.status_code': '状态码:',
'monitor.modal.response_time': '响应时间:',
'monitor.modal.module': '模块:',
'monitor.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': '无数据',
'monitor.section.request_headers': '请求头',
'monitor.section.request_body': '请求体',
'monitor.section.response_headers': '响应头',
'monitor.section.response_body': '响应体',
'monitor.section.no_response': '无响应体数据',
'monitor.section.response_failed': '响应体处理失败: ',
'monitor.section.truncated': '[内容已截断,完整内容请导出查看]',
'monitor.section.large_data_warning': '⚠️ 数据量较大({count}字符),可能影响显示性能',
'monitor.section.showing_chars': '仅显示前{count}字符',
'monitor.section.basic_info': '基本信息',
'monitor.section.no_data': '无数据',
// ============ 限流状态 ============
'status.rate_limited': '限流',
'monitor.status.rate_limited': '限流',
// ============ 日志级别选择 ============
'log_level.all_with_debug': '全部级别 (含DEBUG)',
'log_level.error': '错误日志',
'log_level.warning': '警告日志',
'log_level.info': '信息日志',
'log_level.debug': '调试日志',
'monitor.log_level.all_with_debug': '全部级别 (含DEBUG)',
'monitor.log_level.error': '错误日志',
'monitor.log_level.warning': '警告日志',
'monitor.log_level.info': '信息日志',
'monitor.log_level.debug': '调试日志',
// ============ DeadLetter ============
'dl.reprocess': '重新处理',
'dl.reprocess_success': 'DeadLetter重新处理成功',
'dl.reprocess_failed': 'DeadLetter重新处理失败',
'monitor.dl.reprocess': '重新处理',
'monitor.dl.reprocess_success': 'DeadLetter重新处理成功',
'monitor.dl.reprocess_failed': 'DeadLetter重新处理失败',
// ============ 后端告警消息映射 ============
'alert.scheduler_not_running': '调度器未运行',
'alert.db_connection_failed': '数据库连接失败',
'alert.redis_connection_failed': 'Redis连接失败',
'alert.event_listener_stopped': '事件监听器已停止',
'alert.binlog_listener_stopped': 'Binlog监听器已停止',
'alert.high_error_rate': '错误率过高',
'alert.memory_warning': '内存使用警告',
'alert.cpu_warning': 'CPU使用警告',
'monitor.alert.scheduler_not_running': '调度器未运行',
'monitor.alert.db_connection_failed': '数据库连接失败',
'monitor.alert.redis_connection_failed': 'Redis连接失败',
'monitor.alert.event_listener_stopped': '事件监听器已停止',
'monitor.alert.binlog_listener_stopped': 'Binlog监听器已停止',
'monitor.alert.high_error_rate': '错误率过高',
'monitor.alert.memory_warning': '内存使用警告',
'monitor.alert.cpu_warning': 'CPU使用警告',
// ================================================================================
// MDS模块 (数据清洗管理系统)
@@ -659,5 +669,12 @@ window.__i18n_zh_CN__ = {
'mds.other.tip': '提示',
'mds.other.warning': '警告',
'mds.other.error': '错误',
'mds.other.success': '成功'
'mds.other.success': '成功',
// ============ 补充翻译键 ============
'monitor.other.yes': '是',
'monitor.other.no': '否',
'monitor.other.data_points': '个时间点',
'monitor.col.is_slow': '是否慢请求',
'monitor.col.is_error': '是否错误'
};
+344 -185
View File
@@ -842,8 +842,8 @@
<div class="history-logs-container">
<div class="history-logs-header">
<div class="history-logs-header-left">
<h1 data-i18n="page.history_logs">📊 日志历史查询</h1>
<span class="badge" id="history-query-badge" data-i18n="other.linked_query">联动查询</span>
<h1 data-i18n="monitor.page.history_logs">📊 日志历史查询</h1>
<span class="badge" id="history-query-badge" data-i18n="monitor.other.linked_query">联动查询</span>
</div>
<div class="history-logs-header-right">
<select id="lang-selector" class="lang-selector" onchange="i18n.switchLanguage(this.value)">
@@ -858,7 +858,7 @@
</div>
<div class="query-filters">
<select id="history-log-level" class="filter-select">
<option value="" data-i18n="filter.level">全部级别</option>
<option value="" data-i18n="monitor.filter.level">全部级别</option>
<option value="DEBUG">≥ DEBUG</option>
<option value="INFO">≥ INFO</option>
<option value="WARNING">≥ WARNING</option>
@@ -866,19 +866,19 @@
<option value="CRITICAL">≥ CRITICAL</option>
</select>
<select id="history-query-type" class="filter-select">
<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>
<option value="all" data-i18n="monitor.filter.type">全部数据</option>
<option value="http" data-i18n="monitor.tab.http">接收请求</option>
<option value="outbound" data-i18n="monitor.tab.outbound">发送请求</option>
<option value="logs" data-i18n="monitor.tab.logs">系统日志</option>
</select>
</div>
<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>
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('10m')" data-i18n="monitor.time.last_10m">10分钟</button>
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('30m')" data-i18n="monitor.time.last_30m">30分钟</button>
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('1h')" data-i18n="monitor.time.last_1h">1小时</button>
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('6h')" data-i18n="monitor.time.last_6h">6小时</button>
<button class="btn btn-secondary header-btn" onclick="quickTimeRange('24h')" data-i18n="monitor.time.last_24h">24小时</button>
<button class="btn btn-secondary header-btn header-btn-reset" onclick="resetHistoryQuery()" data-i18n="monitor.btn.reset">重置</button>
<button class="btn btn-primary header-btn" id="query-btn" onclick="executeHistoryQuery()" data-i18n="monitor.btn.query">查询</button>
</div>
<div id="precise-mode-container"></div>
</div>
@@ -887,36 +887,36 @@
<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> <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>
<button class="tab-btn active" data-tab="logs" onclick="switchResultTab('logs')"><i class="bi bi-journal-text"></i> <span data-i18n="monitor.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="monitor.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="monitor.tab.outbound">发送请求</span></button>
<button class="tab-btn" data-tab="timeline" onclick="switchResultTab('timeline')"><i class="bi bi-clock-history"></i> <span data-i18n="monitor.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="monitor.tab.chart">图表分析</span></button>
</div>
<div class="result-tabs-right">
<div class="result-summary-inline">
<span class="summary-label">接收:</span>
<span class="summary-label" data-i18n="monitor.metric.http_requests">接收:</span>
<span class="badge badge-info" id="http-count">0</span>
<span class="summary-label">发送:</span>
<span class="summary-label" data-i18n="monitor.metric.outbound_requests">发送:</span>
<span class="badge badge-info" id="outbound-count">0</span>
<span class="summary-label">日志:</span>
<span class="summary-label" data-i18n="monitor.tab.logs">日志:</span>
<span class="badge badge-info" id="logs-count">0</span>
<span class="summary-separator" id="logs-levels-separator" style="display:none;">|</span>
<span class="summary-label" id="logs-levels-label" style="display:none;">级别:</span>
<span class="summary-label debug" id="logs-debug-label" style="display:none;">DEBUG:</span>
<span class="summary-label" id="logs-levels-label" style="display:none;" data-i18n="monitor.col.level">级别:</span>
<span class="summary-label debug" id="logs-debug-label" style="display:none;" data-i18n="monitor.level.debug">DEBUG:</span>
<span class="badge badge-debug" id="logs-debug-count" style="display:none;">0</span>
<span class="summary-label info" id="logs-info-label" style="display:none;">INFO:</span>
<span class="summary-label info" id="logs-info-label" style="display:none;" data-i18n="monitor.level.info">INFO:</span>
<span class="badge badge-info" id="logs-info-count" style="display:none;">0</span>
<span class="summary-label warning" id="logs-warning-label" style="display:none;">WARNING:</span>
<span class="summary-label warning" id="logs-warning-label" style="display:none;" data-i18n="monitor.level.warning">WARNING:</span>
<span class="badge badge-warning" id="logs-warning-count" style="display:none;">0</span>
<span class="summary-label error" id="logs-error-label" style="display:none;">ERROR:</span>
<span class="summary-label error" id="logs-error-label" style="display:none;" data-i18n="monitor.level.error">ERROR:</span>
<span class="badge badge-error" id="logs-error-count" style="display:none;">0</span>
<span class="summary-label critical" id="logs-critical-label" style="display:none;">CRITICAL:</span>
<span class="summary-label critical" id="logs-critical-label" style="display:none;" data-i18n="monitor.level.critical">CRITICAL:</span>
<span class="badge badge-critical" id="logs-critical-count" style="display:none;">0</span>
</div>
<div class="export-dropdown">
<button class="btn btn-secondary dropdown-toggle" onclick="toggleExportMenu()">
<i class="bi bi-download"></i> 导出
<i class="bi bi-download"></i> <span data-i18n="monitor.btn.export">导出</span>
</button>
<div class="export-menu" id="export-menu" style="display:none;">
<a href="javascript:void(0)" onclick="exportData('logs', 'csv')">导出系统日志 CSV</a>
@@ -929,34 +929,34 @@
</div>
<div class="pagination-container-inline" id="http-pagination" style="display:none;">
<select id="http-page-size" class="page-size-select-small" onchange="changePageSize('http')">
<option value="50">50条/页</option>
<option value="100">100条/页</option>
<option value="200">200条/页</option>
<option value="500">500条/页</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
</select>
<span class="pagination-text"> <input type="number" id="http-page-input" class="page-input-small" min="1" onchange="goToPage('http', this.value)"> / <span id="http-total-pages">1</span></span>
<span class="pagination-text"><span data-i18n="monitor.pagination.page">Page</span> <input type="number" id="http-page-input" class="page-input-small" min="1" onchange="goToPage('http', this.value)"> / <span id="http-total-pages">1</span></span>
<button class="btn-page-small" onclick="goToPrevPage('http')" id="http-prev-btn"></button>
<button class="btn-page-small" onclick="goToNextPage('http')" id="http-next-btn"></button>
</div>
<div class="pagination-container-inline" id="outbound-pagination" style="display:none;">
<select id="outbound-page-size" class="page-size-select-small" onchange="changePageSize('outbound')">
<option value="50">50条/页</option>
<option value="100">100条/页</option>
<option value="200">200条/页</option>
<option value="500">500条/页</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
</select>
<span class="pagination-text"> <input type="number" id="outbound-page-input" class="page-input-small" min="1" onchange="goToPage('outbound', this.value)"> / <span id="outbound-total-pages">1</span></span>
<span class="pagination-text"><span data-i18n="monitor.pagination.page">Page</span> <input type="number" id="outbound-page-input" class="page-input-small" min="1" onchange="goToPage('outbound', this.value)"> / <span id="outbound-total-pages">1</span></span>
<button class="btn-page-small" onclick="goToPrevPage('outbound')" id="outbound-prev-btn"></button>
<button class="btn-page-small" onclick="goToNextPage('outbound')" id="outbound-next-btn"></button>
</div>
<div class="pagination-container-inline" id="logs-pagination" style="display:none;">
<select id="logs-page-size" class="page-size-select-small" onchange="changePageSize('logs')">
<option value="50">50条/页</option>
<option value="100">100条/页</option>
<option value="200">200条/页</option>
<option value="500">500条/页</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
</select>
<span class="pagination-text"> <input type="number" id="logs-page-input" class="page-input-small" min="1" onchange="goToPage('logs', this.value)"> / <span id="logs-total-pages">1</span></span>
<span class="pagination-text"><span data-i18n="monitor.pagination.page">Page</span> <input type="number" id="logs-page-input" class="page-input-small" min="1" onchange="goToPage('logs', this.value)"> / <span id="logs-total-pages">1</span></span>
<button class="btn-page-small" onclick="goToPrevPage('logs')" id="logs-prev-btn"></button>
<button class="btn-page-small" onclick="goToNextPage('logs')" id="logs-next-btn"></button>
</div>
@@ -966,11 +966,11 @@
<div class="result-tab-content" id="tab-http">
<div class="tab-filters">
<div class="filter-item">
<label class="filter-label">关键词:</label>
<label class="filter-label" data-i18n="monitor.filter.keyword">关键词:</label>
<input type="text" id="filter-keyword-http" class="filter-input" placeholder="搜索路径/请求体/响应体" style="width:300px;">
</div>
<div class="filter-item">
<label class="filter-label">方法:</label>
<label class="filter-label" data-i18n="monitor.filter.method">方法:</label>
<select id="filter-method" class="filter-select" style="width:90px;">
<option value="">全部</option>
<option value="GET">GET</option>
@@ -981,11 +981,11 @@
</select>
</div>
<div class="filter-item">
<label class="filter-label">IP:</label>
<label class="filter-label" data-i18n="monitor.filter.client_ip">IP:</label>
<input type="text" id="filter-client-ip" class="filter-input" placeholder="如:192.168.1.1" style="min-width:100px;">
</div>
<div class="filter-item">
<label class="filter-label">状态码:</label>
<label class="filter-label" data-i18n="monitor.col.status_code">状态码:</label>
<div class="range-input-group">
<input type="number" id="filter-status-min" class="filter-input-small" placeholder="最小" min="100" max="999" style="width:90px;">
<span class="range-separator">-</span>
@@ -993,7 +993,7 @@
</div>
</div>
<div class="filter-item">
<label class="filter-label">耗时(ms):</label>
<label class="filter-label" data-i18n="monitor.col.duration">耗时(ms):</label>
<div class="range-input-group">
<input type="number" id="filter-duration-min" class="filter-input-small" placeholder="最小" min="0" style="width:90px;">
<span class="range-separator">-</span>
@@ -1005,25 +1005,25 @@
</div>
<div class="filter-item">
<select id="saved-templates" class="filter-select" onchange="loadQueryTemplate()" style="max-width:120px;">
<option value="" data-i18n="template.saved_queries">已保存查询...</option>
<option value="" data-i18n="monitor.template.saved_queries">已保存查询...</option>
</select>
</div>
</div>
<table class="history-table" id="http-results-table" data-result-table>
<thead>
<tr>
<th>序号</th>
<th>时间</th>
<th>方法</th>
<th>端点</th>
<th>状态码</th>
<th>响应时间</th>
<th>客户端IP</th>
<th>操作</th>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.time">时间</th>
<th data-i18n="monitor.col.method">方法</th>
<th data-i18n="monitor.col.path">端点</th>
<th data-i18n="monitor.col.status_code">状态码</th>
<th data-i18n="monitor.col.duration">响应时间</th>
<th data-i18n="monitor.col.client_ip">客户端IP</th>
<th data-i18n="monitor.col.operation">操作</th>
</tr>
</thead>
<tbody id="http-results-body">
<tr><td colspan="8" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
<tr><td colspan="8" class="no-data" data-i18n="monitor.status.no_data">暂无数据</td></tr>
</tbody>
</table>
</div>
@@ -1031,11 +1031,11 @@
<div class="result-tab-content" id="tab-outbound">
<div class="tab-filters">
<div class="filter-item">
<label class="filter-label">关键词:</label>
<label class="filter-label" data-i18n="monitor.filter.keyword">关键词:</label>
<input type="text" id="filter-keyword-outbound" class="filter-input" placeholder="搜索URL/请求体/响应体" style="width:300px;">
</div>
<div class="filter-item">
<label class="filter-label">方法:</label>
<label class="filter-label" data-i18n="monitor.filter.method">方法:</label>
<select id="filter-method-outbound" class="filter-select" style="width:90px;">
<option value="">全部</option>
<option value="GET">GET</option>
@@ -1046,7 +1046,7 @@
</select>
</div>
<div class="filter-item">
<label class="filter-label">状态码:</label>
<label class="filter-label" data-i18n="monitor.col.status_code">状态码:</label>
<div class="range-input-group">
<input type="number" id="filter-status-min-outbound" class="filter-input-small" placeholder="最小" min="100" max="999" style="width:90px;">
<span class="range-separator">-</span>
@@ -1054,7 +1054,7 @@
</div>
</div>
<div class="filter-item">
<label class="filter-label">耗时(ms):</label>
<label class="filter-label" data-i18n="monitor.col.duration">耗时(ms):</label>
<div class="range-input-group">
<input type="number" id="filter-duration-min-outbound" class="filter-input-small" placeholder="最小" min="0" style="width:90px;">
<span class="range-separator">-</span>
@@ -1065,18 +1065,18 @@
<table class="history-table" id="outbound-results-table" data-result-table>
<thead>
<tr>
<th>序号</th>
<th>时间</th>
<th>方法</th>
<th>URL</th>
<th>状态码</th>
<th>响应时间</th>
<th>模块</th>
<th>操作</th>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.time">时间</th>
<th data-i18n="monitor.col.method">方法</th>
<th data-i18n="monitor.col.URL">URL</th>
<th data-i18n="monitor.col.status_code">状态码</th>
<th data-i18n="monitor.col.duration">响应时间</th>
<th data-i18n="monitor.col.模块">模块</th>
<th data-i18n="monitor.col.operation">操作</th>
</tr>
</thead>
<tbody id="outbound-results-body">
<tr><td colspan="8" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
<tr><td colspan="8" class="no-data" data-i18n="monitor.status.no_data">暂无数据</td></tr>
</tbody>
</table>
</div>
@@ -1084,27 +1084,27 @@
<div class="result-tab-content active" id="tab-logs">
<div class="tab-filters">
<div class="filter-item">
<label class="filter-label">关键词:</label>
<input type="text" id="filter-keyword" class="filter-input" placeholder="搜索消息内容" style="width:300px;">
<label class="filter-label" data-i18n="monitor.filter.keyword">关键词:</label>
<input type="text" id="filter-keyword" class="filter-input" data-i18n-placeholder="monitor.filter.keyword_placeholder" placeholder="搜索消息内容" style="width:300px;">
</div>
<div class="filter-item">
<label class="filter-label">模块:</label>
<input type="text" id="filter-module" class="filter-input" placeholder="多个用逗号分隔" style="min-width:300px;">
<label class="filter-label" data-i18n="monitor.filter.module">模块:</label>
<input type="text" id="filter-module" class="filter-input" data-i18n-placeholder="monitor.filter.module_placeholder" placeholder="多个用逗号分隔" style="min-width:300px;">
</div>
</div>
<table class="history-table" id="logs-results-table" data-result-table>
<thead>
<tr>
<th>序号</th>
<th>时间</th>
<th>级别</th>
<th>模块</th>
<th>消息</th>
<th>操作</th>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.time">时间</th>
<th data-i18n="monitor.col.level">级别</th>
<th data-i18n="monitor.col.module">模块</th>
<th data-i18n="monitor.col.message">消息</th>
<th data-i18n="monitor.col.operation">操作</th>
</tr>
</thead>
<tbody id="logs-results-body">
<tr><td colspan="6" class="no-data" data-i18n="status.no_data">暂无数据</td></tr>
<tr><td colspan="6" class="no-data" data-i18n="monitor.status.no_data">暂无数据</td></tr>
</tbody>
</table>
</div>
@@ -1112,7 +1112,7 @@
<!-- 时间线标签页 -->
<div class="result-tab-content" id="tab-timeline">
<div class="timeline-container" id="timeline-container">
<div class="timeline-empty" data-i18n="timeline.no_data">暂无数据,请先执行查询</div>
<div class="timeline-empty" data-i18n="monitor.timeline.no_data">暂无数据,请先执行查询</div>
</div>
</div>
@@ -1121,21 +1121,21 @@
<div class="chart-container">
<div class="chart-row">
<div class="chart-card">
<h4>请求量趋势</h4>
<h4 data-i18n="monitor.chart.request_trend">📊 请求量趋势</h4>
<canvas id="chart-trend"></canvas>
</div>
<div class="chart-card">
<h4>日志级别分布</h4>
<h4 data-i18n="monitor.chart.level_distribution">📊 日志级别分布</h4>
<canvas id="chart-levels"></canvas>
</div>
</div>
<div class="chart-row">
<div class="chart-card">
<h4>状态码分布</h4>
<h4 data-i18n="monitor.chart.status_distribution">📈 状态码分布</h4>
<canvas id="chart-status"></canvas>
</div>
<div class="chart-card">
<h4>慢请求TOP10</h4>
<h4 data-i18n="monitor.chart.slow_requests">⏱️ 慢请求TOP10</h4>
<canvas id="chart-slow"></canvas>
</div>
</div>
@@ -1181,7 +1181,7 @@
const endDate = new Date(endTime);
if (startDate >= endDate) {
alert(i18n.t('error.time_range_invalid'));
alert(i18n.t('monitor.error.time_range_invalid'));
return;
}
}
@@ -1194,14 +1194,14 @@
try {
const badge = document.getElementById('history-query-badge');
if (badge) {
badge.textContent = i18n.t('status.querying');
badge.textContent = i18n.t('monitor.status.querying');
badge.className = 'badge warning';
}
const queryBtn = document.getElementById('query-btn');
if (queryBtn) {
queryBtn.disabled = true;
queryBtn.textContent = i18n.t('status.querying');
queryBtn.textContent = i18n.t('monitor.status.querying');
}
let url = `${API_BASE}/history/query?timezone_offset=${timezoneOffset}`;
@@ -1306,29 +1306,29 @@
switchResultTab(currentTab);
if (badge) {
badge.textContent = i18n.t('success.query_complete');
badge.textContent = i18n.t('monitor.success.query_complete');
badge.className = 'badge healthy';
}
if (queryBtn) {
queryBtn.disabled = false;
queryBtn.textContent = i18n.t('btn.query');
queryBtn.textContent = i18n.t('monitor.btn.query');
}
} catch (error) {
console.error('执行历史查询失败:', error);
alert(i18n.t('error.query_failed'));
alert(i18n.t('monitor.error.query_failed'));
const badge = document.getElementById('history-query-badge');
if (badge) {
badge.textContent = i18n.t('error.query_failed');
badge.textContent = i18n.t('monitor.error.query_failed');
badge.className = 'badge error';
}
const queryBtn = document.getElementById('query-btn');
if (queryBtn) {
queryBtn.disabled = false;
queryBtn.textContent = i18n.t('btn.query');
queryBtn.textContent = i18n.t('monitor.btn.query');
}
}
}
@@ -1353,9 +1353,9 @@
document.getElementById('result-tabs').style.display = 'none';
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>';
document.getElementById('http-results-body').innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
document.getElementById('outbound-results-body').innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
document.getElementById('logs-results-body').innerHTML = '<tr><td colspan="6" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
historyQueryData = { http: [], outbound: [], logs: [] };
// 清理缓存的排序数据
@@ -1365,7 +1365,7 @@
const badge = document.getElementById('history-query-badge');
if (badge) {
badge.textContent = i18n.t('other.linked_query');
badge.textContent = i18n.t('monitor.other.linked_query');
badge.className = '';
}
}
@@ -1553,7 +1553,7 @@
const endTime = document.getElementById('history-end-time').value;
if (!startTime || !endTime) {
alert(i18n.t('error.time_range_required'));
alert(i18n.t('monitor.error.time_range_required'));
return;
}
@@ -1630,25 +1630,93 @@
// 时间线渲染
let timelineData = [];
async function loadTimeline() {
const startTime = document.getElementById('history-start-time').value;
const endTime = document.getElementById('history-end-time').value;
if (!startTime || !endTime) return;
let url = `${API_BASE}/history/timeline?start_time=${encodeURIComponent(startTime)}&end_time=${encodeURIComponent(endTime)}&limit=200`;
const logLevel = document.getElementById('history-log-level').value;
if (logLevel) url += `&level=${logLevel}`;
try {
const response = await fetch(url);
const data = await response.json();
timelineData = data.events || [];
function loadTimeline() {
// 复用已查询的数据,避免重复请求
if (!historyQueryData || (!historyQueryData.http?.length && !historyQueryData.outbound?.length && !historyQueryData.logs?.length)) {
timelineData = [];
renderTimeline();
} catch (error) {
console.error('加载时间线失败:', error);
return;
}
// 转换为统一事件格式(与后端逻辑一致)
const events = [];
// HTTP请求事件
for (const req of historyQueryData.http || []) {
events.push({
id: `http_${req.id}`,
timestamp: req.timestamp,
type: 'http',
icon: '📥',
level: req.status_code < 400 ? 'INFO' : 'ERROR',
summary: `${req.method} ${req.path}`,
detail: {
method: req.method,
path: req.path,
status_code: req.status_code,
duration: req.response_time,
client_ip: req.client_ip,
is_error: req.is_error,
is_slow: req.is_slow
}
});
}
// 发送请求事件
for (const req of historyQueryData.outbound || []) {
events.push({
id: `outbound_${req.id}`,
timestamp: req.timestamp,
type: 'outbound',
icon: '📤',
level: req.status_code < 400 ? 'INFO' : 'ERROR',
summary: `${req.method} ${req.url?.substring(0, 80) || ''}`,
detail: {
method: req.method,
url: req.url,
status_code: req.status_code,
duration: req.duration * 1000,
module: req.module,
is_error: req.is_error,
is_slow: req.is_slow
}
});
}
// 系统日志事件
const iconMap = {
'DEBUG': '🔍',
'INFO': '📝',
'WARNING': '⚠️',
'ERROR': '❌',
'CRITICAL': '🔥'
};
for (const log of historyQueryData.logs || []) {
events.push({
id: `log_${log.id}`,
timestamp: log.timestamp,
type: 'log',
icon: iconMap[log.level] || '📝',
level: log.level,
summary: log.message?.substring(0, 100) || '',
detail: {
level: log.level,
module: log.module,
function: log.function,
line_number: log.line_number,
message: log.message,
stack_trace: log.stack_trace
}
});
}
// 按时间戳排序(降序)
events.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
// 应用限制(最多200条)
timelineData = events.slice(0, 200);
renderTimeline();
}
function renderTimeline() {
@@ -1656,25 +1724,63 @@
if (!container) return;
if (timelineData.length === 0) {
container.innerHTML = '<div class="timeline-empty">' + i18n.t('status.no_data') + '</div>';
container.innerHTML = '<div class="timeline-empty">' + i18n.t('monitor.status.no_data') + '</div>';
return;
}
// 异常检测
const anomalies = detectAnomalies(timelineData);
// 异常摘要
let summaryHtml = '';
// 统计摘要(一次遍历完成所有统计)
const stats = { total: timelineData.length, http: 0, outbound: 0, log: 0, errors: 0, warnings: 0, slow: 0 };
for (const e of timelineData) {
if (e.type === 'http') stats.http++;
else if (e.type === 'outbound') stats.outbound++;
else if (e.type === 'log') stats.log++;
if (e.level === 'ERROR' || e.level === 'CRITICAL') stats.errors++;
else if (e.level === 'WARNING') stats.warnings++;
if (e.detail?.is_slow) stats.slow++;
}
// 统计摘要HTML
const summaryTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.summary_title') : '📊 时间线统计';
const totalLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.total_events') : '总事件';
const httpLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.http_requests') : 'HTTP请求';
const outboundLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.outbound_requests') : '发送请求';
const logLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.system_logs') : '系统日志';
const errorLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.errors') : '错误';
const warningLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.warnings') : '警告';
const slowLabel = typeof i18n !== 'undefined' ? i18n.t('monitor.timeline.slow_requests') : '慢请求';
let summaryHtml = `<div style="background:#f0f9ff;border:1px solid #bae6fd;padding:12px;margin-bottom:16px;border-radius:6px;">
<strong style="color:#0369a1;">${summaryTitle}</strong>
<div style="display:flex;gap:16px;margin-top:8px;font-size:12px;color:#0c4a6e;flex-wrap:wrap;">
<span><strong>${stats.total}</strong> ${totalLabel}</span>
<span>📥 <strong>${stats.http}</strong> ${httpLabel}</span>
<span>📤 <strong>${stats.outbound}</strong> ${outboundLabel}</span>
<span>📝 <strong>${stats.log}</strong> ${logLabel}</span>
${stats.errors > 0 ? `<span style="color:#dc2626;">❌ <strong>${stats.errors}</strong> ${errorLabel}</span>` : ''}
${stats.warnings > 0 ? `<span style="color:#f59e0b;">⚠️ <strong>${stats.warnings}</strong> ${warningLabel}</span>` : ''}
${stats.slow > 0 ? `<span style="color:#ef4444;">⏱️ <strong>${stats.slow}</strong> ${slowLabel}</span>` : ''}
</div>
</div>`;
// 异常告警HTML(如果有异常)
if (anomalies.length > 0) {
summaryHtml = `<div style="background:#fef2f2;border:1px solid #fecaca;padding:12px;margin-bottom:16px;border-radius:6px;">
<strong style="color:#dc2626;">⚠️ 发现 ${anomalies.length} 处异常</strong>
const anomalyText = typeof i18n !== 'undefined' ?
i18n.t('monitor.timeline.anomaly_detected', {count: anomalies.length}) :
`发现 ${anomalies.length} 处异常`;
summaryHtml += `<div style="background:#fef2f2;border:1px solid #fecaca;padding:12px;margin-bottom:16px;border-radius:6px;">
<strong style="color:#dc2626;">⚠️ ${anomalyText}</strong>
<div style="font-size:12px;color:#991b1b;margin-top:4px;">
${anomalies.map(a => a.description).join('、')}
</div>
</div>`;
}
container.innerHTML = summaryHtml + timelineData.map(event => {
const renderedHtml = timelineData.map(event => {
const isError = event.level === 'ERROR' || event.level === 'CRITICAL';
const isWarning = event.level === 'WARNING';
const isAnomaly = anomalies.some(a => a.eventId === event.id);
@@ -1692,6 +1798,8 @@
</div>
`;
}).join('');
container.innerHTML = summaryHtml + renderedHtml;
}
// 异常检测算法
@@ -1726,10 +1834,13 @@
const threshold = avg * 3;
events.forEach(e => {
if (e.type === 'http' && e.detail?.duration > threshold) {
const slowReqText = typeof i18n !== 'undefined' ?
i18n.t('monitor.timeline.slow_anomaly', {duration: e.detail.duration.toFixed(0), avg: avg.toFixed(0)}) :
`异常慢请求(${e.detail.duration.toFixed(0)}ms > 平均${avg.toFixed(0)}ms×3)`;
anomalies.push({
eventId: e.id,
type: 'slow_anomaly',
description: `异常慢请求(${e.detail.duration.toFixed(0)}ms > 平均${avg.toFixed(0)}ms×3)`
description: slowReqText
});
}
});
@@ -1746,10 +1857,13 @@
});
Object.entries(errorMessages).forEach(([msg, ids]) => {
if (ids.length >= 3) {
const dupErrorText = typeof i18n !== 'undefined' ?
i18n.t('monitor.timeline.duplicate_error', {msg: msg.substring(0, 20), count: ids.length}) :
`重复错误"${msg.substring(0, 20)}..."出现${ids.length}`;
anomalies.push({
eventId: ids[ids.length - 1],
type: 'duplicate_error',
description: `重复错误"${msg.substring(0, 20)}..."出现${ids.length}`
description: dupErrorText
});
}
});
@@ -1769,7 +1883,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;">' + i18n.t('chart.query_required') + '</div>';
document.getElementById('tab-chart').innerHTML = '<div style="padding:40px;text-align:center;color:#999;">' + i18n.t('monitor.chart.query_required') + '</div>';
return;
}
@@ -1797,8 +1911,9 @@
<div style="background:${color};width:${pct}%;height:100%;transition:width 0.3s;"></div>
</div>
</div>`;
}).join('') || '<div style="color:#999;text-align:center;">无数据</div>';
levelCanvas.parentElement.innerHTML = '<h4>📊 日志级别分布</h4><div style="padding:8px 0;">' + levelHtml + '</div>';
}).join('') || `<div style="color:#999;text-align:center;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_data') : '无数据'}</div>`;
const levelTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.level_distribution') : '📊 日志级别分布';
levelCanvas.parentElement.innerHTML = `<h4>${levelTitle}</h4><div style="padding:8px 0;">` + levelHtml + '</div>';
}
// 渲染状态码分布(饼图样式)
@@ -1815,8 +1930,9 @@
<div style="width:60px;height:60px;border-radius:50%;background:${color};display:flex;align-items:center;justify-content:center;color:white;font-weight:bold;font-size:14px;">${pct}%</div>
<div style="margin-top:4px;font-size:12px;">${k} (${v})</div>
</div>`;
}).join('') || '<div style="color:#999;text-align:center;">无数据</div>';
statusCanvas.parentElement.innerHTML = '<h4>📈 状态码分布</h4><div style="text-align:center;padding:8px 0;">' + statusHtml + '</div>';
}).join('') || `<div style="color:#999;text-align:center;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_data') : '无数据'}</div>`;
const statusTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.status_distribution') : '📈 状态码分布';
statusCanvas.parentElement.innerHTML = `<h4>${statusTitle}</h4><div style="text-align:center;padding:8px 0;">` + statusHtml + '</div>';
}
// 渲染慢请求TOP10(表格)
@@ -1838,8 +1954,9 @@
</tr>`).join('')}
</tbody>
</table>`
: '<div style="color:#22c55e;text-align:center;padding:20px;">✅ 无慢请求</div>';
slowCanvas.parentElement.innerHTML = '<h4>⏱️ 慢请求TOP10</h4>' + slowHtml;
: `<div style="color:#22c55e;text-align:center;padding:20px;">✅ ${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_slow') : '无慢请求'}</div>`;
const slowTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.slow_requests') : '⏱️ 慢请求TOP10';
slowCanvas.parentElement.innerHTML = `<h4>${slowTitle}</h4>` + slowHtml;
}
// 渲染趋势摘要
@@ -1850,23 +1967,24 @@
const trendHtml = trend.length > 0
? `<div style="padding:12px;">
<div style="display:flex;justify-content:space-around;text-align:center;">
<div><div style="font-size:28px;font-weight:bold;color:#3b82f6;">${summary.total_requests || 0}</div><div style="font-size:12px;color:#6b7280;">总请求</div></div>
<div><div style="font-size:28px;font-weight:bold;color:#ef4444;">${summary.error_count || 0}</div><div style="font-size:12px;color:#6b7280;">错误</div></div>
<div><div style="font-size:28px;font-weight:bold;color:#f59e0b;">${summary.slow_count || 0}</div><div style="font-size:12px;color:#6b7280;">慢请求</div></div>
<div><div style="font-size:28px;font-weight:bold;color:#3b82f6;">${summary.total_requests || 0}</div><div style="font-size:12px;color:#6b7280;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.total_requests') : '总请求'}</div></div>
<div><div style="font-size:28px;font-weight:bold;color:#ef4444;">${summary.error_count || 0}</div><div style="font-size:12px;color:#6b7280;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.error_count') : '错误'}</div></div>
<div><div style="font-size:28px;font-weight:bold;color:#f59e0b;">${summary.slow_count || 0}</div><div style="font-size:12px;color:#6b7280;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.slow_count') : '慢请求'}</div></div>
</div>
<div style="margin-top:12px;padding-top:12px;border-top:1px solid #e5e7eb;font-size:12px;color:#6b7280;">
时间范围: ${trend.length}个时间点 | 日志总数: ${summary.total_logs || 0}
${typeof i18n !== 'undefined' ? i18n.t('monitor.stats.time_range') : '时间范围'}: ${trend.length}${typeof i18n !== 'undefined' ? i18n.t('monitor.other.data_points') : '个时间点'} | ${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.log_count') : '日志总数'}: ${summary.total_logs || 0}${typeof i18n !== 'undefined' ? i18n.t('monitor.pagination.items') : '条'}
</div>
</div>`
: '<div style="color:#999;text-align:center;padding:20px;">无趋势数据</div>';
trendCanvas.parentElement.innerHTML = '<h4>📊 请求量趋势</h4>' + trendHtml;
: `<div style="color:#999;text-align:center;padding:20px;">${typeof i18n !== 'undefined' ? i18n.t('monitor.chart.no_trend') : '无趋势数据'}</div>`;
const trendTitle = typeof i18n !== 'undefined' ? i18n.t('monitor.chart.request_trend') : '📊 请求量趋势';
trendCanvas.parentElement.innerHTML = `<h4>${trendTitle}</h4>` + trendHtml;
}
} catch (error) {
console.error('渲染图表失败:', error);
const chartContainer = document.querySelector('.chart-container');
if (chartContainer) {
chartContainer.innerHTML = '<div style="padding:40px;text-align:center;color:#ef4444;">' + i18n.t('chart.load_failed') + '</div>';
chartContainer.innerHTML = '<div style="padding:40px;text-align:center;color:#ef4444;">' + i18n.t('monitor.chart.load_failed') + '</div>';
}
}
}
@@ -1882,11 +2000,11 @@
const btn = document.getElementById('realtime-btn');
if (realtimeTracking) {
btn.textContent = i18n.t('btn.realtime_on');
btn.textContent = i18n.t('monitor.btn.realtime_on');
btn.className = 'btn btn-primary';
startRealtimeTracking();
} else {
btn.textContent = i18n.t('btn.realtime_off');
btn.textContent = i18n.t('monitor.btn.realtime_off');
btn.className = 'btn btn-secondary';
stopRealtimeTracking();
}
@@ -1902,7 +2020,7 @@
// 10分钟自动暂停
if (trackingDuration >= 600) {
stopRealtimeTracking();
alert(i18n.t('error.auto_pause'));
alert(i18n.t('monitor.error.auto_pause'));
return;
}
@@ -1938,7 +2056,7 @@
}
realtimeTracking = false;
const btn = document.getElementById('realtime-btn');
btn.textContent = i18n.t('btn.realtime_off');
btn.textContent = i18n.t('monitor.btn.realtime_off');
btn.className = 'btn btn-secondary';
}
@@ -1964,14 +2082,14 @@
if (!select) return;
const templates = getTemplates();
select.innerHTML = '<option value="">' + i18n.t('template.saved_queries') + '</option>' +
select.innerHTML = '<option value="">' + i18n.t('monitor.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(i18n.t('error.max_templates'));
alert(i18n.t('monitor.error.max_templates'));
return;
}
@@ -1997,7 +2115,7 @@
templates.push(template);
saveTemplates(templates);
alert(i18n.t('success.template_saved', {name: name}));
alert(i18n.t('monitor.success.template_saved', {name: name}));
}
function loadQueryTemplate() {
@@ -2133,7 +2251,7 @@
if (!tbody) return;
if (historyQueryData.http.length === 0) {
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
sortedHttpData = [];
return;
}
@@ -2170,7 +2288,7 @@
if (!tbody) return;
if (historyQueryData.outbound.length === 0) {
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
tbody.innerHTML = '<tr><td colspan="8" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
sortedOutboundData = [];
return;
}
@@ -2207,7 +2325,7 @@
if (!tbody) return;
if (historyQueryData.logs.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="no-data">' + i18n.t('status.no_data') + '</td></tr>';
tbody.innerHTML = '<tr><td colspan="6" class="no-data">' + i18n.t('monitor.status.no_data') + '</td></tr>';
sortedLogsData = [];
return;
}
@@ -2255,26 +2373,40 @@
return pairs.length > 0 ? '?' + pairs.join('&') : '-';
}
function showHistoryHttpDetail(index) {
async function showHistoryHttpDetail(index) {
const req = sortedHttpData[index];
if (!req) return;
// 懒加载:如果数据不包含request_body,则从API加载完整数据
let detailData = req;
if (!req.hasOwnProperty('request_body')) {
try {
const response = await fetch(`${API_BASE}/history/http/${req.id}`);
detailData = await response.json();
} catch (error) {
console.error('加载详情失败:', error);
alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
return;
}
}
const detail = `
<div style="max-height: 60vh; overflow-y: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">请求ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求方法</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;"><span class="api-method ${(req.method || '').toLowerCase()}">${req.method || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求路径</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.path || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">查询参数</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${formatQueryParams(req.query_params)}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">状态码</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono api-status ${(req.status_code || 0) >= 400 ? 'error' : 'success'}">${req.status_code || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">响应时间</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.duration ? req.duration.toFixed(2) + 'ms' : '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">客户端IP</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.client_ip || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">User Agent</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.user_agent || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">是否慢请求</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${req.is_slow ? '是' : '否'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">是否错误</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${req.is_error ? '是' : '否'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">错误信息</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #f5222d;" class="font-mono">${req.error_message || '-'}</td></tr>
<tr><td style="padding: 8px; color: #8c8c8c;">请求体</td><td style="padding: 8px;" class="font-mono">${req.request_body || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">请求ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求方法</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;"><span class="api-method ${(detailData.method || '').toLowerCase()}">${detailData.method || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求路径</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.path || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">查询参数</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${formatQueryParams(detailData.query_params)}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">状态码</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono api-status ${(detailData.status_code || 0) >= 400 ? 'error' : 'success'}">${detailData.status_code || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">响应时间</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.duration ? detailData.duration.toFixed(2) + 'ms' : '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">客户端IP</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.client_ip || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">User Agent</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.user_agent || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_slow') : '是否慢请求'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${detailData.is_slow ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_error') : '是否错误'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${detailData.is_error ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.error_message') : '错误信息'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #f5222d;" class="font-mono">${detailData.error_message || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求体</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;">${detailData.request_body || '-'}</pre></td></tr>
<tr><td style="padding: 8px; color: #8c8c8c;">响应体</td><td style="padding: 8px;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;">${detailData.response_body || '-'}</pre></td></tr>
</table>
</div>
`;
@@ -2282,25 +2414,38 @@
showDetailModal('接收请求详情', detail);
}
function showHistoryOutboundDetail(index) {
async function showHistoryOutboundDetail(index) {
const req = sortedOutboundData[index];
if (!req) return;
// 懒加载:如果数据不包含request_body,则从API加载完整数据
let detailData = req;
if (!req.hasOwnProperty('request_body')) {
try {
const response = await fetch(`${API_BASE}/history/outbound/${req.id}`);
detailData = await response.json();
} catch (error) {
console.error('加载详情失败:', error);
alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
return;
}
}
const detail = `
<div style="max-height: 60vh; overflow-y: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">请求ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求方法</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;"><span class="api-method ${(req.method || '').toLowerCase()}">${req.method || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求URL</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.url || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">状态码</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono api-status ${(req.status_code || 0) >= 400 ? 'error' : 'success'}">${req.status_code || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">响应时间</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.duration ? req.duration.toFixed(2) + 'ms' : '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">模块</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.module || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">是否慢请求</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${req.is_slow ? '是' : '否'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">是否错误</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${req.is_error ? '是' : '否'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">错误信息</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #f5222d;" class="font-mono">${req.error_message || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求体</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${req.request_body || '-'}</td></tr>
<tr><td style="padding: 8px; color: #8c8c8c;">响应体</td><td style="padding: 8px;" class="font-mono">${req.response_body || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">请求ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求方法</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;"><span class="api-method ${(detailData.method || '').toLowerCase()}">${detailData.method || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求URL</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.url || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">状态码</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono api-status ${(detailData.status_code || 0) >= 400 ? 'error' : 'success'}">${detailData.status_code || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">响应时间</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.duration ? detailData.duration.toFixed(2) + 'ms' : '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">模块</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.module || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_slow') : '是否慢请求'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${detailData.is_slow ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.is_error') : '是否错误'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;">${detailData.is_error ? (typeof i18n !== 'undefined' ? i18n.t('monitor.other.yes') : '是') : (typeof i18n !== 'undefined' ? i18n.t('monitor.other.no') : '否')}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">${typeof i18n !== 'undefined' ? i18n.t('monitor.col.error_message') : '错误信息'}</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #f5222d;" class="font-mono">${detailData.error_message || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">请求体</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;">${detailData.request_body || '-'}</pre></td></tr>
<tr><td style="padding: 8px; color: #8c8c8c;">响应体</td><td style="padding: 8px;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;">${detailData.response_body || '-'}</pre></td></tr>
</table>
</div>
`;
@@ -2308,20 +2453,34 @@
showDetailModal('发送请求详情', detail);
}
function showHistoryLogDetail(index) {
async function showHistoryLogDetail(index) {
const log = sortedLogsData[index];
if (!log) return;
// 懒加载:如果数据不包含stack_trace,则从API加载完整数据
let detailData = log;
if (!log.hasOwnProperty('stack_trace')) {
try {
const response = await fetch(`${API_BASE}/history/log/${log.id}`);
detailData = await response.json();
} catch (error) {
console.error('加载详情失败:', error);
alert(i18n.t('monitor.error.detail_load_failed') || '加载详情失败');
return;
}
}
const detail = `
<div style="max-height: 60vh; overflow-y: auto;">
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">日志ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${log.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${log.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">日志级别</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono"><span class="log-level-badge ${(log.level || '').toLowerCase()}">${log.level || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">模块</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${log.module || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">函数</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${log.function || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">行号</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${log.line_number || '-'}</td></tr>
<tr><td style="padding: 8px; color: #8c8c8c;">消息</td><td style="padding: 8px;" class="font-mono">${log.message || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; width: 120px; color: #8c8c8c;">日志ID</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.id || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">时间戳</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.timestamp || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">日志级别</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono"><span class="log-level-badge ${(detailData.level || '').toLowerCase()}">${detailData.level || '-'}</span></td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">模块</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.module || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">函数</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.function || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">行号</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono">${detailData.line_number || '-'}</td></tr>
<tr><td style="padding: 8px; border-bottom: 1px solid #e8e8e8; color: #8c8c8c;">消息</td><td style="padding: 8px; border-bottom: 1px solid #e8e8e8;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;">${detailData.message || '-'}</pre></td></tr>
${detailData.stack_trace ? `<tr><td style="padding: 8px; color: #8c8c8c;">堆栈</td><td style="padding: 8px;" class="font-mono"><pre style="margin:0;white-space:pre-wrap;word-break:break-all;color:#f5222d;">${detailData.stack_trace}</pre></td></tr>` : ''}
</table>
</div>
`;
+157 -157
View File
@@ -16,13 +16,13 @@
<div class="container">
<header class="header">
<nav class="nav-menu">
<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>
<a href="#" class="nav-item active" data-page="overview" data-i18n="monitor.nav.overview">📊 Overview</a>
<a href="#" class="nav-item" data-page="database" data-i18n="monitor.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="monitor.nav.events">☎️ 事件处理</a>
<a href="#" class="nav-item" data-page="scheduler" data-i18n="monitor.nav.scheduler">⏰ 定时任务</a>
<a href="#" class="nav-item" data-page="api-requests" data-i18n="monitor.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="monitor.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="monitor.nav.logs">📋 日志<span id="logs-badge" class="nav-badge" style="display: none;"></span></a>
</nav>
<div class="header-info">
<select id="lang-selector" class="lang-selector" onchange="i18n.switchLanguage(this.value)">
@@ -30,9 +30,9 @@
<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>&nbsp;&nbsp;--</span>
<button id="refresh-btn" class="btn btn-primary" onclick="refreshAll()" data-i18n="btn.refresh">刷新</button>
<span id="status-indicator" class="status healthy" data-i18n="monitor.status.healthy">● 系统正常</span>
<span id="last-update"><span data-i18n="monitor.other.last_update">最后更新</span>&nbsp;&nbsp;--</span>
<button id="refresh-btn" class="btn btn-primary" onclick="refreshAll()" data-i18n="monitor.btn.refresh">刷新</button>
</div>
</header>
@@ -41,20 +41,20 @@
<!-- 资源监控卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.resource">资源使用</h2>
<span class="badge" id="resource-badge" data-i18n="status.running">运行中</span>
<h2 data-i18n="monitor.card.resource">资源使用</h2>
<span class="badge" id="resource-badge" data-i18n="monitor.status.running">运行中</span>
</div>
<div class="card-body">
<div class="metrics-grid">
<div class="metric-item">
<div class="metric-label" data-i18n="metric.cpu">CPU 使用率</div>
<div class="metric-label" data-i18n="monitor.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" data-i18n="metric.memory">内存使用</div>
<div class="metric-label" data-i18n="monitor.metric.memory">内存使用</div>
<div class="metric-value" id="memory-value">
<span id="memory-usage">-- MB</span>
<span id="memory-percent" class="memory-percent">--%</span>
@@ -64,11 +64,11 @@
</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.threads">线程数</div>
<div class="metric-label" data-i18n="monitor.metric.threads">线程数</div>
<div class="metric-value" id="threads-value">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.uptime">运行时间</div>
<div class="metric-label" data-i18n="monitor.metric.uptime">运行时间</div>
<div class="metric-value" id="uptime-value">--</div>
</div>
</div>
@@ -81,21 +81,21 @@
<!-- 数据库状态卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.db_status">账套状态</h2>
<span class="badge" id="db-badge" data-i18n="status.checking">检查中</span>
<h2 data-i18n="monitor.card.db_status">账套状态</h2>
<span class="badge" id="db-badge" data-i18n="monitor.status.checking">检查中</span>
</div>
<div class="card-body">
<div class="db-summary" id="db-summary">
<div class="summary-item">
<span class="summary-label" data-i18n="metric.total_connections">总连接数</span>
<span class="summary-label" data-i18n="monitor.metric.total_connections">总连接数</span>
<span class="summary-value" id="db-total">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.healthy">健康</span>
<span class="summary-label" data-i18n="monitor.metric.healthy">健康</span>
<span class="summary-value healthy" id="db-healthy">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.unhealthy">异常</span>
<span class="summary-label" data-i18n="monitor.metric.unhealthy">异常</span>
<span class="summary-value error" id="db-unhealthy">--</span>
</div>
</div>
@@ -108,8 +108,8 @@
<!-- 提醒信息卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.recent_alerts">最近告警</h2>
<button class="btn btn-small" onclick="clearAlerts()" data-i18n="btn.clear">清空</button>
<h2 data-i18n="monitor.card.recent_alerts">最近告警</h2>
<button class="btn btn-small" onclick="clearAlerts()" data-i18n="monitor.btn.clear">清空</button>
</div>
<div class="card-body">
<div class="alert-list" id="alert-list">
@@ -121,21 +121,21 @@
<!-- 接收请求卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.api_requests">接收请求</h2>
<span class="badge" id="http-badge" data-i18n="status.running">监控中</span>
<h2 data-i18n="monitor.card.api_requests">接收请求</h2>
<span class="badge" id="http-badge" data-i18n="monitor.status.running">监控中</span>
</div>
<div class="card-body">
<div class="http-summary" id="http-summary">
<div class="summary-item">
<span class="summary-label" data-i18n="metric.requests_total">总请求数</span>
<span class="summary-label" data-i18n="monitor.metric.requests_total">总请求数</span>
<span class="summary-value" id="http-total">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.error_rate">错误率</span>
<span class="summary-label" data-i18n="monitor.metric.error_rate">错误率</span>
<span class="summary-value" id="http-error-rate">--%</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.avg_time">平均响应</span>
<span class="summary-label" data-i18n="monitor.metric.avg_time">平均响应</span>
<span class="summary-value" id="http-avg-time">--ms</span>
</div>
<div class="summary-item">
@@ -149,21 +149,21 @@
<!-- 发送请求卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.outbound_requests">发送请求</h2>
<span class="badge" id="outbound-badge" data-i18n="status.running">监控中</span>
<h2 data-i18n="monitor.card.outbound_requests">发送请求</h2>
<span class="badge" id="outbound-badge" data-i18n="monitor.status.running">监控中</span>
</div>
<div class="card-body">
<div class="outbound-summary" id="outbound-summary">
<div class="summary-item">
<span class="summary-label" data-i18n="metric.requests_total">总请求数</span>
<span class="summary-label" data-i18n="monitor.metric.requests_total">总请求数</span>
<span class="summary-value" id="outbound-total">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.error_rate">错误率</span>
<span class="summary-label" data-i18n="monitor.metric.error_rate">错误率</span>
<span class="summary-value" id="outbound-error-rate">--%</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.avg_time">平均响应</span>
<span class="summary-label" data-i18n="monitor.metric.avg_time">平均响应</span>
<span class="summary-value" id="outbound-avg">--ms</span>
</div>
</div>
@@ -173,17 +173,17 @@
<!-- 定时任务监控卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.scheduler">定时任务</h2>
<span class="badge" id="scheduler-badge" data-i18n="status.checking">检查中</span>
<h2 data-i18n="monitor.card.scheduler">定时任务</h2>
<span class="badge" id="scheduler-badge" data-i18n="monitor.status.checking">检查中</span>
</div>
<div class="card-body">
<div class="scheduler-info" id="scheduler-info">
<div class="info-item">
<span class="info-label" data-i18n="metric.scheduler_status">调度器状态</span>
<span class="info-label" data-i18n="monitor.metric.scheduler_status">调度器状态</span>
<span class="info-value" id="scheduler-status">--</span>
</div>
<div class="info-item">
<span class="info-label" data-i18n="metric.job_count">任务数量</span>
<span class="info-label" data-i18n="monitor.metric.job_count">任务数量</span>
<span class="info-value" id="job-count">--</span>
</div>
</div>
@@ -196,8 +196,8 @@
<!-- 数据库详细信息卡片 -->
<section class="card full-width">
<div class="card-header">
<h2 data-i18n="card.mysql">MySQL</h2>
<span class="badge" id="db-detail-badge" data-i18n="status.checking">检查中</span>
<h2 data-i18n="monitor.card.mysql">MySQL</h2>
<span class="badge" id="db-detail-badge" data-i18n="monitor.status.checking">检查中</span>
</div>
<div class="card-body">
<!-- 数据库详细信息 -->
@@ -205,17 +205,17 @@
<table class="db-detail-table" id="db-detail-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.db_name">账套名称</th>
<th data-i18n="monitor.metric.connection_status">连接状态</th>
<th data-i18n="monitor.col.last_check">最后检查</th>
<th data-i18n="monitor.col.current_connections">当前连接</th>
<th data-i18n="monitor.col.max_connections">最大连接</th>
<th data-i18n="monitor.col.min_connections">最小连接</th>
<th data-i18n="monitor.col.idle_connections">空闲连接</th>
<th data-i18n="monitor.col.used_connections">使用中连接</th>
<th data-i18n="monitor.col.usage">使用率</th>
<th data-i18n="monitor.col.processed_records">处理记录</th>
<th data-i18n="monitor.col.count">次数</th>
</tr>
</thead>
<tbody id="db-detail-tbody">
@@ -229,62 +229,62 @@
<!-- Redis 监控卡片 -->
<section class="card full-width">
<div class="card-header">
<h2 data-i18n="card.redis">Redis</h2>
<span class="badge" id="redis-badge" data-i18n="status.checking">检查中</span>
<h2 data-i18n="monitor.card.redis">Redis</h2>
<span class="badge" id="redis-badge" data-i18n="monitor.status.checking">检查中</span>
</div>
<div class="card-body">
<div class="redis-metrics-grid">
<div class="metric-item">
<div class="metric-label" data-i18n="metric.connection_status">连接状态</div>
<div class="metric-label" data-i18n="monitor.metric.connection_status">连接状态</div>
<div class="metric-value" id="redis-status">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.host">主机</div>
<div class="metric-label" data-i18n="monitor.metric.host">主机</div>
<div class="metric-value" id="redis-host">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.port">端口</div>
<div class="metric-label" data-i18n="monitor.metric.port">端口</div>
<div class="metric-value" id="redis-port">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.database">数据库</div>
<div class="metric-label" data-i18n="monitor.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" data-i18n="metric.used_connections">使用连接数</div>
<div class="metric-label" data-i18n="monitor.metric.used_connections">使用连接数</div>
<div class="metric-value" id="redis-connections-used">0</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.max_connections">最大连接数</div>
<div class="metric-label" data-i18n="monitor.metric.max_connections">最大连接数</div>
<div class="metric-value" id="redis-connections-max">0</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.connection_usage">连接使用率</div>
<div class="metric-label" data-i18n="monitor.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" data-i18n="metric.buffer_size">缓冲大小</div>
<div class="metric-label" data-i18n="monitor.metric.buffer_size">缓冲大小</div>
<div class="metric-value" id="redis-buffer-size">0</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.buffer_threshold">缓冲阈值</div>
<div class="metric-label" data-i18n="monitor.metric.buffer_threshold">缓冲阈值</div>
<div class="metric-value" id="redis-buffer-threshold">0</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.buffer_usage">缓冲使用率</div>
<div class="metric-label" data-i18n="monitor.metric.buffer_usage">缓冲使用率</div>
<div class="metric-value" id="redis-buffer-usage">0%</div>
</div>
</div>
<div class="chart-container">
<h3 data-i18n="chart.redis_connections">Redis 连接池使用情况</h3>
<h3 data-i18n="monitor.chart.redis_connections">Redis 连接池使用情况</h3>
<canvas id="redis-connections-chart"></canvas>
</div>
<div class="chart-container">
<h3 data-i18n="chart.redis_buffer">Redis 缓冲大小变化</h3>
<h3 data-i18n="monitor.chart.redis_buffer">Redis 缓冲大小变化</h3>
<canvas id="redis-buffer-chart"></canvas>
</div>
</div>
@@ -295,7 +295,7 @@
<main class="main-content page-content" id="page-api-requests">
<section class="card full-width">
<div class="card-header">
<h2 data-i18n="card.http_requests_log">接收请求记录</h2>
<h2 data-i18n="monitor.card.http_requests_log">接收请求记录</h2>
<div class="card-actions">
<div class="date-selector" style="display: none;">
<label for="api-date-picker">选择日期:</label>
@@ -306,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>
<span data-i18n="monitor.other.show_internal">显示内部请求</span>
</label>
</div>
<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>
<button class="btn btn-sm" onclick="refreshAPIRequests()" data-i18n="monitor.btn.refresh">刷新</button>
<button class="btn btn-sm btn-secondary" onclick="resetAPIStats()" data-i18n="monitor.btn.reset_stats">重置统计</button>
</div>
</div>
<div class="card-body">
@@ -318,15 +318,15 @@
<table class="api-requests-table" id="api-requests-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.timestamp">时间戳</th>
<th data-i18n="monitor.col.method">方法</th>
<th data-i18n="monitor.col.path">端点</th>
<th data-i18n="monitor.col.query_params">查询参数</th>
<th data-i18n="monitor.col.status">状态码</th>
<th data-i18n="monitor.col.duration">响应时间</th>
<th data-i18n="monitor.col.client_ip">客户端IP</th>
<th data-i18n="monitor.col.error_message">错误信息</th>
</tr>
</thead>
<tbody id="api-requests-tbody">
@@ -381,9 +381,9 @@
<main class="main-content page-content" id="page-scheduler">
<section class="card full-width">
<div class="card-header">
<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>
<h2 data-i18n="monitor.card.scheduler_detail">定时任务详情</h2>
<span class="badge" id="scheduler-detail-badge" data-i18n="monitor.status.checking">检查中</span>
<button class="btn btn-small" onclick="refreshSchedulerPage()" data-i18n="monitor.btn.refresh">刷新</button>
</div>
<div class="card-body">
<div class="scheduler-detail-grid" id="scheduler-detail-grid">
@@ -397,7 +397,7 @@
<main class="main-content page-content" id="page-outbound-requests">
<section class="card full-width">
<div class="card-header">
<h2 data-i18n="card.outbound_requests_log">发送请求记录</h2>
<h2 data-i18n="monitor.card.outbound_requests_log">发送请求记录</h2>
<div class="card-actions">
<div class="date-selector" style="display: none;">
<label for="outbound-date-picker">选择日期:</label>
@@ -408,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>
<span data-i18n="monitor.other.show_internal">显示内部请求</span>
</label>
</div>
<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>
<button class="btn btn-sm" onclick="refreshOutboundRequests()" data-i18n="monitor.btn.refresh">刷新</button>
<button class="btn btn-sm btn-secondary" onclick="resetOutboundStats()" data-i18n="monitor.btn.reset_stats">重置统计</button>
</div>
</div>
<div class="card-body">
@@ -421,14 +421,14 @@
<table class="outbound-requests-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.timestamp">时间戳</th>
<th data-i18n="monitor.col.method">方法</th>
<th data-i18n="monitor.col.url">URL</th>
<th data-i18n="monitor.col.status">状态码</th>
<th data-i18n="monitor.col.duration">响应时间</th>
<th data-i18n="monitor.col.module">模块</th>
<th data-i18n="monitor.col.error_message">错误信息</th>
</tr>
</thead>
<tbody id="outbound-requests-table">
@@ -447,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')" data-i18n="btn.live_logs">实时日志</button>
<button class="btn btn-primary" onclick="window.open('/monitor/history-logs', '_blank')" data-i18n="btn.history_query">历史查询</button>
<button class="btn btn-primary" onclick="window.open('/monitor/live-logs', '_blank')" data-i18n="monitor.btn.live_logs">实时日志</button>
<button class="btn btn-primary" onclick="window.open('/monitor/history-logs', '_blank')" data-i18n="monitor.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>
<span data-i18n="monitor.other.show_read">显示已读</span>
</label>
</div>
<select id="log-page-level" onchange="fetchLogsPage()">
<option value="" data-i18n="filter.all_logs">全部日志</option>
<option value="" data-i18n="monitor.filter.all_logs">全部日志</option>
<option value="error">错误日志</option>
<option value="warning">警告日志</option>
</select>
<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>
<button class="btn btn-small" onclick="markAllLogsAsRead()" data-i18n="monitor.btn.mark_all_read">标记全部已读</button>
<button class="btn btn-small" onclick="clearReadStatus()" data-i18n="monitor.btn.clear_read_status">清空已读状态</button>
<button class="btn btn-small" onclick="refreshLogsPage()" data-i18n="monitor.btn.refresh">刷新</button>
</div>
</div>
<div class="card-body">
@@ -471,12 +471,12 @@
<table class="logs-table" id="logs-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.index">序号</th>
<th data-i18n="monitor.col.time">时间</th>
<th data-i18n="monitor.col.level">级别</th>
<th data-i18n="monitor.col.module">模块</th>
<th data-i18n="monitor.col.message">消息</th>
<th data-i18n="monitor.col.is_read">已读</th>
</tr>
</thead>
<tbody id="logs-tbody">
@@ -493,55 +493,55 @@
<!-- 事件监听卡片 -->
<section class="card full-width">
<div class="card-header">
<h2 data-i18n="card.event_listener">事件监听</h2>
<h2 data-i18n="monitor.card.event_listener">事件监听</h2>
<div class="events-actions">
<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>
<button class="btn btn-small" onclick="refreshEventStats()" data-i18n="monitor.btn.refresh">刷新</button>
<button class="btn btn-small" onclick="flushAllEvents()" data-i18n="monitor.btn.flush_all">立即刷新所有</button>
<button class="btn btn-small btn-secondary" onclick="resetEventStats()" data-i18n="monitor.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" data-i18n="metric.events_received">总接收事件</span>
<span class="summary-label" data-i18n="monitor.metric.events_received">总接收事件</span>
<span class="summary-value" id="events-total-received">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.events_processed">已处理事件</span>
<span class="summary-label" data-i18n="monitor.metric.events_processed">已处理事件</span>
<span class="summary-value healthy" id="events-total-processed">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.events_interrupted">中断处理</span>
<span class="summary-label" data-i18n="monitor.metric.events_interrupted">中断处理</span>
<span class="summary-value error" id="events-total-failed">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.pending">待处理</span>
<span class="summary-label" data-i18n="monitor.metric.pending">待处理</span>
<span class="summary-value" id="events-total-pending">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.overall_success_rate">整体成功率</span>
<span class="summary-label" data-i18n="monitor.metric.overall_success_rate">整体成功率</span>
<span class="summary-value" id="events-success-rate">--%</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.active_event_types">活跃事件类型</span>
<span class="summary-label" data-i18n="monitor.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" data-i18n="metric.backpressure_status">背压状态</span>
<span class="summary-label" data-i18n="monitor.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" data-i18n="metric.backpressure_pending">待处理事件</span>
<span class="summary-label" data-i18n="monitor.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" data-i18n="metric.backpressure_usage">背压使用率</span>
<span class="summary-label" data-i18n="monitor.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" data-i18n="metric.event_loop_status">事件循环状态</span>
<span class="summary-label" data-i18n="monitor.metric.event_loop_status">事件循环状态</span>
<span class="summary-value" id="event-loop-health">正常</span>
</div>
</div>
@@ -549,17 +549,17 @@
<table class="events-table" id="events-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.description">描述</th>
<th data-i18n="monitor.col.total_received">总接收</th>
<th data-i18n="monitor.col.pending">待处理</th>
<th data-i18n="monitor.col.processed">已处理</th>
<th data-i18n="monitor.col.interrupted">中断</th>
<th data-i18n="monitor.col.completion_rate">完成率</th>
<th data-i18n="monitor.col.avg_latency">平均延迟</th>
<th data-i18n="monitor.col.operation">操作</th>
<th data-i18n="monitor.col.last_action">最后动作</th>
<th data-i18n="monitor.col.status">状态</th>
<th data-i18n="monitor.col.operation">操作</th>
</tr>
</thead>
<tbody id="events-tbody">
@@ -577,21 +577,21 @@
<!-- 回调跟踪器卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.callback_tracker">回调跟踪器</h2>
<span class="badge" id="callback-badge" data-i18n="status.monitoring">监控中</span>
<h2 data-i18n="monitor.card.callback_tracker">回调跟踪器</h2>
<span class="badge" id="callback-badge" data-i18n="monitor.status.monitoring">监控中</span>
</div>
<div class="card-body">
<div class="metrics-grid">
<div class="metric-item">
<div class="metric-label" data-i18n="metric.pending_callbacks">待处理回调</div>
<div class="metric-label" data-i18n="monitor.metric.pending_callbacks">待处理回调</div>
<div class="metric-value" id="callback-pending">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.max_retries">最大重试次数</div>
<div class="metric-label" data-i18n="monitor.metric.max_retries">最大重试次数</div>
<div class="metric-value" id="callback-max-retries">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.pending_retries">待处理重试</div>
<div class="metric-label" data-i18n="monitor.metric.pending_retries">待处理重试</div>
<div class="metric-value" id="callback-pending-retries">--</div>
</div>
</div>
@@ -601,25 +601,25 @@
<!-- 事件去重器卡片 -->
<section class="card">
<div class="card-header">
<h2 data-i18n="card.event_deduplicator">事件去重器</h2>
<span class="badge" id="deduplicator-badge" data-i18n="status.monitoring">监控中</span>
<h2 data-i18n="monitor.card.event_deduplicator">事件去重器</h2>
<span class="badge" id="deduplicator-badge" data-i18n="monitor.status.monitoring">监控中</span>
</div>
<div class="card-body">
<div class="metrics-grid">
<div class="metric-item">
<div class="metric-label" data-i18n="metric.total_entries">总存储条目</div>
<div class="metric-label" data-i18n="monitor.metric.total_entries">总存储条目</div>
<div class="metric-value" id="deduplicator-total">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.active_items">活跃项目</div>
<div class="metric-label" data-i18n="monitor.metric.active_items">活跃项目</div>
<div class="metric-value" id="deduplicator-active">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.ttl_seconds">TTL (秒)</div>
<div class="metric-label" data-i18n="monitor.metric.ttl_seconds">TTL (秒)</div>
<div class="metric-value" id="deduplicator-ttl">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.max_entries">最大条目</div>
<div class="metric-label" data-i18n="monitor.metric.max_entries">最大条目</div>
<div class="metric-value" id="deduplicator-max">--</div>
</div>
</div>
@@ -633,40 +633,40 @@
<div class="card-header">
<h2>Dead Letter Queue</h2>
<div class="dead-letter-actions">
<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>
<button class="btn btn-small" onclick="refreshDeadLetterStats()" data-i18n="monitor.btn.refresh">刷新</button>
<button class="btn btn-small btn-secondary" onclick="clearDeadLetters()" data-i18n="monitor.btn.clear">清空</button>
</div>
</div>
<div class="card-body">
<div class="metrics-grid">
<div class="metric-item">
<div class="metric-label" data-i18n="metric.pending_events">待写入事件</div>
<div class="metric-label" data-i18n="monitor.metric.pending_events">待写入事件</div>
<div class="metric-value" id="dlq-pending">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.total_events">总事件数</div>
<div class="metric-label" data-i18n="monitor.metric.total_events">总事件数</div>
<div class="metric-value" id="dlq-total">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.file_size">文件大小</div>
<div class="metric-label" data-i18n="monitor.metric.file_size">文件大小</div>
<div class="metric-value" id="dlq-file-size">--</div>
</div>
<div class="metric-item">
<div class="metric-label" data-i18n="metric.running_status">运行状态</div>
<div class="metric-label" data-i18n="monitor.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" data-i18n="metric.dl_total">DL总数</span>
<span class="summary-label" data-i18n="monitor.metric.dl_total">DL总数</span>
<span class="summary-value" id="dead-letter-total">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.recent_dl">最近DL</span>
<span class="summary-label" data-i18n="monitor.metric.recent_dl">最近DL</span>
<span class="summary-value" id="dead-letter-recent">--</span>
</div>
<div class="summary-item">
<span class="summary-label" data-i18n="metric.process_success_rate">处理成功率</span>
<span class="summary-label" data-i18n="monitor.metric.process_success_rate">处理成功率</span>
<span class="summary-value" id="dead-letter-success-rate">--%</span>
</div>
</div>
@@ -674,13 +674,13 @@
<table class="dead-letter-table" id="dead-letter-table">
<thead>
<tr>
<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>
<th data-i18n="monitor.col.id">ID</th>
<th data-i18n="monitor.col.event_type">事件类型</th>
<th data-i18n="monitor.col.time">时间</th>
<th data-i18n="monitor.col.database">数据库</th>
<th data-i18n="monitor.col.table"></th>
<th data-i18n="monitor.col.error_message">错误信息</th>
<th data-i18n="monitor.col.operation">操作</th>
</tr>
</thead>
<tbody id="dead-letter-tbody">
File diff suppressed because it is too large Load Diff
+18 -18
View File
@@ -331,10 +331,10 @@
<div class="live-logs-container">
<div class="live-logs-header">
<div class="live-logs-header-left">
<h3 data-i18n="page.live_logs">实时日志流</h3>
<h3 data-i18n="monitor.page.live_logs">实时日志流</h3>
<div class="connection-status">
<div class="status-dot connecting" id="statusDot"></div>
<span id="statusText" data-i18n="status.connecting">连接中...</span>
<span id="statusText" data-i18n="monitor.status.connecting">连接中...</span>
</div>
</div>
@@ -345,9 +345,9 @@
<option value="de-DE">🇩🇪 Deutsch</option>
</select>
<div class="header-control-group">
<label data-i18n="filter.level">级别:</label>
<label data-i18n="monitor.filter.level">级别:</label>
<select id="levelFilter" onchange="applyFilters()">
<option value="" data-i18n="filter.level">全部</option>
<option value="" data-i18n="monitor.filter.level">全部</option>
<option value="INFO" selected>INFO</option>
<option value="WARNING">WARNING</option>
<option value="ERROR">ERROR</option>
@@ -360,7 +360,7 @@
</div>
<div class="header-control-group">
<label data-i18n="other.auto_scroll">自动滚动</label>
<label data-i18n="monitor.other.auto_scroll">自动滚动</label>
<label class="toggle-switch">
<input type="checkbox" id="autoScroll" checked onchange="toggleAutoScroll()">
<span class="toggle-slider"></span>
@@ -368,17 +368,17 @@
</div>
<button class="btn btn-secondary" onclick="togglePause()">
<span id="pauseBtnText" data-i18n="btn.pause">暂停</span>
<span id="pauseBtnText" data-i18n="monitor.btn.pause">暂停</span>
</button>
<button class="btn btn-danger" onclick="clearLogs()" data-i18n="btn.clear">清空</button>
<button class="btn btn-danger" onclick="clearLogs()" data-i18n="monitor.btn.clear">清空</button>
<button class="btn btn-primary" onclick="openInNewWindow()" data-i18n="btn.new_window">新窗口</button>
<button class="btn btn-primary" onclick="openInNewWindow()" data-i18n="monitor.btn.new_window">新窗口</button>
</div>
</div>
<div class="log-stats">
<span data-i18n="metric.total">总日志: <strong id="totalCount">0</strong></span>
<span data-i18n="monitor.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>
@@ -387,7 +387,7 @@
</div>
<div class="logs-container" id="logsContainer">
<div class="no-logs" id="noLogs" data-i18n="other.waiting_logs">
<div class="no-logs" id="noLogs" data-i18n="monitor.other.waiting_logs">
正在等待日志数据...
</div>
</div>
@@ -549,7 +549,7 @@
if (isDomReady) {
const statusText = document.getElementById('statusText');
if (statusText) {
statusText.textContent = i18n.t('error.connection_failed');
statusText.textContent = i18n.t('monitor.error.connection_failed');
}
}
return;
@@ -605,14 +605,14 @@
switch (status) {
case 'connected':
dot.classList.add('connected');
text.textContent = i18n.t('status.connected');
text.textContent = i18n.t('monitor.status.connected');
break;
case 'connecting':
dot.classList.add('connecting');
text.textContent = i18n.t('status.connecting');
text.textContent = i18n.t('monitor.status.connecting');
break;
case 'disconnected':
text.textContent = i18n.t('status.disconnected') + ' (' + i18n.t('other.auto_reconnect') + ')';
text.textContent = i18n.t('monitor.status.disconnected') + ' (' + i18n.t('monitor.other.auto_reconnect') + ')';
break;
}
}
@@ -727,7 +727,7 @@
}
if (logs.length === 0) {
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('other.waiting_logs') + '</div>';
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('monitor.other.waiting_logs') + '</div>';
lastRenderIndex = 0;
return;
}
@@ -743,7 +743,7 @@
});
if (filteredLogs.length === 0) {
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('other.no_matching_logs') + '</div>';
container.innerHTML = '<div class="no-logs" id="noLogs">' + i18n.t('monitor.other.no_matching_logs') + '</div>';
lastRenderIndex = 0;
return;
}
@@ -816,7 +816,7 @@
const autoScrollIndicator = document.getElementById('autoScrollIndicator');
if (pauseBtnText) {
pauseBtnText.textContent = isPaused ? i18n.t('btn.resume') : i18n.t('btn.pause');
pauseBtnText.textContent = isPaused ? i18n.t('monitor.btn.resume') : i18n.t('monitor.btn.pause');
}
if (autoScrollIndicator) {
autoScrollIndicator.classList.toggle('show', isPaused);
@@ -861,7 +861,7 @@
updateStats();
const logsContainer = document.getElementById('logsContainer');
if (logsContainer) {
logsContainer.innerHTML = '<div class="no-logs">' + i18n.t('success.logs_cleared') + '</div>';
logsContainer.innerHTML = '<div class="no-logs">' + i18n.t('monitor.success.logs_cleared') + '</div>';
}
}