mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: 完成容器监控功能
This commit is contained in:
parent
53845e60b6
commit
e50c4c39c1
@ -1,6 +1,8 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
@ -64,6 +66,21 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) ContainerStats(c *gin.Context) {
|
||||
containerID, ok := c.Params.Get("id")
|
||||
if !ok {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error container id in path"))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := containerService.ContainerStats(containerID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, result)
|
||||
}
|
||||
|
||||
func (b *BaseApi) Inspect(c *gin.Context) {
|
||||
var req dto.InspectReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
|
@ -37,6 +37,18 @@ type ContainerCreate struct {
|
||||
RestartPolicy string `json:"restartPolicy"`
|
||||
}
|
||||
|
||||
type ContainterStats struct {
|
||||
CPUPercent float64 `json:"cpuPercent"`
|
||||
Memory float64 `json:"memory"`
|
||||
Cache float64 `json:"cache"`
|
||||
IORead float64 `json:"ioRead"`
|
||||
IOWrite float64 `json:"ioWrite"`
|
||||
NetworkRX float64 `json:"networkRX"`
|
||||
NetworkTX float64 `json:"networkTX"`
|
||||
|
||||
ShotTime time.Time `json:"shotTime"`
|
||||
}
|
||||
|
||||
type VolumeHelper struct {
|
||||
SourceDir string `json:"sourceDir"`
|
||||
ContainerDir string `json:"containerDir"`
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -33,6 +34,7 @@ type IContainerService interface {
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
ContainerLogs(param dto.ContainerLog) (string, error)
|
||||
ContainerStats(id string) (*dto.ContainterStats, error)
|
||||
Inspect(req dto.InspectReq) (string, error)
|
||||
DeleteNetwork(req dto.BatchDelete) error
|
||||
CreateNetwork(req dto.NetworkCreat) error
|
||||
@ -159,27 +161,27 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
|
||||
func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {
|
||||
var err error
|
||||
ctx := context.Background()
|
||||
dc, err := docker.NewDockerClient()
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch req.Operation {
|
||||
case constant.ContainerOpStart:
|
||||
err = dc.ContainerStart(ctx, req.ContainerID, types.ContainerStartOptions{})
|
||||
err = client.ContainerStart(ctx, req.ContainerID, types.ContainerStartOptions{})
|
||||
case constant.ContainerOpStop:
|
||||
err = dc.ContainerStop(ctx, req.ContainerID, nil)
|
||||
err = client.ContainerStop(ctx, req.ContainerID, nil)
|
||||
case constant.ContainerOpRestart:
|
||||
err = dc.ContainerRestart(ctx, req.ContainerID, nil)
|
||||
err = client.ContainerRestart(ctx, req.ContainerID, nil)
|
||||
case constant.ContainerOpKill:
|
||||
err = dc.ContainerKill(ctx, req.ContainerID, "SIGKILL")
|
||||
err = client.ContainerKill(ctx, req.ContainerID, "SIGKILL")
|
||||
case constant.ContainerOpPause:
|
||||
err = dc.ContainerPause(ctx, req.ContainerID)
|
||||
err = client.ContainerPause(ctx, req.ContainerID)
|
||||
case constant.ContainerOpUnpause:
|
||||
err = dc.ContainerUnpause(ctx, req.ContainerID)
|
||||
err = client.ContainerUnpause(ctx, req.ContainerID)
|
||||
case constant.ContainerOpRename:
|
||||
err = dc.ContainerRename(ctx, req.ContainerID, req.NewName)
|
||||
err = client.ContainerRename(ctx, req.ContainerID, req.NewName)
|
||||
case constant.ContainerOpRemove:
|
||||
err = dc.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{RemoveVolumes: true, RemoveLinks: true, Force: true})
|
||||
err = client.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{RemoveVolumes: true, RemoveLinks: true, Force: true})
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -213,6 +215,40 @@ func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ContainerStats(id string) (*dto.ContainterStats, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := client.ContainerStats(context.TODO(), id, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var stats *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &stats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data dto.ContainterStats
|
||||
previousCPU := stats.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := stats.PreCPUStats.SystemUsage
|
||||
data.CPUPercent = calculateCPUPercentUnix(previousCPU, previousSystem, stats)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(stats.BlkioStats)
|
||||
data.Memory = float64(stats.MemoryStats.Usage) / 1024 / 1024
|
||||
if cache, ok := stats.MemoryStats.Stats["cache"]; ok {
|
||||
data.Cache = float64(cache) / 1024 / 1024
|
||||
}
|
||||
data.Memory = data.Memory - data.Cache
|
||||
data.NetworkRX, data.NetworkTX = calculateNetwork(stats.Networks)
|
||||
data.ShotTime = stats.Read
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) PageNetwork(req dto.PageInfo) (int64, interface{}, error) {
|
||||
client, err := docker.NewDockerClient()
|
||||
if err != nil {
|
||||
@ -404,3 +440,35 @@ func stringsToMap(list []string) map[string]string {
|
||||
}
|
||||
return lableMap
|
||||
}
|
||||
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *types.StatsJSON) float64 {
|
||||
var (
|
||||
cpuPercent = 0.0
|
||||
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
|
||||
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
|
||||
)
|
||||
|
||||
if systemDelta > 0.0 && cpuDelta > 0.0 {
|
||||
cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CPUStats.CPUUsage.PercpuUsage)) * 100.0
|
||||
}
|
||||
return cpuPercent
|
||||
}
|
||||
func calculateBlockIO(blkio types.BlkioStats) (blkRead float64, blkWrite float64) {
|
||||
for _, bioEntry := range blkio.IoServiceBytesRecursive {
|
||||
switch strings.ToLower(bioEntry.Op) {
|
||||
case "read":
|
||||
blkRead = (blkRead + float64(bioEntry.Value)) / 1024 / 1024
|
||||
case "write":
|
||||
blkWrite = (blkWrite + float64(bioEntry.Value)) / 1024 / 1024
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func calculateNetwork(network map[string]types.NetworkStats) (float64, float64) {
|
||||
var rx, tx float64
|
||||
|
||||
for _, v := range network {
|
||||
rx += float64(v.RxBytes) / 1024
|
||||
tx += float64(v.TxBytes) / 1024
|
||||
}
|
||||
return rx, tx
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/utils/docker"
|
||||
"github.com/docker/docker/api/types"
|
||||
@ -95,10 +96,31 @@ func TestNetwork(t *testing.T) {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
_, err = client.NetworkCreate(context.TODO(), "test", types.NetworkCreate{})
|
||||
res, err := client.ContainerStatsOneShot(context.TODO(), "30e4d3395b87")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
var state *types.StatsJSON
|
||||
if err := json.Unmarshal(body, &state); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(body))
|
||||
|
||||
var data dto.ContainterStats
|
||||
previousCPU := state.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := state.PreCPUStats.SystemUsage
|
||||
data.CPUPercent = calculateCPUPercentUnix(previousCPU, previousSystem, state)
|
||||
data.IORead, data.IOWrite = calculateBlockIO(state.BlkioStats)
|
||||
data.Memory = float64(state.MemoryStats.Usage)
|
||||
data.NetworkRX, data.NetworkTX = calculateNetwork(state.Networks)
|
||||
fmt.Println(data)
|
||||
}
|
||||
|
||||
func TestContainer(t *testing.T) {
|
||||
|
@ -25,6 +25,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
||||
baRouter.POST("", baseApi.ContainerCreate)
|
||||
withRecordRouter.POST("operate", baseApi.ContainerOperation)
|
||||
withRecordRouter.POST("/log", baseApi.ContainerLogs)
|
||||
withRecordRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||
|
||||
baRouter.POST("/repo/search", baseApi.SearchRepo)
|
||||
baRouter.PUT("/repo/:id", baseApi.UpdateRepo)
|
||||
|
@ -37,6 +37,16 @@ export namespace Container {
|
||||
state: string;
|
||||
runTime: string;
|
||||
}
|
||||
export interface ContainerStats {
|
||||
cpuPercent: number;
|
||||
memory: number;
|
||||
cache: number;
|
||||
ioRead: number;
|
||||
ioWrite: number;
|
||||
networkRX: number;
|
||||
networkTX: number;
|
||||
shotTime: Date;
|
||||
}
|
||||
export interface ContainerLogSearch {
|
||||
containerID: string;
|
||||
mode: string;
|
||||
|
@ -12,6 +12,9 @@ export const createContainer = (params: Container.ContainerCreate) => {
|
||||
export const getContainerLog = (params: Container.ContainerLogSearch) => {
|
||||
return http.post<string>(`/containers/log`, params);
|
||||
};
|
||||
export const ContainerStats = (id: string) => {
|
||||
return http.get<Container.ContainerStats>(`/containers/stats/${id}`);
|
||||
};
|
||||
|
||||
export const ContainerOperator = (params: Container.ContainerOperate) => {
|
||||
return http.post(`/containers/operate`, params);
|
||||
|
@ -226,6 +226,10 @@ export default {
|
||||
scope: 'IP Scope',
|
||||
gateway: 'Gateway',
|
||||
|
||||
monitor: 'Monitor',
|
||||
refreshTime: 'Refresh time',
|
||||
cache: 'Cache',
|
||||
|
||||
volume: 'Volume',
|
||||
volumeName: 'Name',
|
||||
mountpoint: 'Mountpoint',
|
||||
|
@ -190,6 +190,10 @@ export default {
|
||||
onFailure: '失败后重启(默认重启 5 次)',
|
||||
no: '不重启',
|
||||
|
||||
monitor: '监控',
|
||||
refreshTime: '刷新间隔',
|
||||
cache: '缓存',
|
||||
|
||||
image: '镜像',
|
||||
imagePull: '拉取镜像',
|
||||
imagePush: '推送镜像',
|
||||
|
@ -49,6 +49,7 @@ export function dateFromat(row: number, col: number, dataStr: any) {
|
||||
return `${String(y)}-${String(m)}-${String(d)} ${String(h)}:${String(minute)}:${String(second)}`;
|
||||
}
|
||||
|
||||
// 20221013151302
|
||||
export function dateFromatForName(dataStr: any) {
|
||||
const date = new Date(dataStr);
|
||||
const y = date.getFullYear();
|
||||
@ -65,6 +66,7 @@ export function dateFromatForName(dataStr: any) {
|
||||
return `${String(y)}${String(m)}${String(d)}${String(h)}${String(minute)}${String(second)}`;
|
||||
}
|
||||
|
||||
// 10-13 \n 15:13
|
||||
export function dateFromatWithoutYear(dataStr: any) {
|
||||
const date = new Date(dataStr);
|
||||
let m: string | number = date.getMonth() + 1;
|
||||
@ -78,6 +80,18 @@ export function dateFromatWithoutYear(dataStr: any) {
|
||||
return `${String(m)}-${String(d)}\n${String(h)}:${String(minute)}`;
|
||||
}
|
||||
|
||||
// 20221013151302
|
||||
export function dateFromatForSecond(dataStr: any) {
|
||||
const date = new Date(dataStr);
|
||||
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;
|
||||
let second: string | number = date.getSeconds();
|
||||
second = second < 10 ? `0${String(second)}` : second;
|
||||
return `${String(h)}:${String(minute)}:${String(second)}`;
|
||||
}
|
||||
|
||||
export function getRandomStr(e: number): string {
|
||||
const t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
|
||||
const a = t.length;
|
||||
|
@ -157,12 +157,14 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<CreateDialog @search="search" ref="dialogCreateRef" />
|
||||
<MonitorDialog ref="dialogMonitorRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import CreateDialog from '@/views/container/container/create/index.vue';
|
||||
import MonitorDialog from '@/views/container/container/monitor/index.vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
@ -239,15 +241,16 @@ const search = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const dialogCreateRef = ref<DialogExpose>();
|
||||
|
||||
interface DialogExpose {
|
||||
acceptParams: () => void;
|
||||
}
|
||||
const onCreate = async () => {
|
||||
const dialogCreateRef = ref();
|
||||
const onCreate = () => {
|
||||
dialogCreateRef.value!.acceptParams();
|
||||
};
|
||||
|
||||
const dialogMonitorRef = ref();
|
||||
const onMonitor = (containerID: string) => {
|
||||
dialogMonitorRef.value!.acceptParams({ containerID: containerID });
|
||||
};
|
||||
|
||||
const onInspect = async (id: string) => {
|
||||
const res = await inspect({ id: id, type: 'container' });
|
||||
detailInfo.value = JSON.stringify(JSON.parse(res.data), null, 2);
|
||||
@ -364,6 +367,12 @@ const onOperate = async (operation: string) => {
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('container.monitor'),
|
||||
click: (row: Container.ContainerInfo) => {
|
||||
onMonitor(row.containerID);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.reName'),
|
||||
click: (row: Container.ContainerInfo) => {
|
||||
|
288
frontend/src/views/container/container/monitor/index.vue
Normal file
288
frontend/src/views/container/container/monitor/index.vue
Normal file
@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="monitorVisiable"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
:close-on-click-modal="false"
|
||||
width="70%"
|
||||
>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ $t('container.monitor') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<span>{{ $t('container.refreshTime') }}</span>
|
||||
<el-select style="margin-left: 10px" v-model="timeInterval" @change="changeTimer">
|
||||
<el-option label="1s" :value="1" />
|
||||
<el-option label="3s" :value="3" />
|
||||
<el-option label="5s" :value="5" />
|
||||
<el-option label="10s" :value="10" />
|
||||
<el-option label="30s" :value="30" />
|
||||
<el-option label="60s" :value="60" />
|
||||
</el-select>
|
||||
<el-row :gutter="20" style="margin-top: 10px">
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<div id="cpuChart" style="width: 100%; height: 230px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<div id="memoryChart" style="width: 100%; height: 230px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" style="margin-top: 10px">
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<div id="ioChart" style="width: 100%; height: 230px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<div id="networkChart" style="width: 100%; height: 230px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { ContainerStats } from '@/api/modules/container';
|
||||
import { dateFromatForSecond } from '@/utils/util';
|
||||
import * as echarts from 'echarts';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const monitorVisiable = ref(false);
|
||||
const timeInterval = ref();
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
let isInit = ref<boolean>(true);
|
||||
interface DialogProps {
|
||||
containerID: string;
|
||||
}
|
||||
const dialogData = ref<DialogProps>({
|
||||
containerID: '',
|
||||
});
|
||||
|
||||
function changeChartSize() {
|
||||
echarts.getInstanceByDom(document.getElementById('cpuChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('memoryChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('ioChart') as HTMLElement)?.resize();
|
||||
echarts.getInstanceByDom(document.getElementById('networkChart') as HTMLElement)?.resize();
|
||||
}
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
monitorVisiable.value = true;
|
||||
dialogData.value.containerID = params.containerID;
|
||||
cpuDatas.value = [];
|
||||
memDatas.value = [];
|
||||
cacheDatas.value = [];
|
||||
ioReadDatas.value = [];
|
||||
ioWriteDatas.value = [];
|
||||
netTxDatas.value = [];
|
||||
netRxDatas.value = [];
|
||||
timeDatas.value = [];
|
||||
timeInterval.value = 5;
|
||||
isInit.value = true;
|
||||
loadData();
|
||||
window.addEventListener('resize', changeChartSize);
|
||||
timer = setInterval(async () => {
|
||||
if (monitorVisiable.value) {
|
||||
isInit.value = false;
|
||||
loadData();
|
||||
}
|
||||
}, 1000 * timeInterval.value);
|
||||
};
|
||||
|
||||
const cpuDatas = ref<Array<string>>([]);
|
||||
const memDatas = ref<Array<string>>([]);
|
||||
const cacheDatas = ref<Array<string>>([]);
|
||||
const ioReadDatas = ref<Array<string>>([]);
|
||||
const ioWriteDatas = ref<Array<string>>([]);
|
||||
const netTxDatas = ref<Array<string>>([]);
|
||||
const netRxDatas = ref<Array<string>>([]);
|
||||
const timeDatas = ref<Array<string>>([]);
|
||||
|
||||
const changeTimer = () => {
|
||||
clearInterval(Number(timer));
|
||||
timer = setInterval(async () => {
|
||||
if (monitorVisiable.value) {
|
||||
loadData();
|
||||
}
|
||||
}, 1000 * timeInterval.value);
|
||||
};
|
||||
|
||||
const loadData = async () => {
|
||||
const res = await ContainerStats(dialogData.value.containerID);
|
||||
cpuDatas.value.push(res.data.cpuPercent.toFixed(2));
|
||||
if (cpuDatas.value.length > 20) {
|
||||
cpuDatas.value.splice(0, 1);
|
||||
}
|
||||
memDatas.value.push(res.data.memory.toFixed(2));
|
||||
if (memDatas.value.length > 20) {
|
||||
memDatas.value.splice(0, 1);
|
||||
}
|
||||
cacheDatas.value.push(res.data.cache.toFixed(2));
|
||||
if (cacheDatas.value.length > 20) {
|
||||
cacheDatas.value.splice(0, 1);
|
||||
}
|
||||
ioReadDatas.value.push(res.data.ioRead.toFixed(2));
|
||||
if (ioReadDatas.value.length > 20) {
|
||||
ioReadDatas.value.splice(0, 1);
|
||||
}
|
||||
ioWriteDatas.value.push(res.data.ioWrite.toFixed(2));
|
||||
if (ioWriteDatas.value.length > 20) {
|
||||
ioWriteDatas.value.splice(0, 1);
|
||||
}
|
||||
netTxDatas.value.push(res.data.networkTX.toFixed(2));
|
||||
if (netTxDatas.value.length > 20) {
|
||||
netTxDatas.value.splice(0, 1);
|
||||
}
|
||||
netRxDatas.value.push(res.data.networkRX.toFixed(2));
|
||||
if (netRxDatas.value.length > 20) {
|
||||
netRxDatas.value.splice(0, 1);
|
||||
}
|
||||
timeDatas.value.push(dateFromatForSecond(res.data.shotTime));
|
||||
if (timeDatas.value.length > 20) {
|
||||
timeDatas.value.splice(0, 1);
|
||||
}
|
||||
|
||||
let cpuYDatas = {
|
||||
name: 'CPU',
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: cpuDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
freshChart('cpuChart', ['CPU'], timeDatas.value, [cpuYDatas], 'CPU', '%');
|
||||
|
||||
let memoryYDatas = {
|
||||
name: i18n.global.t('monitor.memory'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: memDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
let cacheYDatas = {
|
||||
name: i18n.global.t('container.cache'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: cacheDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
freshChart(
|
||||
'memoryChart',
|
||||
[i18n.global.t('monitor.memory'), i18n.global.t('monitor.cache')],
|
||||
timeDatas.value,
|
||||
[memoryYDatas, cacheYDatas],
|
||||
i18n.global.t('monitor.memory'),
|
||||
' MB',
|
||||
);
|
||||
|
||||
let ioReadYDatas = {
|
||||
name: i18n.global.t('monitor.read'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: ioReadDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
let ioWriteYDatas = {
|
||||
name: i18n.global.t('monitor.write'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: ioWriteDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
freshChart(
|
||||
'ioChart',
|
||||
[i18n.global.t('monitor.read'), i18n.global.t('monitor.write')],
|
||||
timeDatas.value,
|
||||
[ioReadYDatas, ioWriteYDatas],
|
||||
i18n.global.t('monitor.disk') + ' IO',
|
||||
'MB',
|
||||
);
|
||||
|
||||
let netTxYDatas = {
|
||||
name: i18n.global.t('monitor.up'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: netTxDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
let netRxYDatas = {
|
||||
name: i18n.global.t('monitor.down'),
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: '#ebdee3',
|
||||
},
|
||||
data: netRxDatas.value,
|
||||
showSymbol: false,
|
||||
};
|
||||
freshChart(
|
||||
'networkChart',
|
||||
[i18n.global.t('monitor.up'), i18n.global.t('monitor.down')],
|
||||
timeDatas.value,
|
||||
[netTxYDatas, netRxYDatas],
|
||||
i18n.global.t('monitor.network'),
|
||||
'KB/s',
|
||||
);
|
||||
};
|
||||
|
||||
function freshChart(chartName: string, legendDatas: any, xDatas: any, yDatas: any, yTitle: string, formatStr: string) {
|
||||
if (isInit.value) {
|
||||
echarts.init(document.getElementById(chartName) as HTMLElement);
|
||||
}
|
||||
let itemChart = echarts.getInstanceByDom(document.getElementById(chartName) as HTMLElement);
|
||||
const option = {
|
||||
title: [
|
||||
{
|
||||
left: 'center',
|
||||
text: yTitle,
|
||||
},
|
||||
],
|
||||
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;
|
||||
},
|
||||
},
|
||||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||||
legend: {
|
||||
data: legendDatas,
|
||||
right: 10,
|
||||
},
|
||||
xAxis: { data: xDatas, boundaryGap: false },
|
||||
yAxis: { name: '( ' + formatStr + ' )' },
|
||||
series: yDatas,
|
||||
};
|
||||
itemChart?.setOption(option, true);
|
||||
}
|
||||
|
||||
const onClose = async () => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
window.removeEventListener('resize', changeChartSize);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
@ -131,6 +131,7 @@ const loadLogs = async (path: string) => {
|
||||
const onCloseLog = async () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
const loadBuildDir = async (path: string) => {
|
||||
|
@ -406,6 +406,7 @@ onMounted(() => {
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
Loading…
Reference in New Issue
Block a user