better history for metrics
This commit is contained in:
@@ -30,12 +30,16 @@ function initCharts() {
|
|||||||
backgroundColor: "rgba(96, 165, 250, 0.1)",
|
backgroundColor: "rgba(96, 165, 250, 0.1)",
|
||||||
fill: true,
|
fill: true,
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
|
pointRadius: 0,
|
||||||
|
pointHoverRadius: 4,
|
||||||
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: false },
|
legend: { display: false },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -53,7 +57,12 @@ function initCharts() {
|
|||||||
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
ticks: { color: "#94a3b8", maxTicksLimit: 10 },
|
ticks: {
|
||||||
|
color: "#94a3b8",
|
||||||
|
maxTicksLimit: 6,
|
||||||
|
autoSkip: true,
|
||||||
|
maxRotation: 0,
|
||||||
|
},
|
||||||
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -72,12 +81,16 @@ function initCharts() {
|
|||||||
backgroundColor: "rgba(192, 132, 252, 0.1)",
|
backgroundColor: "rgba(192, 132, 252, 0.1)",
|
||||||
fill: true,
|
fill: true,
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
|
pointRadius: 0,
|
||||||
|
pointHoverRadius: 4,
|
||||||
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: { display: false },
|
legend: { display: false },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@@ -95,7 +108,12 @@ function initCharts() {
|
|||||||
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
ticks: { color: "#94a3b8", maxTicksLimit: 10 },
|
ticks: {
|
||||||
|
color: "#94a3b8",
|
||||||
|
maxTicksLimit: 6,
|
||||||
|
autoSkip: true,
|
||||||
|
maxRotation: 0,
|
||||||
|
},
|
||||||
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
grid: { color: "rgba(100, 116, 139, 0.1)" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fileURLToPath } from "url";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { upsertClientRow, setOnlineState, listClients, markAllClientsOffline, getBuild, getBuildByTag, getAllBuilds, deleteExpiredBuilds, deleteBuild, getNotificationScreenshot, clearNotificationScreenshots, deleteClientRow, getClientIp, banIp, isIpBanned, clientExists, deleteExpiredSharedFiles, getChatHistory, insertChatMessage, getOnlineClientCountForUser, deleteExpiredChatMessages, recordBuildClaim } from "./db";
|
import { upsertClientRow, setOnlineState, listClients, markAllClientsOffline, getBuild, getBuildByTag, getAllBuilds, deleteExpiredBuilds, deleteBuild, getNotificationScreenshot, clearNotificationScreenshots, deleteClientRow, getClientIp, banIp, isIpBanned, clientExists, deleteExpiredSharedFiles, getChatHistory, insertChatMessage, getOnlineClientCountForUser, deleteExpiredChatMessages, recordBuildClaim, getClientMetricsSummary } from "./db";
|
||||||
import { handleFrame, handleHello, handlePing, handlePong } from "./wsHandlers";
|
import { handleFrame, handleHello, handlePing, handlePong } from "./wsHandlers";
|
||||||
import { getMessageByteLength, getMaxPayloadLimit, isAllowedClientMessageType } from "./wsValidation";
|
import { getMessageByteLength, getMaxPayloadLimit, isAllowedClientMessageType } from "./wsValidation";
|
||||||
import { ClientInfo, ClientRole } from "./types";
|
import { ClientInfo, ClientRole } from "./types";
|
||||||
@@ -140,6 +140,19 @@ import type { SocketData } from "./sessions/types";
|
|||||||
import { SERVER_VERSION } from "./version";
|
import { SERVER_VERSION } from "./version";
|
||||||
|
|
||||||
|
|
||||||
|
metrics.setSnapshotEnricher((snapshot) => {
|
||||||
|
const summary = getClientMetricsSummary();
|
||||||
|
snapshot.clients.total = summary.total;
|
||||||
|
snapshot.clients.online = summary.online;
|
||||||
|
snapshot.clients.offline = summary.total - summary.online;
|
||||||
|
snapshot.clients.byOS = summary.byOS;
|
||||||
|
snapshot.clients.byCountry = summary.byCountry;
|
||||||
|
snapshot.sessions.console = sessionManager.getConsoleSessionCount();
|
||||||
|
snapshot.sessions.remoteDesktop = sessionManager.getRdSessionCount();
|
||||||
|
snapshot.sessions.fileBrowser = sessionManager.getFileBrowserSessionCount();
|
||||||
|
snapshot.sessions.process = sessionManager.getProcessSessionCount();
|
||||||
|
});
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
const isAuthorizedAgent = (req: Request, url: URL) =>
|
const isAuthorizedAgent = (req: Request, url: URL) =>
|
||||||
isAuthorizedAgentRequest(req, url, config.auth.agentToken);
|
isAuthorizedAgentRequest(req, url, config.auth.agentToken);
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ describe("MetricsCollector", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("history is capped at maxHistoryPoints", () => {
|
test("history is capped at maxHistoryPoints", () => {
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 4500; i++) {
|
||||||
const snap = metrics.getSnapshot();
|
const snap = metrics.getSnapshot();
|
||||||
metrics.recordHistoryEntry(snap);
|
metrics.recordHistoryEntry(snap);
|
||||||
}
|
}
|
||||||
|
|
||||||
const history = metrics.getHistory();
|
const history = metrics.getHistory();
|
||||||
expect(history.length).toBeLessThanOrEqual(60);
|
expect(history.length).toBeLessThanOrEqual(4320);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getHistory returns a copy", () => {
|
test("getHistory returns a copy", () => {
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ class MetricsCollector {
|
|||||||
private receivedPerSecond: number = 0;
|
private receivedPerSecond: number = 0;
|
||||||
|
|
||||||
private history: MetricsHistory[] = [];
|
private history: MetricsHistory[] = [];
|
||||||
private maxHistoryPoints: number = 60;
|
private maxHistoryPoints: number = 4320;
|
||||||
|
private snapshotEnricher: ((snapshot: MetricsSnapshot) => void) | null = null;
|
||||||
|
|
||||||
private pingValues: number[] = [];
|
private pingValues: number[] = [];
|
||||||
private maxPingHistory: number = 1000;
|
private maxPingHistory: number = 1000;
|
||||||
@@ -205,7 +206,21 @@ class MetricsCollector {
|
|||||||
return { min, max, avg, count: this.pingValues.length };
|
return { min, max, avg, count: this.pingValues.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
private recordHistory() {}
|
setSnapshotEnricher(fn: ((snapshot: MetricsSnapshot) => void) | null): void {
|
||||||
|
this.snapshotEnricher = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private recordHistory() {
|
||||||
|
const snapshot = this.getSnapshot();
|
||||||
|
if (this.snapshotEnricher) {
|
||||||
|
try {
|
||||||
|
this.snapshotEnricher(snapshot);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("metrics snapshot enricher failed:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.recordHistoryEntry(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
private trackEventLoopDelay() {
|
private trackEventLoopDelay() {
|
||||||
const intervalMs = 1000;
|
const intervalMs = 1000;
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ export async function handleMiscRoutes(
|
|||||||
snapshot.sessions.fileBrowser = deps.getFileBrowserSessionCount();
|
snapshot.sessions.fileBrowser = deps.getFileBrowserSessionCount();
|
||||||
snapshot.sessions.process = deps.getProcessSessionCount();
|
snapshot.sessions.process = deps.getProcessSessionCount();
|
||||||
|
|
||||||
metrics.recordHistoryEntry(snapshot);
|
|
||||||
|
|
||||||
const history = metrics.getHistory();
|
const history = metrics.getHistory();
|
||||||
|
|
||||||
return new Response(JSON.stringify({ snapshot, history }), {
|
return new Response(JSON.stringify({ snapshot, history }), {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
github.com/gen2brain/x264-go/x264c v0.0.0-20221204084822-82ee2951dea2 h1:4GzTsZK/uG+tHvWR2ORcTCGTC4+mgOPhkUdz/Ro9alg=
|
github.com/gen2brain/x264-go/x264c v0.0.0-20221204084822-82ee2951dea2 h1:4GzTsZK/uG+tHvWR2ORcTCGTC4+mgOPhkUdz/Ro9alg=
|
||||||
github.com/gen2brain/x264-go/x264c v0.0.0-20221204084822-82ee2951dea2/go.mod h1:MUMv+ZURlTO3qH8QAt2gxiIY5X4NMaxObz2ImZOMvyA=
|
github.com/gen2brain/x264-go/x264c v0.0.0-20221204084822-82ee2951dea2/go.mod h1:MUMv+ZURlTO3qH8QAt2gxiIY5X4NMaxObz2ImZOMvyA=
|
||||||
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
|
|||||||
Reference in New Issue
Block a user