feat(monitoring): add throughput metrics and expose them in ops UI

This commit is contained in:
2026-02-13 14:19:19 +00:00
parent 96cefe984a
commit 8be1e87bdc
9 changed files with 71 additions and 14 deletions

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## 2026-02-13 - 5.2.0 - feat(monitoring)
add throughput metrics and expose them in ops UI
- MetricsManager now reports bytesInPerSecond and bytesOutPerSecond as part of throughput
- Extended IServerStats with requestsPerSecond and throughput {bytesIn, bytesOut, bytesInPerSecond, bytesOutPerSecond}
- Stats handler updated to include requestsPerSecond and throughput; fallback stats initialize throughput fields to zero
- Web UI ops overview displays Throughput In/Out (bits/s) and total bytes with new formatting helper
- Bumped dependency @push.rocks/smartproxy to ^23.1.6
## 2026-02-13 - 5.1.0 - feat(acme) ## 2026-02-13 - 5.1.0 - feat(acme)
Integrate SmartAcme DNS-01 handling and add certificate provisioning for SmartProxy Integrate SmartAcme DNS-01 handling and add certificate provisioning for SmartProxy

View File

@@ -49,7 +49,7 @@
"@push.rocks/smartnetwork": "^4.4.0", "@push.rocks/smartnetwork": "^4.4.0",
"@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartproxy": "^23.1.5", "@push.rocks/smartproxy": "^23.1.6",
"@push.rocks/smartradius": "^1.1.1", "@push.rocks/smartradius": "^1.1.1",
"@push.rocks/smartrequest": "^5.0.1", "@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smartrx": "^3.0.10", "@push.rocks/smartrx": "^3.0.10",

10
pnpm-lock.yaml generated
View File

@@ -75,8 +75,8 @@ importers:
specifier: ^4.2.3 specifier: ^4.2.3
version: 4.2.3 version: 4.2.3
'@push.rocks/smartproxy': '@push.rocks/smartproxy':
specifier: ^23.1.5 specifier: ^23.1.6
version: 23.1.5(@push.rocks/smartserve@2.0.1)(socks@2.8.7) version: 23.1.6(@push.rocks/smartserve@2.0.1)(socks@2.8.7)
'@push.rocks/smartradius': '@push.rocks/smartradius':
specifier: ^1.1.1 specifier: ^1.1.1
version: 1.1.1 version: 1.1.1
@@ -1040,8 +1040,8 @@ packages:
'@push.rocks/smartpromise@4.2.3': '@push.rocks/smartpromise@4.2.3':
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
'@push.rocks/smartproxy@23.1.5': '@push.rocks/smartproxy@23.1.6':
resolution: {integrity: sha512-Ak8jUm0XMQgHO57syoP+DkeKiw4GQwq4uQuLxoUj42w95a0liOQ3WQTKoHHNlbRjlCzFjszHXNh+D+7GW1IlXA==} resolution: {integrity: sha512-cwTK4d7vOP0nEZzkZSg9Ua7R+J7SIGId9G815GNTRYYZP20TZbvmWDZW/1gf2lw3AuAy2MRIJMPO2BZ7JnZckw==}
'@push.rocks/smartpuppeteer@2.0.5': '@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
@@ -6441,7 +6441,7 @@ snapshots:
'@push.rocks/smartpromise@4.2.3': {} '@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartproxy@23.1.5(@push.rocks/smartserve@2.0.1)(socks@2.8.7)': '@push.rocks/smartproxy@23.1.6(@push.rocks/smartserve@2.0.1)(socks@2.8.7)':
dependencies: dependencies:
'@push.rocks/lik': 6.2.2 '@push.rocks/lik': 6.2.2
'@push.rocks/smartacme': 8.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7) '@push.rocks/smartacme': 8.0.0(@push.rocks/smartserve@2.0.1)(socks@2.8.7)

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '5.1.0', version: '5.2.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }

View File

@@ -147,8 +147,10 @@ export class MetricsManager {
requestsPerSecond: proxyMetrics ? proxyMetrics.requests.perSecond() : 0, requestsPerSecond: proxyMetrics ? proxyMetrics.requests.perSecond() : 0,
throughput: proxyMetrics ? { throughput: proxyMetrics ? {
bytesIn: proxyMetrics.totals.bytesIn(), bytesIn: proxyMetrics.totals.bytesIn(),
bytesOut: proxyMetrics.totals.bytesOut() bytesOut: proxyMetrics.totals.bytesOut(),
} : { bytesIn: 0, bytesOut: 0 }, bytesInPerSecond: proxyMetrics.throughput.instant().in,
bytesOutPerSecond: proxyMetrics.throughput.instant().out,
} : { bytesIn: 0, bytesOut: 0, bytesInPerSecond: 0, bytesOutPerSecond: 0 },
}; };
}); });
} }

View File

