@oneuptime/common 11.3.9 → 11.3.11
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/AnalyticsModels/MutableMetric.ts +333 -0
- package/Server/Services/AlertService.ts +167 -206
- package/Server/Services/AlertStateTimelineService.ts +121 -1
- package/Server/Services/AnalyticsDatabaseService.ts +19 -23
- package/Server/Services/IncidentService.ts +179 -252
- package/Server/Services/IncidentStateTimelineService.ts +120 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MetricAggregationService.ts +73 -3
- package/Server/Services/MetricService.ts +228 -0
- package/Server/Services/MutableMetricService.ts +275 -0
- package/Server/Services/TelemetryAttributeService.ts +183 -1
- package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +2 -0
- package/Server/Utils/AnalyticsDatabase/Statement.ts +2 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +145 -0
- package/Tests/Types/Monitor/MonitorStepEntityScope.test.ts +0 -56
- package/Types/AnalyticsDatabase/AnalyticsTableEngine.ts +1 -0
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +1 -0
- package/Types/Dashboard/DashboardTemplates.ts +267 -7
- package/Types/Monitor/MonitorStepMetricMonitor.ts +0 -27
- package/build/dist/Models/AnalyticsModels/MutableMetric.js +275 -0
- package/build/dist/Models/AnalyticsModels/MutableMetric.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +133 -170
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +97 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +16 -15
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +142 -207
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +97 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +49 -3
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +155 -0
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/MutableMetricService.js +182 -0
- package/build/dist/Server/Services/MutableMetricService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js +131 -6
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +2 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +2 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableEngine.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +252 -6
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +0 -13
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/package.json +1 -1
|
@@ -43,11 +43,12 @@ import MonitorStatus from "../../Models/DatabaseModels/MonitorStatus";
|
|
|
43
43
|
import MonitorStatusTimeline from "../../Models/DatabaseModels/MonitorStatusTimeline";
|
|
44
44
|
import User from "../../Models/DatabaseModels/User";
|
|
45
45
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
46
|
-
import
|
|
46
|
+
import MutableMetricService from "./MutableMetricService";
|
|
47
47
|
import GlobalConfigService from "./GlobalConfigService";
|
|
48
48
|
import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
|
|
49
49
|
import IncidentMetricType from "../../Types/Incident/IncidentMetricType";
|
|
50
|
-
import
|
|
50
|
+
import MutableMetric from "../../Models/AnalyticsModels/MutableMetric";
|
|
51
|
+
import { MetricPointType } from "../../Models/AnalyticsModels/Metric";
|
|
51
52
|
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
52
53
|
import OneUptimeDate from "../../Types/Date";
|
|
53
54
|
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
|
@@ -1765,12 +1766,14 @@ ${incident.remediationNotes || "No remediation notes provided."}
|
|
|
1765
1766
|
|
|
1766
1767
|
// only emit if the incident has been resolved
|
|
1767
1768
|
if (resolvedTimeline && resolvedTimeline.startsAt) {
|
|
1768
|
-
const postmortemMetric:
|
|
1769
|
+
const postmortemMetric: MutableMetric = new MutableMetric();
|
|
1769
1770
|
postmortemMetric.projectId = projectId;
|
|
1770
1771
|
postmortemMetric.primaryEntityId = incidentId;
|
|
1771
1772
|
postmortemMetric.primaryEntityType = ServiceType.Incident;
|
|
1772
1773
|
postmortemMetric.name =
|
|
1773
1774
|
IncidentMetricType.PostmortemCompletionTime;
|
|
1775
|
+
postmortemMetric.metricPointId =
|
|
1776
|
+
IncidentMetricType.PostmortemCompletionTime;
|
|
1774
1777
|
postmortemMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
1775
1778
|
postmortemPostedAt,
|
|
1776
1779
|
resolvedTimeline.startsAt,
|
|
@@ -1794,11 +1797,8 @@ ${incident.remediationNotes || "No remediation notes provided."}
|
|
|
1794
1797
|
postmortemRetentionDays,
|
|
1795
1798
|
);
|
|
1796
1799
|
|
|
1797
|
-
await
|
|
1798
|
-
|
|
1799
|
-
props: {
|
|
1800
|
-
isRoot: true,
|
|
1801
|
-
},
|
|
1800
|
+
await MutableMetricService.createMutableMetrics({
|
|
1801
|
+
metrics: [postmortemMetric],
|
|
1802
1802
|
});
|
|
1803
1803
|
|
|
1804
1804
|
const postmortemMetricType: MetricType = new MetricType();
|
|
@@ -2019,11 +2019,12 @@ ${incidentSeverity.name}
|
|
|
2019
2019
|
|
|
2020
2020
|
// emit severity change metric
|
|
2021
2021
|
try {
|
|
2022
|
-
const severityChangeMetric:
|
|
2022
|
+
const severityChangeMetric: MutableMetric = new MutableMetric();
|
|
2023
2023
|
severityChangeMetric.projectId = projectId;
|
|
2024
2024
|
severityChangeMetric.primaryEntityId = incidentId;
|
|
2025
2025
|
severityChangeMetric.primaryEntityType = ServiceType.Incident;
|
|
2026
2026
|
severityChangeMetric.name = IncidentMetricType.SeverityChange;
|
|
2027
|
+
severityChangeMetric.metricPointId = `severity-change:${ObjectID.generate().toString()}`;
|
|
2027
2028
|
severityChangeMetric.value = 1;
|
|
2028
2029
|
severityChangeMetric.attributes = {
|
|
2029
2030
|
incidentId: incidentId.toString(),
|
|
@@ -2046,11 +2047,8 @@ ${incidentSeverity.name}
|
|
|
2046
2047
|
severityRetentionDays,
|
|
2047
2048
|
);
|
|
2048
2049
|
|
|
2049
|
-
await
|
|
2050
|
-
|
|
2051
|
-
props: {
|
|
2052
|
-
isRoot: true,
|
|
2053
|
-
},
|
|
2050
|
+
await MutableMetricService.createMutableMetrics({
|
|
2051
|
+
metrics: [severityChangeMetric],
|
|
2054
2052
|
});
|
|
2055
2053
|
|
|
2056
2054
|
const severityChangeMetricType: MetricType = new MetricType();
|
|
@@ -2492,14 +2490,18 @@ ${incidentSeverity.name}
|
|
|
2492
2490
|
}
|
|
2493
2491
|
|
|
2494
2492
|
if (incident.projectId && incident.id) {
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2493
|
+
const metricRetentionDays: number =
|
|
2494
|
+
await this.getMetricRetentionDays();
|
|
2495
|
+
|
|
2496
|
+
await MutableMetricService.tombstoneEntityMetrics({
|
|
2497
|
+
projectId: incident.projectId,
|
|
2498
|
+
primaryEntityId: incident.id,
|
|
2499
|
+
primaryEntityType: ServiceType.Incident,
|
|
2500
|
+
metricNames: Object.values(IncidentMetricType),
|
|
2501
|
+
retentionDate: OneUptimeDate.addRemoveDays(
|
|
2502
|
+
OneUptimeDate.getCurrentDate(),
|
|
2503
|
+
metricRetentionDays,
|
|
2504
|
+
),
|
|
2503
2505
|
});
|
|
2504
2506
|
}
|
|
2505
2507
|
}
|
|
@@ -2639,6 +2641,7 @@ ${incidentSeverity.name}
|
|
|
2639
2641
|
select: {
|
|
2640
2642
|
projectId: true,
|
|
2641
2643
|
declaredAt: true,
|
|
2644
|
+
postmortemPostedAt: true,
|
|
2642
2645
|
monitors: {
|
|
2643
2646
|
_id: true,
|
|
2644
2647
|
name: true,
|
|
@@ -2740,6 +2743,7 @@ ${incidentSeverity.name}
|
|
|
2740
2743
|
incidentId: data.incidentId,
|
|
2741
2744
|
},
|
|
2742
2745
|
select: {
|
|
2746
|
+
_id: true,
|
|
2743
2747
|
projectId: true,
|
|
2744
2748
|
incidentStateId: true,
|
|
2745
2749
|
incidentState: {
|
|
@@ -2765,18 +2769,10 @@ ${incidentSeverity.name}
|
|
|
2765
2769
|
incidentStateTimelines[0];
|
|
2766
2770
|
|
|
2767
2771
|
/*
|
|
2768
|
-
* Serialize concurrent refreshes for this incident across pods.
|
|
2769
|
-
*
|
|
2770
|
-
*
|
|
2771
|
-
*
|
|
2772
|
-
* close-together transitions (e.g. auto-acknowledge then auto-resolve)
|
|
2773
|
-
* could interleave: both read a snapshot missing a metric and both
|
|
2774
|
-
* insert it. The Metric table is a plain MergeTree with no row collapse,
|
|
2775
|
-
* so those duplicates would be permanent and re-inflate the very Sum/Avg
|
|
2776
|
-
* widgets this change fixes. A best-effort distributed lock keyed on the
|
|
2777
|
-
* incident makes the read+insert atomic; if the lock can't be taken we
|
|
2778
|
-
* still proceed (the existence check alone covers the common sequential
|
|
2779
|
-
* case).
|
|
2772
|
+
* Serialize concurrent refreshes for this incident across pods. Mutable
|
|
2773
|
+
* metrics are versioned inserts, but the replace operation also tombstones
|
|
2774
|
+
* stale metric-point identities. The lock keeps that read+insert cycle
|
|
2775
|
+
* ordered for each incident.
|
|
2780
2776
|
*/
|
|
2781
2777
|
let metricRefreshMutex: SemaphoreMutex | null = null;
|
|
2782
2778
|
try {
|
|
@@ -2796,84 +2792,7 @@ ${incidentSeverity.name}
|
|
|
2796
2792
|
}
|
|
2797
2793
|
|
|
2798
2794
|
try {
|
|
2799
|
-
|
|
2800
|
-
* Write-once metrics — no deletes.
|
|
2801
|
-
*
|
|
2802
|
-
* The raw Metric table (`MetricItemV3`) feeds an AggregatingMergeTree
|
|
2803
|
-
* materialized view (`MetricItemAggMV1m_mv`) that accumulates
|
|
2804
|
-
* `sumState`/`countState` per (projectId, name, primaryEntityId,
|
|
2805
|
-
* minute). The MV trigger fires only on INSERT — an `ALTER ... DELETE`
|
|
2806
|
-
* mutation on the source rolls back nothing in the MV. So the old
|
|
2807
|
-
* "delete every metric for this incident, then re-insert" refresh both
|
|
2808
|
-
* (a) inflated those rollups (the Sum-of-IncidentCount widget read high)
|
|
2809
|
-
* and (b) issued one heavy `ALTER ... DELETE` mutation per state
|
|
2810
|
-
* transition per incident — a mutation storm.
|
|
2811
|
-
*
|
|
2812
|
-
* Every incident metric has a single eventually-final value:
|
|
2813
|
-
* IncidentCount is constant `value = 1`; MTTA/MTTR/duration/time-in-state
|
|
2814
|
-
* are each fixed once the relevant state transition has happened. So we
|
|
2815
|
-
* load what we've already emitted for this incident and insert each
|
|
2816
|
-
* metric exactly once, when it becomes final. Repeat refreshes that find
|
|
2817
|
-
* nothing new are pure no-ops (no insert, no mutation).
|
|
2818
|
-
*/
|
|
2819
|
-
const existingMetrics: Array<Metric> = await MetricService.findBy({
|
|
2820
|
-
query: {
|
|
2821
|
-
projectId: incident.projectId,
|
|
2822
|
-
primaryEntityId: data.incidentId,
|
|
2823
|
-
},
|
|
2824
|
-
select: {
|
|
2825
|
-
name: true,
|
|
2826
|
-
time: true,
|
|
2827
|
-
attributes: true,
|
|
2828
|
-
},
|
|
2829
|
-
skip: 0,
|
|
2830
|
-
limit: LIMIT_PER_PROJECT,
|
|
2831
|
-
props: {
|
|
2832
|
-
isRoot: true,
|
|
2833
|
-
},
|
|
2834
|
-
});
|
|
2835
|
-
|
|
2836
|
-
const existingMetricNames: Set<string> = new Set<string>(
|
|
2837
|
-
existingMetrics
|
|
2838
|
-
.map((metric: Metric) => {
|
|
2839
|
-
return metric.name;
|
|
2840
|
-
})
|
|
2841
|
-
.filter(Boolean) as Array<string>,
|
|
2842
|
-
);
|
|
2843
|
-
|
|
2844
|
-
/*
|
|
2845
|
-
* Identity of a TimeInState row: the state plus the instant that state
|
|
2846
|
-
* began (a state can be entered more than once over a re-opened
|
|
2847
|
-
* incident). Second granularity is robust against DateTime64 round-trip
|
|
2848
|
-
* jitter and two distinct entries into the same state within one second
|
|
2849
|
-
* never happen.
|
|
2850
|
-
*/
|
|
2851
|
-
const buildTimeInStateKey: (
|
|
2852
|
-
stateId: string | undefined,
|
|
2853
|
-
startsAt: Date | undefined,
|
|
2854
|
-
) => string = (
|
|
2855
|
-
stateId: string | undefined,
|
|
2856
|
-
startsAt: Date | undefined,
|
|
2857
|
-
): string => {
|
|
2858
|
-
return `${stateId || ""}|${
|
|
2859
|
-
startsAt ? OneUptimeDate.toUnixTimestamp(startsAt) : ""
|
|
2860
|
-
}`;
|
|
2861
|
-
};
|
|
2862
|
-
|
|
2863
|
-
const existingTimeInStateKeys: Set<string> = new Set<string>(
|
|
2864
|
-
existingMetrics
|
|
2865
|
-
.filter((metric: Metric) => {
|
|
2866
|
-
return metric.name === IncidentMetricType.TimeInState;
|
|
2867
|
-
})
|
|
2868
|
-
.map((metric: Metric) => {
|
|
2869
|
-
return buildTimeInStateKey(
|
|
2870
|
-
metric.attributes?.["incidentStateId"] as string | undefined,
|
|
2871
|
-
metric.time,
|
|
2872
|
-
);
|
|
2873
|
-
}),
|
|
2874
|
-
);
|
|
2875
|
-
|
|
2876
|
-
const itemsToSave: Array<Metric> = [];
|
|
2795
|
+
const itemsToSave: Array<MutableMetric> = [];
|
|
2877
2796
|
|
|
2878
2797
|
const metricRetentionDays: number = await this.getMetricRetentionDays();
|
|
2879
2798
|
const incidentMetricRetentionDate: Date = OneUptimeDate.addRemoveDays(
|
|
@@ -2921,36 +2840,27 @@ ${incidentSeverity.name}
|
|
|
2921
2840
|
ownerTeamNames: ownerTeamNames.join(", "),
|
|
2922
2841
|
};
|
|
2923
2842
|
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
incidentCountMetric.primaryEntityId = incident.id!;
|
|
2937
|
-
incidentCountMetric.primaryEntityType = ServiceType.Incident;
|
|
2938
|
-
incidentCountMetric.name = IncidentMetricType.IncidentCount;
|
|
2939
|
-
incidentCountMetric.value = 1;
|
|
2940
|
-
incidentCountMetric.attributes = { ...baseMetricAttributes };
|
|
2941
|
-
incidentCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
|
2942
|
-
incidentCountMetric.attributes,
|
|
2943
|
-
);
|
|
2843
|
+
const incidentCountMetric: MutableMetric = new MutableMetric();
|
|
2844
|
+
|
|
2845
|
+
incidentCountMetric.projectId = incident.projectId;
|
|
2846
|
+
incidentCountMetric.primaryEntityId = incident.id!;
|
|
2847
|
+
incidentCountMetric.primaryEntityType = ServiceType.Incident;
|
|
2848
|
+
incidentCountMetric.name = IncidentMetricType.IncidentCount;
|
|
2849
|
+
incidentCountMetric.metricPointId = IncidentMetricType.IncidentCount;
|
|
2850
|
+
incidentCountMetric.value = 1;
|
|
2851
|
+
incidentCountMetric.attributes = { ...baseMetricAttributes };
|
|
2852
|
+
incidentCountMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
|
2853
|
+
incidentCountMetric.attributes,
|
|
2854
|
+
);
|
|
2944
2855
|
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2856
|
+
incidentCountMetric.time = incidentStartsAt;
|
|
2857
|
+
incidentCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
2858
|
+
incidentCountMetric.time,
|
|
2859
|
+
);
|
|
2860
|
+
incidentCountMetric.metricPointType = MetricPointType.Sum;
|
|
2861
|
+
incidentCountMetric.retentionDate = incidentMetricRetentionDate;
|
|
2951
2862
|
|
|
2952
|
-
|
|
2953
|
-
}
|
|
2863
|
+
itemsToSave.push(incidentCountMetric);
|
|
2954
2864
|
|
|
2955
2865
|
// Always register the metric type so it shows up in the type catalog.
|
|
2956
2866
|
const metricType: MetricType = new MetricType();
|
|
@@ -2982,39 +2892,35 @@ ${incidentSeverity.name}
|
|
|
2982
2892
|
metricType.unit = "seconds";
|
|
2983
2893
|
metricTypesMap[IncidentMetricType.TimeToAcknowledge] = metricType;
|
|
2984
2894
|
|
|
2985
|
-
|
|
2986
|
-
if (!existingMetricNames.has(IncidentMetricType.TimeToAcknowledge)) {
|
|
2987
|
-
const timeToAcknowledgeMetric: Metric = new Metric();
|
|
2988
|
-
|
|
2989
|
-
timeToAcknowledgeMetric.projectId = incident.projectId;
|
|
2990
|
-
timeToAcknowledgeMetric.primaryEntityId = incident.id!;
|
|
2991
|
-
timeToAcknowledgeMetric.primaryEntityType = ServiceType.Incident;
|
|
2992
|
-
timeToAcknowledgeMetric.name = IncidentMetricType.TimeToAcknowledge;
|
|
2993
|
-
timeToAcknowledgeMetric.value =
|
|
2994
|
-
OneUptimeDate.getDifferenceInSeconds(
|
|
2995
|
-
ackIncidentStateTimeline?.startsAt ||
|
|
2996
|
-
OneUptimeDate.getCurrentDate(),
|
|
2997
|
-
incidentStartsAt,
|
|
2998
|
-
);
|
|
2999
|
-
timeToAcknowledgeMetric.attributes = { ...baseMetricAttributes };
|
|
3000
|
-
timeToAcknowledgeMetric.attributeKeys =
|
|
3001
|
-
TelemetryUtil.getAttributeKeys(
|
|
3002
|
-
timeToAcknowledgeMetric.attributes,
|
|
3003
|
-
);
|
|
2895
|
+
const timeToAcknowledgeMetric: MutableMetric = new MutableMetric();
|
|
3004
2896
|
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
2897
|
+
timeToAcknowledgeMetric.projectId = incident.projectId;
|
|
2898
|
+
timeToAcknowledgeMetric.primaryEntityId = incident.id!;
|
|
2899
|
+
timeToAcknowledgeMetric.primaryEntityType = ServiceType.Incident;
|
|
2900
|
+
timeToAcknowledgeMetric.name = IncidentMetricType.TimeToAcknowledge;
|
|
2901
|
+
timeToAcknowledgeMetric.metricPointId =
|
|
2902
|
+
IncidentMetricType.TimeToAcknowledge;
|
|
2903
|
+
timeToAcknowledgeMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
2904
|
+
ackIncidentStateTimeline?.startsAt ||
|
|
2905
|
+
OneUptimeDate.getCurrentDate(),
|
|
2906
|
+
incidentStartsAt,
|
|
2907
|
+
);
|
|
2908
|
+
timeToAcknowledgeMetric.attributes = { ...baseMetricAttributes };
|
|
2909
|
+
timeToAcknowledgeMetric.attributeKeys =
|
|
2910
|
+
TelemetryUtil.getAttributeKeys(timeToAcknowledgeMetric.attributes);
|
|
3015
2911
|
|
|
3016
|
-
|
|
3017
|
-
|
|
2912
|
+
timeToAcknowledgeMetric.time =
|
|
2913
|
+
ackIncidentStateTimeline?.startsAt ||
|
|
2914
|
+
incident.declaredAt ||
|
|
2915
|
+
incident.createdAt ||
|
|
2916
|
+
OneUptimeDate.getCurrentDate();
|
|
2917
|
+
timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
2918
|
+
timeToAcknowledgeMetric.time,
|
|
2919
|
+
);
|
|
2920
|
+
timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
|
|
2921
|
+
timeToAcknowledgeMetric.retentionDate = incidentMetricRetentionDate;
|
|
2922
|
+
|
|
2923
|
+
itemsToSave.push(timeToAcknowledgeMetric);
|
|
3018
2924
|
}
|
|
3019
2925
|
}
|
|
3020
2926
|
|
|
@@ -3038,50 +2944,37 @@ ${incidentSeverity.name}
|
|
|
3038
2944
|
metricType.unit = "seconds";
|
|
3039
2945
|
metricTypesMap[IncidentMetricType.TimeToResolve] = metricType;
|
|
3040
2946
|
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
timeToResolveMetric.
|
|
3056
|
-
|
|
3057
|
-
);
|
|
2947
|
+
const timeToResolveMetric: MutableMetric = new MutableMetric();
|
|
2948
|
+
|
|
2949
|
+
timeToResolveMetric.projectId = incident.projectId;
|
|
2950
|
+
timeToResolveMetric.primaryEntityId = incident.id!;
|
|
2951
|
+
timeToResolveMetric.primaryEntityType = ServiceType.Incident;
|
|
2952
|
+
timeToResolveMetric.name = IncidentMetricType.TimeToResolve;
|
|
2953
|
+
timeToResolveMetric.metricPointId = IncidentMetricType.TimeToResolve;
|
|
2954
|
+
timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
2955
|
+
resolvedIncidentStateTimeline?.startsAt ||
|
|
2956
|
+
OneUptimeDate.getCurrentDate(),
|
|
2957
|
+
incidentStartsAt,
|
|
2958
|
+
);
|
|
2959
|
+
timeToResolveMetric.attributes = { ...baseMetricAttributes };
|
|
2960
|
+
timeToResolveMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
|
2961
|
+
timeToResolveMetric.attributes,
|
|
2962
|
+
);
|
|
3058
2963
|
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
2964
|
+
timeToResolveMetric.time =
|
|
2965
|
+
resolvedIncidentStateTimeline?.startsAt ||
|
|
2966
|
+
incident.declaredAt ||
|
|
2967
|
+
incident.createdAt ||
|
|
2968
|
+
OneUptimeDate.getCurrentDate();
|
|
2969
|
+
timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
2970
|
+
timeToResolveMetric.time,
|
|
2971
|
+
);
|
|
2972
|
+
timeToResolveMetric.metricPointType = MetricPointType.Sum;
|
|
2973
|
+
timeToResolveMetric.retentionDate = incidentMetricRetentionDate;
|
|
3069
2974
|
|
|
3070
|
-
|
|
3071
|
-
}
|
|
2975
|
+
itemsToSave.push(timeToResolveMetric);
|
|
3072
2976
|
}
|
|
3073
2977
|
|
|
3074
|
-
/*
|
|
3075
|
-
* Incident duration — write-once, finalized at resolution.
|
|
3076
|
-
*
|
|
3077
|
-
* The previous implementation recomputed duration as
|
|
3078
|
-
* (latest state transition − start) on every refresh, so an open
|
|
3079
|
-
* incident's duration grew and had to be deleted + re-inserted each
|
|
3080
|
-
* time. With no deletes we emit a single final duration once the
|
|
3081
|
-
* incident reaches a resolved state: (first resolution − start). An
|
|
3082
|
-
* incident that is never resolved has no duration row (its lifetime is
|
|
3083
|
-
* not yet final), which keeps the "Avg Duration" widget meaningful.
|
|
3084
|
-
*/
|
|
3085
2978
|
if (isIncidentResolved && resolvedIncidentStateTimeline) {
|
|
3086
2979
|
// register the metric type so the catalog stays complete across refreshes.
|
|
3087
2980
|
const metricType: MetricType = new MetricType();
|
|
@@ -3090,34 +2983,70 @@ ${incidentSeverity.name}
|
|
|
3090
2983
|
metricType.unit = "seconds";
|
|
3091
2984
|
metricTypesMap[IncidentMetricType.IncidentDuration] = metricType;
|
|
3092
2985
|
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
2986
|
+
const incidentEndsAt: Date =
|
|
2987
|
+
resolvedIncidentStateTimeline.startsAt ||
|
|
2988
|
+
OneUptimeDate.getCurrentDate();
|
|
2989
|
+
|
|
2990
|
+
const incidentDurationMetric: MutableMetric = new MutableMetric();
|
|
2991
|
+
|
|
2992
|
+
incidentDurationMetric.projectId = incident.projectId;
|
|
2993
|
+
incidentDurationMetric.primaryEntityId = incident.id!;
|
|
2994
|
+
incidentDurationMetric.primaryEntityType = ServiceType.Incident;
|
|
2995
|
+
incidentDurationMetric.name = IncidentMetricType.IncidentDuration;
|
|
2996
|
+
incidentDurationMetric.metricPointId =
|
|
2997
|
+
IncidentMetricType.IncidentDuration;
|
|
2998
|
+
incidentDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
2999
|
+
incidentEndsAt,
|
|
3000
|
+
incidentStartsAt,
|
|
3001
|
+
);
|
|
3002
|
+
incidentDurationMetric.attributes = { ...baseMetricAttributes };
|
|
3003
|
+
incidentDurationMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
|
3004
|
+
incidentDurationMetric.attributes,
|
|
3005
|
+
);
|
|
3099
3006
|
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
incidentDurationMetric.
|
|
3103
|
-
|
|
3104
|
-
|
|
3007
|
+
incidentDurationMetric.time = incidentEndsAt;
|
|
3008
|
+
incidentDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
3009
|
+
incidentDurationMetric.time,
|
|
3010
|
+
);
|
|
3011
|
+
incidentDurationMetric.metricPointType = MetricPointType.Sum;
|
|
3012
|
+
incidentDurationMetric.retentionDate = incidentMetricRetentionDate;
|
|
3013
|
+
|
|
3014
|
+
itemsToSave.push(incidentDurationMetric);
|
|
3015
|
+
|
|
3016
|
+
if (incident.postmortemPostedAt) {
|
|
3017
|
+
const postmortemMetricType: MetricType = new MetricType();
|
|
3018
|
+
postmortemMetricType.name =
|
|
3019
|
+
IncidentMetricType.PostmortemCompletionTime;
|
|
3020
|
+
postmortemMetricType.description =
|
|
3021
|
+
"Time from incident resolution to postmortem publication";
|
|
3022
|
+
postmortemMetricType.unit = "seconds";
|
|
3023
|
+
metricTypesMap[IncidentMetricType.PostmortemCompletionTime] =
|
|
3024
|
+
postmortemMetricType;
|
|
3025
|
+
|
|
3026
|
+
const postmortemMetric: MutableMetric = new MutableMetric();
|
|
3027
|
+
|
|
3028
|
+
postmortemMetric.projectId = incident.projectId;
|
|
3029
|
+
postmortemMetric.primaryEntityId = incident.id!;
|
|
3030
|
+
postmortemMetric.primaryEntityType = ServiceType.Incident;
|
|
3031
|
+
postmortemMetric.name = IncidentMetricType.PostmortemCompletionTime;
|
|
3032
|
+
postmortemMetric.metricPointId =
|
|
3033
|
+
IncidentMetricType.PostmortemCompletionTime;
|
|
3034
|
+
postmortemMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
3035
|
+
incident.postmortemPostedAt,
|
|
3105
3036
|
incidentEndsAt,
|
|
3106
|
-
incidentStartsAt,
|
|
3107
3037
|
);
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3038
|
+
postmortemMetric.attributes = { ...baseMetricAttributes };
|
|
3039
|
+
postmortemMetric.attributeKeys = TelemetryUtil.getAttributeKeys(
|
|
3040
|
+
postmortemMetric.attributes,
|
|
3111
3041
|
);
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
incidentDurationMetric.time,
|
|
3042
|
+
postmortemMetric.time = incident.postmortemPostedAt;
|
|
3043
|
+
postmortemMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
3044
|
+
postmortemMetric.time,
|
|
3116
3045
|
);
|
|
3117
|
-
|
|
3118
|
-
|
|
3046
|
+
postmortemMetric.metricPointType = MetricPointType.Sum;
|
|
3047
|
+
postmortemMetric.retentionDate = incidentMetricRetentionDate;
|
|
3119
3048
|
|
|
3120
|
-
itemsToSave.push(
|
|
3049
|
+
itemsToSave.push(postmortemMetric);
|
|
3121
3050
|
}
|
|
3122
3051
|
}
|
|
3123
3052
|
|
|
@@ -3127,24 +3056,16 @@ ${incidentSeverity.name}
|
|
|
3127
3056
|
continue;
|
|
3128
3057
|
}
|
|
3129
3058
|
|
|
3130
|
-
// write-once: each completed state transition is emitted exactly once.
|
|
3131
|
-
const timeInStateKey: string = buildTimeInStateKey(
|
|
3132
|
-
timeline.incidentStateId?.toString(),
|
|
3133
|
-
timeline.startsAt,
|
|
3134
|
-
);
|
|
3135
|
-
if (existingTimeInStateKeys.has(timeInStateKey)) {
|
|
3136
|
-
continue;
|
|
3137
|
-
}
|
|
3138
|
-
|
|
3139
3059
|
const stateName: string =
|
|
3140
3060
|
timeline.incidentState?.name?.toString() || "Unknown";
|
|
3141
3061
|
|
|
3142
|
-
const timeInStateMetric:
|
|
3062
|
+
const timeInStateMetric: MutableMetric = new MutableMetric();
|
|
3143
3063
|
|
|
3144
3064
|
timeInStateMetric.projectId = incident.projectId;
|
|
3145
3065
|
timeInStateMetric.primaryEntityId = incident.id!;
|
|
3146
3066
|
timeInStateMetric.primaryEntityType = ServiceType.Incident;
|
|
3147
3067
|
timeInStateMetric.name = IncidentMetricType.TimeInState;
|
|
3068
|
+
timeInStateMetric.metricPointId = `time-in-state:${timeline.id!.toString()}`;
|
|
3148
3069
|
timeInStateMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
3149
3070
|
timeline.endsAt,
|
|
3150
3071
|
timeline.startsAt,
|
|
@@ -3188,15 +3109,21 @@ ${incidentSeverity.name}
|
|
|
3188
3109
|
metricTypesMap[timeInStateMetricType.name] = timeInStateMetricType;
|
|
3189
3110
|
}
|
|
3190
3111
|
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3112
|
+
await MutableMetricService.replaceEntityMetrics({
|
|
3113
|
+
projectId: incident.projectId,
|
|
3114
|
+
primaryEntityId: incident.id!,
|
|
3115
|
+
primaryEntityType: ServiceType.Incident,
|
|
3116
|
+
metricNames: [
|
|
3117
|
+
IncidentMetricType.IncidentCount,
|
|
3118
|
+
IncidentMetricType.TimeToAcknowledge,
|
|
3119
|
+
IncidentMetricType.TimeToResolve,
|
|
3120
|
+
IncidentMetricType.IncidentDuration,
|
|
3121
|
+
IncidentMetricType.TimeInState,
|
|
3122
|
+
IncidentMetricType.PostmortemCompletionTime,
|
|
3123
|
+
],
|
|
3124
|
+
metrics: itemsToSave,
|
|
3125
|
+
retentionDate: incidentMetricRetentionDate,
|
|
3126
|
+
});
|
|
3200
3127
|
|
|
3201
3128
|
TelemetryUtil.indexMetricNameServiceNameMap({
|
|
3202
3129
|
metricNameServiceNameMap: metricTypesMap,
|