mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
feat(dashboard): more System History metrics, persistence & localized labels
- Sample swap %, TCP/UDP connection counts and disk-usage % on the host ticker - System History: Swap overlaid on the RAM tab, plus new Connections and Disk Usage tabs - Persist the host time-series across restarts: gob snapshot beside the DB, written on a timer and at shutdown, restored on boot - Live-refresh the open chart (2s for short ranges, 10s for longer) - Localize CPU/RAM/Swap and the new tab/chart titles across all 13 languages and route legend series names through i18n
This commit is contained in:
parent
4b11c54206
commit
d4c020f365
18 changed files with 203 additions and 33 deletions
|
|
@ -3,12 +3,14 @@ import type { ReactNode } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal, Select, Tabs } from 'antd';
|
||||
import {
|
||||
ApiOutlined,
|
||||
DashboardOutlined,
|
||||
DatabaseOutlined,
|
||||
DeploymentUnitOutlined,
|
||||
GlobalOutlined,
|
||||
HddOutlined,
|
||||
LineChartOutlined,
|
||||
PieChartOutlined,
|
||||
TeamOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
|
|
@ -43,11 +45,13 @@ interface MetricDef {
|
|||
}
|
||||
|
||||
const METRICS: MetricDef[] = [
|
||||
{ key: 'cpu', tab: 'CPU', title: 'pages.index.historyTitleCpu', icon: <DashboardOutlined />, valueMax: 100, unit: '%', stroke: '' },
|
||||
{ key: 'mem', tab: 'RAM', title: 'pages.index.historyTitleMem', icon: <DatabaseOutlined />, valueMax: 100, unit: '%', stroke: '#7c4dff' },
|
||||
{ key: 'cpu', tab: 'CPU', tabKey: 'pages.index.cpu', title: 'pages.index.historyTitleCpu', icon: <DashboardOutlined />, valueMax: 100, unit: '%', stroke: '' },
|
||||
{ key: 'mem', tab: 'RAM', tabKey: 'pages.index.memory', title: 'pages.index.historyTitleMem', icon: <DatabaseOutlined />, valueMax: 100, unit: '%', stroke: '#7c4dff', key2: 'swap', stroke2: '#ffa940', name1: 'pages.index.memory', name2: 'pages.index.swap' },
|
||||
{ key: 'netUp', tab: 'Bandwidth', tabKey: 'pages.index.historyTabBandwidth', title: 'pages.index.historyTitleNetwork', icon: <GlobalOutlined />, valueMax: null, unit: 'B/s', stroke: '#1890ff', key2: 'netDown', stroke2: '#13c2c2', name1: 'Up', name2: 'Down' },
|
||||
{ key: 'pktUp', tab: 'Packets', tabKey: 'pages.index.historyTabPackets', title: 'pages.index.historyTitlePackets', icon: <DeploymentUnitOutlined />, valueMax: null, unit: 'pkt/s', stroke: '#2f54eb', key2: 'pktDown', stroke2: '#36cfc9', name1: 'Up', name2: 'Down' },
|
||||
{ key: 'tcpCount', tab: 'Connections', tabKey: 'pages.index.historyTabConnections', title: 'pages.index.historyTitleConnections', icon: <ApiOutlined />, valueMax: null, unit: '', stroke: '#597ef7', key2: 'udpCount', stroke2: '#73d13d', name1: 'TCP', name2: 'UDP' },
|
||||
{ key: 'diskRead', tab: 'Disk I/O', tabKey: 'pages.index.historyTabDisk', title: 'pages.index.historyTitleDisk', icon: <HddOutlined />, valueMax: null, unit: 'B/s', stroke: '#eb2f96', key2: 'diskWrite', stroke2: '#722ed1', name1: 'Read', name2: 'Write' },
|
||||
{ key: 'diskUsage', tab: 'Disk Usage', tabKey: 'pages.index.historyTabDiskUsage', title: 'pages.index.historyTitleDiskUsage', icon: <PieChartOutlined />, valueMax: 100, unit: '%', stroke: '#13c2c2' },
|
||||
{ key: 'online', tab: 'Online', tabKey: 'pages.index.historyTabOnline', title: 'pages.index.historyTitleOnline', icon: <TeamOutlined />, valueMax: null, unit: '', stroke: '#52c41a' },
|
||||
{ key: 'load1', tab: 'Load', tabKey: 'pages.index.historyTabLoad', title: 'pages.index.historyTitleLoad', icon: <LineChartOutlined />, valueMax: null, unit: '', stroke: '#fa8c16', key2: 'load5', stroke2: '#f5222d', name1: '1m', name2: '5m', key3: 'load15', stroke3: '#a0d911', name3: '15m' },
|
||||
];
|
||||
|
|
@ -64,7 +68,9 @@ function unitFormatter(unit: string, activeKey: string): (v: number) => string {
|
|||
}
|
||||
return (v) => {
|
||||
const n = Number(v) || 0;
|
||||
if (activeKey === 'online') return String(Math.round(n));
|
||||
if (activeKey === 'online' || activeKey === 'tcpCount' || activeKey === 'udpCount') {
|
||||
return Math.round(n).toLocaleString();
|
||||
}
|
||||
return n.toFixed(2);
|
||||
};
|
||||
}
|
||||
|
|
@ -97,6 +103,7 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
|||
const [timestamps, setTimestamps] = useState<number[]>([]);
|
||||
|
||||
const activeMetric = useMemo(() => METRICS.find((m) => m.key === activeKey), [activeKey]);
|
||||
const trName = (n?: string) => (n && n.startsWith('pages.') ? t(n) : n);
|
||||
const strokeColor = activeMetric?.stroke || status?.cpu?.color || '#008771';
|
||||
const yFormatter = useMemo(
|
||||
() => unitFormatter(activeMetric?.unit ?? '', activeKey),
|
||||
|
|
@ -178,6 +185,13 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
|||
if (open) fetchBucket();
|
||||
}, [open, activeKey, bucket, fetchBucket]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return undefined;
|
||||
const ms = bucket <= 30 ? 2000 : 10000;
|
||||
const id = window.setInterval(() => fetchBucket(), ms);
|
||||
return () => window.clearInterval(id);
|
||||
}, [open, bucket, fetchBucket]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
|
|
@ -226,9 +240,9 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
|||
data3={activeMetric?.key3 ? points3 : undefined}
|
||||
stroke2={activeMetric?.stroke2}
|
||||
stroke3={activeMetric?.stroke3}
|
||||
name1={activeMetric?.name1}
|
||||
name2={activeMetric?.name2}
|
||||
name3={activeMetric?.name3}
|
||||
name1={trName(activeMetric?.name1)}
|
||||
name2={trName(activeMetric?.name2)}
|
||||
name3={trName(activeMetric?.name3)}
|
||||
labels={labels}
|
||||
height={260}
|
||||
stroke={strokeColor}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ type ServerController struct {
|
|||
// NewServerController creates a new ServerController, initializes routes, and starts background tasks.
|
||||
func NewServerController(g *gin.RouterGroup) *ServerController {
|
||||
a := &ServerController{}
|
||||
service.RestoreSystemMetrics()
|
||||
a.initRouter(g)
|
||||
a.startTask()
|
||||
return a
|
||||
|
|
@ -84,6 +85,11 @@ func (a *ServerController) startTask() {
|
|||
a.xrayMetricsService.Sample(time.Now())
|
||||
websocket.BroadcastStatus(status)
|
||||
})
|
||||
c.AddFunc("@every 1m", func() {
|
||||
if err := service.PersistSystemMetrics(); err != nil {
|
||||
logger.Warning("persist system metrics failed:", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// status returns the current server status information.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/config"
|
||||
"github.com/mhsanaei/3x-ui/v3/logger"
|
||||
)
|
||||
|
||||
// MetricSample is one point of any time-series we keep in memory.
|
||||
|
|
@ -59,6 +65,34 @@ func (h *metricHistory) drop(metric string) {
|
|||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// snapshot returns a deep copy of every series, safe to serialize without
|
||||
// holding the lock during disk I/O.
|
||||
func (h *metricHistory) snapshot() map[string][]MetricSample {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
out := make(map[string][]MetricSample, len(h.metrics))
|
||||
for k, v := range h.metrics {
|
||||
cp := make([]MetricSample, len(v))
|
||||
copy(cp, v)
|
||||
out[k] = cp
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// restore replaces the in-memory series with a previously persisted set,
|
||||
// re-applying the per-series capacity cap so a tampered or oversized file
|
||||
// can't grow the working set unbounded.
|
||||
func (h *metricHistory) restore(data map[string][]MetricSample) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
for k, v := range data {
|
||||
if len(v) > metricCapacityDefault {
|
||||
v = v[len(v)-metricCapacityDefault:]
|
||||
}
|
||||
h.metrics[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate returns up to maxPoints buckets of size bucketSeconds,
|
||||
// each bucket carrying the arithmetic mean of the underlying samples.
|
||||
// Bucket alignment is to absolute Unix-second boundaries so two
|
||||
|
|
@ -137,7 +171,7 @@ var (
|
|||
// status sample. Exposed for documentation/test purposes; the
|
||||
// controller validates incoming names against an allow-list.
|
||||
var SystemMetricKeys = []string{
|
||||
"cpu", "mem", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "online", "load1", "load5", "load15",
|
||||
"cpu", "mem", "swap", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "diskUsage", "tcpCount", "udpCount", "online", "load1", "load5", "load15",
|
||||
}
|
||||
|
||||
// NodeMetricKeys lists the per-node metric names NodeHeartbeatJob writes.
|
||||
|
|
@ -150,3 +184,54 @@ var NodeMetricKeys = []string{"cpu", "mem"}
|
|||
var XrayMetricKeys = []string{
|
||||
"xrAlloc", "xrSys", "xrHeapObjects", "xrNumGC", "xrPauseNs",
|
||||
}
|
||||
|
||||
// systemMetricsStorePath is where the host time-series is persisted between
|
||||
// restarts. It lives next to the database so a single volume mount carries
|
||||
// both. Only systemMetrics is persisted — node and xray series are cheap to
|
||||
// rebuild and tied to live connections.
|
||||
func systemMetricsStorePath() string {
|
||||
return filepath.Join(config.GetDBFolderPath(), "system_metrics.gob")
|
||||
}
|
||||
|
||||
// PersistSystemMetrics writes the host time-series to disk via a temp file +
|
||||
// rename so a crash mid-write can't corrupt the previous snapshot. Called on a
|
||||
// timer and at shutdown.
|
||||
func PersistSystemMetrics() error {
|
||||
path := systemMetricsStorePath()
|
||||
tmp := path + ".tmp"
|
||||
f, err := os.Create(tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gob.NewEncoder(f).Encode(systemMetrics.snapshot()); err != nil {
|
||||
f.Close()
|
||||
os.Remove(tmp)
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
os.Remove(tmp)
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, path)
|
||||
}
|
||||
|
||||
// RestoreSystemMetrics loads a previously persisted host time-series on startup.
|
||||
// A missing file is not an error (first boot). Aggregation already windows by
|
||||
// time, so any gap from downtime is handled by the readers.
|
||||
func RestoreSystemMetrics() {
|
||||
path := systemMetricsStorePath()
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
logger.Warning("restore system metrics failed:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
var data map[string][]MetricSample
|
||||
if err := gob.NewDecoder(f).Decode(&data); err != nil {
|
||||
logger.Warning("decode system metrics failed:", err)
|
||||
return
|
||||
}
|
||||
systemMetrics.restore(data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -565,12 +565,22 @@ func (s *ServerService) AppendStatusSample(t time.Time, status *Status) {
|
|||
if status.Mem.Total > 0 {
|
||||
systemMetrics.append("mem", t, float64(status.Mem.Current)*100.0/float64(status.Mem.Total))
|
||||
}
|
||||
if status.Swap.Total > 0 {
|
||||
systemMetrics.append("swap", t, float64(status.Swap.Current)*100.0/float64(status.Swap.Total))
|
||||
} else {
|
||||
systemMetrics.append("swap", t, 0)
|
||||
}
|
||||
systemMetrics.append("netUp", t, float64(status.NetIO.Up))
|
||||
systemMetrics.append("netDown", t, float64(status.NetIO.Down))
|
||||
systemMetrics.append("diskRead", t, float64(status.DiskIO.Read))
|
||||
systemMetrics.append("diskWrite", t, float64(status.DiskIO.Write))
|
||||
if status.Disk.Total > 0 {
|
||||
systemMetrics.append("diskUsage", t, float64(status.Disk.Current)*100.0/float64(status.Disk.Total))
|
||||
}
|
||||
systemMetrics.append("pktUp", t, float64(status.NetIO.PktUp))
|
||||
systemMetrics.append("pktDown", t, float64(status.NetIO.PktDown))
|
||||
systemMetrics.append("tcpCount", t, float64(status.TcpCount))
|
||||
systemMetrics.append("udpCount", t, float64(status.UdpCount))
|
||||
online := 0
|
||||
if p != nil && p.IsRunning() {
|
||||
online = len(p.GetOnlineClients())
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@
|
|||
},
|
||||
"index": {
|
||||
"title": "نظرة عامة",
|
||||
"cpu": "CPU",
|
||||
"cpu": "المعالج",
|
||||
"logicalProcessors": "المعالجات المنطقية",
|
||||
"frequency": "التردد",
|
||||
"swap": "Swap",
|
||||
"swap": "التبديل",
|
||||
"storage": "تخزين",
|
||||
"memory": "RAM",
|
||||
"memory": "الذاكرة",
|
||||
"threads": "خيوط",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "إيقاف",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "إدخال/إخراج القرص",
|
||||
"historyTitleOnline": "العملاء المتصلون",
|
||||
"historyTitleLoad": "متوسط حمل النظام (1 / 5 / 15 دقيقة)",
|
||||
"historyTitleConnections": "الاتصالات النشطة (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "استخدام مساحة القرص",
|
||||
"historyTabBandwidth": "عرض النطاق",
|
||||
"historyTabPackets": "الحزم",
|
||||
"historyTabDisk": "Disk I/O",
|
||||
"historyTabDisk": "قرص I/O",
|
||||
"historyTabOnline": "متصل",
|
||||
"historyTabLoad": "الحِمل",
|
||||
"historyTabConnections": "الاتصالات",
|
||||
"historyTabDiskUsage": "استخدام القرص",
|
||||
"charts": "الرسوم البيانية",
|
||||
"xrayMetricsTitle": "مقاييس Xray",
|
||||
"xrayTitleHeap": "ذاكرة الكومة المخصصة",
|
||||
|
|
|
|||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "Disk I/O",
|
||||
"historyTitleOnline": "Online Clients",
|
||||
"historyTitleLoad": "System Load Average (1m / 5m / 15m)",
|
||||
"historyTitleConnections": "Active Connections (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Disk Space Usage",
|
||||
"historyTabBandwidth": "Bandwidth",
|
||||
"historyTabPackets": "Packets",
|
||||
"historyTabDisk": "Disk I/O",
|
||||
"historyTabOnline": "Online",
|
||||
"historyTabLoad": "Load",
|
||||
"historyTabConnections": "Connections",
|
||||
"historyTabDiskUsage": "Disk Usage",
|
||||
"charts": "Charts",
|
||||
"xrayMetricsTitle": "Xray Metrics",
|
||||
"xrayTitleHeap": "Allocated Heap Memory",
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
"frequency": "Frecuencia",
|
||||
"swap": "Swap",
|
||||
"storage": "Almacenamiento",
|
||||
"memory": "RAM",
|
||||
"memory": "Memoria",
|
||||
"threads": "Hilos",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Detener",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "E/S de Disco",
|
||||
"historyTitleOnline": "Clientes en Línea",
|
||||
"historyTitleLoad": "Carga Media del Sistema (1 / 5 / 15 min)",
|
||||
"historyTitleConnections": "Conexiones Activas (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Uso del Espacio en Disco",
|
||||
"historyTabBandwidth": "Ancho de Banda",
|
||||
"historyTabPackets": "Paquetes",
|
||||
"historyTabDisk": "Disco I/O",
|
||||
"historyTabOnline": "En línea",
|
||||
"historyTabLoad": "Carga",
|
||||
"historyTabConnections": "Conexiones",
|
||||
"historyTabDiskUsage": "Uso de Disco",
|
||||
"charts": "Gráficos",
|
||||
"xrayMetricsTitle": "Métricas de Xray",
|
||||
"xrayTitleHeap": "Memoria Heap Asignada",
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@
|
|||
},
|
||||
"index": {
|
||||
"title": "نمای کلی",
|
||||
"cpu": "CPU",
|
||||
"cpu": "پردازنده",
|
||||
"logicalProcessors": "پردازندههای منطقی",
|
||||
"frequency": "فرکانس",
|
||||
"swap": "Swap",
|
||||
"swap": "سواپ",
|
||||
"storage": "ذخیرهسازی",
|
||||
"memory": "RAM",
|
||||
"memory": "حافظه",
|
||||
"threads": "نخها",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "توقف",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "ورودی/خروجی دیسک",
|
||||
"historyTitleOnline": "کاربران آنلاین",
|
||||
"historyTitleLoad": "میانگین بار سیستم (۱ / ۵ / ۱۵ دقیقه)",
|
||||
"historyTitleConnections": "اتصالات فعال (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "مصرف فضای دیسک",
|
||||
"historyTabBandwidth": "پهنای باند",
|
||||
"historyTabPackets": "بستهها",
|
||||
"historyTabDisk": "Disk I/O",
|
||||
"historyTabDisk": "دیسک I/O",
|
||||
"historyTabOnline": "آنلاین",
|
||||
"historyTabLoad": "بار",
|
||||
"historyTabConnections": "اتصالات",
|
||||
"historyTabDiskUsage": "مصرف دیسک",
|
||||
"charts": "نمودارها",
|
||||
"xrayMetricsTitle": "متریکهای Xray",
|
||||
"xrayTitleHeap": "حافظهی Heap تخصیصیافته",
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
"frequency": "Frekuensi",
|
||||
"swap": "Swap",
|
||||
"storage": "Penyimpanan",
|
||||
"memory": "RAM",
|
||||
"memory": "Memori",
|
||||
"threads": "Thread",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Hentikan",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "I/O Disk",
|
||||
"historyTitleOnline": "Klien Online",
|
||||
"historyTitleLoad": "Rata-rata Beban Sistem (1 / 5 / 15 mnt)",
|
||||
"historyTitleConnections": "Koneksi Aktif (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Penggunaan Ruang Disk",
|
||||
"historyTabBandwidth": "Bandwidth",
|
||||
"historyTabPackets": "Paket",
|
||||
"historyTabDisk": "Disk I/O",
|
||||
"historyTabOnline": "Online",
|
||||
"historyTabLoad": "Beban",
|
||||
"historyTabConnections": "Koneksi",
|
||||
"historyTabDiskUsage": "Penggunaan Disk",
|
||||
"charts": "Grafik",
|
||||
"xrayMetricsTitle": "Metrik Xray",
|
||||
"xrayTitleHeap": "Memori Heap Teralokasi",
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@
|
|||
"cpu": "CPU",
|
||||
"logicalProcessors": "論理プロセッサ",
|
||||
"frequency": "周波数",
|
||||
"swap": "Swap",
|
||||
"swap": "スワップ",
|
||||
"storage": "ストレージ",
|
||||
"memory": "RAM",
|
||||
"memory": "メモリ",
|
||||
"threads": "スレッド",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "停止",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "ディスク I/O",
|
||||
"historyTitleOnline": "オンラインクライアント",
|
||||
"historyTitleLoad": "システム平均負荷(1分 / 5分 / 15分)",
|
||||
"historyTitleConnections": "アクティブな接続 (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "ディスク使用率",
|
||||
"historyTabBandwidth": "帯域幅",
|
||||
"historyTabPackets": "パケット",
|
||||
"historyTabDisk": "ディスク I/O",
|
||||
"historyTabOnline": "オンライン",
|
||||
"historyTabLoad": "負荷",
|
||||
"historyTabConnections": "接続数",
|
||||
"historyTabDiskUsage": "ディスク使用量",
|
||||
"charts": "チャート",
|
||||
"xrayMetricsTitle": "Xray メトリクス",
|
||||
"xrayTitleHeap": "割り当て済みヒープメモリ",
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
"frequency": "Frequência",
|
||||
"swap": "Swap",
|
||||
"storage": "Armazenamento",
|
||||
"memory": "RAM",
|
||||
"memory": "Memória",
|
||||
"threads": "Threads",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Parar",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "E/S de Disco",
|
||||
"historyTitleOnline": "Clientes Online",
|
||||
"historyTitleLoad": "Média de Carga do Sistema (1 / 5 / 15 min)",
|
||||
"historyTitleConnections": "Conexões Ativas (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Uso do Espaço em Disco",
|
||||
"historyTabBandwidth": "Largura de Banda",
|
||||
"historyTabPackets": "Pacotes",
|
||||
"historyTabDisk": "Disco I/O",
|
||||
"historyTabOnline": "Online",
|
||||
"historyTabLoad": "Carga",
|
||||
"historyTabConnections": "Conexões",
|
||||
"historyTabDiskUsage": "Uso de Disco",
|
||||
"charts": "Gráficos",
|
||||
"xrayMetricsTitle": "Métricas do Xray",
|
||||
"xrayTitleHeap": "Memória Heap Alocada",
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@
|
|||
},
|
||||
"index": {
|
||||
"title": "Дашборд",
|
||||
"cpu": "CPU",
|
||||
"cpu": "ЦП",
|
||||
"logicalProcessors": "Логические процессоры",
|
||||
"frequency": "Частота",
|
||||
"swap": "Swap",
|
||||
"swap": "Подкачка",
|
||||
"storage": "Диск",
|
||||
"memory": "RAM",
|
||||
"memory": "Память",
|
||||
"threads": "Потоки",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Стоп",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "Дисковый ввод-вывод",
|
||||
"historyTitleOnline": "Клиенты онлайн",
|
||||
"historyTitleLoad": "Средняя нагрузка системы (1 / 5 / 15 мин)",
|
||||
"historyTitleConnections": "Активные соединения (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Использование дискового пространства",
|
||||
"historyTabBandwidth": "Пропускная способность",
|
||||
"historyTabPackets": "Пакеты",
|
||||
"historyTabDisk": "Диск I/O",
|
||||
"historyTabOnline": "Онлайн",
|
||||
"historyTabLoad": "Нагрузка",
|
||||
"historyTabConnections": "Соединения",
|
||||
"historyTabDiskUsage": "Использование диска",
|
||||
"charts": "Графики",
|
||||
"xrayMetricsTitle": "Метрики Xray",
|
||||
"xrayTitleHeap": "Выделенная память кучи",
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@
|
|||
"cpu": "CPU",
|
||||
"logicalProcessors": "Mantıksal işlemciler",
|
||||
"frequency": "Frekans",
|
||||
"swap": "Swap",
|
||||
"swap": "Takas",
|
||||
"storage": "Depolama",
|
||||
"memory": "RAM",
|
||||
"memory": "Bellek",
|
||||
"threads": "İş parçacığı",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Durdur",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "Disk G/Ç",
|
||||
"historyTitleOnline": "Çevrimiçi İstemciler",
|
||||
"historyTitleLoad": "Sistem Yük Ortalaması (1d / 5d / 15d)",
|
||||
"historyTitleConnections": "Etkin Bağlantılar (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Disk Alanı Kullanımı",
|
||||
"historyTabBandwidth": "Bant Genişliği",
|
||||
"historyTabPackets": "Paketler",
|
||||
"historyTabDisk": "Disk G/Ç",
|
||||
"historyTabOnline": "Çevrimiçi",
|
||||
"historyTabLoad": "Yük",
|
||||
"historyTabConnections": "Bağlantılar",
|
||||
"historyTabDiskUsage": "Disk Kullanımı",
|
||||
"charts": "Grafikler",
|
||||
"xrayMetricsTitle": "Xray Metrikleri",
|
||||
"xrayTitleHeap": "Ayrılan Yığın Belleği",
|
||||
|
|
|
|||
|
|
@ -128,12 +128,12 @@
|
|||
},
|
||||
"index": {
|
||||
"title": "Огляд",
|
||||
"cpu": "CPU",
|
||||
"cpu": "ЦП",
|
||||
"logicalProcessors": "Логічні процесори",
|
||||
"frequency": "Частота",
|
||||
"swap": "Swap",
|
||||
"swap": "Підкачка",
|
||||
"storage": "Сховище",
|
||||
"memory": "RAM",
|
||||
"memory": "Пам’ять",
|
||||
"threads": "Потоки",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Стоп",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "Дисковий ввід-вивід",
|
||||
"historyTitleOnline": "Клієнти онлайн",
|
||||
"historyTitleLoad": "Середнє навантаження системи (1 / 5 / 15 хв)",
|
||||
"historyTitleConnections": "Активні з’єднання (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Використання дискового простору",
|
||||
"historyTabBandwidth": "Пропускна здатність",
|
||||
"historyTabPackets": "Пакети",
|
||||
"historyTabDisk": "Диск I/O",
|
||||
"historyTabOnline": "Онлайн",
|
||||
"historyTabLoad": "Навантаження",
|
||||
"historyTabConnections": "З’єднання",
|
||||
"historyTabDiskUsage": "Використання диска",
|
||||
"charts": "Графіки",
|
||||
"xrayMetricsTitle": "Метрики Xray",
|
||||
"xrayTitleHeap": "Виділена пам’ять купи",
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@
|
|||
"frequency": "Tần số",
|
||||
"swap": "Swap",
|
||||
"storage": "Lưu trữ",
|
||||
"memory": "RAM",
|
||||
"memory": "Bộ nhớ",
|
||||
"threads": "Luồng",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "Dừng",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "I/O đĩa",
|
||||
"historyTitleOnline": "Máy khách trực tuyến",
|
||||
"historyTitleLoad": "Tải trung bình hệ thống (1 / 5 / 15 phút)",
|
||||
"historyTitleConnections": "Kết nối đang hoạt động (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "Sử dụng dung lượng đĩa",
|
||||
"historyTabBandwidth": "Băng thông",
|
||||
"historyTabPackets": "Gói tin",
|
||||
"historyTabDisk": "Đĩa I/O",
|
||||
"historyTabOnline": "Trực tuyến",
|
||||
"historyTabLoad": "Tải",
|
||||
"historyTabConnections": "Kết nối",
|
||||
"historyTabDiskUsage": "Sử dụng đĩa",
|
||||
"charts": "Biểu đồ",
|
||||
"xrayMetricsTitle": "Chỉ số Xray",
|
||||
"xrayTitleHeap": "Bộ nhớ Heap đã cấp phát",
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@
|
|||
"cpu": "CPU",
|
||||
"logicalProcessors": "逻辑处理器",
|
||||
"frequency": "频率",
|
||||
"swap": "Swap",
|
||||
"swap": "交换空间",
|
||||
"storage": "存储",
|
||||
"memory": "RAM",
|
||||
"memory": "内存",
|
||||
"threads": "线程",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "停止",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "磁盘 I/O",
|
||||
"historyTitleOnline": "在线客户端",
|
||||
"historyTitleLoad": "系统平均负载(1 分钟 / 5 分钟 / 15 分钟)",
|
||||
"historyTitleConnections": "活动连接 (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "磁盘空间使用率",
|
||||
"historyTabBandwidth": "带宽",
|
||||
"historyTabPackets": "数据包",
|
||||
"historyTabDisk": "磁盘 I/O",
|
||||
"historyTabOnline": "在线",
|
||||
"historyTabLoad": "负载",
|
||||
"historyTabConnections": "连接数",
|
||||
"historyTabDiskUsage": "磁盘使用量",
|
||||
"charts": "图表",
|
||||
"xrayMetricsTitle": "Xray 指标",
|
||||
"xrayTitleHeap": "已分配的堆内存",
|
||||
|
|
|
|||
|
|
@ -131,9 +131,9 @@
|
|||
"cpu": "CPU",
|
||||
"logicalProcessors": "邏輯處理器",
|
||||
"frequency": "頻率",
|
||||
"swap": "Swap",
|
||||
"swap": "交換空間",
|
||||
"storage": "儲存",
|
||||
"memory": "RAM",
|
||||
"memory": "記憶體",
|
||||
"threads": "執行緒",
|
||||
"xrayStatus": "Xray",
|
||||
"stopXray": "停止",
|
||||
|
|
@ -162,11 +162,15 @@
|
|||
"historyTitleDisk": "磁碟 I/O",
|
||||
"historyTitleOnline": "線上用戶端",
|
||||
"historyTitleLoad": "系統平均負載(1 分鐘 / 5 分鐘 / 15 分鐘)",
|
||||
"historyTitleConnections": "使用中的連線 (TCP / UDP)",
|
||||
"historyTitleDiskUsage": "磁碟空間使用率",
|
||||
"historyTabBandwidth": "頻寬",
|
||||
"historyTabPackets": "封包",
|
||||
"historyTabDisk": "磁碟 I/O",
|
||||
"historyTabOnline": "線上",
|
||||
"historyTabLoad": "負載",
|
||||
"historyTabConnections": "連線數",
|
||||
"historyTabDiskUsage": "磁碟使用量",
|
||||
"charts": "圖表",
|
||||
"xrayMetricsTitle": "Xray 指標",
|
||||
"xrayTitleHeap": "已配置的堆積記憶體",
|
||||
|
|
|
|||
|
|
@ -469,6 +469,9 @@ func (s *Server) stop(stopXray bool, stopTgBot bool) error {
|
|||
if s.cron != nil {
|
||||
s.cron.Stop()
|
||||
}
|
||||
if err := service.PersistSystemMetrics(); err != nil {
|
||||
logger.Warning("persist system metrics on shutdown failed:", err)
|
||||
}
|
||||
if stopXray {
|
||||
service.StopTrafficWriter()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue