@oneuptime/common 7.0.3336 → 7.0.3341
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/Metric.ts +2 -0
- package/Server/Services/AlertService.ts +270 -0
- package/Server/Services/AlertStateTimelineService.ts +10 -0
- package/Server/Services/IncidentService.ts +298 -0
- package/Server/Services/IncidentStateTimelineService.ts +8 -0
- package/Types/Alerts/AlertMetricType.ts +8 -0
- package/Types/Date.ts +12 -0
- package/Types/Incident/IncidentMetricType.ts +8 -0
- package/build/dist/Models/AnalyticsModels/Metric.js +2 -0
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +208 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +7 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +233 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +7 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Types/Alerts/AlertMetricType.js +9 -0
- package/build/dist/Types/Alerts/AlertMetricType.js.map +1 -0
- package/build/dist/Types/Date.js +9 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Incident/IncidentMetricType.js +9 -0
- package/build/dist/Types/Incident/IncidentMetricType.js.map +1 -0
- package/package.json +2 -2
|
@@ -28,6 +28,16 @@ import AlertState from "Common/Models/DatabaseModels/AlertState";
|
|
|
28
28
|
import AlertStateTimeline from "Common/Models/DatabaseModels/AlertStateTimeline";
|
|
29
29
|
import User from "Common/Models/DatabaseModels/User";
|
|
30
30
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
31
|
+
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
|
32
|
+
import logger from "../Utils/Logger";
|
|
33
|
+
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
|
34
|
+
import MetricService from "./MetricService";
|
|
35
|
+
import OneUptimeDate from "../../Types/Date";
|
|
36
|
+
import Metric, {
|
|
37
|
+
MetricPointType,
|
|
38
|
+
ServiceType,
|
|
39
|
+
} from "../../Models/AnalyticsModels/Metric";
|
|
40
|
+
import AlertMetricType from "../../Types/Alerts/AlertMetricType";
|
|
31
41
|
|
|
32
42
|
export class Service extends DatabaseService<Model> {
|
|
33
43
|
public constructor() {
|
|
@@ -564,5 +574,265 @@ export class Service extends DatabaseService<Model> {
|
|
|
564
574
|
props: props || {},
|
|
565
575
|
});
|
|
566
576
|
}
|
|
577
|
+
|
|
578
|
+
public async refreshAlertMetrics(data: { alertId: ObjectID }): Promise<void> {
|
|
579
|
+
const alert: Model | null = await this.findOneById({
|
|
580
|
+
id: data.alertId,
|
|
581
|
+
select: {
|
|
582
|
+
projectId: true,
|
|
583
|
+
monitor: {
|
|
584
|
+
_id: true,
|
|
585
|
+
name: true,
|
|
586
|
+
},
|
|
587
|
+
alertSeverity: {
|
|
588
|
+
name: true,
|
|
589
|
+
_id: true,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
props: {
|
|
593
|
+
isRoot: true,
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
if (!alert) {
|
|
598
|
+
throw new BadDataException("Alert not found");
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (!alert.projectId) {
|
|
602
|
+
throw new BadDataException("Incient Project ID not found");
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// get alert state timeline
|
|
606
|
+
|
|
607
|
+
const alertStateTimelines: Array<AlertStateTimeline> =
|
|
608
|
+
await AlertStateTimelineService.findBy({
|
|
609
|
+
query: {
|
|
610
|
+
alertId: data.alertId,
|
|
611
|
+
},
|
|
612
|
+
select: {
|
|
613
|
+
projectId: true,
|
|
614
|
+
alertStateId: true,
|
|
615
|
+
alertState: {
|
|
616
|
+
isAcknowledgedState: true,
|
|
617
|
+
isResolvedState: true,
|
|
618
|
+
},
|
|
619
|
+
startsAt: true,
|
|
620
|
+
endsAt: true,
|
|
621
|
+
},
|
|
622
|
+
sort: {
|
|
623
|
+
startsAt: SortOrder.Ascending,
|
|
624
|
+
},
|
|
625
|
+
skip: 0,
|
|
626
|
+
limit: LIMIT_PER_PROJECT,
|
|
627
|
+
props: {
|
|
628
|
+
isRoot: true,
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
const firstAlertStateTimeline: AlertStateTimeline | undefined =
|
|
633
|
+
alertStateTimelines[0];
|
|
634
|
+
|
|
635
|
+
// delete all the alert metrics with this alert id because its a refresh.
|
|
636
|
+
|
|
637
|
+
await MetricService.deleteBy({
|
|
638
|
+
query: {
|
|
639
|
+
serviceId: data.alertId,
|
|
640
|
+
},
|
|
641
|
+
props: {
|
|
642
|
+
isRoot: true,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const itemsToSave: Array<Metric> = [];
|
|
647
|
+
|
|
648
|
+
// now we need to create new metrics for this alert - TimeToAcknowledge, TimeToResolve, AlertCount, AlertDuration
|
|
649
|
+
|
|
650
|
+
const alertStartsAt: Date =
|
|
651
|
+
firstAlertStateTimeline?.startsAt ||
|
|
652
|
+
alert.createdAt ||
|
|
653
|
+
OneUptimeDate.getCurrentDate();
|
|
654
|
+
|
|
655
|
+
const alertCountMetric: Metric = new Metric();
|
|
656
|
+
|
|
657
|
+
alertCountMetric.projectId = alert.projectId;
|
|
658
|
+
alertCountMetric.serviceId = alert.id!;
|
|
659
|
+
alertCountMetric.serviceType = ServiceType.Alert;
|
|
660
|
+
alertCountMetric.name = AlertMetricType.AlertCount;
|
|
661
|
+
alertCountMetric.description = "Number of alerts created";
|
|
662
|
+
alertCountMetric.value = 1;
|
|
663
|
+
alertCountMetric.unit = "";
|
|
664
|
+
alertCountMetric.attributes = {
|
|
665
|
+
alertId: data.alertId.toString(),
|
|
666
|
+
projectId: alert.projectId.toString(),
|
|
667
|
+
monitorId: alert.monitor?.id!.toString() || "",
|
|
668
|
+
monitorName: alert.monitor?.name!.toString() || "",
|
|
669
|
+
alertSeverityId: alert.alertSeverity?.id!.toString() || "",
|
|
670
|
+
alertSeverityName: alert.alertSeverity?.name!.toString() || "",
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
alertCountMetric.time = alertStartsAt;
|
|
674
|
+
alertCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
675
|
+
alertCountMetric.time,
|
|
676
|
+
);
|
|
677
|
+
alertCountMetric.metricPointType = MetricPointType.Sum;
|
|
678
|
+
|
|
679
|
+
itemsToSave.push(alertCountMetric);
|
|
680
|
+
|
|
681
|
+
// is the alert acknowledged?
|
|
682
|
+
const isAlertAcknowledged: boolean = alertStateTimelines.some(
|
|
683
|
+
(timeline: AlertStateTimeline) => {
|
|
684
|
+
return timeline.alertState?.isAcknowledgedState;
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
if (isAlertAcknowledged) {
|
|
689
|
+
const ackAlertStateTimeline: AlertStateTimeline | undefined =
|
|
690
|
+
alertStateTimelines.find((timeline: AlertStateTimeline) => {
|
|
691
|
+
return timeline.alertState?.isAcknowledgedState;
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
if (ackAlertStateTimeline) {
|
|
695
|
+
const timeToAcknowledgeMetric: Metric = new Metric();
|
|
696
|
+
|
|
697
|
+
timeToAcknowledgeMetric.projectId = alert.projectId;
|
|
698
|
+
timeToAcknowledgeMetric.serviceId = alert.id!;
|
|
699
|
+
timeToAcknowledgeMetric.serviceType = ServiceType.Alert;
|
|
700
|
+
timeToAcknowledgeMetric.name = AlertMetricType.TimeToAcknowledge;
|
|
701
|
+
timeToAcknowledgeMetric.description =
|
|
702
|
+
"Time taken to acknowledge the alert";
|
|
703
|
+
timeToAcknowledgeMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
704
|
+
ackAlertStateTimeline?.startsAt || OneUptimeDate.getCurrentDate(),
|
|
705
|
+
alertStartsAt,
|
|
706
|
+
);
|
|
707
|
+
timeToAcknowledgeMetric.unit = "seconds";
|
|
708
|
+
timeToAcknowledgeMetric.attributes = {
|
|
709
|
+
alertId: data.alertId.toString(),
|
|
710
|
+
projectId: alert.projectId.toString(),
|
|
711
|
+
monitorId: alert.monitor?.id!.toString() || "",
|
|
712
|
+
monitorName: alert.monitor?.name!.toString() || "",
|
|
713
|
+
alertSeverityId: alert.alertSeverity?.id!.toString() || "",
|
|
714
|
+
alertSeverityName: alert.alertSeverity?.name!.toString() || "",
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
timeToAcknowledgeMetric.time =
|
|
718
|
+
ackAlertStateTimeline?.startsAt ||
|
|
719
|
+
alert.createdAt ||
|
|
720
|
+
OneUptimeDate.getCurrentDate();
|
|
721
|
+
timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
722
|
+
timeToAcknowledgeMetric.time,
|
|
723
|
+
);
|
|
724
|
+
timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
|
|
725
|
+
|
|
726
|
+
itemsToSave.push(timeToAcknowledgeMetric);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// time to resolve
|
|
731
|
+
const isAlertResolved: boolean = alertStateTimelines.some(
|
|
732
|
+
(timeline: AlertStateTimeline) => {
|
|
733
|
+
return timeline.alertState?.isResolvedState;
|
|
734
|
+
},
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
if (isAlertResolved) {
|
|
738
|
+
const resolvedAlertStateTimeline: AlertStateTimeline | undefined =
|
|
739
|
+
alertStateTimelines.find((timeline: AlertStateTimeline) => {
|
|
740
|
+
return timeline.alertState?.isResolvedState;
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
if (resolvedAlertStateTimeline) {
|
|
744
|
+
const timeToResolveMetric: Metric = new Metric();
|
|
745
|
+
|
|
746
|
+
timeToResolveMetric.projectId = alert.projectId;
|
|
747
|
+
timeToResolveMetric.serviceId = alert.id!;
|
|
748
|
+
timeToResolveMetric.serviceType = ServiceType.Alert;
|
|
749
|
+
timeToResolveMetric.name = AlertMetricType.TimeToResolve;
|
|
750
|
+
timeToResolveMetric.description = "Time taken to resolve the alert";
|
|
751
|
+
timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
752
|
+
resolvedAlertStateTimeline?.startsAt ||
|
|
753
|
+
OneUptimeDate.getCurrentDate(),
|
|
754
|
+
alertStartsAt,
|
|
755
|
+
);
|
|
756
|
+
timeToResolveMetric.unit = "seconds";
|
|
757
|
+
timeToResolveMetric.attributes = {
|
|
758
|
+
alertId: data.alertId.toString(),
|
|
759
|
+
projectId: alert.projectId.toString(),
|
|
760
|
+
monitorId: alert.monitor?.id!.toString() || "",
|
|
761
|
+
monitorName: alert.monitor?.name!.toString() || "",
|
|
762
|
+
alertSeverityId: alert.alertSeverity?.id!.toString() || "",
|
|
763
|
+
alertSeverityName: alert.alertSeverity?.name!.toString() || "",
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
timeToResolveMetric.time =
|
|
767
|
+
resolvedAlertStateTimeline?.startsAt ||
|
|
768
|
+
alert.createdAt ||
|
|
769
|
+
OneUptimeDate.getCurrentDate();
|
|
770
|
+
timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
771
|
+
timeToResolveMetric.time,
|
|
772
|
+
);
|
|
773
|
+
timeToResolveMetric.metricPointType = MetricPointType.Sum;
|
|
774
|
+
|
|
775
|
+
itemsToSave.push(timeToResolveMetric);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// alert duration
|
|
780
|
+
|
|
781
|
+
const alertDurationMetric: Metric = new Metric();
|
|
782
|
+
|
|
783
|
+
const lastAlertStateTimeline: AlertStateTimeline | undefined =
|
|
784
|
+
alertStateTimelines[alertStateTimelines.length - 1];
|
|
785
|
+
|
|
786
|
+
if (lastAlertStateTimeline) {
|
|
787
|
+
const alertEndsAt: Date =
|
|
788
|
+
lastAlertStateTimeline.startsAt || OneUptimeDate.getCurrentDate();
|
|
789
|
+
|
|
790
|
+
// save metric.
|
|
791
|
+
|
|
792
|
+
alertDurationMetric.projectId = alert.projectId;
|
|
793
|
+
alertDurationMetric.serviceId = alert.id!;
|
|
794
|
+
alertDurationMetric.serviceType = ServiceType.Alert;
|
|
795
|
+
alertDurationMetric.name = AlertMetricType.AlertDuration;
|
|
796
|
+
alertDurationMetric.description = "Duration of the alert";
|
|
797
|
+
alertDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
798
|
+
alertEndsAt,
|
|
799
|
+
alertStartsAt,
|
|
800
|
+
);
|
|
801
|
+
alertDurationMetric.unit = "seconds";
|
|
802
|
+
alertDurationMetric.attributes = {
|
|
803
|
+
alertId: data.alertId.toString(),
|
|
804
|
+
projectId: alert.projectId.toString(),
|
|
805
|
+
monitorId: alert.monitor?.id!.toString() || "",
|
|
806
|
+
monitorName: alert.monitor?.name!.toString() || "",
|
|
807
|
+
alertSeverityId: alert.alertSeverity?.id!.toString() || "",
|
|
808
|
+
alertSeverityName: alert.alertSeverity?.name!.toString() || "",
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
alertDurationMetric.time =
|
|
812
|
+
lastAlertStateTimeline?.startsAt ||
|
|
813
|
+
alert.createdAt ||
|
|
814
|
+
OneUptimeDate.getCurrentDate();
|
|
815
|
+
alertDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
816
|
+
alertDurationMetric.time,
|
|
817
|
+
);
|
|
818
|
+
alertDurationMetric.metricPointType = MetricPointType.Sum;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
await MetricService.createMany({
|
|
822
|
+
items: itemsToSave,
|
|
823
|
+
props: {
|
|
824
|
+
isRoot: true,
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
// index attributes.
|
|
829
|
+
TelemetryUtil.indexAttributes({
|
|
830
|
+
attributes: ["monitorId", "projectId", "alertId", "monitorNames"],
|
|
831
|
+
projectId: alert.projectId,
|
|
832
|
+
telemetryType: TelemetryType.Metric,
|
|
833
|
+
}).catch((err: Error) => {
|
|
834
|
+
logger.error(err);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
567
837
|
}
|
|
568
838
|
export default new Service();
|
|
@@ -18,6 +18,7 @@ import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
|
18
18
|
import { JSONObject } from "../../Types/JSON";
|
|
19
19
|
import AlertInternalNote from "../../Models/DatabaseModels/AlertInternalNote";
|
|
20
20
|
import AlertInternalNoteService from "./AlertInternalNoteService";
|
|
21
|
+
import logger from "../Utils/Logger";
|
|
21
22
|
|
|
22
23
|
export class Service extends DatabaseService<AlertStateTimeline> {
|
|
23
24
|
public constructor() {
|
|
@@ -173,6 +174,15 @@ export class Service extends DatabaseService<AlertStateTimeline> {
|
|
|
173
174
|
props: onCreate.createBy.props,
|
|
174
175
|
});
|
|
175
176
|
|
|
177
|
+
AlertService.refreshAlertMetrics({
|
|
178
|
+
alertId: createdItem.alertId,
|
|
179
|
+
}).catch((error: Error) => {
|
|
180
|
+
logger.error(
|
|
181
|
+
"Error while refreshing alert metrics after alert state timeline creation",
|
|
182
|
+
);
|
|
183
|
+
logger.error(error);
|
|
184
|
+
});
|
|
185
|
+
|
|
176
186
|
return createdItem;
|
|
177
187
|
}
|
|
178
188
|
|
|
@@ -34,6 +34,16 @@ import MonitorStatus from "Common/Models/DatabaseModels/MonitorStatus";
|
|
|
34
34
|
import MonitorStatusTimeline from "Common/Models/DatabaseModels/MonitorStatusTimeline";
|
|
35
35
|
import User from "Common/Models/DatabaseModels/User";
|
|
36
36
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
37
|
+
import MetricService from "./MetricService";
|
|
38
|
+
import IncidentMetricType from "../../Types/Incident/IncidentMetricType";
|
|
39
|
+
import Metric, {
|
|
40
|
+
MetricPointType,
|
|
41
|
+
ServiceType,
|
|
42
|
+
} from "../../Models/AnalyticsModels/Metric";
|
|
43
|
+
import OneUptimeDate from "../../Types/Date";
|
|
44
|
+
import TelemetryUtil from "../Utils/Telemetry/Telemetry";
|
|
45
|
+
import TelemetryType from "../../Types/Telemetry/TelemetryType";
|
|
46
|
+
import logger from "../Utils/Logger";
|
|
37
47
|
|
|
38
48
|
export class Service extends DatabaseService<Model> {
|
|
39
49
|
public constructor() {
|
|
@@ -139,6 +149,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
139
149
|
isRoot: true,
|
|
140
150
|
},
|
|
141
151
|
});
|
|
152
|
+
|
|
153
|
+
// store incident metric
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
protected override async onBeforeCreate(
|
|
@@ -737,5 +749,291 @@ export class Service extends DatabaseService<Model> {
|
|
|
737
749
|
props: props || {},
|
|
738
750
|
});
|
|
739
751
|
}
|
|
752
|
+
|
|
753
|
+
public async refreshIncidentMetrics(data: {
|
|
754
|
+
incidentId: ObjectID;
|
|
755
|
+
}): Promise<void> {
|
|
756
|
+
const incident: Model | null = await this.findOneById({
|
|
757
|
+
id: data.incidentId,
|
|
758
|
+
select: {
|
|
759
|
+
projectId: true,
|
|
760
|
+
monitors: {
|
|
761
|
+
_id: true,
|
|
762
|
+
name: true,
|
|
763
|
+
},
|
|
764
|
+
incidentSeverity: {
|
|
765
|
+
_id: true,
|
|
766
|
+
name: true,
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
props: {
|
|
770
|
+
isRoot: true,
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
if (!incident) {
|
|
775
|
+
throw new BadDataException("Incident not found");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (!incident.projectId) {
|
|
779
|
+
throw new BadDataException("Incient Project ID not found");
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// get incident state timeline
|
|
783
|
+
|
|
784
|
+
const incidentStateTimelines: Array<IncidentStateTimeline> =
|
|
785
|
+
await IncidentStateTimelineService.findBy({
|
|
786
|
+
query: {
|
|
787
|
+
incidentId: data.incidentId,
|
|
788
|
+
},
|
|
789
|
+
select: {
|
|
790
|
+
projectId: true,
|
|
791
|
+
incidentStateId: true,
|
|
792
|
+
incidentState: {
|
|
793
|
+
isAcknowledgedState: true,
|
|
794
|
+
isResolvedState: true,
|
|
795
|
+
},
|
|
796
|
+
startsAt: true,
|
|
797
|
+
endsAt: true,
|
|
798
|
+
},
|
|
799
|
+
sort: {
|
|
800
|
+
startsAt: SortOrder.Ascending,
|
|
801
|
+
},
|
|
802
|
+
skip: 0,
|
|
803
|
+
limit: LIMIT_PER_PROJECT,
|
|
804
|
+
props: {
|
|
805
|
+
isRoot: true,
|
|
806
|
+
},
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const firstIncidentStateTimeline: IncidentStateTimeline | undefined =
|
|
810
|
+
incidentStateTimelines[0];
|
|
811
|
+
|
|
812
|
+
// delete all the incident metrics with this incident id because its a refresh.
|
|
813
|
+
|
|
814
|
+
await MetricService.deleteBy({
|
|
815
|
+
query: {
|
|
816
|
+
serviceId: data.incidentId,
|
|
817
|
+
},
|
|
818
|
+
props: {
|
|
819
|
+
isRoot: true,
|
|
820
|
+
},
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
const itemsToSave: Array<Metric> = [];
|
|
824
|
+
|
|
825
|
+
// now we need to create new metrics for this incident - TimeToAcknowledge, TimeToResolve, IncidentCount, IncidentDuration
|
|
826
|
+
|
|
827
|
+
const incidentStartsAt: Date =
|
|
828
|
+
firstIncidentStateTimeline?.startsAt ||
|
|
829
|
+
incident.createdAt ||
|
|
830
|
+
OneUptimeDate.getCurrentDate();
|
|
831
|
+
|
|
832
|
+
const incidentCountMetric: Metric = new Metric();
|
|
833
|
+
|
|
834
|
+
incidentCountMetric.projectId = incident.projectId;
|
|
835
|
+
incidentCountMetric.serviceId = incident.id!;
|
|
836
|
+
incidentCountMetric.serviceType = ServiceType.Incident;
|
|
837
|
+
incidentCountMetric.name = IncidentMetricType.IncidentCount;
|
|
838
|
+
incidentCountMetric.description = "Number of incidents created";
|
|
839
|
+
incidentCountMetric.value = 1;
|
|
840
|
+
incidentCountMetric.unit = "";
|
|
841
|
+
incidentCountMetric.attributes = {
|
|
842
|
+
incidentId: data.incidentId.toString(),
|
|
843
|
+
projectId: incident.projectId.toString(),
|
|
844
|
+
monitorIds:
|
|
845
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
846
|
+
return monitor._id?.toString();
|
|
847
|
+
}) || [],
|
|
848
|
+
monitorNames:
|
|
849
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
850
|
+
return monitor.name?.toString();
|
|
851
|
+
}) || [],
|
|
852
|
+
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
|
853
|
+
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
incidentCountMetric.time = incidentStartsAt;
|
|
857
|
+
incidentCountMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
858
|
+
incidentCountMetric.time,
|
|
859
|
+
);
|
|
860
|
+
incidentCountMetric.metricPointType = MetricPointType.Sum;
|
|
861
|
+
|
|
862
|
+
itemsToSave.push(incidentCountMetric);
|
|
863
|
+
|
|
864
|
+
// is the incident acknowledged?
|
|
865
|
+
const isIncidentAcknowledged: boolean = incidentStateTimelines.some(
|
|
866
|
+
(timeline: IncidentStateTimeline) => {
|
|
867
|
+
return timeline.incidentState?.isAcknowledgedState;
|
|
868
|
+
},
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
if (isIncidentAcknowledged) {
|
|
872
|
+
const ackIncidentStateTimeline: IncidentStateTimeline | undefined =
|
|
873
|
+
incidentStateTimelines.find((timeline: IncidentStateTimeline) => {
|
|
874
|
+
return timeline.incidentState?.isAcknowledgedState;
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
if (ackIncidentStateTimeline) {
|
|
878
|
+
const timeToAcknowledgeMetric: Metric = new Metric();
|
|
879
|
+
|
|
880
|
+
timeToAcknowledgeMetric.projectId = incident.projectId;
|
|
881
|
+
timeToAcknowledgeMetric.serviceId = incident.id!;
|
|
882
|
+
timeToAcknowledgeMetric.serviceType = ServiceType.Incident;
|
|
883
|
+
timeToAcknowledgeMetric.name = IncidentMetricType.TimeToAcknowledge;
|
|
884
|
+
timeToAcknowledgeMetric.description =
|
|
885
|
+
"Time taken to acknowledge the incident";
|
|
886
|
+
timeToAcknowledgeMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
887
|
+
ackIncidentStateTimeline?.startsAt || OneUptimeDate.getCurrentDate(),
|
|
888
|
+
incidentStartsAt,
|
|
889
|
+
);
|
|
890
|
+
timeToAcknowledgeMetric.unit = "seconds";
|
|
891
|
+
timeToAcknowledgeMetric.attributes = {
|
|
892
|
+
incidentId: data.incidentId.toString(),
|
|
893
|
+
projectId: incident.projectId.toString(),
|
|
894
|
+
monitorIds:
|
|
895
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
896
|
+
return monitor._id?.toString();
|
|
897
|
+
}) || [],
|
|
898
|
+
monitorNames:
|
|
899
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
900
|
+
return monitor.name?.toString();
|
|
901
|
+
}) || [],
|
|
902
|
+
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
|
903
|
+
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
timeToAcknowledgeMetric.time =
|
|
907
|
+
ackIncidentStateTimeline?.startsAt ||
|
|
908
|
+
incident.createdAt ||
|
|
909
|
+
OneUptimeDate.getCurrentDate();
|
|
910
|
+
timeToAcknowledgeMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
911
|
+
timeToAcknowledgeMetric.time,
|
|
912
|
+
);
|
|
913
|
+
timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
|
|
914
|
+
|
|
915
|
+
itemsToSave.push(timeToAcknowledgeMetric);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// time to resolve
|
|
920
|
+
const isIncidentResolved: boolean = incidentStateTimelines.some(
|
|
921
|
+
(timeline: IncidentStateTimeline) => {
|
|
922
|
+
return timeline.incidentState?.isResolvedState;
|
|
923
|
+
},
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
if (isIncidentResolved) {
|
|
927
|
+
const resolvedIncidentStateTimeline: IncidentStateTimeline | undefined =
|
|
928
|
+
incidentStateTimelines.find((timeline: IncidentStateTimeline) => {
|
|
929
|
+
return timeline.incidentState?.isResolvedState;
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
if (resolvedIncidentStateTimeline) {
|
|
933
|
+
const timeToResolveMetric: Metric = new Metric();
|
|
934
|
+
|
|
935
|
+
timeToResolveMetric.projectId = incident.projectId;
|
|
936
|
+
timeToResolveMetric.serviceId = incident.id!;
|
|
937
|
+
timeToResolveMetric.serviceType = ServiceType.Incident;
|
|
938
|
+
timeToResolveMetric.name = IncidentMetricType.TimeToResolve;
|
|
939
|
+
timeToResolveMetric.description = "Time taken to resolve the incident";
|
|
940
|
+
timeToResolveMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
941
|
+
resolvedIncidentStateTimeline?.startsAt ||
|
|
942
|
+
OneUptimeDate.getCurrentDate(),
|
|
943
|
+
incidentStartsAt,
|
|
944
|
+
);
|
|
945
|
+
timeToResolveMetric.unit = "seconds";
|
|
946
|
+
timeToResolveMetric.attributes = {
|
|
947
|
+
incidentId: data.incidentId.toString(),
|
|
948
|
+
projectId: incident.projectId.toString(),
|
|
949
|
+
monitorIds:
|
|
950
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
951
|
+
return monitor._id?.toString();
|
|
952
|
+
}) || [],
|
|
953
|
+
monitorNames:
|
|
954
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
955
|
+
return monitor.name?.toString();
|
|
956
|
+
}) || [],
|
|
957
|
+
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
|
958
|
+
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
timeToResolveMetric.time =
|
|
962
|
+
resolvedIncidentStateTimeline?.startsAt ||
|
|
963
|
+
incident.createdAt ||
|
|
964
|
+
OneUptimeDate.getCurrentDate();
|
|
965
|
+
timeToResolveMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
966
|
+
timeToResolveMetric.time,
|
|
967
|
+
);
|
|
968
|
+
timeToResolveMetric.metricPointType = MetricPointType.Sum;
|
|
969
|
+
|
|
970
|
+
itemsToSave.push(timeToResolveMetric);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// incident duration
|
|
975
|
+
|
|
976
|
+
const incidentDurationMetric: Metric = new Metric();
|
|
977
|
+
|
|
978
|
+
const lastIncidentStateTimeline: IncidentStateTimeline | undefined =
|
|
979
|
+
incidentStateTimelines[incidentStateTimelines.length - 1];
|
|
980
|
+
|
|
981
|
+
if (lastIncidentStateTimeline) {
|
|
982
|
+
const incidentEndsAt: Date =
|
|
983
|
+
lastIncidentStateTimeline.startsAt || OneUptimeDate.getCurrentDate();
|
|
984
|
+
|
|
985
|
+
// save metric.
|
|
986
|
+
|
|
987
|
+
incidentDurationMetric.projectId = incident.projectId;
|
|
988
|
+
incidentDurationMetric.serviceId = incident.id!;
|
|
989
|
+
incidentDurationMetric.serviceType = ServiceType.Incident;
|
|
990
|
+
incidentDurationMetric.name = IncidentMetricType.IncidentDuration;
|
|
991
|
+
incidentDurationMetric.description = "Duration of the incident";
|
|
992
|
+
incidentDurationMetric.value = OneUptimeDate.getDifferenceInSeconds(
|
|
993
|
+
incidentEndsAt,
|
|
994
|
+
incidentStartsAt,
|
|
995
|
+
);
|
|
996
|
+
incidentDurationMetric.unit = "seconds";
|
|
997
|
+
incidentDurationMetric.attributes = {
|
|
998
|
+
incidentId: data.incidentId.toString(),
|
|
999
|
+
projectId: incident.projectId.toString(),
|
|
1000
|
+
monitorIds:
|
|
1001
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
1002
|
+
return monitor._id?.toString();
|
|
1003
|
+
}) || [],
|
|
1004
|
+
monitorNames:
|
|
1005
|
+
incident.monitors?.map((monitor: Monitor) => {
|
|
1006
|
+
return monitor.name?.toString();
|
|
1007
|
+
}) || [],
|
|
1008
|
+
incidentSeverityId: incident.incidentSeverity?._id?.toString(),
|
|
1009
|
+
incidentSeverityName: incident.incidentSeverity?.name?.toString(),
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
incidentDurationMetric.time =
|
|
1013
|
+
lastIncidentStateTimeline?.startsAt ||
|
|
1014
|
+
incident.createdAt ||
|
|
1015
|
+
OneUptimeDate.getCurrentDate();
|
|
1016
|
+
incidentDurationMetric.timeUnixNano = OneUptimeDate.toUnixNano(
|
|
1017
|
+
incidentDurationMetric.time,
|
|
1018
|
+
);
|
|
1019
|
+
incidentDurationMetric.metricPointType = MetricPointType.Sum;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
await MetricService.createMany({
|
|
1023
|
+
items: itemsToSave,
|
|
1024
|
+
props: {
|
|
1025
|
+
isRoot: true,
|
|
1026
|
+
},
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
// index attributes.
|
|
1030
|
+
TelemetryUtil.indexAttributes({
|
|
1031
|
+
attributes: ["monitorIds", "projectId", "incidentId", "monitorNames"],
|
|
1032
|
+
projectId: incident.projectId,
|
|
1033
|
+
telemetryType: TelemetryType.Metric,
|
|
1034
|
+
}).catch((err: Error) => {
|
|
1035
|
+
logger.error(err);
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
740
1038
|
}
|
|
741
1039
|
export default new Service();
|
|
@@ -19,6 +19,7 @@ import IncidentState from "Common/Models/DatabaseModels/IncidentState";
|
|
|
19
19
|
import IncidentStateTimeline from "Common/Models/DatabaseModels/IncidentStateTimeline";
|
|
20
20
|
import User from "Common/Models/DatabaseModels/User";
|
|
21
21
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
22
|
+
import logger from "../Utils/Logger";
|
|
22
23
|
|
|
23
24
|
export class Service extends DatabaseService<IncidentStateTimeline> {
|
|
24
25
|
public constructor() {
|
|
@@ -227,6 +228,13 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
IncidentService.refreshIncidentMetrics({
|
|
232
|
+
incidentId: createdItem.incidentId,
|
|
233
|
+
}).catch((error: Error) => {
|
|
234
|
+
logger.error(`Error while refreshing incident metrics:`);
|
|
235
|
+
logger.error(error);
|
|
236
|
+
});
|
|
237
|
+
|
|
230
238
|
return createdItem;
|
|
231
239
|
}
|
|
232
240
|
|
package/Types/Date.ts
CHANGED
|
@@ -845,6 +845,18 @@ export default class OneUptimeDate {
|
|
|
845
845
|
return minutes;
|
|
846
846
|
}
|
|
847
847
|
|
|
848
|
+
public static getDifferenceInSeconds(date: Date, date2: Date): number {
|
|
849
|
+
date = this.fromString(date);
|
|
850
|
+
date2 = this.fromString(date2);
|
|
851
|
+
const seconds: number = moment(date).diff(moment(date2), "seconds");
|
|
852
|
+
|
|
853
|
+
if (seconds < 0) {
|
|
854
|
+
return seconds * -1;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
return seconds;
|
|
858
|
+
}
|
|
859
|
+
|
|
848
860
|
public static getDifferenceInMonths(date: Date, date2: Date): number {
|
|
849
861
|
date = this.fromString(date);
|
|
850
862
|
date2 = this.fromString(date2);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
enum IncidentMetricType {
|
|
2
|
+
TimeToAcknowledge = "oneuptime.incident.time-to-acknowledge",
|
|
3
|
+
TimeToResolve = "oneuptime.incident.time-to-resolve",
|
|
4
|
+
IncidentCount = "oneuptime.incident.count",
|
|
5
|
+
IncidentDuration = "oneuptime.incident.duration",
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default IncidentMetricType;
|