feat(monitoring): add frontend and backend protocol distribution metrics to network stats

This commit is contained in:
2026-04-04 16:45:02 +00:00
parent 94f53f0259
commit 6c3d8714a2
11 changed files with 135 additions and 16 deletions

View File

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

View File

@@ -56,6 +56,8 @@ export interface INetworkState {
requestsPerSecond: number;
requestsTotal: number;
backends: interfaces.data.IBackendInfo[];
frontendProtocols: interfaces.data.IProtocolDistribution | null;
backendProtocols: interfaces.data.IProtocolDistribution | null;
lastUpdated: number;
isLoading: boolean;
error: string | null;
@@ -154,6 +156,8 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
requestsPerSecond: 0,
requestsTotal: 0,
backends: [],
frontendProtocols: null,
backendProtocols: null,
lastUpdated: 0,
isLoading: false,
error: null,
@@ -523,6 +527,8 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
requestsTotal: networkStatsResponse.requestsTotal || 0,
backends: networkStatsResponse.backends || [],
frontendProtocols: networkStatsResponse.frontendProtocols || null,
backendProtocols: networkStatsResponse.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1845,6 +1851,8 @@ async function dispatchCombinedRefreshActionInner() {
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
@@ -1866,6 +1874,8 @@ async function dispatchCombinedRefreshActionInner() {
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,

View File

@@ -274,6 +274,12 @@ export class OpsViewNetwork extends DeesElement {
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
}
.protocolChartGrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
`,
];
@@ -305,6 +311,9 @@ export class OpsViewNetwork extends DeesElement {
.yAxisFormatter=${(val: number) => `${val} Mbit/s`}
></dees-chart-area>
<!-- Protocol Distribution Charts -->
${this.renderProtocolCharts()}
<!-- Top IPs Section -->
${this.renderTopIPs()}
@@ -527,7 +536,54 @@ export class OpsViewNetwork extends DeesElement {
`;
}
private renderProtocolCharts(): TemplateResult {
const fp = this.networkState.frontendProtocols;
const bp = this.networkState.backendProtocols;
const protoColors: Record<string, string> = {
'HTTP/1.1': '#1976d2',
'HTTP/2': '#388e3c',
'HTTP/3': '#7b1fa2',
'WebSocket': '#f57c00',
'Other': '#757575',
};
const buildDonutData = (dist: interfaces.data.IProtocolDistribution | null) => {
if (!dist) return [];
const items: Array<{ name: string; value: number; color: string }> = [];
if (dist.h1Active > 0) items.push({ name: 'HTTP/1.1', value: dist.h1Active, color: protoColors['HTTP/1.1'] });
if (dist.h2Active > 0) items.push({ name: 'HTTP/2', value: dist.h2Active, color: protoColors['HTTP/2'] });
if (dist.h3Active > 0) items.push({ name: 'HTTP/3', value: dist.h3Active, color: protoColors['HTTP/3'] });
if (dist.wsActive > 0) items.push({ name: 'WebSocket', value: dist.wsActive, color: protoColors['WebSocket'] });
if (dist.otherActive > 0) items.push({ name: 'Other', value: dist.otherActive, color: protoColors['Other'] });
return items;
};
const frontendData = buildDonutData(fp);
const backendData = buildDonutData(bp);
return html`
<div class="protocolChartGrid">
<dees-chart-donut
.label=${'Frontend Protocols'}
.data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
.showLegend=${true}
.showLabels=${true}
.innerRadiusPercent=${'55%'}
.valueFormatter=${(val: number) => `${val} active`}
></dees-chart-donut>
<dees-chart-donut
.label=${'Backend Protocols'}
.data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
.showLegend=${true}
.showLabels=${true}
.innerRadiusPercent=${'55%'}
.valueFormatter=${(val: number) => `${val} active`}
></dees-chart-donut>
</div>
`;
}
private renderTopIPs(): TemplateResult {
if (this.networkState.topIPs.length === 0) {
return html``;