@oneuptime/common 14.0.0 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorService.ts +3 -2
- package/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- package/Server/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -0
- package/Server/Utils/Monitor/AffectedResourceList.ts +31 -73
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -0
- package/Tests/App/Dashboard/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- package/Tests/App/Dashboard/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- package/Tests/Server/Services/DatabaseServiceRuleCriteriaValidation.test.ts +104 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Markdown.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +405 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- package/Tests/Types/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -0
- package/Tests/UI/Components/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/UI/Components/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/Utils/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +3 -2
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +22 -51
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +139 -109
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js.map +1 -0
- package/build/dist/Types/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,6 +27,7 @@ import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
|
|
|
27
27
|
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
|
|
28
28
|
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
|
|
29
29
|
import AffectedResourceList from "./AffectedResourceList";
|
|
30
|
+
import RootCauseList from "./RootCauseList";
|
|
30
31
|
import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
|
|
31
32
|
import MonitorStep from "../../../Types/Monitor/MonitorStep";
|
|
32
33
|
import FilterCondition from "../../../Types/Filter/FilterCondition";
|
|
@@ -1114,9 +1115,7 @@ ${contextBlock}
|
|
|
1114
1115
|
samples: breachingSamples,
|
|
1115
1116
|
totalSamples: ctx.totalSamplesInWindow,
|
|
1116
1117
|
unit: ctx.unit,
|
|
1117
|
-
metricName: ctx.metricName,
|
|
1118
1118
|
unitHeuristicMetricName: unitHeuristicMetricName,
|
|
1119
|
-
alias: ctx.alias,
|
|
1120
1119
|
components: ctx.components || [],
|
|
1121
1120
|
})}`);
|
|
1122
1121
|
}
|
|
@@ -1152,24 +1151,41 @@ ${contextBlock}
|
|
|
1152
1151
|
return sections.join("\n");
|
|
1153
1152
|
}
|
|
1154
1153
|
/**
|
|
1155
|
-
* Build the **Breaching Samples** markdown section — a
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1154
|
+
* Build the **Breaching Samples** markdown section — a numbered list of
|
|
1155
|
+
* the samples that breached, oldest first. Caps the list so a 30-minute
|
|
1156
|
+
* window at 1-second granularity doesn't dump thousands of lines onto
|
|
1157
|
+
* the root-cause page; the caller can always drill in via the metric
|
|
1158
|
+
* explorer link.
|
|
1159
|
+
*
|
|
1160
|
+
* 1. `2026-08-14T10:30:00.000Z` — **1.07 GB**
|
|
1161
|
+
* - `a`: 537 MB
|
|
1162
|
+
* - `b`: 1.5 sec
|
|
1163
|
+
* - `k8s.pod.name`: `web-1`
|
|
1164
|
+
*
|
|
1165
|
+
* It used to be a table — Timestamp | Metric | Alias | Value, a column
|
|
1166
|
+
* per formula component and one per attribute key — which grew wider
|
|
1167
|
+
* with every attribute and read badly in the email card, in Slack and on
|
|
1168
|
+
* the dashboard (see RootCauseList). Its Metric and Alias columns
|
|
1169
|
+
* repeated the same two values on every row, the ones the Metric
|
|
1170
|
+
* Details list directly above already states, so the list leaves them
|
|
1171
|
+
* out; and a component or attribute a sample has no value for is left
|
|
1172
|
+
* out rather than printed as "-".
|
|
1160
1173
|
*
|
|
1161
1174
|
* Timestamps are emitted as inline code wrapping ISO 8601 strings so
|
|
1162
1175
|
* the client-side markdown viewer can localize them to the viewer's
|
|
1163
1176
|
* timezone (without losing the canonical instant).
|
|
1164
1177
|
*/
|
|
1165
1178
|
static formatBreachingSamplesSection(input) {
|
|
1166
|
-
const
|
|
1179
|
+
const MAX_SAMPLES_SHOWN = 20;
|
|
1167
1180
|
// Sort chronologically and de-duplicate any accidental repeats
|
|
1168
1181
|
const sorted = [...input.samples].sort((a, b) => {
|
|
1169
1182
|
return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
1170
1183
|
});
|
|
1171
|
-
const displayedSamples = sorted.slice(0,
|
|
1172
|
-
|
|
1184
|
+
const displayedSamples = sorted.slice(0, MAX_SAMPLES_SHOWN);
|
|
1185
|
+
/*
|
|
1186
|
+
* Collect attribute keys that appear on any displayed sample, so every
|
|
1187
|
+
* item lists the attributes it has in the same order.
|
|
1188
|
+
*/
|
|
1173
1189
|
const attrKeySet = new Set();
|
|
1174
1190
|
for (const s of displayedSamples) {
|
|
1175
1191
|
for (const k of Object.keys(s.attributes || {})) {
|
|
@@ -1177,94 +1193,64 @@ ${contextBlock}
|
|
|
1177
1193
|
}
|
|
1178
1194
|
}
|
|
1179
1195
|
const attrKeys = Array.from(attrKeySet);
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
};
|
|
1188
|
-
/*
|
|
1189
|
-
* Column layout:
|
|
1190
|
-
* Timestamp | Metric | Alias | Value | <component_1> | ... | <attr_1> | ...
|
|
1191
|
-
*
|
|
1192
|
-
* The component columns let the reader see what each variable of a
|
|
1193
|
-
* formula resolved to at the breach time — e.g. "when c = a + b
|
|
1194
|
-
* breached 100, a was 55, b was 46". They are omitted for plain
|
|
1195
|
-
* metric criteria.
|
|
1196
|
-
*/
|
|
1197
|
-
const headerCells = [
|
|
1198
|
-
"Timestamp",
|
|
1199
|
-
"Metric",
|
|
1200
|
-
"Alias",
|
|
1201
|
-
"Value",
|
|
1202
|
-
];
|
|
1203
|
-
/*
|
|
1204
|
-
* The component columns carry no unit in their header any more. Each
|
|
1205
|
-
* cell now names the unit it actually landed on, and auto-scaling
|
|
1206
|
-
* means neighbouring rows can land on different ones — a header that
|
|
1207
|
-
* read "a (By)" over cells reading "900 KB" and "1.2 MB" states a
|
|
1208
|
-
* unit that neither row uses. The configured unit is not lost: the
|
|
1209
|
-
* Components bullet list above the table still records it.
|
|
1210
|
-
*/
|
|
1211
|
-
for (const component of input.components) {
|
|
1212
|
-
headerCells.push(component.alias);
|
|
1213
|
-
}
|
|
1214
|
-
headerCells.push(...attrKeys);
|
|
1215
|
-
const headerRow = `| ${headerCells.join(" | ")} |`;
|
|
1216
|
-
const dividerRow = `| ${headerCells
|
|
1217
|
-
.map(() => {
|
|
1218
|
-
return "---";
|
|
1219
|
-
})
|
|
1220
|
-
.join(" | ")} |`;
|
|
1221
|
-
const metricCell = `\`${escapeCell(input.metricName)}\``;
|
|
1222
|
-
const aliasCell = input.alias
|
|
1223
|
-
? `\`${escapeCell(input.alias)}\``
|
|
1224
|
-
: "-";
|
|
1225
|
-
const dataRows = displayedSamples.map((s) => {
|
|
1226
|
-
const timestampIso = new Date(s.timestamp).toISOString();
|
|
1227
|
-
const cells = [
|
|
1228
|
-
`\`${timestampIso}\``,
|
|
1229
|
-
metricCell,
|
|
1230
|
-
aliasCell,
|
|
1231
|
-
MetricValueFormatter.format({
|
|
1232
|
-
value: s.value,
|
|
1233
|
-
unit: input.unit,
|
|
1234
|
-
metricName: input.unitHeuristicMetricName,
|
|
1235
|
-
}),
|
|
1236
|
-
];
|
|
1196
|
+
const items = displayedSamples.map((s) => {
|
|
1197
|
+
const details = [];
|
|
1198
|
+
/*
|
|
1199
|
+
* What each variable of a formula resolved to at the breach time —
|
|
1200
|
+
* e.g. "when c = a + b breached 100, a was 55, b was 46". Absent
|
|
1201
|
+
* for plain metric criteria.
|
|
1202
|
+
*/
|
|
1237
1203
|
for (const component of input.components) {
|
|
1238
1204
|
const match = (s.componentValues || []).find((cv) => {
|
|
1239
1205
|
return cv.alias === component.alias;
|
|
1240
1206
|
});
|
|
1241
|
-
if (match
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1207
|
+
if (!match || typeof match.value !== "number") {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
/*
|
|
1211
|
+
* Each component keeps its OWN unit. Component values are
|
|
1212
|
+
* indexed off the per-query series and never go through the
|
|
1213
|
+
* sample→threshold-unit conversion the sample's value does, so
|
|
1214
|
+
* they are in the component's legendUnit — which is why each
|
|
1215
|
+
* one names the unit it is in. Its configured unit is in the
|
|
1216
|
+
* Components list of the Metric Details above.
|
|
1217
|
+
*/
|
|
1218
|
+
details.push({
|
|
1219
|
+
label: RootCauseList.code(component.alias),
|
|
1220
|
+
value: MetricValueFormatter.format({
|
|
1251
1221
|
value: match.value,
|
|
1252
1222
|
unit: component.unit,
|
|
1253
1223
|
metricName: MonitorCriteriaEvaluator.metricNameForUnitHeuristics({
|
|
1254
1224
|
metricName: component.name,
|
|
1255
1225
|
isFormula: component.isFormula,
|
|
1256
1226
|
}),
|
|
1257
|
-
})
|
|
1258
|
-
}
|
|
1259
|
-
else {
|
|
1260
|
-
cells.push("-");
|
|
1261
|
-
}
|
|
1227
|
+
}),
|
|
1228
|
+
});
|
|
1262
1229
|
}
|
|
1230
|
+
/*
|
|
1231
|
+
* The attributes identify the series the sample came from. Keys
|
|
1232
|
+
* and values are telemetry, so both go in code spans:
|
|
1233
|
+
* RootCauseList.code keeps whatever they contain inside the span.
|
|
1234
|
+
*/
|
|
1263
1235
|
for (const k of attrKeys) {
|
|
1264
1236
|
const v = s.attributes[k];
|
|
1265
|
-
|
|
1237
|
+
if (v === undefined || v === null) {
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
details.push({
|
|
1241
|
+
label: RootCauseList.code(k),
|
|
1242
|
+
value: RootCauseList.code(String(v)),
|
|
1243
|
+
});
|
|
1266
1244
|
}
|
|
1267
|
-
return
|
|
1245
|
+
return {
|
|
1246
|
+
title: RootCauseList.code(new Date(s.timestamp).toISOString()),
|
|
1247
|
+
value: `**${MetricValueFormatter.format({
|
|
1248
|
+
value: s.value,
|
|
1249
|
+
unit: input.unit,
|
|
1250
|
+
metricName: input.unitHeuristicMetricName,
|
|
1251
|
+
})}**`,
|
|
1252
|
+
details: details,
|
|
1253
|
+
};
|
|
1268
1254
|
});
|
|
1269
1255
|
const lines = [
|
|
1270
1256
|
`**Breaching Samples**`,
|
|
@@ -1273,12 +1259,14 @@ ${contextBlock}
|
|
|
1273
1259
|
totalSamples: input.totalSamples,
|
|
1274
1260
|
}),
|
|
1275
1261
|
"",
|
|
1276
|
-
|
|
1277
|
-
dividerRow,
|
|
1278
|
-
...dataRows,
|
|
1262
|
+
RootCauseList.render(items),
|
|
1279
1263
|
];
|
|
1280
1264
|
if (sorted.length > displayedSamples.length) {
|
|
1281
|
-
|
|
1265
|
+
/*
|
|
1266
|
+
* The blank line matters: without it the note is a lazy
|
|
1267
|
+
* continuation of the last item, not a paragraph of its own.
|
|
1268
|
+
*/
|
|
1269
|
+
lines.push("", `_Showing the first ${displayedSamples.length} of ${sorted.length} breaching samples._`);
|
|
1282
1270
|
}
|
|
1283
1271
|
return lines.join("\n");
|
|
1284
1272
|
}
|
|
@@ -1582,15 +1570,34 @@ ${contextBlock}
|
|
|
1582
1570
|
sections.push(`**Kubernetes Cluster Details**\n${clusterDetails.join("\n")}`);
|
|
1583
1571
|
// Affected resources
|
|
1584
1572
|
if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1573
|
+
/*
|
|
1574
|
+
* Keep the rows that satisfy the criteria that just matched, worst
|
|
1575
|
+
* first — NOT simply the non-zero rows. k8s-node-not-ready fires on
|
|
1576
|
+
* `k8s.node.condition_ready = 0`, so the zero rows are the NotReady
|
|
1577
|
+
* nodes; the old hardcoded `> 0` dropped exactly those and named a
|
|
1578
|
+
* healthy node in the analysis below.
|
|
1579
|
+
*/
|
|
1580
|
+
const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
|
|
1581
|
+
criteriaInstance: input.criteriaInstance,
|
|
1582
|
+
floorEqualityFiresOnFall: true,
|
|
1583
|
+
});
|
|
1584
|
+
const sortedResources = breakdown.affectedResources
|
|
1585
|
+
.map((r) => {
|
|
1586
|
+
var _a;
|
|
1587
|
+
/*
|
|
1588
|
+
* A fall criteria breached on the resource's lowest sample
|
|
1589
|
+
* in the window, not its highest.
|
|
1590
|
+
*/
|
|
1591
|
+
return breach.worstIsLowest
|
|
1592
|
+
? Object.assign(Object.assign({}, r), { metricValue: (_a = r.lowestMetricValue) !== null && _a !== void 0 ? _a : r.metricValue }) : r;
|
|
1593
|
+
})
|
|
1589
1594
|
.filter((r) => {
|
|
1590
|
-
return r.metricValue
|
|
1595
|
+
return breach.matches(r.metricValue);
|
|
1591
1596
|
})
|
|
1592
1597
|
.sort((a, b) => {
|
|
1593
|
-
return
|
|
1598
|
+
return breach.worstIsLowest
|
|
1599
|
+
? a.metricValue - b.metricValue
|
|
1600
|
+
: b.metricValue - a.metricValue;
|
|
1594
1601
|
});
|
|
1595
1602
|
if (sortedResources.length === 0) {
|
|
1596
1603
|
return sections.join("\n");
|
|
@@ -2318,31 +2325,49 @@ ${contextBlock}
|
|
|
2318
2325
|
return sections.length > 0 ? sections.join("\n") : null;
|
|
2319
2326
|
}
|
|
2320
2327
|
/*
|
|
2321
|
-
* Which rows of
|
|
2328
|
+
* Which rows of an affected-resources list actually BREACHED.
|
|
2322
2329
|
*
|
|
2323
2330
|
* The blanket `metricValue > 0` below is right for every count- or
|
|
2324
2331
|
* level-style criteria ("> 0 active health checks", "> 85 % used"), but
|
|
2325
|
-
* it is exactly backwards for the availability signals: the OSD
|
|
2326
|
-
* OSD Out / Quorum Degraded criteria fire on `< 1` over
|
|
2327
|
-
* ceph_osd_in / ceph_mon_quorum_status, so the ZERO rows
|
|
2328
|
-
* daemons. Dropping them rendered a list of the HEALTHY
|
|
2329
|
-
* an incident that tells the reader to "check the root
|
|
2330
|
-
* affected ceph_daemon label".
|
|
2332
|
+
* it is exactly backwards for the availability signals: the Ceph OSD
|
|
2333
|
+
* Down / OSD Out / Quorum Degraded criteria fire on `< 1` over
|
|
2334
|
+
* ceph_osd_up / ceph_osd_in / ceph_mon_quorum_status, so the ZERO rows
|
|
2335
|
+
* are the down daemons. Dropping them rendered a list of the HEALTHY
|
|
2336
|
+
* daemons under an incident that tells the reader to "check the root
|
|
2337
|
+
* cause for the affected ceph_daemon label".
|
|
2331
2338
|
*
|
|
2332
2339
|
* Invert only for a criteria that fires when the metric FALLS.
|
|
2333
2340
|
* Everything else — every `> 0` health check, PG count, latency and
|
|
2334
|
-
* capacity ratio, and every `= 0` recovery criteria —
|
|
2335
|
-
* existing `> 0`, worst-highest-first list byte for byte.
|
|
2341
|
+
* capacity ratio, and (by default) every `= 0` recovery criteria —
|
|
2342
|
+
* keeps the existing `> 0`, worst-highest-first list byte for byte.
|
|
2343
|
+
*
|
|
2344
|
+
* `floorEqualityFiresOnFall` additionally counts a FIRING `= 0` (or
|
|
2345
|
+
* below) as a fall. Kubernetes needs it: k8s-node-not-ready and
|
|
2346
|
+
* k8s-etcd-no-leader fire on `= 0` over k8s.node.condition_ready /
|
|
2347
|
+
* etcd_server_has_leader, and zero is the floor of those metrics, so
|
|
2348
|
+
* the only way to reach it is down. It is gated on the criteria opening
|
|
2349
|
+
* an incident or alert because the same `= 0` is also how the restart,
|
|
2350
|
+
* failed-pod and unavailable-replica templates express RECOVERY, and
|
|
2351
|
+
* that all-clear must not become a list of healthy zero rows. Ceph
|
|
2352
|
+
* leaves it off: all of its `= 0` criteria are recoveries.
|
|
2336
2353
|
*/
|
|
2337
|
-
static
|
|
2338
|
-
var _a;
|
|
2339
|
-
const metricFilters = (((_a = criteriaInstance === null ||
|
|
2354
|
+
static getAffectedResourceBreachPredicate(input) {
|
|
2355
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2356
|
+
const metricFilters = (((_b = (_a = input.criteriaInstance) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.filters) || []).filter((f) => {
|
|
2340
2357
|
return f.checkOn === CheckOn.MetricValue && typeof f.value === "number";
|
|
2341
2358
|
});
|
|
2359
|
+
const opensIncidentOrAlert = ((_d = (_c = input.criteriaInstance) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.createIncidents) === true ||
|
|
2360
|
+
((_f = (_e = input.criteriaInstance) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.createAlerts) === true;
|
|
2342
2361
|
const firesWhenMetricFalls = metricFilters.length > 0 &&
|
|
2343
2362
|
metricFilters.every((f) => {
|
|
2344
|
-
|
|
2345
|
-
f.filterType === FilterType.LessThanOrEqualTo)
|
|
2363
|
+
if (f.filterType === FilterType.LessThan ||
|
|
2364
|
+
f.filterType === FilterType.LessThanOrEqualTo) {
|
|
2365
|
+
return true;
|
|
2366
|
+
}
|
|
2367
|
+
return (input.floorEqualityFiresOnFall === true &&
|
|
2368
|
+
opensIncidentOrAlert &&
|
|
2369
|
+
f.filterType === FilterType.EqualTo &&
|
|
2370
|
+
f.value <= 0);
|
|
2346
2371
|
});
|
|
2347
2372
|
if (!firesWhenMetricFalls) {
|
|
2348
2373
|
return {
|
|
@@ -2356,6 +2381,9 @@ ${contextBlock}
|
|
|
2356
2381
|
matches: (value) => {
|
|
2357
2382
|
return metricFilters.some((f) => {
|
|
2358
2383
|
const threshold = f.value;
|
|
2384
|
+
if (f.filterType === FilterType.EqualTo) {
|
|
2385
|
+
return value === threshold;
|
|
2386
|
+
}
|
|
2359
2387
|
return f.filterType === FilterType.LessThan
|
|
2360
2388
|
? value < threshold
|
|
2361
2389
|
: value <= threshold;
|
|
@@ -2472,7 +2500,9 @@ ${contextBlock}
|
|
|
2472
2500
|
* ceph_osd_in / ceph_mon_quorum_status the criteria is `< 1`, so
|
|
2473
2501
|
* the zero rows are the whole point of the incident.
|
|
2474
2502
|
*/
|
|
2475
|
-
const breach = MonitorCriteriaEvaluator.
|
|
2503
|
+
const breach = MonitorCriteriaEvaluator.getAffectedResourceBreachPredicate({
|
|
2504
|
+
criteriaInstance: input.criteriaInstance,
|
|
2505
|
+
});
|
|
2476
2506
|
const sortedResources = [
|
|
2477
2507
|
...breakdown.affectedResources,
|
|
2478
2508
|
]
|