|
|
|
@ -1,5 +1,5 @@ |
|
|
|
|
<script setup> |
|
|
|
|
import {ref, provide, reactive} from "vue"; |
|
|
|
|
import {ref, provide, reactive, computed} from "vue"; |
|
|
|
|
|
|
|
|
|
import VChart, {THEME_KEY} from "vue-echarts"; |
|
|
|
|
|
|
|
|
@ -7,7 +7,7 @@ import {use} from 'echarts/core' |
|
|
|
|
import {LineChart} from 'echarts/charts' |
|
|
|
|
import {GridComponent, LegendComponent, TitleComponent, TooltipComponent} from 'echarts/components' |
|
|
|
|
import {CanvasRenderer} from 'echarts/renderers' |
|
|
|
|
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'; |
|
|
|
|
import {api} from "boot/axios"; |
|
|
|
|
|
|
|
|
|
use([ |
|
|
|
|
GridComponent, |
|
|
|
@ -20,46 +20,146 @@ use([ |
|
|
|
|
|
|
|
|
|
// provide(THEME_KEY, "dark"); |
|
|
|
|
|
|
|
|
|
const option = reactive({ |
|
|
|
|
grid: { |
|
|
|
|
left: "1%", |
|
|
|
|
right: "2%", |
|
|
|
|
top: "20%", |
|
|
|
|
bottom: "1%", |
|
|
|
|
containLabel: true |
|
|
|
|
}, |
|
|
|
|
title: { |
|
|
|
|
text: "CPU使用率", |
|
|
|
|
left: "center", |
|
|
|
|
textStyle: { |
|
|
|
|
fontSize: 16 |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
tooltip: { |
|
|
|
|
trigger: 'axis' |
|
|
|
|
}, |
|
|
|
|
xAxis: {}, |
|
|
|
|
yAxis: {}, |
|
|
|
|
series: [ |
|
|
|
|
{ |
|
|
|
|
data: [ |
|
|
|
|
[0, 20], |
|
|
|
|
[10, 10], |
|
|
|
|
[20, 64], |
|
|
|
|
[40, 12], |
|
|
|
|
[50, 15], |
|
|
|
|
], |
|
|
|
|
type: 'line', |
|
|
|
|
smooth: true |
|
|
|
|
} |
|
|
|
|
] |
|
|
|
|
}); |
|
|
|
|
const options = ref([]) |
|
|
|
|
|
|
|
|
|
const timeRange = ref([ |
|
|
|
|
new Date(Number(new Date()) - 86400000), |
|
|
|
|
new Date() |
|
|
|
|
new Date('2024-04-17 12:00:00'), |
|
|
|
|
new Date('2024-04-17 16:00:00'), |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
const availableMetricItems = [ |
|
|
|
|
"up", |
|
|
|
|
"node_load5", |
|
|
|
|
"node_sockstat_TCP_tw", |
|
|
|
|
"node_cpu_seconds_total", |
|
|
|
|
"node_memory_Cached_bytes", |
|
|
|
|
"node_memory_Buffers_bytes", |
|
|
|
|
"node_memory_MemFree_bytes", |
|
|
|
|
"node_disk_read_bytes_total", |
|
|
|
|
"node_filesystem_free_bytes", |
|
|
|
|
"node_filesystem_size_bytes", |
|
|
|
|
"node_memory_MemTotal_bytes", |
|
|
|
|
"node_netstat_Tcp_CurrEstab", |
|
|
|
|
"node_filesystem_avail_bytes", |
|
|
|
|
"node_disk_written_bytes_total", |
|
|
|
|
"node_disk_reads_completed_total", |
|
|
|
|
"node_network_receive_drop_total", |
|
|
|
|
"node_disk_writes_completed_total", |
|
|
|
|
"node_network_receive_bytes_total", |
|
|
|
|
"node_network_transmit_drop_total", |
|
|
|
|
"node_network_transmit_bytes_total", |
|
|
|
|
"node_network_receive_packets_total", |
|
|
|
|
"node_network_transmit_packets_total", |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
const metricText = { |
|
|
|
|
"up": "服务器离线/上线", |
|
|
|
|
"node_load5": "5分钟负载", |
|
|
|
|
"node_sockstat_TCP_tw": "TIME-WAIT连接", |
|
|
|
|
"node_cpu_seconds_total": "CPU使用时间", |
|
|
|
|
"node_memory_Cached_bytes": "缓存内存", |
|
|
|
|
"node_memory_Buffers_bytes": "缓冲内存", |
|
|
|
|
"node_memory_MemFree_bytes": "空闲内存", |
|
|
|
|
"node_disk_read_bytes_total": "磁盘读总字节", |
|
|
|
|
"node_disk_written_bytes_total": "磁盘写总字节", |
|
|
|
|
"node_filesystem_free_bytes": "文件系统空闲空间", |
|
|
|
|
"node_filesystem_size_bytes": "文件系统总大小", |
|
|
|
|
"node_memory_MemTotal_bytes": "总内存", |
|
|
|
|
"node_netstat_Tcp_CurrEstab": "已连接TCP数", |
|
|
|
|
"node_filesystem_avail_bytes": "文件系统可用空间", |
|
|
|
|
"node_disk_reads_completed_total": "磁盘IO读次数", |
|
|
|
|
"node_disk_writes_completed_total": "磁盘IO写次数", |
|
|
|
|
"node_network_receive_drop_total": "接收丢包数", |
|
|
|
|
"node_network_transmit_drop_total": "发送丢包数", |
|
|
|
|
"node_network_receive_bytes_total": "接收总字节", |
|
|
|
|
"node_network_transmit_bytes_total": "发送总字节", |
|
|
|
|
"node_network_receive_packets_total": "总接收包数", |
|
|
|
|
"node_network_transmit_packets_total": "总发送包数" |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
* @type {import('vue').Ref<string[]>} |
|
|
|
|
*/ |
|
|
|
|
const selectedMetric = ref([ |
|
|
|
|
"node_cpu_seconds_total", |
|
|
|
|
"node_memory_MemFree_bytes", |
|
|
|
|
"node_filesystem_free_bytes", |
|
|
|
|
"node_disk_read_bytes_total", |
|
|
|
|
"node_disk_written_bytes_total", |
|
|
|
|
"node_network_receive_bytes_total", |
|
|
|
|
"node_network_transmit_bytes_total", |
|
|
|
|
"node_network_receive_packets_total", |
|
|
|
|
"node_network_transmit_packets_total", |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
const instanceId = ref('7273a1ea-0089-4674-b606-b1b8d809d866') |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @typedef {{ |
|
|
|
|
* "instanceId": string, |
|
|
|
|
* "start": number, |
|
|
|
|
* "end": number, |
|
|
|
|
* "metricItems": string[] |
|
|
|
|
* }} MetricRequest |
|
|
|
|
*/ |
|
|
|
|
function getMetricData() { |
|
|
|
|
api.post('/panel/metrics/list', { |
|
|
|
|
instanceId: instanceId.value, |
|
|
|
|
start: Number(timeRange.value[0]), |
|
|
|
|
end: Number(timeRange.value[1]), |
|
|
|
|
metricItems: selectedMetric.value |
|
|
|
|
}) |
|
|
|
|
.then(r => { |
|
|
|
|
const response = r.data |
|
|
|
|
const opts = [] |
|
|
|
|
const metricOptionsMap = new Map |
|
|
|
|
for (const selectedMetricElement of selectedMetric.value) { |
|
|
|
|
metricOptionsMap.set(selectedMetricElement, { |
|
|
|
|
grid: {left: "1%", right: "2%", top: "20%", bottom: "1%", containLabel: true}, |
|
|
|
|
title: {text: metricText[selectedMetricElement], left: "center", textStyle: {fontSize: 16}}, |
|
|
|
|
tooltip: {trigger: 'axis'}, |
|
|
|
|
xAxis: { |
|
|
|
|
type: 'time' |
|
|
|
|
}, |
|
|
|
|
yAxis: { |
|
|
|
|
type: 'value', scale: true, |
|
|
|
|
axisLabel: { |
|
|
|
|
color: '#444343', |
|
|
|
|
formatter: function (value, index) { |
|
|
|
|
if (value >= 1e9) { |
|
|
|
|
return (value / 1e9).toFixed(2) + 'G'; |
|
|
|
|
} else if (value >= 1e6) { |
|
|
|
|
return (value / 1e6).toFixed(2) + 'M'; |
|
|
|
|
} else if (value >= 1e3) { |
|
|
|
|
return (value / 1e3).toFixed(2) + 'K'; |
|
|
|
|
} else { |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
series: [{data: [], type: 'line', smooth: true}] |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const dataItem of response.data) { |
|
|
|
|
for (const metricItem in dataItem.metricData) { |
|
|
|
|
const value = dataItem.metricData[metricItem] |
|
|
|
|
metricOptionsMap.get(metricItem).series[0].data.push([dataItem.time / 1000, value]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const metricOptionsMapElement of metricOptionsMap.values()) { |
|
|
|
|
opts.push(metricOptionsMapElement) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
options.value = opts |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
getMetricData() |
|
|
|
|
|
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
<template> |
|
|
|
@ -70,23 +170,27 @@ const timeRange = ref([ |
|
|
|
|
<el-date-picker v-model="timeRange" type="datetimerange"/> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<q-separator vertical></q-separator> |
|
|
|
|
<q-select filled dense options-dense |
|
|
|
|
v-model="selectedMetric" |
|
|
|
|
multiple |
|
|
|
|
:options="availableMetricItems" |
|
|
|
|
:option-label="opt => metricText[opt]" |
|
|
|
|
label="想要查看的指标数据" |
|
|
|
|
style="width: 35rem" |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<q-separator vertical></q-separator> |
|
|
|
|
|
|
|
|
|
<q-input class="flex-1" v-model="instanceId" label="实例ID"/> |
|
|
|
|
|
|
|
|
|
<q-separator vertical></q-separator> |
|
|
|
|
|
|
|
|
|
<q-btn unelevated color="primary">刷新</q-btn> |
|
|
|
|
<q-btn unelevated color="primary" @click="getMetricData">刷新</q-btn> |
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
<div class="flex flex-row gap-10"> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart :autoresize=true class="chart" :option="option"/> |
|
|
|
|
<v-chart v-for="option in options" :key="option" :autoresize=true class="chart" :option="option"/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</template> |
|
|
|
|