feat(monitoring): add backend protocol metrics to network stats and ops dashboard
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element';
|
||||
import * as appstate from '../appstate.js';
|
||||
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
||||
import { viewHostCss } from './shared/css.js';
|
||||
import { type IStatsTile } from '@design.estate/dees-catalog';
|
||||
|
||||
@@ -198,6 +199,38 @@ export class OpsViewNetwork extends DeesElement {
|
||||
color: ${cssManager.bdTheme('#00796b', '#4db6ac')};
|
||||
}
|
||||
|
||||
.protocolBadge.h1 {
|
||||
background: ${cssManager.bdTheme('#e3f2fd', '#1a2c3a')};
|
||||
color: ${cssManager.bdTheme('#1976d2', '#5a9fd4')};
|
||||
}
|
||||
|
||||
.protocolBadge.h2 {
|
||||
background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
|
||||
color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
|
||||
}
|
||||
|
||||
.protocolBadge.h3 {
|
||||
background: ${cssManager.bdTheme('#f3e5f5', '#2a1a3a')};
|
||||
color: ${cssManager.bdTheme('#7b1fa2', '#ba68c8')};
|
||||
}
|
||||
|
||||
.protocolBadge.unknown {
|
||||
background: ${cssManager.bdTheme('#f5f5f5', '#2a2a2a')};
|
||||
color: ${cssManager.bdTheme('#757575', '#999999')};
|
||||
}
|
||||
|
||||
.suppressionBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
|
||||
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.statusBadge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -265,6 +298,9 @@ export class OpsViewNetwork extends DeesElement {
|
||||
<!-- Top IPs Section -->
|
||||
${this.renderTopIPs()}
|
||||
|
||||
<!-- Backend Protocols Section -->
|
||||
${this.renderBackendProtocols()}
|
||||
|
||||
<!-- Requests Table -->
|
||||
<dees-table
|
||||
.data=${this.networkRequests}
|
||||
@@ -519,6 +555,106 @@ export class OpsViewNetwork extends DeesElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderBackendProtocols(): TemplateResult {
|
||||
const backends = this.networkState.backends;
|
||||
if (!backends || backends.length === 0) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<dees-table
|
||||
.data=${backends}
|
||||
.displayFunction=${(item: interfaces.data.IBackendInfo) => {
|
||||
const totalErrors = item.connectErrors + item.handshakeErrors + item.requestErrors;
|
||||
const protocolClass = item.protocol.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
|
||||
return {
|
||||
'Backend': item.backend,
|
||||
'Domain': item.domain || '-',
|
||||
'Protocol': html`
|
||||
<span class="protocolBadge ${protocolClass}">${item.protocol.toUpperCase()}</span>
|
||||
${item.h2Suppressed ? html`<span class="suppressionBadge" title="H2 suppressed: ${item.h2ConsecutiveFailures ?? 0} failures, cooldown ${item.h2CooldownRemainingSecs ?? 0}s">H2 suppressed</span>` : ''}
|
||||
${item.h3Suppressed ? html`<span class="suppressionBadge" title="H3 suppressed: ${item.h3ConsecutiveFailures ?? 0} failures, cooldown ${item.h3CooldownRemainingSecs ?? 0}s">H3 suppressed</span>` : ''}
|
||||
`,
|
||||
'Active': item.activeConnections,
|
||||
'Total': this.formatNumber(item.totalConnections),
|
||||
'Avg Connect': item.avgConnectTimeMs > 0 ? `${item.avgConnectTimeMs.toFixed(1)}ms` : '-',
|
||||
'Pool Hit Rate': item.poolHitRate > 0 ? `${(item.poolHitRate * 100).toFixed(1)}%` : '-',
|
||||
'Errors': totalErrors > 0
|
||||
? html`<span class="statusBadge error">${totalErrors}</span>`
|
||||
: html`<span class="statusBadge success">0</span>`,
|
||||
'Cache Age': item.cacheAgeSecs != null ? `${Math.round(item.cacheAgeSecs)}s` : '-',
|
||||
};
|
||||
}}
|
||||
.dataActions=${[
|
||||
{
|
||||
name: 'View Details',
|
||||
iconName: 'lucide:info',
|
||||
type: ['inRow', 'doubleClick', 'contextmenu'] as any,
|
||||
actionFunc: async (actionData: any) => {
|
||||
await this.showBackendDetails(actionData.item);
|
||||
}
|
||||
}
|
||||
]}
|
||||
heading1="Backend Protocols"
|
||||
heading2="Auto-detected backend protocols and connection pool health"
|
||||
searchable
|
||||
.pagination=${false}
|
||||
dataName="backend"
|
||||
></dees-table>
|
||||
`;
|
||||
}
|
||||
|
||||
private async showBackendDetails(backend: interfaces.data.IBackendInfo) {
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
|
||||
await DeesModal.createAndShow({
|
||||
heading: `Backend: ${backend.backend}`,
|
||||
content: html`
|
||||
<div style="padding: 20px;">
|
||||
<dees-dataview-codebox
|
||||
.heading=${'Backend Details'}
|
||||
progLang="json"
|
||||
.codeToDisplay=${JSON.stringify({
|
||||
backend: backend.backend,
|
||||
domain: backend.domain,
|
||||
protocol: backend.protocol,
|
||||
activeConnections: backend.activeConnections,
|
||||
totalConnections: backend.totalConnections,
|
||||
avgConnectTimeMs: backend.avgConnectTimeMs,
|
||||
poolHitRate: backend.poolHitRate,
|
||||
errors: {
|
||||
connect: backend.connectErrors,
|
||||
handshake: backend.handshakeErrors,
|
||||
request: backend.requestErrors,
|
||||
h2Failures: backend.h2Failures,
|
||||
},
|
||||
suppression: {
|
||||
h2Suppressed: backend.h2Suppressed,
|
||||
h3Suppressed: backend.h3Suppressed,
|
||||
h2CooldownRemainingSecs: backend.h2CooldownRemainingSecs,
|
||||
h3CooldownRemainingSecs: backend.h3CooldownRemainingSecs,
|
||||
h2ConsecutiveFailures: backend.h2ConsecutiveFailures,
|
||||
h3ConsecutiveFailures: backend.h3ConsecutiveFailures,
|
||||
},
|
||||
h3Port: backend.h3Port,
|
||||
cacheAgeSecs: backend.cacheAgeSecs,
|
||||
}, null, 2)}
|
||||
></dees-dataview-codebox>
|
||||
</div>
|
||||
`,
|
||||
menuOptions: [
|
||||
{
|
||||
name: 'Copy Backend Key',
|
||||
iconName: 'lucide:Copy',
|
||||
action: async () => {
|
||||
await navigator.clipboard.writeText(backend.backend);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
private async updateNetworkData() {
|
||||
// Track requests/sec history for the trend sparkline (moved out of render)
|
||||
const reqPerSec = this.networkState.requestsPerSecond || 0;
|
||||
|
||||
Reference in New Issue
Block a user