From f9ffd04166e51cf836f28d1b47d4ef74dcab940e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 29 Dec 2024 23:20:03 -0600 Subject: [PATCH] refactor: update conversion --- .../container/container-block-chart.tsx | 52 +++-- .../container/container-cpu-chart.tsx | 213 +++++++++--------- .../container/container-memory-chart.tsx | 20 +- .../container/container-network-chart.tsx | 53 +++-- .../dashboard/monitoring/container/show.tsx | 41 ++-- .../dashboard/monitoring/docker/show.tsx | 2 +- apps/monitoring/src/monitoring/containers.ts | 96 ++++++-- 7 files changed, 287 insertions(+), 190 deletions(-) diff --git a/apps/dokploy/components/dashboard/monitoring/container/container-block-chart.tsx b/apps/dokploy/components/dashboard/monitoring/container/container-block-chart.tsx index 70c312219..5d9ee085f 100644 --- a/apps/dokploy/components/dashboard/monitoring/container/container-block-chart.tsx +++ b/apps/dokploy/components/dashboard/monitoring/container/container-block-chart.tsx @@ -17,13 +17,26 @@ import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; interface ContainerMetric { timestamp: string; - BlockIO: string; + BlockIO: { + read: number; + write: number; + readUnit: string; + writeUnit: string; + }; } interface Props { data: ContainerMetric[]; } +interface FormattedMetric { + timestamp: string; + read: number; + write: number; + readUnit: string; + writeUnit: string; +} + const chartConfig = { read: { label: "Read", @@ -35,29 +48,22 @@ const chartConfig = { }, } satisfies ChartConfig; -const parseBlockIO = (blockIO: string) => { - const [read, write] = blockIO.split(" / "); - return { - read: Number.parseFloat(read), - write: parseFloat(write), - readUnit: read?.replace(/[\d.]/g, ""), - writeUnit: write?.replace(/[\d.]/g, ""), - }; -}; - export const ContainerBlockChart = ({ data }: Props) => { - const formattedData = data.map((metric) => { - const { read, write, readUnit, writeUnit } = parseBlockIO(metric.BlockIO); - return { - timestamp: metric.timestamp, - read, - write, - readUnit, - writeUnit, - }; - }); + const formattedData = data.map((metric) => ({ + timestamp: metric.timestamp, + read: metric.BlockIO.read, + write: metric.BlockIO.write, + readUnit: metric.BlockIO.readUnit, + writeUnit: metric.BlockIO.writeUnit, + })); - const latestData = formattedData[formattedData.length - 1] || {}; + const latestData = formattedData[formattedData.length - 1] || { + timestamp: "", + read: 0, + write: 0, + readUnit: "B", + writeUnit: "B", + }; return ( @@ -115,7 +121,7 @@ export const ContainerBlockChart = ({ data }: Props) => { cursor={false} content={({ active, payload, label }) => { if (active && payload && payload.length) { - const data = payload[0].payload; + const data = payload?.[0]?.payload; return (
diff --git a/apps/dokploy/components/dashboard/monitoring/container/container-cpu-chart.tsx b/apps/dokploy/components/dashboard/monitoring/container/container-cpu-chart.tsx index 656313057..445e03e12 100644 --- a/apps/dokploy/components/dashboard/monitoring/container/container-cpu-chart.tsx +++ b/apps/dokploy/components/dashboard/monitoring/container/container-cpu-chart.tsx @@ -1,125 +1,128 @@ import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, } from "@/components/ui/card"; import { - ChartConfig, - ChartContainer, - ChartLegend, - ChartLegendContent, - ChartTooltip, + type ChartConfig, + ChartContainer, + ChartLegend, + ChartLegendContent, + ChartTooltip, } from "@/components/ui/chart"; import { formatTimestamp } from "@/lib/utils"; import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; interface ContainerMetric { - timestamp: string; - CPUPerc: string; + timestamp: string; + CPU: number; } interface Props { - data: ContainerMetric[]; + data: ContainerMetric[]; } const chartConfig = { - cpu: { - label: "CPU", - color: "hsl(var(--chart-1))", - }, + cpu: { + label: "CPU", + color: "hsl(var(--chart-1))", + }, } satisfies ChartConfig; export const ContainerCPUChart = ({ data }: Props) => { - const formattedData = data.map((metric) => ({ - timestamp: metric.timestamp, - cpu: parseFloat(metric.CPUPerc.replace("%", "")), - })); + const formattedData = data.map((metric) => ({ + timestamp: metric.timestamp, + cpu: metric.CPU, + })); - const latestData = formattedData[formattedData.length - 1] || {}; + const latestData = formattedData[formattedData.length - 1] || { + timestamp: "", + cpu: 0, + }; - return ( - - - CPU - CPU Usage: {latestData.cpu}% - - - - - - - - - - - - formatTimestamp(value)} - /> - `${value}%`} domain={[0, 100]} /> - { - if (active && payload && payload.length) { - const data = payload[0].payload; - return ( -
-
-
- - Time - - - {formatTimestamp(label)} - -
-
- - CPU - - {data.cpu}% -
-
-
- ); - } - return null; - }} - /> - - } - verticalAlign="bottom" - align="center" - /> -
-
-
-
- ); + return ( + + + CPU + CPU Usage: {latestData.cpu}% + + + + + + + + + + + + formatTimestamp(value)} + /> + `${value}%`} domain={[0, 100]} /> + { + if (active && payload && payload.length) { + const data = payload?.[0]?.payload; + return ( +
+
+
+ + Time + + + {formatTimestamp(label)} + +
+
+ + CPU + + {data.cpu}% +
+
+
+ ); + } + return null; + }} + /> + + } + verticalAlign="bottom" + align="center" + /> +
+
+
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/monitoring/container/container-memory-chart.tsx b/apps/dokploy/components/dashboard/monitoring/container/container-memory-chart.tsx index 1d59641f1..f4b3236e6 100644 --- a/apps/dokploy/components/dashboard/monitoring/container/container-memory-chart.tsx +++ b/apps/dokploy/components/dashboard/monitoring/container/container-memory-chart.tsx @@ -17,8 +17,12 @@ import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; interface ContainerMetric { timestamp: string; - MemPerc: string; - MemUsage: string; + Memory: { + percentage: number; + used: number; + total: number; + unit: string; + }; } interface Props { @@ -35,11 +39,15 @@ const chartConfig = { export const ContainerMemoryChart = ({ data }: Props) => { const formattedData = data.map((metric) => ({ timestamp: metric.timestamp, - memory: parseFloat(metric.MemPerc.replace("%", "")), - usage: metric.MemUsage, + memory: metric.Memory.percentage, + usage: `${metric.Memory.used} / ${metric.Memory.total} ${metric.Memory.unit}`, })); - const latestData = formattedData[formattedData.length - 1] || {}; + const latestData = formattedData[formattedData.length - 1] || { + timestamp: "", + memory: 0, + usage: "0 / 0 B", + }; return ( @@ -81,7 +89,7 @@ export const ContainerMemoryChart = ({ data }: Props) => { cursor={false} content={({ active, payload, label }) => { if (active && payload && payload.length) { - const data = payload[0].payload; + const data = payload?.[0]?.payload; return (
diff --git a/apps/dokploy/components/dashboard/monitoring/container/container-network-chart.tsx b/apps/dokploy/components/dashboard/monitoring/container/container-network-chart.tsx index 42b697cc8..d51e89687 100644 --- a/apps/dokploy/components/dashboard/monitoring/container/container-network-chart.tsx +++ b/apps/dokploy/components/dashboard/monitoring/container/container-network-chart.tsx @@ -17,13 +17,26 @@ import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; interface ContainerMetric { timestamp: string; - NetIO: string; + Network: { + input: number; + output: number; + inputUnit: string; + outputUnit: string; + }; } interface Props { data: ContainerMetric[]; } +interface FormattedMetric { + timestamp: string; + input: number; + output: number; + inputUnit: string; + outputUnit: string; +} + const chartConfig = { input: { label: "Input", @@ -35,31 +48,21 @@ const chartConfig = { }, } satisfies ChartConfig; -const parseNetworkIO = (netIO: string) => { - const [input, output] = netIO.split(" / "); - return { - input: parseFloat(input), - output: parseFloat(output), - inputUnit: input.replace(/[\d.]/g, ""), - outputUnit: output.replace(/[\d.]/g, ""), - }; -}; - export const ContainerNetworkChart = ({ data }: Props) => { - const formattedData = data.map((metric) => { - const { input, output, inputUnit, outputUnit } = parseNetworkIO( - metric.NetIO, - ); - return { - timestamp: metric.timestamp, - input, - output, - inputUnit, - outputUnit, - }; - }); + const formattedData: FormattedMetric[] = data.map((metric) => ({ + timestamp: metric.timestamp, + input: metric.Network.input, + output: metric.Network.output, + inputUnit: metric.Network.inputUnit, + outputUnit: metric.Network.outputUnit, + })); - const latestData = formattedData[formattedData.length - 1] || {}; + const latestData = formattedData[formattedData.length - 1] || { + input: 0, + output: 0, + inputUnit: "B", + outputUnit: "B", + }; return ( @@ -117,7 +120,7 @@ export const ContainerNetworkChart = ({ data }: Props) => { cursor={false} content={({ active, payload, label }) => { if (active && payload && payload.length) { - const data = payload[0].payload; + const data = payload?.[0]?.payload; return (
diff --git a/apps/dokploy/components/dashboard/monitoring/container/show.tsx b/apps/dokploy/components/dashboard/monitoring/container/show.tsx index 26e0f05f3..a6af5a6ec 100644 --- a/apps/dokploy/components/dashboard/monitoring/container/show.tsx +++ b/apps/dokploy/components/dashboard/monitoring/container/show.tsx @@ -25,15 +25,28 @@ const DATA_POINTS_OPTIONS = { interface ContainerMetric { timestamp: string; - BlockIO: string; - CPUPerc: string; + CPU: number; + Memory: { + percentage: number; + used: number; + total: number; + unit: string; + }; + Network: { + input: number; + output: number; + inputUnit: string; + outputUnit: string; + }; + BlockIO: { + read: number; + write: number; + readUnit: string; + writeUnit: string; + }; Container: string; ID: string; - MemPerc: string; - MemUsage: string; Name: string; - NetIO: string; - PIDs: string; } interface Props { @@ -69,7 +82,7 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => { const data = await response.json(); if (!Array.isArray(data) || data.length === 0) { - throw new Error("No hay datos disponibles"); + throw new Error("No data available"); } setHistoricalData(data); @@ -147,7 +160,7 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => {

CPU Usage

-

{metrics.CPUPerc}

+

{metrics.CPU}%

@@ -155,9 +168,9 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => {

Memory Usage

-

{metrics.MemPerc}

+

{metrics.Memory.percentage}%

- {metrics.MemUsage} + {metrics.Memory.used} {metrics.Memory.unit} / {metrics.Memory.total} {metrics.Memory.unit}

@@ -166,7 +179,7 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => {

Network I/O

-

{metrics.NetIO}

+

{metrics.Network.input} {metrics.Network.inputUnit} / {metrics.Network.output} {metrics.Network.outputUnit}

@@ -174,7 +187,7 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => {

Block I/O

-

{metrics.BlockIO}

+

{metrics.BlockIO.read} {metrics.BlockIO.readUnit} / {metrics.BlockIO.write} {metrics.BlockIO.writeUnit}

@@ -192,10 +205,6 @@ export const ContainerMonitoring = ({ appName, BASE_URL }: Props) => {

Name

{metrics.Name}

-
-

PIDs

-

{metrics.PIDs}

-
diff --git a/apps/dokploy/components/dashboard/monitoring/docker/show.tsx b/apps/dokploy/components/dashboard/monitoring/docker/show.tsx index c1a379d63..7ff326601 100644 --- a/apps/dokploy/components/dashboard/monitoring/docker/show.tsx +++ b/apps/dokploy/components/dashboard/monitoring/docker/show.tsx @@ -71,7 +71,7 @@ export const DockerMonitoring = ({ appName, BASE_URL }: Props) => { const data = await response.json(); if (!Array.isArray(data) || data.length === 0) { - throw new Error("No hay datos disponibles"); + throw new Error("No data available"); } const formattedData = data.map((metric: SystemMetrics) => ({ diff --git a/apps/monitoring/src/monitoring/containers.ts b/apps/monitoring/src/monitoring/containers.ts index c97d0ee52..66a646003 100644 --- a/apps/monitoring/src/monitoring/containers.ts +++ b/apps/monitoring/src/monitoring/containers.ts @@ -2,8 +2,8 @@ import { exec } from "node:child_process"; import fs from "node:fs"; import util from "node:util"; export const execAsync = util.promisify(exec); -import { containerLogFile, serverLogFile } from "../constants.js"; -import path, { join } from "node:path"; +import { containerLogFile } from "../constants.js"; +import { join } from "node:path"; const REFRESH_RATE_CONTAINER = Number( process.env.REFRESH_RATE_CONTAINER || 4000, @@ -20,7 +20,32 @@ interface Container { MemUsage: string; Name: string; NetIO: string; - PIDs: string; +} + +interface ProcessedContainer { + timestamp: string; + BlockIO: { + read: number; + write: number; + readUnit: string; + writeUnit: string; + }; + CPU: number; + Container: string; + ID: string; + Memory: { + percentage: number; + used: number; + total: number; + unit: string; + }; + Name: string; + Network: { + input: number; + output: number; + inputUnit: string; + outputUnit: string; + }; } function getServiceName(containerName: string): string { @@ -30,11 +55,57 @@ function getServiceName(containerName: string): string { return match ? match[1] : containerName; } -// console.log( -// getServiceName("testing-testing-cg5shr.1.6oupbsgicchhrsgmb4mljxgvt"), -// ); // testing-testing-cg5shr -// console.log(getServiceName("dokploy-traefik.1.bxaajgx0fmv6l6y37tl5uyf64")); // dokploy-traefik -// console.log(getServiceName("plausible_events_db")); // plausible_events_db (sin cambios) +function processContainerData(container: Container): ProcessedContainer { + // Process CPU + const cpu = Number.parseFloat(container.CPUPerc.replace("%", "")); + + // Process Memory + const memPerc = Number.parseFloat(container.MemPerc.replace("%", "")); + const [used, total] = container.MemUsage.split(" / "); + const usedValue = Number.parseFloat(used); + const totalValue = Number.parseFloat(total); + const memUnit = used.replace(/[\d.]/g, ""); + + // Process Network I/O + const [input, output] = container.NetIO.split(" / "); + const networkInput = Number.parseFloat(input); + const networkOutput = Number.parseFloat(output); + const inputUnit = input.replace(/[\d.]/g, ""); + const outputUnit = output.replace(/[\d.]/g, ""); + + // Process Block I/O + const [read, write] = container.BlockIO.split(" / "); + const blockRead = Number.parseFloat(read); + const blockWrite = Number.parseFloat(write); + const readUnit = read.replace(/[\d.]/g, ""); + const writeUnit = write.replace(/[\d.]/g, ""); + + return { + timestamp: new Date().toISOString(), + CPU: cpu, + Memory: { + percentage: memPerc, + used: usedValue, + total: totalValue, + unit: memUnit, + }, + Network: { + input: networkInput, + output: networkOutput, + inputUnit, + outputUnit, + }, + BlockIO: { + read: blockRead, + write: blockWrite, + readUnit, + writeUnit, + }, + Container: container.Container, + ID: container.ID, + Name: container.Name, + }; +} export const logContainerMetrics = () => { console.log("Initialized container metrics"); @@ -56,7 +127,7 @@ export const logContainerMetrics = () => { setInterval(async () => { try { const { stdout } = await execAsync( - "docker stats --no-stream --format '{{json .}}'", + 'docker stats --no-stream --format \'{"BlockIO":"{{.BlockIO}}","CPUPerc":"{{.CPUPerc}}","Container":"{{.Container}}","ID":"{{.ID}}","MemPerc":"{{.MemPerc}}","MemUsage":"{{.MemUsage}}","Name":"{{.Name}}","NetIO":"{{.NetIO}}"}\'', ); const jsonString = `[${stdout.trim().split("\n").join(",")}]`; @@ -71,17 +142,14 @@ export const logContainerMetrics = () => { const serviceName = getServiceName(container.Name); const containerPath = join(containerLogFile, `${serviceName}.log`); - // Obtener o crear el handle del archivo let fileHandle = fileHandles.get(serviceName); if (!fileHandle) { fileHandle = await fs.promises.open(containerPath, "a"); fileHandles.set(serviceName, fileHandle); } - const logLine = `${JSON.stringify({ - timestamp: new Date().toISOString(), - ...container, - })}\n`; + const processedData = processContainerData(container); + const logLine = `${JSON.stringify(processedData)}\n`; await fileHandle.write(logLine); } catch (error) {