|
81 | 81 | <div class="card-header d-flex align-items-center justify-content-between"> |
82 | 82 | <div> |
83 | 83 | <h5 class="mb-0">Coordination alerts</h5> |
84 | | - <small class="text-muted">Warnings and errors distilled from health checks and queue visibility.</small> |
| 84 | + <small class="text-muted">{{ coordinationAlertSummary }}</small> |
85 | 85 | </div> |
86 | 86 |
|
87 | 87 | <span class="worker-health__pill" :class="statusToneClass(coordinationAlertRollup)"> |
|
107 | 107 | <p v-if="coordinationAlertDetails(alert)" class="worker-health__cell-meta mb-0"> |
108 | 108 | {{ coordinationAlertDetails(alert) }} |
109 | 109 | </p> |
| 110 | + |
| 111 | + <div class="worker-health__alert-facts"> |
| 112 | + <span class="worker-health__pill worker-health__pill--muted"> |
| 113 | + {{ coordinationAlertSourceLabel(alert) }} |
| 114 | + </span> |
| 115 | + <span |
| 116 | + v-if="coordinationAlertCategoryLabel(alert)" |
| 117 | + class="worker-health__pill worker-health__pill--muted" |
| 118 | + > |
| 119 | + {{ coordinationAlertCategoryLabel(alert) }} |
| 120 | + </span> |
| 121 | + <span |
| 122 | + v-for="fact in coordinationAlertFacts(alert)" |
| 123 | + :key="`${alert.source || 'alert'}:${alert.key || 'unknown'}:${fact}`" |
| 124 | + class="worker-health__pill worker-health__pill--muted" |
| 125 | + > |
| 126 | + {{ fact }} |
| 127 | + </span> |
| 128 | + </div> |
110 | 129 | </article> |
111 | 130 | </div> |
112 | 131 | </div> |
@@ -512,6 +531,26 @@ export default { |
512 | 531 | return 'ok'; |
513 | 532 | }, |
514 | 533 |
|
| 534 | + coordinationAlertSummary() { |
| 535 | + const errors = this.coordinationAlerts.filter((alert) => alert.status === 'error').length; |
| 536 | + const warnings = this.coordinationAlerts.filter((alert) => alert.status === 'warning').length; |
| 537 | + const parts = []; |
| 538 | +
|
| 539 | + if (errors > 0) { |
| 540 | + parts.push(`${errors.toLocaleString()} error${errors === 1 ? '' : 's'}`); |
| 541 | + } |
| 542 | +
|
| 543 | + if (warnings > 0) { |
| 544 | + parts.push(`${warnings.toLocaleString()} warning${warnings === 1 ? '' : 's'}`); |
| 545 | + } |
| 546 | +
|
| 547 | + if (parts.length === 0) { |
| 548 | + return 'Warnings and errors distilled from health checks and queue visibility.'; |
| 549 | + } |
| 550 | +
|
| 551 | + return `${parts.join(' and ')} distilled from health checks and queue visibility.`; |
| 552 | + }, |
| 553 | +
|
515 | 554 | categorizedChecks() { |
516 | 555 | const definitions = [ |
517 | 556 | { |
@@ -942,6 +981,67 @@ export default { |
942 | 981 | return `Queues: ${label}${suffix}`; |
943 | 982 | }, |
944 | 983 |
|
| 984 | + coordinationAlertSourceLabel(alert) { |
| 985 | + const source = typeof alert?.source === 'string' ? alert.source : ''; |
| 986 | +
|
| 987 | + if (source === 'health_check') { |
| 988 | + return 'Health check'; |
| 989 | + } |
| 990 | +
|
| 991 | + if (source === 'queue_visibility') { |
| 992 | + return 'Queue visibility'; |
| 993 | + } |
| 994 | +
|
| 995 | + return 'Coordination'; |
| 996 | + }, |
| 997 | +
|
| 998 | + coordinationAlertCategoryLabel(alert) { |
| 999 | + const category = typeof alert?.category === 'string' |
| 1000 | + ? alert.category.toLowerCase() |
| 1001 | + : null; |
| 1002 | +
|
| 1003 | + if (category === 'correctness') { |
| 1004 | + return 'Correctness'; |
| 1005 | + } |
| 1006 | +
|
| 1007 | + if (category === 'acceleration') { |
| 1008 | + return 'Acceleration'; |
| 1009 | + } |
| 1010 | +
|
| 1011 | + return null; |
| 1012 | + }, |
| 1013 | +
|
| 1014 | + coordinationAlertFacts(alert) { |
| 1015 | + const facts = []; |
| 1016 | + const queueCount = Number(alert?.queue_count); |
| 1017 | + const backlogCount = Number(alert?.backlog_count); |
| 1018 | + const stalePollerCount = Number(alert?.stale_poller_count); |
| 1019 | + const candidateCount = Number(alert?.candidate_count); |
| 1020 | + const maxAgeMs = Number(alert?.max_age_ms); |
| 1021 | +
|
| 1022 | + if (Number.isFinite(queueCount) && queueCount > 0) { |
| 1023 | + facts.push(`${queueCount.toLocaleString()} queue${queueCount === 1 ? '' : 's'}`); |
| 1024 | + } |
| 1025 | +
|
| 1026 | + if (Number.isFinite(backlogCount) && backlogCount > 0) { |
| 1027 | + facts.push(`backlog ${backlogCount.toLocaleString()}`); |
| 1028 | + } |
| 1029 | +
|
| 1030 | + if (Number.isFinite(stalePollerCount) && stalePollerCount > 0) { |
| 1031 | + facts.push(`stale pollers ${stalePollerCount.toLocaleString()}`); |
| 1032 | + } |
| 1033 | +
|
| 1034 | + if (Number.isFinite(candidateCount) && candidateCount > 0) { |
| 1035 | + facts.push(`repair candidates ${candidateCount.toLocaleString()}`); |
| 1036 | + } |
| 1037 | +
|
| 1038 | + if (Number.isFinite(maxAgeMs) && maxAgeMs > 0) { |
| 1039 | + facts.push(`max age ${this.durationMillisecondsLabel(maxAgeMs)}`); |
| 1040 | + } |
| 1041 | +
|
| 1042 | + return facts; |
| 1043 | + }, |
| 1044 | +
|
945 | 1045 | integerLabel(value) { |
946 | 1046 | const number = Number(value || 0); |
947 | 1047 |
|
@@ -1296,6 +1396,13 @@ export default { |
1296 | 1396 | line-height: 1.4; |
1297 | 1397 | } |
1298 | 1398 |
|
| 1399 | +.worker-health__alert-facts { |
| 1400 | + display: flex; |
| 1401 | + flex-wrap: wrap; |
| 1402 | + gap: 0.5rem; |
| 1403 | + margin-top: 0.85rem; |
| 1404 | +} |
| 1405 | +
|
1299 | 1406 | .worker-health__panel { |
1300 | 1407 | min-width: 0; |
1301 | 1408 | } |
|
0 commit comments