@oneuptime/common 11.3.5 → 11.3.7
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/Server/Infrastructure/Queue.ts +103 -16
- package/Server/Services/AlertService.ts +302 -202
- package/Server/Services/AnalyticsDatabaseService.ts +9 -5
- package/Server/Services/IncidentService.ts +410 -306
- package/Server/Services/TeamMemberService.ts +38 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +31 -0
- package/Server/Utils/Monitor/IncomingRequestIncidentGrouping.ts +428 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +158 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +26 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +147 -2
- package/Server/Utils/Monitor/MonitorResource.ts +74 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +2 -1
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +32 -0
- package/Tests/Server/Utils/Monitor/IncomingRequestIncidentGrouping.test.ts +389 -0
- package/Tests/Server/Utils/Monitor/SeriesAbsenceResolutionGuard.test.ts +177 -0
- package/Tests/Types/Events/Recurring.test.ts +475 -0
- package/Tests/Types/Monitor/CriteriaFilter.test.ts +348 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +331 -0
- package/Tests/UI/Components/Alert.test.tsx +23 -15
- package/Tests/UI/Components/Breadcrumbs.test.tsx +18 -6
- package/Tests/UI/Components/Button.test.tsx +3 -3
- package/Tests/UI/Components/DictionaryFilterOperator.test.ts +174 -0
- package/Tests/UI/Components/EmptyState/EmptyState.test.tsx +4 -3
- package/Tests/UI/Components/Input.test.tsx +3 -2
- package/Tests/UI/Components/Pill.test.tsx +4 -2
- package/Tests/UI/Components/SideMenuItem.test.tsx +18 -8
- package/Tests/UI/Components/TextArea.test.tsx +6 -4
- package/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.ts +74 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +22 -0
- package/UI/Components/Dictionary/Dictionary.tsx +123 -50
- package/UI/Components/Dictionary/DictionaryFilterOperator.ts +96 -1
- package/UI/Components/MasterPage/MasterPage.tsx +5 -3
- package/build/dist/Server/Infrastructure/Queue.js +83 -15
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +255 -161
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +9 -5
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +322 -236
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +37 -1
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +26 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/IncomingRequestIncidentGrouping.js +288 -0
- package/build/dist/Server/Utils/Monitor/IncomingRequestIncidentGrouping.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +119 -8
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +24 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +103 -2
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +62 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.js +8 -0
- package/build/dist/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +10 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +60 -9
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js +69 -0
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +5 -3
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@ import AlertOwnerUser from "../../Models/DatabaseModels/AlertOwnerUser";
|
|
|
31
31
|
import AlertStateTimeline from "../../Models/DatabaseModels/AlertStateTimeline";
|
|
32
32
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
33
33
|
import logger from "../Utils/Logger";
|
|
34
|
+
import Semaphore from "../Infrastructure/Semaphore";
|
|
34
35
|
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
|
35
36
|
import MetricService from "./MetricService";
|
|
36
37
|
import GlobalConfigService from "./GlobalConfigService";
|
|
@@ -979,7 +980,7 @@ ${alertSeverity.name}
|
|
|
979
980
|
return Service.DEFAULT_METRIC_RETENTION_DAYS;
|
|
980
981
|
}
|
|
981
982
|
async refreshAlertMetrics(data) {
|
|
982
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
|
|
983
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11;
|
|
983
984
|
const alert = await this.findOneById({
|
|
984
985
|
id: data.alertId,
|
|
985
986
|
select: {
|
|
@@ -1028,181 +1029,274 @@ ${alertSeverity.name}
|
|
|
1028
1029
|
},
|
|
1029
1030
|
});
|
|
1030
1031
|
const firstAlertStateTimeline = alertStateTimelines[0];
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
projectId: alert.projectId.toString(),
|
|
1057
|
-
monitorId: (_b = (_a = alert.monitor) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
1058
|
-
monitorName: (_d = (_c = alert.monitor) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.toString(),
|
|
1059
|
-
alertSeverityId: (_f = (_e = alert.alertSeverity) === null || _e === void 0 ? void 0 : _e._id) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
1060
|
-
alertSeverityName: (_h = (_g = alert.alertSeverity) === null || _g === void 0 ? void 0 : _g.name) === null || _h === void 0 ? void 0 : _h.toString(),
|
|
1061
|
-
};
|
|
1062
|
-
alertCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(alertCountMetric.attributes);
|
|
1063
|
-
alertCountMetric.time = alertStartsAt;
|
|
1064
|
-
alertCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(alertCountMetric.time);
|
|
1065
|
-
alertCountMetric.metricPointType = MetricPointType.Sum;
|
|
1066
|
-
alertCountMetric.retentionDate = alertMetricRetentionDate;
|
|
1067
|
-
itemsToSave.push(alertCountMetric);
|
|
1068
|
-
const metricType = new MetricType();
|
|
1069
|
-
metricType.name = alertCountMetric.name;
|
|
1070
|
-
metricType.description = "Number of alerts created";
|
|
1071
|
-
metricType.unit = "";
|
|
1072
|
-
metricType.services = [];
|
|
1073
|
-
metricTypesMap[alertCountMetric.name] = metricType;
|
|
1074
|
-
// is the alert acknowledged?
|
|
1075
|
-
const isAlertAcknowledged = alertStateTimelines.some((timeline) => {
|
|
1076
|
-
var _a;
|
|
1077
|
-
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isAcknowledgedState;
|
|
1078
|
-
});
|
|
1079
|
-
if (isAlertAcknowledged) {
|
|
1080
|
-
const ackAlertStateTimeline = alertStateTimelines.find((timeline) => {
|
|
1081
|
-
var _a;
|
|
1082
|
-
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isAcknowledgedState;
|
|
1032
|
+
/*
|
|
1033
|
+
* Serialize concurrent refreshes for this alert across pods. The
|
|
1034
|
+
* write-once guard below is read-then-insert (findBy existing, then a
|
|
1035
|
+
* conditional createMany). Both call sites fire refresh fire-and-forget
|
|
1036
|
+
* after releasing the per-alert state-timeline mutex, so two
|
|
1037
|
+
* close-together transitions (e.g. auto-acknowledge then auto-resolve)
|
|
1038
|
+
* could interleave: both read a snapshot missing a metric and both
|
|
1039
|
+
* insert it. The Metric table is a plain MergeTree with no row collapse,
|
|
1040
|
+
* so those duplicates would be permanent and re-inflate the very Sum/Avg
|
|
1041
|
+
* widgets this change fixes. A best-effort distributed lock keyed on the
|
|
1042
|
+
* alert makes the read+insert atomic; if the lock can't be taken we still
|
|
1043
|
+
* proceed (the existence check alone covers the common sequential case).
|
|
1044
|
+
*/
|
|
1045
|
+
let metricRefreshMutex = null;
|
|
1046
|
+
try {
|
|
1047
|
+
metricRefreshMutex = await Semaphore.lock({
|
|
1048
|
+
key: data.alertId.toString(),
|
|
1049
|
+
namespace: "AlertService.refreshAlertMetrics",
|
|
1050
|
+
lockTimeout: 30000,
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
catch (err) {
|
|
1054
|
+
logger.error(err, {
|
|
1055
|
+
projectId: (_a = alert.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
1056
|
+
alertId: (_b = alert.id) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
1083
1057
|
});
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1058
|
+
}
|
|
1059
|
+
try {
|
|
1060
|
+
/*
|
|
1061
|
+
* Write-once metrics — no deletes.
|
|
1062
|
+
*
|
|
1063
|
+
* The raw Metric table (`MetricItemV3`) feeds an AggregatingMergeTree
|
|
1064
|
+
* materialized view (`MetricItemAggMV1m_mv`) that accumulates
|
|
1065
|
+
* `sumState`/`countState` per (projectId, name, primaryEntityId,
|
|
1066
|
+
* minute). The MV trigger fires only on INSERT — an `ALTER ... DELETE`
|
|
1067
|
+
* mutation on the source rolls back nothing in the MV. So the old
|
|
1068
|
+
* "delete every metric for this alert, then re-insert" refresh both
|
|
1069
|
+
* (a) inflated those rollups and (b) issued one heavy `ALTER ... DELETE`
|
|
1070
|
+
* mutation per state transition per alert — a mutation storm.
|
|
1071
|
+
*
|
|
1072
|
+
* Every alert metric has a single eventually-final value: AlertCount is
|
|
1073
|
+
* constant `value = 1`; MTTA/MTTR/duration are each fixed once the
|
|
1074
|
+
* relevant state transition has happened. So we load what we've already
|
|
1075
|
+
* emitted for this alert and insert each metric exactly once, when it
|
|
1076
|
+
* becomes final. Repeat refreshes that find nothing new are no-ops.
|
|
1077
|
+
*/
|
|
1078
|
+
const existingMetricNames = new Set((await MetricService.findBy({
|
|
1079
|
+
query: {
|
|
1080
|
+
projectId: alert.projectId,
|
|
1081
|
+
primaryEntityId: data.alertId,
|
|
1082
|
+
},
|
|
1083
|
+
select: {
|
|
1084
|
+
name: true,
|
|
1085
|
+
},
|
|
1086
|
+
skip: 0,
|
|
1087
|
+
limit: LIMIT_PER_PROJECT,
|
|
1088
|
+
props: {
|
|
1089
|
+
isRoot: true,
|
|
1090
|
+
},
|
|
1091
|
+
}))
|
|
1092
|
+
.map((metric) => {
|
|
1093
|
+
return metric.name;
|
|
1094
|
+
})
|
|
1095
|
+
.filter(Boolean));
|
|
1096
|
+
const itemsToSave = [];
|
|
1097
|
+
const metricTypesMap = {};
|
|
1098
|
+
const metricRetentionDays = await this.getMetricRetentionDays();
|
|
1099
|
+
const alertMetricRetentionDate = OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), metricRetentionDays);
|
|
1100
|
+
// now we need to create new metrics for this alert - TimeToAcknowledge, TimeToResolve, AlertCount, AlertDuration
|
|
1101
|
+
const alertStartsAt = (firstAlertStateTimeline === null || firstAlertStateTimeline === void 0 ? void 0 : firstAlertStateTimeline.startsAt) ||
|
|
1102
|
+
alert.createdAt ||
|
|
1103
|
+
OneUptimeDate.getCurrentDate();
|
|
1104
|
+
// register the metric type so the catalog stays complete across refreshes.
|
|
1105
|
+
const alertCountMetricType = new MetricType();
|
|
1106
|
+
alertCountMetricType.name = AlertMetricType.AlertCount;
|
|
1107
|
+
alertCountMetricType.description = "Number of alerts created";
|
|
1108
|
+
alertCountMetricType.unit = "";
|
|
1109
|
+
alertCountMetricType.services = [];
|
|
1110
|
+
metricTypesMap[AlertMetricType.AlertCount] = alertCountMetricType;
|
|
1111
|
+
// write-once: AlertCount is a constant value=1 keyed by the alert.
|
|
1112
|
+
if (!existingMetricNames.has(AlertMetricType.AlertCount)) {
|
|
1113
|
+
const alertCountMetric = new Metric();
|
|
1114
|
+
alertCountMetric.projectId = alert.projectId;
|
|
1115
|
+
alertCountMetric.primaryEntityId = alert.id;
|
|
1116
|
+
alertCountMetric.primaryEntityType = ServiceType.Alert;
|
|
1117
|
+
alertCountMetric.name = AlertMetricType.AlertCount;
|
|
1118
|
+
alertCountMetric.value = 1;
|
|
1119
|
+
alertCountMetric.attributes = {
|
|
1092
1120
|
alertId: data.alertId.toString(),
|
|
1093
1121
|
projectId: alert.projectId.toString(),
|
|
1094
|
-
monitorId: (
|
|
1095
|
-
monitorName: (
|
|
1096
|
-
alertSeverityId: (
|
|
1097
|
-
alertSeverityName: (
|
|
1122
|
+
monitorId: (_d = (_c = alert.monitor) === null || _c === void 0 ? void 0 : _c._id) === null || _d === void 0 ? void 0 : _d.toString(),
|
|
1123
|
+
monitorName: (_f = (_e = alert.monitor) === null || _e === void 0 ? void 0 : _e.name) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
1124
|
+
alertSeverityId: (_h = (_g = alert.alertSeverity) === null || _g === void 0 ? void 0 : _g._id) === null || _h === void 0 ? void 0 : _h.toString(),
|
|
1125
|
+
alertSeverityName: (_k = (_j = alert.alertSeverity) === null || _j === void 0 ? void 0 : _j.name) === null || _k === void 0 ? void 0 : _k.toString(),
|
|
1098
1126
|
};
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
|
|
1106
|
-
timeToAcknowledgeMetric.retentionDate = alertMetricRetentionDate;
|
|
1107
|
-
itemsToSave.push(timeToAcknowledgeMetric);
|
|
1108
|
-
const metricType = new MetricType();
|
|
1109
|
-
metricType.name = timeToAcknowledgeMetric.name;
|
|
1110
|
-
metricType.description = "Time taken to acknowledge the alert";
|
|
1111
|
-
metricType.unit = "seconds";
|
|
1112
|
-
metricTypesMap[timeToAcknowledgeMetric.name] = metricType;
|
|
1127
|
+
alertCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(alertCountMetric.attributes);
|
|
1128
|
+
alertCountMetric.time = alertStartsAt;
|
|
1129
|
+
alertCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(alertCountMetric.time);
|
|
1130
|
+
alertCountMetric.metricPointType = MetricPointType.Sum;
|
|
1131
|
+
alertCountMetric.retentionDate = alertMetricRetentionDate;
|
|
1132
|
+
itemsToSave.push(alertCountMetric);
|
|
1113
1133
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1134
|
+
// is the alert acknowledged?
|
|
1135
|
+
const isAlertAcknowledged = alertStateTimelines.some((timeline) => {
|
|
1136
|
+
var _a;
|
|
1137
|
+
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isAcknowledgedState;
|
|
1138
|
+
});
|
|
1139
|
+
if (isAlertAcknowledged) {
|
|
1140
|
+
const ackAlertStateTimeline = alertStateTimelines.find((timeline) => {
|
|
1141
|
+
var _a;
|
|
1142
|
+
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isAcknowledgedState;
|
|
1143
|
+
});
|
|
1144
|
+
if (ackAlertStateTimeline) {
|
|
1145
|
+
// register the metric type so the catalog stays complete across refreshes.
|
|
1146
|
+
const metricType = new MetricType();
|
|
1147
|
+
metricType.name = AlertMetricType.TimeToAcknowledge;
|
|
1148
|
+
metricType.description = "Time taken to acknowledge the alert";
|
|
1149
|
+
metricType.unit = "seconds";
|
|
1150
|
+
metricTypesMap[AlertMetricType.TimeToAcknowledge] = metricType;
|
|
1151
|
+
// write-once: MTTA is fixed once the alert is first acknowledged.
|
|
1152
|
+
if (!existingMetricNames.has(AlertMetricType.TimeToAcknowledge)) {
|
|
1153
|
+
const timeToAcknowledgeMetric = new Metric();
|
|
1154
|
+
timeToAcknowledgeMetric.projectId = alert.projectId;
|
|
1155
|
+
timeToAcknowledgeMetric.primaryEntityId = alert.id;
|
|
1156
|
+
timeToAcknowledgeMetric.primaryEntityType = ServiceType.Alert;
|
|
1157
|
+
timeToAcknowledgeMetric.name = AlertMetricType.TimeToAcknowledge;
|
|
1158
|
+
timeToAcknowledgeMetric.value =
|
|
1159
|
+
OneUptimeDate.getDifferenceInSeconds((ackAlertStateTimeline === null || ackAlertStateTimeline === void 0 ? void 0 : ackAlertStateTimeline.startsAt) ||
|
|
1160
|
+
OneUptimeDate.getCurrentDate(), alertStartsAt);
|
|
1161
|
+
timeToAcknowledgeMetric.attributes = {
|
|
1162
|
+
alertId: data.alertId.toString(),
|
|
1163
|
+
projectId: alert.projectId.toString(),
|
|
1164
|
+
monitorId: (_m = (_l = alert.monitor) === null || _l === void 0 ? void 0 : _l._id) === null || _m === void 0 ? void 0 : _m.toString(),
|
|
1165
|
+
monitorName: (_p = (_o = alert.monitor) === null || _o === void 0 ? void 0 : _o.name) === null || _p === void 0 ? void 0 : _p.toString(),
|
|
1166
|
+
alertSeverityId: (_r = (_q = alert.alertSeverity) === null || _q === void 0 ? void 0 : _q._id) === null || _r === void 0 ? void 0 : _r.toString(),
|
|
1167
|
+
alertSeverityName: (_t = (_s = alert.alertSeverity) === null || _s === void 0 ? void 0 : _s.name) === null || _t === void 0 ? void 0 : _t.toString(),
|
|
1168
|
+
};
|
|
1169
|
+
timeToAcknowledgeMetric.attributeKeys =
|
|
1170
|
+
TelemetryUtil.getAttributeKeys(timeToAcknowledgeMetric.attributes);
|
|
1171
|
+
timeToAcknowledgeMetric.time =
|
|
1172
|
+
(ackAlertStateTimeline === null || ackAlertStateTimeline === void 0 ? void 0 : ackAlertStateTimeline.startsAt) ||
|
|
1173
|
+
alert.createdAt ||
|
|
1174
|
+
OneUptimeDate.getCurrentDate();
|
|
1175
|
+
timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(timeToAcknowledgeMetric.time);
|
|
1176
|
+
timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
|
|
1177
|
+
timeToAcknowledgeMetric.retentionDate = alertMetricRetentionDate;
|
|
1178
|
+
itemsToSave.push(timeToAcknowledgeMetric);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
// time to resolve
|
|
1183
|
+
const isAlertResolved = alertStateTimelines.some((timeline) => {
|
|
1184
|
+
var _a;
|
|
1185
|
+
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
|
|
1186
|
+
});
|
|
1121
1187
|
const resolvedAlertStateTimeline = alertStateTimelines.find((timeline) => {
|
|
1122
1188
|
var _a;
|
|
1123
1189
|
return (_a = timeline.alertState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
|
|
1124
1190
|
});
|
|
1125
|
-
if (resolvedAlertStateTimeline) {
|
|
1126
|
-
|
|
1127
|
-
timeToResolveMetric.projectId = alert.projectId;
|
|
1128
|
-
timeToResolveMetric.primaryEntityId = alert.id;
|
|
1129
|
-
timeToResolveMetric.primaryEntityType = ServiceType.Alert;
|
|
1130
|
-
timeToResolveMetric.name = AlertMetricType.TimeToResolve;
|
|
1131
|
-
timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds((resolvedAlertStateTimeline === null || resolvedAlertStateTimeline === void 0 ? void 0 : resolvedAlertStateTimeline.startsAt) ||
|
|
1132
|
-
OneUptimeDate.getCurrentDate(), alertStartsAt);
|
|
1133
|
-
timeToResolveMetric.attributes = {
|
|
1134
|
-
alertId: data.alertId.toString(),
|
|
1135
|
-
projectId: alert.projectId.toString(),
|
|
1136
|
-
monitorId: (_t = (_s = alert.monitor) === null || _s === void 0 ? void 0 : _s._id) === null || _t === void 0 ? void 0 : _t.toString(),
|
|
1137
|
-
monitorName: (_v = (_u = alert.monitor) === null || _u === void 0 ? void 0 : _u.name) === null || _v === void 0 ? void 0 : _v.toString(),
|
|
1138
|
-
alertSeverityId: (_x = (_w = alert.alertSeverity) === null || _w === void 0 ? void 0 : _w._id) === null || _x === void 0 ? void 0 : _x.toString(),
|
|
1139
|
-
alertSeverityName: (_z = (_y = alert.alertSeverity) === null || _y === void 0 ? void 0 : _y.name) === null || _z === void 0 ? void 0 : _z.toString(),
|
|
1140
|
-
};
|
|
1141
|
-
timeToResolveMetric.attributeKeys = TelemetryUtil.getAttributeKeys(timeToResolveMetric.attributes);
|
|
1142
|
-
timeToResolveMetric.time =
|
|
1143
|
-
(resolvedAlertStateTimeline === null || resolvedAlertStateTimeline === void 0 ? void 0 : resolvedAlertStateTimeline.startsAt) ||
|
|
1144
|
-
alert.createdAt ||
|
|
1145
|
-
OneUptimeDate.getCurrentDate();
|
|
1146
|
-
timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(timeToResolveMetric.time);
|
|
1147
|
-
timeToResolveMetric.metricPointType = MetricPointType.Sum;
|
|
1148
|
-
timeToResolveMetric.retentionDate = alertMetricRetentionDate;
|
|
1149
|
-
itemsToSave.push(timeToResolveMetric);
|
|
1191
|
+
if (isAlertResolved && resolvedAlertStateTimeline) {
|
|
1192
|
+
// register the metric type so the catalog stays complete across refreshes.
|
|
1150
1193
|
const metricType = new MetricType();
|
|
1151
|
-
metricType.name =
|
|
1194
|
+
metricType.name = AlertMetricType.TimeToResolve;
|
|
1152
1195
|
metricType.description = "Time taken to resolve the alert";
|
|
1153
1196
|
metricType.unit = "seconds";
|
|
1154
|
-
metricTypesMap[
|
|
1197
|
+
metricTypesMap[AlertMetricType.TimeToResolve] = metricType;
|
|
1198
|
+
// write-once: MTTR is fixed once the alert is first resolved.
|
|
1199
|
+
if (!existingMetricNames.has(AlertMetricType.TimeToResolve)) {
|
|
1200
|
+
const timeToResolveMetric = new Metric();
|
|
1201
|
+
timeToResolveMetric.projectId = alert.projectId;
|
|
1202
|
+
timeToResolveMetric.primaryEntityId = alert.id;
|
|
1203
|
+
timeToResolveMetric.primaryEntityType = ServiceType.Alert;
|
|
1204
|
+
timeToResolveMetric.name = AlertMetricType.TimeToResolve;
|
|
1205
|
+
timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds((resolvedAlertStateTimeline === null || resolvedAlertStateTimeline === void 0 ? void 0 : resolvedAlertStateTimeline.startsAt) ||
|
|
1206
|
+
OneUptimeDate.getCurrentDate(), alertStartsAt);
|
|
1207
|
+
timeToResolveMetric.attributes = {
|
|
1208
|
+
alertId: data.alertId.toString(),
|
|
1209
|
+
projectId: alert.projectId.toString(),
|
|
1210
|
+
monitorId: (_v = (_u = alert.monitor) === null || _u === void 0 ? void 0 : _u._id) === null || _v === void 0 ? void 0 : _v.toString(),
|
|
1211
|
+
monitorName: (_x = (_w = alert.monitor) === null || _w === void 0 ? void 0 : _w.name) === null || _x === void 0 ? void 0 : _x.toString(),
|
|
1212
|
+
alertSeverityId: (_z = (_y = alert.alertSeverity) === null || _y === void 0 ? void 0 : _y._id) === null || _z === void 0 ? void 0 : _z.toString(),
|
|
1213
|
+
alertSeverityName: (_1 = (_0 = alert.alertSeverity) === null || _0 === void 0 ? void 0 : _0.name) === null || _1 === void 0 ? void 0 : _1.toString(),
|
|
1214
|
+
};
|
|
1215
|
+
timeToResolveMetric.attributeKeys = TelemetryUtil.getAttributeKeys(timeToResolveMetric.attributes);
|
|
1216
|
+
timeToResolveMetric.time =
|
|
1217
|
+
(resolvedAlertStateTimeline === null || resolvedAlertStateTimeline === void 0 ? void 0 : resolvedAlertStateTimeline.startsAt) ||
|
|
1218
|
+
alert.createdAt ||
|
|
1219
|
+
OneUptimeDate.getCurrentDate();
|
|
1220
|
+
timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(timeToResolveMetric.time);
|
|
1221
|
+
timeToResolveMetric.metricPointType = MetricPointType.Sum;
|
|
1222
|
+
timeToResolveMetric.retentionDate = alertMetricRetentionDate;
|
|
1223
|
+
itemsToSave.push(timeToResolveMetric);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
/*
|
|
1227
|
+
* Alert duration — write-once, finalized at resolution.
|
|
1228
|
+
*
|
|
1229
|
+
* Previously recomputed as (latest state transition − start) on every
|
|
1230
|
+
* refresh, which grew for open alerts and required delete + re-insert.
|
|
1231
|
+
* With no deletes we emit a single final duration once the alert reaches
|
|
1232
|
+
* a resolved state: (first resolution − start). An alert that is never
|
|
1233
|
+
* resolved has no duration row (its lifetime is not yet final).
|
|
1234
|
+
*/
|
|
1235
|
+
if (isAlertResolved && resolvedAlertStateTimeline) {
|
|
1236
|
+
// register the metric type so the catalog stays complete across refreshes.
|
|
1237
|
+
const metricType = new MetricType();
|
|
1238
|
+
metricType.name = AlertMetricType.AlertDuration;
|
|
1239
|
+
metricType.description = "Duration of the alert";
|
|
1240
|
+
metricType.unit = "seconds";
|
|
1241
|
+
metricTypesMap[AlertMetricType.AlertDuration] = metricType;
|
|
1242
|
+
if (!existingMetricNames.has(AlertMetricType.AlertDuration)) {
|
|
1243
|
+
const alertEndsAt = resolvedAlertStateTimeline.startsAt ||
|
|
1244
|
+
OneUptimeDate.getCurrentDate();
|
|
1245
|
+
const alertDurationMetric = new Metric();
|
|
1246
|
+
alertDurationMetric.projectId = alert.projectId;
|
|
1247
|
+
alertDurationMetric.primaryEntityId = alert.id;
|
|
1248
|
+
alertDurationMetric.primaryEntityType = ServiceType.Alert;
|
|
1249
|
+
alertDurationMetric.name = AlertMetricType.AlertDuration;
|
|
1250
|
+
alertDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(alertEndsAt, alertStartsAt);
|
|
1251
|
+
alertDurationMetric.attributes = {
|
|
1252
|
+
alertId: data.alertId.toString(),
|
|
1253
|
+
projectId: alert.projectId.toString(),
|
|
1254
|
+
monitorId: (_3 = (_2 = alert.monitor) === null || _2 === void 0 ? void 0 : _2._id) === null || _3 === void 0 ? void 0 : _3.toString(),
|
|
1255
|
+
monitorName: (_5 = (_4 = alert.monitor) === null || _4 === void 0 ? void 0 : _4.name) === null || _5 === void 0 ? void 0 : _5.toString(),
|
|
1256
|
+
alertSeverityId: (_7 = (_6 = alert.alertSeverity) === null || _6 === void 0 ? void 0 : _6._id) === null || _7 === void 0 ? void 0 : _7.toString(),
|
|
1257
|
+
alertSeverityName: (_9 = (_8 = alert.alertSeverity) === null || _8 === void 0 ? void 0 : _8.name) === null || _9 === void 0 ? void 0 : _9.toString(),
|
|
1258
|
+
};
|
|
1259
|
+
alertDurationMetric.attributeKeys = TelemetryUtil.getAttributeKeys(alertDurationMetric.attributes);
|
|
1260
|
+
alertDurationMetric.time = alertEndsAt;
|
|
1261
|
+
alertDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(alertDurationMetric.time);
|
|
1262
|
+
alertDurationMetric.metricPointType = MetricPointType.Sum;
|
|
1263
|
+
alertDurationMetric.retentionDate = alertMetricRetentionDate;
|
|
1264
|
+
itemsToSave.push(alertDurationMetric);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
// write-once: a refresh that finds nothing new inserts nothing.
|
|
1268
|
+
if (itemsToSave.length > 0) {
|
|
1269
|
+
await MetricService.createMany({
|
|
1270
|
+
items: itemsToSave,
|
|
1271
|
+
props: {
|
|
1272
|
+
isRoot: true,
|
|
1273
|
+
},
|
|
1274
|
+
});
|
|
1155
1275
|
}
|
|
1276
|
+
TelemetryUtil.indexMetricNameServiceNameMap({
|
|
1277
|
+
metricNameServiceNameMap: metricTypesMap,
|
|
1278
|
+
projectId: alert.projectId,
|
|
1279
|
+
}).catch((err) => {
|
|
1280
|
+
var _a, _b;
|
|
1281
|
+
logger.error(err, {
|
|
1282
|
+
projectId: (_a = alert.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
1283
|
+
alertId: (_b = alert.id) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
1284
|
+
});
|
|
1285
|
+
});
|
|
1156
1286
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
projectId: alert.projectId.toString(),
|
|
1170
|
-
monitorId: (_1 = (_0 = alert.monitor) === null || _0 === void 0 ? void 0 : _0._id) === null || _1 === void 0 ? void 0 : _1.toString(),
|
|
1171
|
-
monitorName: (_3 = (_2 = alert.monitor) === null || _2 === void 0 ? void 0 : _2.name) === null || _3 === void 0 ? void 0 : _3.toString(),
|
|
1172
|
-
alertSeverityId: (_5 = (_4 = alert.alertSeverity) === null || _4 === void 0 ? void 0 : _4._id) === null || _5 === void 0 ? void 0 : _5.toString(),
|
|
1173
|
-
alertSeverityName: (_7 = (_6 = alert.alertSeverity) === null || _6 === void 0 ? void 0 : _6.name) === null || _7 === void 0 ? void 0 : _7.toString(),
|
|
1174
|
-
};
|
|
1175
|
-
alertDurationMetric.attributeKeys = TelemetryUtil.getAttributeKeys(alertDurationMetric.attributes);
|
|
1176
|
-
alertDurationMetric.time =
|
|
1177
|
-
(lastAlertStateTimeline === null || lastAlertStateTimeline === void 0 ? void 0 : lastAlertStateTimeline.startsAt) ||
|
|
1178
|
-
alert.createdAt ||
|
|
1179
|
-
OneUptimeDate.getCurrentDate();
|
|
1180
|
-
alertDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(alertDurationMetric.time);
|
|
1181
|
-
alertDurationMetric.metricPointType = MetricPointType.Sum;
|
|
1182
|
-
alertDurationMetric.retentionDate = alertMetricRetentionDate;
|
|
1183
|
-
itemsToSave.push(alertDurationMetric);
|
|
1184
|
-
const metricType = new MetricType();
|
|
1185
|
-
metricType.name = alertDurationMetric.name;
|
|
1186
|
-
metricType.description = "Duration of the alert";
|
|
1187
|
-
metricType.unit = "seconds";
|
|
1188
|
-
metricTypesMap[alertDurationMetric.name] = metricType;
|
|
1287
|
+
finally {
|
|
1288
|
+
if (metricRefreshMutex) {
|
|
1289
|
+
try {
|
|
1290
|
+
await Semaphore.release(metricRefreshMutex);
|
|
1291
|
+
}
|
|
1292
|
+
catch (err) {
|
|
1293
|
+
logger.error(err, {
|
|
1294
|
+
projectId: (_10 = alert.projectId) === null || _10 === void 0 ? void 0 : _10.toString(),
|
|
1295
|
+
alertId: (_11 = alert.id) === null || _11 === void 0 ? void 0 : _11.toString(),
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1189
1299
|
}
|
|
1190
|
-
await MetricService.createMany({
|
|
1191
|
-
items: itemsToSave,
|
|
1192
|
-
props: {
|
|
1193
|
-
isRoot: true,
|
|
1194
|
-
},
|
|
1195
|
-
});
|
|
1196
|
-
TelemetryUtil.indexMetricNameServiceNameMap({
|
|
1197
|
-
metricNameServiceNameMap: metricTypesMap,
|
|
1198
|
-
projectId: alert.projectId,
|
|
1199
|
-
}).catch((err) => {
|
|
1200
|
-
var _a, _b;
|
|
1201
|
-
logger.error(err, {
|
|
1202
|
-
projectId: (_a = alert.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
1203
|
-
alertId: (_b = alert.id) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
1204
|
-
});
|
|
1205
|
-
});
|
|
1206
1300
|
}
|
|
1207
1301
|
async isAlertResolved(data) {
|
|
1208
1302
|
const alert = await this.findOneBy({
|