mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-11-08 10:41:16 +08:00
feat: 监控前端界面显示
This commit is contained in:
parent
4ba55298b4
commit
7a42e34c2b
15 changed files with 674 additions and 117 deletions
|
|
@ -1,6 +1,6 @@
|
|||
system:
|
||||
port: 9999
|
||||
db_type: mysql
|
||||
db_type: sqlite
|
||||
level: debug
|
||||
|
||||
jwt:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
)
|
||||
|
||||
func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
||||
|
|
@ -18,7 +22,71 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
|
|||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if global.CONF.System.DbType == "sqlite" {
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
}
|
||||
|
||||
// stampStart := req.StartTime.Unix()
|
||||
// stampEnd := req.EndTime.Unix()
|
||||
var backdatas []dto.MonitorData
|
||||
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
|
||||
var bases []model.MonitorBase
|
||||
if err := global.DB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
var itemData dto.MonitorData
|
||||
itemData.Param = "base"
|
||||
for _, base := range bases {
|
||||
itemData.Date = append(itemData.Date, base.CreatedAt)
|
||||
itemData.Value = append(itemData.Value, base)
|
||||
}
|
||||
backdatas = append(backdatas, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "io" {
|
||||
var bases []model.MonitorIO
|
||||
if err := global.DB.
|
||||
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
var itemData dto.MonitorData
|
||||
itemData.Param = "io"
|
||||
for _, base := range bases {
|
||||
itemData.Date = append(itemData.Date, base.CreatedAt)
|
||||
itemData.Value = append(itemData.Value, base)
|
||||
}
|
||||
backdatas = append(backdatas, itemData)
|
||||
}
|
||||
if req.Param == "all" || req.Param == "network" {
|
||||
var bases []model.MonitorNetwork
|
||||
if err := global.DB.
|
||||
Where("name = ? AND created_at > ? AND created_at < ?", req.Info, req.StartTime, req.EndTime).
|
||||
Find(&bases).Error; err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
var itemData dto.MonitorData
|
||||
itemData.Param = "network"
|
||||
for _, base := range bases {
|
||||
itemData.Date = append(itemData.Date, base.CreatedAt)
|
||||
itemData.Value = append(itemData.Value, base)
|
||||
}
|
||||
backdatas = append(backdatas, itemData)
|
||||
}
|
||||
helper.SuccessWithData(c, backdatas)
|
||||
}
|
||||
|
||||
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
|
||||
netStat, _ := net.IOCounters(true)
|
||||
var options []string
|
||||
for _, net := range netStat {
|
||||
options = append(options, net.Name)
|
||||
}
|
||||
helper.SuccessWithData(c, options)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
)
|
||||
|
||||
func TestMonito(t *testing.T) {
|
||||
totalPercent, _ := cpu.Percent(3*time.Second, false) // 总 cpu 使用
|
||||
perPercents, _ := cpu.Percent(3*time.Second, true) // 各 cpu 使用
|
||||
fmt.Println("================totalPercent============", totalPercent)
|
||||
fmt.Println("================perPercents=============", perPercents)
|
||||
|
||||
info, _ := load.Avg()
|
||||
info2, _ := load.Misc()
|
||||
fmt.Printf("load: \n loadxx: %v load1: %v, load5: %v, load15: %v \n\n", info2, info.Load1, info.Load5, info.Load15)
|
||||
|
||||
memory, _ := mem.VirtualMemory()
|
||||
fmt.Printf("memory: \n memory used: %v, use persent: %v \n\n", memory.Used, memory.UsedPercent)
|
||||
|
||||
diskPart, _ := disk.Partitions(true)
|
||||
// fmt.Println("================disk=============", diskPart)
|
||||
for _, part := range diskPart {
|
||||
diskInfo, _ := disk.Usage(part.Mountpoint)
|
||||
fmt.Printf("inode,disk(%v): \n inode persent: %v, disk used: %v, persent: %v \n", part.Mountpoint, diskInfo.InodesUsedPercent, diskInfo.Used, diskInfo.UsedPercent)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
ioStat, _ := disk.IOCounters()
|
||||
for _, v := range ioStat {
|
||||
fmt.Printf("io: \n name: %v, readCount: %v, writeCount: %v \n\n", v.Name, v.ReadCount, v.WriteCount)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
net, _ := net.IOCounters(false)
|
||||
for _, v := range net {
|
||||
fmt.Printf("netio: \n %v: send:%v recv:%v \n\n", v.Name, v.BytesSent, v.BytesRecv)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
|
@ -3,8 +3,14 @@ package dto
|
|||
import "time"
|
||||
|
||||
type MonitorSearch struct {
|
||||
Param string `json:"param" validate:"required,oneof=all cpu memory load disk inode io iops network"`
|
||||
Param string `json:"param" validate:"required,oneof=all cpu memory load io network"`
|
||||
Info string `json:"info"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Unit string `josn:"unit"`
|
||||
}
|
||||
|
||||
type MonitorData struct {
|
||||
Param string `json:"param" validate:"required,oneof=cpu memory load io network"`
|
||||
Date []time.Time `json:"date"`
|
||||
Value []interface{} `json:"value"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,18 @@ type MonitorIO struct {
|
|||
WriteTime uint64 `gorm:"type:decimal" json:"writeTime"`
|
||||
ReadByte uint64 `gorm:"type:decimal(32)" json:"readByte"`
|
||||
WriteByte uint64 `gorm:"type:decimal(32)" json:"writeByte"`
|
||||
|
||||
Read uint64 `gorm:"type:decimal" json:"read"`
|
||||
Write uint64 `gorm:"type:decimal" json:"write"`
|
||||
Count uint64 `gorm:"type:decimal" json:"count"`
|
||||
Time uint64 `gorm:"type:decimal" json:"time"`
|
||||
}
|
||||
|
||||
type MonitorNetwork struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
BytesSent uint64 `gorm:"type:decimal(32)" json:"bytesSent"`
|
||||
BytesRecv uint64 `gorm:"type:decimal(32)" json:"bytesRecv"`
|
||||
Name string `json:"name"`
|
||||
BytesSent uint64 `gorm:"type:decimal(32)" json:"bytesSent"`
|
||||
BytesRecv uint64 `gorm:"type:decimal(32)" json:"bytesRecv"`
|
||||
Up float64 `gorm:"type:float" json:"up"`
|
||||
Down float64 `gorm:"type:float" json:"down"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,15 +47,43 @@ func (m *monitor) Run() {
|
|||
itemIO.WriteByte = v.WriteBytes
|
||||
itemIO.ReadTime = v.ReadTime
|
||||
itemIO.WriteTime = v.WriteTime
|
||||
var aheadData model.MonitorIO
|
||||
if err := global.DB.Where("name = ?", v.Name).Order("created_at").Find(&aheadData).Error; err != nil {
|
||||
_ = global.DB.Create(&itemIO)
|
||||
continue
|
||||
}
|
||||
stime := time.Since(aheadData.CreatedAt).Seconds()
|
||||
itemIO.Read = uint64(float64(v.ReadBytes-aheadData.ReadByte) / stime)
|
||||
itemIO.Write = uint64(float64(v.WriteBytes-aheadData.WriteByte) / stime)
|
||||
|
||||
itemIO.Count = uint64(float64(v.ReadCount-aheadData.ReadCount) / stime)
|
||||
writeCount := uint64(float64(v.WriteCount-aheadData.WriteCount) / stime)
|
||||
if writeCount > itemIO.Count {
|
||||
itemIO.Count = writeCount
|
||||
}
|
||||
|
||||
itemIO.Time = uint64(float64(v.ReadTime-aheadData.ReadTime) / stime)
|
||||
writeTime := uint64(float64(v.WriteTime-aheadData.WriteTime) / stime)
|
||||
if writeTime > itemIO.Time {
|
||||
itemIO.Time = writeTime
|
||||
}
|
||||
_ = global.DB.Create(&itemIO)
|
||||
}
|
||||
|
||||
netStat, _ := net.IOCounters(true)
|
||||
for _, v := range netStat {
|
||||
var itemNet model.MonitorNetwork
|
||||
var aheadData model.MonitorNetwork
|
||||
itemNet.Name = v.Name
|
||||
itemNet.BytesSent = v.BytesSent
|
||||
itemNet.BytesRecv = v.BytesRecv
|
||||
if err := global.DB.Where("name = ?", v.Name).Order("created_at").Find(&aheadData).Error; err != nil {
|
||||
_ = global.DB.Create(&itemNet)
|
||||
continue
|
||||
}
|
||||
stime := time.Since(aheadData.CreatedAt).Seconds()
|
||||
itemNet.Up = float64(v.BytesSent-aheadData.BytesSent) / 1024 / stime
|
||||
itemNet.Down = float64(v.BytesRecv-aheadData.BytesRecv) / 1024 / stime
|
||||
_ = global.DB.Create(&itemNet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ func Routers() *gin.Engine {
|
|||
systemRouter.InitGroupRouter(PrivateGroup)
|
||||
systemRouter.InitCommandRouter(PrivateGroup)
|
||||
systemRouter.InitTerminalRouter(PrivateGroup)
|
||||
systemRouter.InitMonitorRouter(PrivateGroup)
|
||||
systemRouter.InitOperationLogRouter(PrivateGroup)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ type RouterGroup struct {
|
|||
HostRouter
|
||||
GroupRouter
|
||||
CommandRouter
|
||||
MonitorRouter
|
||||
OperationLogRouter
|
||||
}
|
||||
|
||||
|
|
|
|||
19
backend/router/ro_monitor.go
Normal file
19
backend/router/ro_monitor.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
v1 "github.com/1Panel-dev/1Panel/app/api/v1"
|
||||
"github.com/1Panel-dev/1Panel/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MonitorRouter struct{}
|
||||
|
||||
func (s *MonitorRouter) InitMonitorRouter(Router *gin.RouterGroup) {
|
||||
monitorRouter := Router.Group("monitors").Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
monitorRouter.POST("/search", baseApi.LoadMonitor)
|
||||
monitorRouter.GET("/netoptions", baseApi.GetNetworkOptions)
|
||||
}
|
||||
}
|
||||
13
frontend/src/api/interface/monitor.ts
Normal file
13
frontend/src/api/interface/monitor.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export namespace Monitor {
|
||||
export interface MonitorData {
|
||||
param: string;
|
||||
date: Array<Date>;
|
||||
value: Array<any>;
|
||||
}
|
||||
export interface MonitorSearch {
|
||||
param: string;
|
||||
info: string;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
}
|
||||
}
|
||||
10
frontend/src/api/modules/monitor.ts
Normal file
10
frontend/src/api/modules/monitor.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import http from '@/api';
|
||||
import { Monitor } from '../interface/monitor';
|
||||
|
||||
export const loadMonitor = (param: Monitor.MonitorSearch) => {
|
||||
return http.post<Array<Monitor.MonitorData>>(`/monitors/search`, param);
|
||||
};
|
||||
|
||||
export const getNetworkOptions = () => {
|
||||
return http.get<Array<string>>(`/monitors/netoptions`);
|
||||
};
|
||||
|
|
@ -11,6 +11,13 @@ export default {
|
|||
login: 'Login',
|
||||
conn: 'Connect',
|
||||
},
|
||||
search: {
|
||||
timeStart: 'Time start',
|
||||
timeEnd: 'Time end',
|
||||
timeRange: 'To',
|
||||
dateStart: 'Date start',
|
||||
dateEnd: 'Date end',
|
||||
},
|
||||
table: {
|
||||
name: 'Name',
|
||||
group: 'Group',
|
||||
|
|
@ -92,6 +99,25 @@ export default {
|
|||
changePassword: 'Change Password',
|
||||
logout: 'Logout',
|
||||
},
|
||||
monitor: {
|
||||
avgLoad: 'Average load',
|
||||
loadDetail: 'Load detail',
|
||||
resourceUsage: 'Resource utilization rate',
|
||||
min: 'Minutes',
|
||||
read: 'Read',
|
||||
write: 'Write',
|
||||
count: 'Times',
|
||||
readWriteCount: 'Read or write Times',
|
||||
readWriteTime: 'Read or write delay',
|
||||
today: 'Today',
|
||||
yestoday: 'Yestoday',
|
||||
lastNDay: 'Last {0} day',
|
||||
memory: 'Memory',
|
||||
disk: 'Disk',
|
||||
network: 'Network',
|
||||
up: 'Up',
|
||||
down: 'Down',
|
||||
},
|
||||
terminal: {
|
||||
conn: 'connection',
|
||||
testConn: 'Test connection',
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ export default {
|
|||
login: '登陆',
|
||||
conn: '连接',
|
||||
},
|
||||
search: {
|
||||
timeStart: '开始时间',
|
||||
timeEnd: '结束时间',
|
||||
timeRange: '至',
|
||||
dateStart: '开始日期',
|
||||
dateEnd: '结束日期',
|
||||
},
|
||||
table: {
|
||||
name: '名称',
|
||||
group: '组',
|
||||
|
|
@ -92,6 +99,25 @@ export default {
|
|||
changePassword: '修改密码',
|
||||
logout: '退出登录',
|
||||
},
|
||||
monitor: {
|
||||
avgLoad: '平均负载',
|
||||
loadDetail: '负载详情',
|
||||
resourceUsage: '资源使用率',
|
||||
min: '分钟',
|
||||
read: '读取',
|
||||
write: '写入',
|
||||
count: '次',
|
||||
readWriteCount: '读写次数',
|
||||
readWriteTime: '读写延迟',
|
||||
today: '今天',
|
||||
yestoday: '昨天',
|
||||
lastNDay: '近 {0} 天',
|
||||
memory: '内存',
|
||||
disk: '磁盘',
|
||||
network: '网络',
|
||||
up: '上行',
|
||||
down: '下行',
|
||||
},
|
||||
terminal: {
|
||||
conn: '连接',
|
||||
testConn: '连接测试',
|
||||
|
|
|
|||
|
|
@ -48,3 +48,16 @@ export function dateFromat(row: number, col: number, dataStr: any) {
|
|||
second = second < 10 ? `0${String(second)}` : second;
|
||||
return `${String(y)}-${String(m)}-${String(d)} ${String(h)}:${String(minute)}:${String(second)}`;
|
||||
}
|
||||
|
||||
export function dateFromatWithoutYear(dataStr: any) {
|
||||
const date = new Date(dataStr);
|
||||
let m: string | number = date.getMonth() + 1;
|
||||
m = m < 10 ? `0${String(m)}` : m;
|
||||
let d: string | number = date.getDate();
|
||||
d = d < 10 ? `0${String(d)}` : d;
|
||||
let h: string | number = date.getHours();
|
||||
h = h < 10 ? `0${String(h)}` : h;
|
||||
let minute: string | number = date.getMinutes();
|
||||
minute = minute < 10 ? `0${String(minute)}` : minute;
|
||||
return `${String(m)}-${String(d)}\n${String(h)}:${String(minute)}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,80 +4,116 @@
|
|||
<el-col :span="24">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span style="font-size: '30px'; font-weight: 500">平均负载</span>
|
||||
<el-radio-group style="float: right" size="small" v-model="loadRadio">
|
||||
<el-radio-button label="昨天" />
|
||||
<el-radio-button label="今天" />
|
||||
<el-radio-button label="最近 7 天" />
|
||||
<el-radio-button label="最近 30 天" />
|
||||
<el-radio-button label="自定义时间" />
|
||||
</el-radio-group>
|
||||
<span style="font-size: 16px; font-weight: 500">{{ $t('monitor.avgLoad') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('load')"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; right: 20px"
|
||||
>
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<div id="loadChart1" style="width: 100%; height: 300%"></div>
|
||||
<div id="loadLoadChart" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span style="font-size: '30px'; font-weight: 500">CPU</span>
|
||||
<el-radio-group style="float: right" size="small" v-model="loadRadio">
|
||||
<el-radio-button label="昨天" />
|
||||
<el-radio-button label="今天" />
|
||||
<el-radio-button label="最近 7 天" />
|
||||
<el-radio-button label="最近 30 天" />
|
||||
<el-radio-button label="自定义时间" />
|
||||
</el-radio-group>
|
||||
<span style="font-size: 16px; font-weight: 500">CPU</span>
|
||||
<el-date-picker
|
||||
@change="search('cpu')"
|
||||
v-model="timeRangeCpu"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; right: 20px"
|
||||
>
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<div id="loadChart2" style="width: 100%; height: 300%"></div>
|
||||
<div id="loadCPUChart" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span style="font-size: '30px'; font-weight: 500">内存</span>
|
||||
<el-radio-group style="float: right" size="small" v-model="loadRadio">
|
||||
<el-radio-button label="昨天" />
|
||||
<el-radio-button label="今天" />
|
||||
<el-radio-button label="最近 7 天" />
|
||||
<el-radio-button label="最近 30 天" />
|
||||
<el-radio-button label="自定义时间" />
|
||||
</el-radio-group>
|
||||
<span style="font-size: 16px; font-weight: 500">{{ $t('monitor.memory') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('memory')"
|
||||
v-model="timeRangeMemory"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; right: 20px"
|
||||
>
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<div id="loadChart3" style="width: 100%; height: 300%"></div>
|
||||
<div id="loadMemoryChart" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span style="font-size: '30px'; font-weight: 500">磁盘IO</span>
|
||||
<el-radio-group style="float: right" size="small" v-model="loadRadio">
|
||||
<el-radio-button label="昨天" />
|
||||
<el-radio-button label="今天" />
|
||||
<el-radio-button label="最近 7 天" />
|
||||
<el-radio-button label="最近 30 天" />
|
||||
<el-radio-button label="自定义时间" />
|
||||
</el-radio-group>
|
||||
<span style="font-size: 16px; font-weight: 500">{{ $t('monitor.disk') }} IO</span>
|
||||
<el-date-picker
|
||||
@change="search('io')"
|
||||
v-model="timeRangeIO"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; right: 20px"
|
||||
>
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<div id="loadChart4" style="width: 100%; height: 300%"></div>
|
||||
<div id="loadIOChart" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span style="font-size: '30px'; font-weight: 500">网络IO:</span>
|
||||
<el-radio-group style="float: right" size="small" v-model="loadRadio">
|
||||
<el-radio-button label="昨天" />
|
||||
<el-radio-button label="今天" />
|
||||
<el-radio-button label="最近 7 天" />
|
||||
<el-radio-button label="最近 30 天" />
|
||||
<el-radio-button label="自定义时间" />
|
||||
</el-radio-group>
|
||||
<span style="font-size: 16px; font-weight: 500">{{ $t('monitor.network') }} IO</span>
|
||||
<el-select
|
||||
v-model="networkChoose"
|
||||
clearable
|
||||
filterable
|
||||
@change="search('network')"
|
||||
style="margin-left: 20px"
|
||||
placeholder="Select"
|
||||
size="small"
|
||||
>
|
||||
<el-option v-for="item in netOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
@change="search('network')"
|
||||
v-model="timeRangeNetwork"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; right: 20px"
|
||||
>
|
||||
</el-date-picker>
|
||||
</template>
|
||||
<div id="loadChart5" style="width: 100%; height: 300%"></div>
|
||||
<div id="loadNetworkChart" style="width: 100%; height: 400px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -85,35 +121,385 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { loadMonitor, getNetworkOptions } from '@/api/modules/monitor';
|
||||
import { Monitor } from '@/api/interface/monitor';
|
||||
import { dateFromatWithoutYear } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const loadRadio = ref();
|
||||
const zoomStart = ref();
|
||||
const monitorBase = ref();
|
||||
const timeRangeLoad = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const timeRangeCpu = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const timeRangeMemory = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const timeRangeIO = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const timeRangeNetwork = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const networkChoose = ref();
|
||||
const netOptions = ref();
|
||||
const shortcuts = [
|
||||
{
|
||||
text: i18n.global.t('monitor.today'),
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date(new Date().setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.yestoday'),
|
||||
value: () => {
|
||||
const yestoday = new Date(new Date().getTime() - 3600 * 1000 * 24 * 1);
|
||||
const end = new Date(yestoday.setHours(23, 59, 59, 999));
|
||||
const start = new Date(yestoday.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [3]),
|
||||
value: () => {
|
||||
const yestoday = new Date(new Date().getTime() - 3600 * 1000 * 24 * 3);
|
||||
const end = new Date(yestoday.setHours(23, 59, 59, 999));
|
||||
const start = new Date(yestoday.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [7]),
|
||||
value: () => {
|
||||
const lastweek = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);
|
||||
const end = new Date(lastweek.setHours(23, 59, 59, 999));
|
||||
const start = new Date(lastweek.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [30]),
|
||||
value: () => {
|
||||
const lastmonth = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);
|
||||
const end = new Date(lastmonth.setHours(23, 59, 59, 999));
|
||||
const start = new Date(lastmonth.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
];
|
||||
const searchTime = ref();
|
||||
const searchInfo = reactive<Monitor.MonitorSearch>({
|
||||
param: '',
|
||||
info: '',
|
||||
startTime: new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
endTime: new Date(),
|
||||
});
|
||||
|
||||
function initCharts(key: string) {
|
||||
console.log(key);
|
||||
const lineChart = echarts.init(document.getElementById(key) as HTMLElement);
|
||||
const search = async (param: string) => {
|
||||
searchInfo.param = param;
|
||||
switch (param) {
|
||||
case 'load':
|
||||
searchTime.value = timeRangeLoad.value;
|
||||
break;
|
||||
case 'cpu':
|
||||
searchTime.value = timeRangeCpu.value;
|
||||
break;
|
||||
case 'memory':
|
||||
searchTime.value = timeRangeMemory.value;
|
||||
break;
|
||||
case 'io':
|
||||
searchTime.value = timeRangeIO.value;
|
||||
break;
|
||||
case 'network':
|
||||
searchTime.value = timeRangeNetwork.value;
|
||||
searchInfo.info = networkChoose.value;
|
||||
break;
|
||||
}
|
||||
if (searchTime.value && searchTime.value.length === 2) {
|
||||
searchInfo.startTime = searchTime.value[0];
|
||||
searchInfo.endTime = searchTime.value[1];
|
||||
}
|
||||
const res = await loadMonitor(searchInfo);
|
||||
if (res.data[0].value === null) {
|
||||
return;
|
||||
}
|
||||
monitorBase.value = res.data;
|
||||
for (const item of monitorBase.value) {
|
||||
switch (item.param) {
|
||||
case 'base':
|
||||
let baseDate = item.date.map(function (item: any) {
|
||||
return dateFromatWithoutYear(item);
|
||||
});
|
||||
if (param === 'cpu' || param === 'all') {
|
||||
let cpuData = item.value.map(function (item: any) {
|
||||
return item.cpu.toFixed(2);
|
||||
});
|
||||
let yDatasOfCpu = { name: 'CPU', type: 'line', data: cpuData, showSymbol: false };
|
||||
initCharts('loadCPUChart', baseDate, yDatasOfCpu, 'CPU', '%');
|
||||
}
|
||||
if (param === 'memory' || param === 'all') {
|
||||
let memoryData = item.value.map(function (item: any) {
|
||||
return item.memory.toFixed(2);
|
||||
});
|
||||
let yDatasOfMem = {
|
||||
name: i18n.global.t('monitor.memory'),
|
||||
type: 'line',
|
||||
data: memoryData,
|
||||
showSymbol: false,
|
||||
};
|
||||
initCharts('loadMemoryChart', baseDate, yDatasOfMem, i18n.global.t('monitor.memory'), '%');
|
||||
}
|
||||
if (param === 'load' || param === 'all') {
|
||||
initLoadCharts(item);
|
||||
}
|
||||
break;
|
||||
case 'io':
|
||||
initIOCharts(item);
|
||||
break;
|
||||
case 'network':
|
||||
let networkDate = item.date.map(function (item: any) {
|
||||
return dateFromatWithoutYear(item);
|
||||
});
|
||||
let networkUp = item.value.map(function (item: any) {
|
||||
return item.up.toFixed(2);
|
||||
});
|
||||
let yDatasOfUp = {
|
||||
name: i18n.global.t('monitor.up'),
|
||||
type: 'line',
|
||||
data: networkUp,
|
||||
showSymbol: false,
|
||||
};
|
||||
let networkOut = item.value.map(function (item: any) {
|
||||
return item.down.toFixed(2);
|
||||
});
|
||||
let yDatasOfDown = {
|
||||
name: i18n.global.t('monitor.down'),
|
||||
type: 'line',
|
||||
data: networkOut,
|
||||
showSymbol: false,
|
||||
};
|
||||
initCharts('loadNetworkChart', networkDate, [yDatasOfUp, yDatasOfDown], 'KB/s', 'KB/s');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadNetworkOptions = async () => {
|
||||
const res = await getNetworkOptions();
|
||||
netOptions.value = res.data;
|
||||
searchInfo.info = netOptions.value && netOptions.value[0];
|
||||
networkChoose.value = searchInfo.info;
|
||||
search('all');
|
||||
};
|
||||
|
||||
function initCharts(chartName: string, xDatas: any, yDatas: any, yTitle: string, formatStr: string) {
|
||||
const lineChart = echarts.init(document.getElementById(chartName) as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
for (const item of datas) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + formatStr + '<br/>';
|
||||
}
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: chartName === 'loadNetworkChart' && [i18n.global.t('monitor.up'), i18n.global.t('monitor.down')],
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
xAxis: { data: xDatas },
|
||||
yAxis: { name: '( ' + formatStr + ' )' },
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: yDatas,
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function initLoadCharts(item: Monitor.MonitorData) {
|
||||
const lineChart = echarts.init(document.getElementById('loadLoadChart') as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
for (const item of datas) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + '%' + '<br/>';
|
||||
}
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
'1 ' + i18n.global.t('monitor.min'),
|
||||
'5 ' + i18n.global.t('monitor.min'),
|
||||
'15 ' + i18n.global.t('monitor.min'),
|
||||
i18n.global.t('monitor.resourceUsage'),
|
||||
],
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
data: item.date.map(function (item: any) {
|
||||
return dateFromatWithoutYear(item);
|
||||
}),
|
||||
},
|
||||
yAxis: [
|
||||
{ type: 'value', name: i18n.global.t('monitor.loadDetail') + ' ( % )' },
|
||||
{
|
||||
type: 'value',
|
||||
name: i18n.global.t('monitor.resourceUsage') + ' ( % )',
|
||||
position: 'right',
|
||||
alignTicks: true,
|
||||
},
|
||||
],
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: [
|
||||
{
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
name: '1 ' + i18n.global.t('monitor.min'),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.cpuLoad1.toFixed(2);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: '5 ' + i18n.global.t('monitor.min'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.cpuLoad5.toFixed(2);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: '15 ' + i18n.global.t('monitor.min'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.cpuLoad15.toFixed(2);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.resourceUsage'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.loadUsage.toFixed(2);
|
||||
}),
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function initIOCharts(item: Monitor.MonitorData) {
|
||||
const lineChart = echarts.init(document.getElementById('loadIOChart') as HTMLElement);
|
||||
const option = {
|
||||
zlevel: 1,
|
||||
z: 1,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (datas: any) {
|
||||
let res = datas[0].name + '<br/>';
|
||||
for (const item of datas) {
|
||||
if (
|
||||
item.seriesName === i18n.global.t('monitor.read') ||
|
||||
item.seriesName === i18n.global.t('monitor.write')
|
||||
) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + ' KB/s' + '<br/>';
|
||||
}
|
||||
if (item.seriesName === i18n.global.t('monitor.readWriteCount')) {
|
||||
res +=
|
||||
item.marker +
|
||||
' ' +
|
||||
item.seriesName +
|
||||
':' +
|
||||
item.data +
|
||||
' ' +
|
||||
i18n.global.t('monitor.count') +
|
||||
'/s' +
|
||||
'<br/>';
|
||||
}
|
||||
if (item.seriesName === i18n.global.t('monitor.readWriteTime')) {
|
||||
res += item.marker + ' ' + item.seriesName + ':' + item.data + ' ms' + '<br/>';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
i18n.global.t('monitor.read'),
|
||||
i18n.global.t('monitor.write'),
|
||||
i18n.global.t('monitor.readWriteCount'),
|
||||
i18n.global.t('monitor.readWriteTime'),
|
||||
],
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
xAxis: {
|
||||
data: item.date.map(function (item: any) {
|
||||
return dateFromatWithoutYear(item);
|
||||
}),
|
||||
},
|
||||
yAxis: [
|
||||
{ type: 'value', name: '( KB/s )' },
|
||||
{ type: 'value', position: 'right', alignTicks: true },
|
||||
],
|
||||
dataZoom: [{ startValue: zoomStart.value }],
|
||||
series: [
|
||||
{
|
||||
name: i18n.global.t('monitor.read'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return (item.read / 1024).toFixed(2);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.write'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return (item.write / 1024).toFixed(2);
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteCount'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.count;
|
||||
}),
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.global.t('monitor.readWriteTime'),
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
data: item.value.map(function (item: any) {
|
||||
return item.time;
|
||||
}),
|
||||
yAxisIndex: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
lineChart.setOption(option, true);
|
||||
}
|
||||
|
||||
function changeChartSize() {
|
||||
echarts.getInstanceByDom(document.getElementById('loadLoadChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadCPUChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadMemoryChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadIOChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('loadNetworkChart') as HTMLElement)?.resize();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
for (let i = 1; i < 6; i++) {
|
||||
initCharts('loadChart' + i);
|
||||
}
|
||||
zoomStart.value = dateFromatWithoutYear(new Date(new Date().setHours(0, 0, 0, 0)));
|
||||
loadNetworkOptions();
|
||||
window.addEventListener('resize', changeChartSize);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', changeChartSize);
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue