Metrics
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import testAuth from "../auth";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
import si from "systeminformation";
|
||||||
|
|
||||||
|
function average<T>(values: Array<T>, p: (v: T) => number) {
|
||||||
|
return values.length > 0
|
||||||
|
? values.reduce((sum, value) => sum + p(value), 0) / values.length
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usage = {
|
||||||
|
cpu: number;
|
||||||
|
memory: number;
|
||||||
|
in: number;
|
||||||
|
out: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const usageSecs: Usage[] = [];
|
||||||
|
|
||||||
|
const usageMins: (Usage & { time: string })[] = [];
|
||||||
|
|
||||||
|
const usageHours: (Usage & { time: string })[] = new Array(12).fill({
|
||||||
|
time: "00:00",
|
||||||
|
cpu: 0,
|
||||||
|
memory: 0,
|
||||||
|
in: 0,
|
||||||
|
out: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
var hr = 0;
|
||||||
|
|
||||||
|
setInterval(async () => {
|
||||||
|
const [mem, load, network] = await Promise.all([
|
||||||
|
si.mem(),
|
||||||
|
si.currentLoad(),
|
||||||
|
si.networkStats(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
usageSecs.push({
|
||||||
|
cpu: load.currentLoad,
|
||||||
|
memory: ((mem.total - mem.available) / mem.total) * 100,
|
||||||
|
in: average(network, (n) => n.rx_bytes),
|
||||||
|
out: average(network, (n) => n.tx_bytes),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (usageSecs.length >= 60) {
|
||||||
|
const now = new Date();
|
||||||
|
const hours = String(now.getHours()).padStart(2, "0");
|
||||||
|
const minutes = String(now.getMinutes()).padStart(2, "0");
|
||||||
|
const time = `${hours}:${minutes}`;
|
||||||
|
|
||||||
|
usageMins.push({
|
||||||
|
time,
|
||||||
|
cpu: average(usageSecs, (s) => s.cpu),
|
||||||
|
memory: average(usageSecs, (s) => s.memory),
|
||||||
|
in: average(usageSecs, (s) => s.in),
|
||||||
|
out: average(usageSecs, (s) => s.out),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (usageMins.length >= 60) {
|
||||||
|
if (hr >= 12) {
|
||||||
|
usageHours.shift();
|
||||||
|
usageHours.push({
|
||||||
|
time,
|
||||||
|
cpu: average(usageMins, (s) => s.cpu),
|
||||||
|
memory: average(usageMins, (s) => s.memory),
|
||||||
|
in: average(usageMins, (s) => s.in),
|
||||||
|
out: average(usageMins, (s) => s.out),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
usageHours[hr] = {
|
||||||
|
time,
|
||||||
|
cpu: average(usageMins, (s) => s.cpu),
|
||||||
|
memory: average(usageMins, (s) => s.memory),
|
||||||
|
in: average(usageMins, (s) => s.in),
|
||||||
|
out: average(usageMins, (s) => s.out),
|
||||||
|
};
|
||||||
|
hr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
usageMins.splice(0, usageSecs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
usageSecs.splice(0, usageSecs.length);
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
export async function GET(req: NextRequest): Promise<NextResponse> {
|
||||||
|
const auth = await testAuth(req);
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ message: auth },
|
||||||
|
{ status: StatusCodes.UNAUTHORIZED }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
message: "ok",
|
||||||
|
metrics: usageHours,
|
||||||
|
});
|
||||||
|
}
|
||||||
+27
-51
@@ -5,54 +5,8 @@ import { Progress } from "@/components/ui/progress";
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { type ChartConfig } from "@/components/ui/chart";
|
import { type ChartConfig } from "@/components/ui/chart";
|
||||||
import GradientAreaChart from "@/components/GradientAreaChart";
|
import GradientAreaChart from "@/components/GradientAreaChart";
|
||||||
|
import { getMetrics } from "@/core/client/metrics";
|
||||||
const cpuData = [
|
import { useEffect, useState } from "react";
|
||||||
{ time: "00:00", cpu: 45 },
|
|
||||||
{ time: "01:00", cpu: 52 },
|
|
||||||
{ time: "02:00", cpu: 38 },
|
|
||||||
{ time: "03:00", cpu: 42 },
|
|
||||||
{ time: "04:00", cpu: 35 },
|
|
||||||
{ time: "05:00", cpu: 48 },
|
|
||||||
{ time: "06:00", cpu: 55 },
|
|
||||||
{ time: "07:00", cpu: 62 },
|
|
||||||
{ time: "08:00", cpu: 78 },
|
|
||||||
{ time: "09:00", cpu: 85 },
|
|
||||||
{ time: "10:00", cpu: 72 },
|
|
||||||
{ time: "11:00", cpu: 68 },
|
|
||||||
{ time: "12:00", cpu: 75 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const memoryData = [
|
|
||||||
{ time: "00:00", memory: 62 },
|
|
||||||
{ time: "01:00", memory: 64 },
|
|
||||||
{ time: "02:00", memory: 58 },
|
|
||||||
{ time: "03:00", memory: 61 },
|
|
||||||
{ time: "04:00", memory: 59 },
|
|
||||||
{ time: "05:00", memory: 63 },
|
|
||||||
{ time: "06:00", memory: 67 },
|
|
||||||
{ time: "07:00", memory: 72 },
|
|
||||||
{ time: "08:00", memory: 78 },
|
|
||||||
{ time: "09:00", memory: 82 },
|
|
||||||
{ time: "10:00", memory: 76 },
|
|
||||||
{ time: "11:00", memory: 74 },
|
|
||||||
{ time: "12:00", memory: 71 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const networkData = [
|
|
||||||
{ time: "00:00", in: 12, out: 8 },
|
|
||||||
{ time: "01:00", in: 15, out: 10 },
|
|
||||||
{ time: "02:00", in: 8, out: 5 },
|
|
||||||
{ time: "03:00", in: 10, out: 7 },
|
|
||||||
{ time: "04:00", in: 6, out: 4 },
|
|
||||||
{ time: "05:00", in: 14, out: 9 },
|
|
||||||
{ time: "06:00", in: 22, out: 15 },
|
|
||||||
{ time: "07:00", in: 35, out: 28 },
|
|
||||||
{ time: "08:00", in: 48, out: 38 },
|
|
||||||
{ time: "09:00", in: 52, out: 42 },
|
|
||||||
{ time: "10:00", in: 45, out: 35 },
|
|
||||||
{ time: "11:00", in: 42, out: 32 },
|
|
||||||
{ time: "12:00", in: 38, out: 28 },
|
|
||||||
];
|
|
||||||
|
|
||||||
const containerMetrics = [
|
const containerMetrics = [
|
||||||
{ name: "api-gateway-1", cpu: 45, memory: 62, network: "12.4 MB/s" },
|
{ name: "api-gateway-1", cpu: 45, memory: 62, network: "12.4 MB/s" },
|
||||||
@@ -79,6 +33,28 @@ const networkChartConfig = {
|
|||||||
} satisfies ChartConfig;
|
} satisfies ChartConfig;
|
||||||
|
|
||||||
export default function MonitoringPage() {
|
export default function MonitoringPage() {
|
||||||
|
const [data, setData] = useState<
|
||||||
|
{
|
||||||
|
time: string;
|
||||||
|
cpu: number;
|
||||||
|
memory: number;
|
||||||
|
in: number;
|
||||||
|
out: number;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchMetrics() {
|
||||||
|
setData(await getMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchMetrics();
|
||||||
|
|
||||||
|
const i = setInterval(fetchMetrics, 30000);
|
||||||
|
|
||||||
|
return () => clearInterval(i);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardLayout
|
<DashboardLayout
|
||||||
title="Monitoring"
|
title="Monitoring"
|
||||||
@@ -130,12 +106,12 @@ export default function MonitoringPage() {
|
|||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
|
||||||
<GradientAreaChart
|
<GradientAreaChart
|
||||||
title="CPU Usage"
|
title="CPU Usage"
|
||||||
data={cpuData}
|
data={data}
|
||||||
config={cpuChartConfig}
|
config={cpuChartConfig}
|
||||||
/>
|
/>
|
||||||
<GradientAreaChart
|
<GradientAreaChart
|
||||||
title="Memory Usage"
|
title="Memory Usage"
|
||||||
data={memoryData}
|
data={data}
|
||||||
config={memoryChartConfig}
|
config={memoryChartConfig}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,7 +119,7 @@ export default function MonitoringPage() {
|
|||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<GradientAreaChart
|
<GradientAreaChart
|
||||||
title="Network I/O"
|
title="Network I/O"
|
||||||
data={networkData}
|
data={data}
|
||||||
config={networkChartConfig}
|
config={networkChartConfig}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { Node } from "@/lib/types";
|
||||||
|
import axios from "axios";
|
||||||
|
import { getNodes } from "./node";
|
||||||
|
|
||||||
|
export async function getMetrics(): Promise<
|
||||||
|
{
|
||||||
|
time: string;
|
||||||
|
cpu: number;
|
||||||
|
memory: number;
|
||||||
|
in: number;
|
||||||
|
out: number;
|
||||||
|
}[]
|
||||||
|
> {
|
||||||
|
const nodes = await getNodes();
|
||||||
|
const nodeMetrics = await Promise.all(nodes.map(getNodeMetrics));
|
||||||
|
|
||||||
|
if (nodeMetrics.length === 0) return [];
|
||||||
|
|
||||||
|
const numNodes = nodeMetrics.length;
|
||||||
|
const numPoints = nodeMetrics[0].length;
|
||||||
|
|
||||||
|
const averaged: (typeof nodeMetrics)[0] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const sum = nodeMetrics.reduce(
|
||||||
|
(acc, metrics) => {
|
||||||
|
const m = metrics[i];
|
||||||
|
acc.cpu += m.cpu;
|
||||||
|
acc.memory += m.memory;
|
||||||
|
acc.in += m.in;
|
||||||
|
acc.out += m.out;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ cpu: 0, memory: 0, in: 0, out: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
averaged.push({
|
||||||
|
time: nodeMetrics[0][i].time,
|
||||||
|
cpu: sum.cpu / numNodes,
|
||||||
|
memory: sum.memory / numNodes,
|
||||||
|
in: sum.in / numNodes,
|
||||||
|
out: sum.out / numNodes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return averaged;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getNodeMetrics(node: Node): Promise<
|
||||||
|
{
|
||||||
|
time: string;
|
||||||
|
cpu: number;
|
||||||
|
memory: number;
|
||||||
|
in: number;
|
||||||
|
out: number;
|
||||||
|
}[]
|
||||||
|
> {
|
||||||
|
const data = await (
|
||||||
|
await axios.get(`http://${node.ip}:3000/api/metrics`, {
|
||||||
|
headers: { Authorization: `ApiKey ${node.key}` },
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
return data.metrics;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user