Files
cloudron-box/dashboard/src/components/app/Graphs.vue
T

153 lines
5.6 KiB
Vue
Raw Normal View History

2025-03-07 11:54:43 +01:00
<script setup>
import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
2025-07-03 16:39:04 +02:00
import { ref, onMounted, useTemplateRef, nextTick, onUnmounted } from 'vue';
2025-03-07 11:54:43 +01:00
import AppsModel from '../../models/AppsModel.js';
2025-07-03 16:39:04 +02:00
import { prettyBinarySize, prettyDecimalSize } from 'pankow/utils';
import SystemModel from '../../models/SystemModel.js';
import { SingleSelect } from 'pankow';
import GraphItem from '../GraphItem.vue';
2025-03-07 11:54:43 +01:00
const { app } = defineProps([ 'app' ]);
const appsModel = AppsModel.create();
2025-07-03 16:39:04 +02:00
const systemModel = SystemModel.create();
2025-03-07 11:54:43 +01:00
const periods = [
2025-07-04 21:37:37 +02:00
{ hours: 0, label: t('app.graphs.period.live'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm:ss A' },
{ hours: 1, intervalSecs: 20, label: t('app.graphs.period.1h'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm:ss A' }, // 180 points
{ hours: 6, intervalSecs: 60, label: t('app.graphs.period.6h'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
{ hours: 12, intervalSecs: 240, label: t('app.graphs.period.12h'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
{ hours: 24, intervalSecs: 480, label: t('app.graphs.period.24h'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
{ hours: 24*7, intervalSecs: 960, label: t('app.graphs.period.7d'), tickFormat: 'DD MMM', tooltipFormat: 'DD MMM hh:mm A' },
{ hours: 24*30, intervalSecs: 960, label: t('app.graphs.period.30d'), tickFormat: 'DD MMM', tooltipFormat: 'DD MMM hh:mm A' }, // 2700 points
2025-03-07 11:54:43 +01:00
];
2025-07-03 16:39:04 +02:00
const busy = ref(true);
const period = ref(periods[0]);
const cpuGraphItem = useTemplateRef('cpuGraphItem');
const memoryGraphItem = useTemplateRef('memoryGraphItem');
const diskGraphItem = useTemplateRef('diskGraphItem');
const networkGraphItem = useTemplateRef('networkGraphItem');
2025-03-07 11:54:43 +01:00
const networkReadTotal = ref(0);
const networkWriteTotal = ref(0);
2025-07-03 16:39:04 +02:00
const blockReadTotal = ref(0);
const blockWriteTotal = ref(0);
2025-03-07 11:54:43 +01:00
const appMemory = app.memoryLimit || app.manifest.memoryLimit || 256*1024*1024;
2025-07-03 16:39:04 +02:00
let systemCpus = {};
let metricStream = null;
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
async function liveRefresh() {
2025-07-03 19:01:40 +02:00
metricStream = await appsModel.getMetricStream(app.id);
2025-07-03 16:39:04 +02:00
metricStream.onerror = (error) => console.log('event stream error:', error);
metricStream.onmessage = (message) => {
const data = JSON.parse(message.data);
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
cpuGraphItem.value.pushData(data.cpu);
memoryGraphItem.value.pushData(data.memory);
diskGraphItem.value.pushData(data.blockReadRate, data.blockWriteRate);
networkGraphItem.value.pushData(data.networkReadRate, data.networkWriteRate);
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
blockReadTotal.value = prettyDecimalSize(data.blockReadTotal);
blockWriteTotal.value = prettyDecimalSize(data.blockWriteTotal);
networkReadTotal.value = prettyDecimalSize(data.networkReadTotal);
networkWriteTotal.value = prettyDecimalSize(data.networkWriteTotal);
};
2025-03-07 11:54:43 +01:00
}
2025-07-03 16:39:04 +02:00
async function onPeriodChange() {
if (metricStream) {
metricStream.close();
metricStream = null;
}
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
if (period.value.hours === 0) return await liveRefresh();
2025-03-07 11:54:43 +01:00
2025-07-04 21:37:37 +02:00
const [error, metrics] = await appsModel.getMetrics(app.id, { fromSecs: period.value.hours * 60 * 60, intervalSecs: period.value.intervalSecs });
2025-07-03 16:39:04 +02:00
if (error) return console.error(error);
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
cpuGraphItem.value.setData(metrics.cpu);
memoryGraphItem.value.setData(metrics.memory);
diskGraphItem.value.setData(metrics.blockReadRate, metrics.blockWriteRate);
networkGraphItem.value.setData(metrics.networkReadRate, metrics.networkWriteRate);
2025-03-07 11:54:43 +01:00
2025-07-03 16:39:04 +02:00
networkReadTotal.value = prettyDecimalSize(metrics.networkReadTotal);
networkWriteTotal.value = prettyDecimalSize(metrics.networkWriteTotal);
blockReadTotal.value = prettyDecimalSize(metrics.blockReadTotal);
blockWriteTotal.value = prettyDecimalSize(metrics.blockWriteTotal);
2025-03-07 11:54:43 +01:00
}
onMounted(async () => {
const [error, result] = await systemModel.cpus();
2025-07-03 16:39:04 +02:00
if (error) return console.error(error);
systemCpus = result;
busy.value = false;
await nextTick();
await onPeriodChange();
});
onUnmounted(async () => {
if (metricStream) metricStream.close();
2025-03-07 11:54:43 +01:00
});
</script>
<template>
2025-07-03 16:39:04 +02:00
<div class="graphs" v-if="!busy">
<div style="text-align: right"><SingleSelect @select="onPeriodChange()" v-model="period" :options="periods" option-label="label"/></div>
<GraphItem ref="cpuGraphItem"
:title="$t('system.cpuUsage.title')"
:subtext="`${app.cpuQuota}% of ${systemCpus.length} Core ${systemCpus[0].model}`"
:period="period"
yscale="cpu"
:dataset-labels="['CPU']"
:dataset-colors="['#9ad0f5']"
:cpu-count="systemCpus.length"
:high-mark="systemCpus.length * app.cpuQuota/100"
2025-07-03 16:39:04 +02:00
>
</GraphItem>
<GraphItem ref="memoryGraphItem"
title="Memory"
:subtext="`RAM: ${prettyBinarySize(app.memoryLimit || app.manifest.memoryLimit || 256*1024*1024)}`"
2025-07-03 16:39:04 +02:00
:period="period"
yscale="memory"
:dataset-labels="['Memory']"
:dataset-colors="['#9ad0f5']"
:memory="appMemory"
:high-mark="appMemory"
2025-07-03 16:39:04 +02:00
>
</GraphItem>
<GraphItem ref="diskGraphItem"
title="Disk I/O"
:subtext="$t('app.graphs.diskIOTotal', { read: blockReadTotal, write: blockWriteTotal })"
:period="period"
yscale="disk"
:dataset-labels="['Read', 'Write']"
:dataset-colors="['#9ad0f5', '#ffb1c1']"
>
</GraphItem>
<GraphItem ref="networkGraphItem"
title="Network I/O"
:subtext="$t('app.graphs.networkIOTotal', { inbound: networkReadTotal, outbound: networkWriteTotal })"
:period="period"
yscale="network"
:dataset-labels="['RX', 'TX']"
:dataset-colors="['#9ad0f5', '#ffb1c1']"
>
</GraphItem>
2025-03-07 11:54:43 +01:00
</div>
</template>