@@ -27,6 +27,8 @@ export class StatsHandler {
cpuUsage: stats.cpuUsage, cpuUsage: stats.cpuUsage,
activeConnections: stats.activeConnections, activeConnections: stats.activeConnections,
totalConnections: stats.totalConnections, totalConnections: stats.totalConnections,
requestsPerSecond: stats.requestsPerSecond,
throughput: stats.throughput,
}, },
history: dataArg.includeHistory ? stats.history : undefined, history: dataArg.includeHistory ? stats.history : undefined,
}; };
@@ -191,6 +193,8 @@ export class StatsHandler {
cpuUsage: stats.cpuUsage, cpuUsage: stats.cpuUsage,
activeConnections: stats.activeConnections, activeConnections: stats.activeConnections,
totalConnections: stats.totalConnections, totalConnections: stats.totalConnections,
requestsPerSecond: stats.requestsPerSecond,
throughput: stats.throughput,
}; };
}) })
); );
@@ -301,6 +305,7 @@ export class StatsHandler {
requestsPerSecond: number; requestsPerSecond: number;
activeConnections: number; activeConnections: number;
totalConnections: number; totalConnections: number;
throughput: interfaces.data.IServerStats['throughput'];
history: Array<{ history: Array<{
timestamp: number; timestamp: number;
value: number; value: number;
@@ -316,15 +321,16 @@ export class StatsHandler {
requestsPerSecond: serverStats.requestsPerSecond, requestsPerSecond: serverStats.requestsPerSecond,
activeConnections: serverStats.activeConnections, activeConnections: serverStats.activeConnections,
totalConnections: serverStats.totalConnections, totalConnections: serverStats.totalConnections,
throughput: serverStats.throughput,
history: [], // TODO: Implement history tracking history: [], // TODO: Implement history tracking
}; };
} }
// Fallback to basic stats if MetricsManager not available // Fallback to basic stats if MetricsManager not available
const uptime = process.uptime(); const uptime = process.uptime();
const memUsage = process.memoryUsage(); const memUsage = process.memoryUsage();
const cpuUsage = plugins.os.loadavg()[0] * 100 / plugins.os.cpus().length; const cpuUsage = plugins.os.loadavg()[0] * 100 / plugins.os.cpus().length;
return { return {
uptime, uptime,
cpuUsage: { cpuUsage: {
@@ -340,6 +346,7 @@ export class StatsHandler {
requestsPerSecond: 0, requestsPerSecond: 0,
activeConnections: 0, activeConnections: 0,
totalConnections: 0, totalConnections: 0,
throughput: { bytesIn: 0, bytesOut: 0, bytesInPerSecond: 0, bytesOutPerSecond: 0 },
history: [], history: [],
}; };
} }

View File

@@ -17,6 +17,13 @@ export interface IServerStats {
}; };
activeConnections: number; activeConnections: number;
totalConnections: number; totalConnections: number;
requestsPerSecond: number;
throughput: {
bytesIn: number;
bytesOut: number;
bytesInPerSecond: number;
bytesOutPerSecond: number;
};
} }
export interface IEmailStats { export interface IEmailStats {

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/dcrouter', name: '@serve.zone/dcrouter',
version: '5.1.0', version: '5.2.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.' description: 'A multifaceted routing service handling mail and SMS delivery functions.'
} }

View File

@@ -126,12 +126,26 @@ export class OpsViewOverview extends DeesElement {
const units = ['B', 'KB', 'MB', 'GB', 'TB']; const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = bytes; let size = bytes;
let unitIndex = 0; let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) { while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024; size /= 1024;
unitIndex++; unitIndex++;
} }
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
private formatBitsPerSecond(bytesPerSecond: number): string {
const bitsPerSecond = bytesPerSecond * 8;
const units = ['bit/s', 'kbit/s', 'Mbit/s', 'Gbit/s'];
let size = bitsPerSecond;
let unitIndex = 0;
while (size >= 1000 && unitIndex < units.length - 1) {
size /= 1000;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`; return `${size.toFixed(1)} ${units[unitIndex]}`;
} }
@@ -162,6 +176,24 @@ export class OpsViewOverview extends DeesElement {
color: '#3b82f6', color: '#3b82f6',
description: `Total: ${this.statsState.serverStats.totalConnections}`, description: `Total: ${this.statsState.serverStats.totalConnections}`,
}, },
{
id: 'throughputIn',
title: 'Throughput In',
value: this.formatBitsPerSecond(this.statsState.serverStats.throughput?.bytesInPerSecond || 0),
type: 'text',
icon: 'download',
color: '#22c55e',
description: `Total: ${this.formatBytes(this.statsState.serverStats.throughput?.bytesIn || 0)}`,
},
{
id: 'throughputOut',
title: 'Throughput Out',
value: this.formatBitsPerSecond(this.statsState.serverStats.throughput?.bytesOutPerSecond || 0),
type: 'text',
icon: 'upload',
color: '#8b5cf6',
description: `Total: ${this.formatBytes(this.statsState.serverStats.throughput?.bytesOut || 0)}`,
},
{ {
id: 'cpu', id: 'cpu',
title: 'CPU Usage', title: 'CPU Usage',