@oneuptime/common 10.0.65 → 10.0.66
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/DockerHostOwnerTeam.ts +464 -0
- package/Models/DatabaseModels/DockerHostOwnerUser.ts +463 -0
- package/Models/DatabaseModels/Index.ts +24 -0
- package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +464 -0
- package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +463 -0
- package/Models/DatabaseModels/KubernetesResource.ts +548 -0
- package/Models/DatabaseModels/MetricPipelineRule.ts +804 -0
- package/Models/DatabaseModels/MetricRecordingRule.ts +470 -0
- package/Models/DatabaseModels/Monitor.ts +2 -0
- package/Models/DatabaseModels/Project.ts +53 -0
- package/Models/DatabaseModels/Service.ts +79 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +508 -0
- package/Models/DatabaseModels/TracePipeline.ts +436 -0
- package/Models/DatabaseModels/TracePipelineProcessor.ts +454 -0
- package/Models/DatabaseModels/TraceRecordingRule.ts +470 -0
- package/Models/DatabaseModels/TraceScrubRule.ts +546 -0
- package/Server/API/KubernetesResourceAPI.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.ts +399 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.ts +205 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.ts +335 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -1
- package/Server/Services/DockerHostOwnerTeamService.ts +10 -0
- package/Server/Services/DockerHostOwnerUserService.ts +10 -0
- package/Server/Services/KubernetesClusterOwnerTeamService.ts +10 -0
- package/Server/Services/KubernetesClusterOwnerUserService.ts +10 -0
- package/Server/Services/KubernetesResourceService.ts +351 -0
- package/Server/Services/MetricPipelineRuleService.ts +10 -0
- package/Server/Services/MetricRecordingRuleService.ts +10 -0
- package/Server/Services/TraceDropFilterService.ts +10 -0
- package/Server/Services/TracePipelineProcessorService.ts +10 -0
- package/Server/Services/TracePipelineService.ts +10 -0
- package/Server/Services/TraceRecordingRuleService.ts +10 -0
- package/Server/Services/TraceScrubRuleService.ts +10 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +71 -9
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +483 -75
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +379 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +502 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +216 -0
- package/Tests/Utils/Metrics/MetricFormulaEvaluator.test.ts +269 -0
- package/Tests/Utils/Metrics/MetricResultUnitConverter.test.ts +231 -0
- package/Tests/Utils/RecordingRuleExpression.test.ts +177 -0
- package/Types/Kubernetes/KubernetesInventoryExtractor.ts +327 -0
- package/Types/Kubernetes/KubernetesObjectParser.ts +1949 -0
- package/Types/Metrics/MetricDownsamplingRetentionDays.ts +49 -0
- package/Types/Metrics/MetricFormulaConfigData.ts +4 -0
- package/Types/Metrics/MetricPipelineRuleFilterCondition.ts +136 -0
- package/Types/Metrics/MetricPipelineRuleType.ts +27 -0
- package/Types/Metrics/RecordingRuleDefinition.ts +180 -0
- package/Types/Monitor/CriteriaFilter.ts +43 -0
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +70 -0
- package/Types/Permission.ts +520 -0
- package/Types/Trace/TraceAggregationType.ts +17 -0
- package/Types/Trace/TraceDropFilterAction.ts +6 -0
- package/Types/Trace/TracePipelineProcessorType.ts +56 -0
- package/Types/Trace/TraceRecordingRuleDefinition.ts +218 -0
- package/Types/Trace/TraceScrubAction.ts +7 -0
- package/Types/Trace/TraceScrubField.ts +8 -0
- package/Types/Trace/TraceScrubPatternType.ts +10 -0
- package/UI/Components/CardSelect/CardSelect.tsx +9 -1
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +6 -10
- package/UI/Components/Forms/Fields/FormField.tsx +1 -0
- package/UI/Components/Forms/Types/Field.ts +1 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +57 -0
- package/UI/Components/Page/Page.tsx +6 -0
- package/Utils/MetricUnitUtil.ts +289 -0
- package/Utils/Metrics/MetricFormulaEvaluator.ts +610 -0
- package/Utils/Metrics/MetricResultUnitConverter.ts +91 -0
- package/Utils/Metrics/RecordingRuleExpression.ts +359 -0
- package/Utils/ValueFormatter.ts +137 -13
- package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +480 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +479 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +24 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +480 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +479 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +590 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MetricPipelineRule.js +836 -0
- package/build/dist/Models/DatabaseModels/MetricPipelineRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MetricRecordingRule.js +497 -0
- package/build/dist/Models/DatabaseModels/MetricRecordingRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +53 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +79 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +536 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TracePipeline.js +462 -0
- package/build/dist/Models/DatabaseModels/TracePipeline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js +476 -0
- package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TraceRecordingRule.js +497 -0
- package/build/dist/Models/DatabaseModels/TraceRecordingRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TraceScrubRule.js +575 -0
- package/build/dist/Models/DatabaseModels/TraceScrubRule.js.map +1 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js +98 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js +144 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js +82 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js +118 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DockerHostOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/DockerHostOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/DockerHostOwnerUserService.js +9 -0
- package/build/dist/Server/Services/DockerHostOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js +9 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesResourceService.js +237 -0
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -0
- package/build/dist/Server/Services/MetricPipelineRuleService.js +9 -0
- package/build/dist/Server/Services/MetricPipelineRuleService.js.map +1 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js +9 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -0
- package/build/dist/Server/Services/TraceDropFilterService.js +9 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -0
- package/build/dist/Server/Services/TracePipelineProcessorService.js +9 -0
- package/build/dist/Server/Services/TracePipelineProcessorService.js.map +1 -0
- package/build/dist/Server/Services/TracePipelineService.js +9 -0
- package/build/dist/Server/Services/TracePipelineService.js.map +1 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js +9 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -0
- package/build/dist/Server/Services/TraceScrubRuleService.js +9 -0
- package/build/dist/Server/Services/TraceScrubRuleService.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +56 -9
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +335 -53
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +277 -5
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +407 -0
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -0
- package/build/dist/Tests/Utils/MetricUnitUtil.test.js +159 -0
- package/build/dist/Tests/Utils/MetricUnitUtil.test.js.map +1 -0
- package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js +224 -0
- package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js.map +1 -0
- package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js +180 -0
- package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js.map +1 -0
- package/build/dist/Tests/Utils/RecordingRuleExpression.test.js +142 -0
- package/build/dist/Tests/Utils/RecordingRuleExpression.test.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +200 -0
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesObjectParser.js +1205 -0
- package/build/dist/Types/Kubernetes/KubernetesObjectParser.js.map +1 -0
- package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js +32 -0
- package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js.map +1 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js +103 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js.map +1 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleType.js +27 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleType.js.map +1 -0
- package/build/dist/Types/Metrics/RecordingRuleDefinition.js +110 -0
- package/build/dist/Types/Metrics/RecordingRuleDefinition.js.map +1 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js +22 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js +2 -0
- package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js.map +1 -0
- package/build/dist/Types/Permission.js +454 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Trace/TraceAggregationType.js +18 -0
- package/build/dist/Types/Trace/TraceAggregationType.js.map +1 -0
- package/build/dist/Types/Trace/TraceDropFilterAction.js +7 -0
- package/build/dist/Types/Trace/TraceDropFilterAction.js.map +1 -0
- package/build/dist/Types/Trace/TracePipelineProcessorType.js +10 -0
- package/build/dist/Types/Trace/TracePipelineProcessorType.js.map +1 -0
- package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js +145 -0
- package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubAction.js +8 -0
- package/build/dist/Types/Trace/TraceScrubAction.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubField.js +9 -0
- package/build/dist/Types/Trace/TraceScrubField.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js +11 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +3 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -9
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +30 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +1 -0
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +232 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js +453 -0
- package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResultUnitConverter.js +61 -0
- package/build/dist/Utils/Metrics/MetricResultUnitConverter.js.map +1 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js +298 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -0
- package/build/dist/Utils/ValueFormatter.js +123 -13
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/package.json +1 -1
|
@@ -45,10 +45,16 @@ import URL from "../../../Types/API/URL";
|
|
|
45
45
|
import IP from "../../../Types/IP/IP";
|
|
46
46
|
import Hostname from "../../../Types/API/Hostname";
|
|
47
47
|
import Port from "../../../Types/Port";
|
|
48
|
+
import { DashboardClientUrl } from "../../EnvironmentConfig";
|
|
48
49
|
import MetricMonitorResponse, {
|
|
49
50
|
KubernetesAffectedResource,
|
|
50
51
|
KubernetesResourceBreakdown,
|
|
51
52
|
} from "../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
|
|
53
|
+
import MetricCriteriaContext, {
|
|
54
|
+
MetricBreachingSample,
|
|
55
|
+
MetricComponent,
|
|
56
|
+
MetricComponentValue,
|
|
57
|
+
} from "../../../Types/Monitor/MetricMonitor/MetricCriteriaContext";
|
|
52
58
|
import MonitorStepDockerMonitor from "../../../Types/Monitor/MonitorStepDockerMonitor";
|
|
53
59
|
|
|
54
60
|
export default class MonitorCriteriaEvaluator {
|
|
@@ -108,13 +114,9 @@ export default class MonitorCriteriaEvaluator {
|
|
|
108
114
|
if (rootCause) {
|
|
109
115
|
input.probeApiIngestResponse.criteriaMetId = criteriaInstance.data?.id;
|
|
110
116
|
input.probeApiIngestResponse.rootCause = `
|
|
111
|
-
**Created because the following criteria was met**:
|
|
117
|
+
**Created because the following criteria was met**:
|
|
112
118
|
|
|
113
119
|
**Criteria Name**: ${criteriaInstance.data?.name}
|
|
114
|
-
`;
|
|
115
|
-
|
|
116
|
-
input.probeApiIngestResponse.rootCause += `
|
|
117
|
-
**Filter Conditions Met**: ${rootCause}
|
|
118
120
|
`;
|
|
119
121
|
|
|
120
122
|
const contextBlock: string | null =
|
|
@@ -122,9 +124,28 @@ export default class MonitorCriteriaEvaluator {
|
|
|
122
124
|
dataToProcess: input.dataToProcess,
|
|
123
125
|
monitorStep: input.monitorStep,
|
|
124
126
|
monitor: input.monitor,
|
|
127
|
+
criteriaInstance: criteriaInstance,
|
|
125
128
|
});
|
|
126
129
|
|
|
127
|
-
|
|
130
|
+
/*
|
|
131
|
+
* For metric monitors, render the metric identity (name, unit,
|
|
132
|
+
* filter attrs, breaching series) before the comparison line so
|
|
133
|
+
* the reader has context when they reach "Filter Conditions Met".
|
|
134
|
+
*/
|
|
135
|
+
const isMetricMonitor: boolean =
|
|
136
|
+
input.monitor.monitorType === MonitorType.Metrics;
|
|
137
|
+
|
|
138
|
+
if (contextBlock && isMetricMonitor) {
|
|
139
|
+
input.probeApiIngestResponse.rootCause += `
|
|
140
|
+
${contextBlock}
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
input.probeApiIngestResponse.rootCause += `
|
|
145
|
+
**Filter Conditions Met**: ${rootCause}
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
if (contextBlock && !isMetricMonitor) {
|
|
128
149
|
input.probeApiIngestResponse.rootCause += `
|
|
129
150
|
${contextBlock}
|
|
130
151
|
`;
|
|
@@ -568,6 +589,7 @@ ${contextBlock}
|
|
|
568
589
|
dataToProcess: DataToProcess;
|
|
569
590
|
monitorStep: MonitorStep;
|
|
570
591
|
monitor: Monitor;
|
|
592
|
+
criteriaInstance?: MonitorCriteriaInstance;
|
|
571
593
|
}): Promise<string | null> {
|
|
572
594
|
// Handle Kubernetes monitors with rich resource context
|
|
573
595
|
if (input.monitor.monitorType === MonitorType.Kubernetes) {
|
|
@@ -581,6 +603,17 @@ ${contextBlock}
|
|
|
581
603
|
return MonitorCriteriaEvaluator.buildDockerRootCauseContext(input);
|
|
582
604
|
}
|
|
583
605
|
|
|
606
|
+
// Handle generic Metric monitors with metric identity + breaching series
|
|
607
|
+
if (
|
|
608
|
+
input.monitor.monitorType === MonitorType.Metrics &&
|
|
609
|
+
input.criteriaInstance
|
|
610
|
+
) {
|
|
611
|
+
return MonitorCriteriaEvaluator.buildMetricRootCauseContext({
|
|
612
|
+
criteriaInstance: input.criteriaInstance,
|
|
613
|
+
monitor: input.monitor,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
584
617
|
const requestDetails: Array<string> = [];
|
|
585
618
|
const responseDetails: Array<string> = [];
|
|
586
619
|
const failureDetails: Array<string> = [];
|
|
@@ -689,6 +722,346 @@ ${contextBlock}
|
|
|
689
722
|
return sections.join("\n");
|
|
690
723
|
}
|
|
691
724
|
|
|
725
|
+
private static buildMetricRootCauseContext(input: {
|
|
726
|
+
criteriaInstance: MonitorCriteriaInstance;
|
|
727
|
+
monitor: Monitor;
|
|
728
|
+
}): string | null {
|
|
729
|
+
/*
|
|
730
|
+
* Pick the first populated metric context across the instance's filters.
|
|
731
|
+
* Only metric-value filters populate this at evaluation time, so this
|
|
732
|
+
* effectively returns the context for the filter that ran.
|
|
733
|
+
*/
|
|
734
|
+
const ctx: MetricCriteriaContext | undefined = (
|
|
735
|
+
input.criteriaInstance.data?.filters || []
|
|
736
|
+
)
|
|
737
|
+
.map((f: CriteriaFilter) => {
|
|
738
|
+
return f.metricCriteriaContext;
|
|
739
|
+
})
|
|
740
|
+
.find(
|
|
741
|
+
(c: MetricCriteriaContext | undefined): c is MetricCriteriaContext => {
|
|
742
|
+
return Boolean(c);
|
|
743
|
+
},
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
if (!ctx) {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const lines: Array<string> = [];
|
|
751
|
+
lines.push(`- Metric: \`${ctx.metricName}\``);
|
|
752
|
+
if (ctx.alias) {
|
|
753
|
+
lines.push(`- Alias: \`${ctx.alias}\``);
|
|
754
|
+
}
|
|
755
|
+
if (ctx.unit) {
|
|
756
|
+
lines.push(`- Unit: ${ctx.unit}`);
|
|
757
|
+
}
|
|
758
|
+
if (ctx.aggregationType) {
|
|
759
|
+
lines.push(`- Aggregation: ${ctx.aggregationType}`);
|
|
760
|
+
}
|
|
761
|
+
if (ctx.isFormula && ctx.formulaExpression) {
|
|
762
|
+
lines.push(`- Formula: \`${ctx.formulaExpression}\``);
|
|
763
|
+
}
|
|
764
|
+
if (ctx.timeWindowMinutes) {
|
|
765
|
+
lines.push(`- Time Window: last ${ctx.timeWindowMinutes} minutes`);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/*
|
|
769
|
+
* For formulas, enumerate the underlying variables so the on-call
|
|
770
|
+
* engineer can trace `c = a + b` back to "a is container.cpu.time,
|
|
771
|
+
* b is container.memory.usage in bytes" without clicking away.
|
|
772
|
+
*/
|
|
773
|
+
if (ctx.components && ctx.components.length > 0) {
|
|
774
|
+
const componentLines: Array<string> = ctx.components.map(
|
|
775
|
+
(component: MetricComponent) => {
|
|
776
|
+
const unitSuffix: string = component.unit
|
|
777
|
+
? ` — unit: ${component.unit}`
|
|
778
|
+
: "";
|
|
779
|
+
const typeSuffix: string = component.isFormula ? " (formula)" : "";
|
|
780
|
+
return ` - \`${component.alias}\` = \`${component.name}\`${typeSuffix}${unitSuffix}`;
|
|
781
|
+
},
|
|
782
|
+
);
|
|
783
|
+
lines.push(`- Components:\n${componentLines.join("\n")}`);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const filterKeys: Array<string> = Object.keys(ctx.filterAttributes || {});
|
|
787
|
+
if (filterKeys.length > 0) {
|
|
788
|
+
const filterLines: Array<string> = filterKeys.map((k: string) => {
|
|
789
|
+
const v: unknown = (ctx.filterAttributes as Record<string, unknown>)[k];
|
|
790
|
+
return ` - \`${k}\` = \`${String(v)}\``;
|
|
791
|
+
});
|
|
792
|
+
lines.push(`- Filters:\n${filterLines.join("\n")}`);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (ctx.groupBy.length > 0) {
|
|
796
|
+
lines.push(
|
|
797
|
+
`- Grouped By: ${ctx.groupBy
|
|
798
|
+
.map((g: string) => {
|
|
799
|
+
return `\`${g}\``;
|
|
800
|
+
})
|
|
801
|
+
.join(", ")}`,
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const sections: Array<string> = [`**Metric Details**\n${lines.join("\n")}`];
|
|
806
|
+
|
|
807
|
+
const breachingSamples: Array<MetricBreachingSample> =
|
|
808
|
+
ctx.breachingSamples && ctx.breachingSamples.length > 0
|
|
809
|
+
? ctx.breachingSamples
|
|
810
|
+
: ctx.breachingSample
|
|
811
|
+
? [ctx.breachingSample]
|
|
812
|
+
: [];
|
|
813
|
+
|
|
814
|
+
if (breachingSamples.length > 0) {
|
|
815
|
+
sections.push(
|
|
816
|
+
`\n\n${MonitorCriteriaEvaluator.formatBreachingSamplesSection({
|
|
817
|
+
samples: breachingSamples,
|
|
818
|
+
totalSamples: ctx.totalSamplesInWindow,
|
|
819
|
+
unit: ctx.unit,
|
|
820
|
+
metricName: ctx.metricName,
|
|
821
|
+
alias: ctx.alias,
|
|
822
|
+
components: ctx.components || [],
|
|
823
|
+
})}`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const deepLink: string | null =
|
|
828
|
+
MonitorCriteriaEvaluator.buildMetricExplorerDeepLink({
|
|
829
|
+
monitor: input.monitor,
|
|
830
|
+
ctx,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
if (deepLink) {
|
|
834
|
+
sections.push(`\n\n[Open metric in dashboard](${deepLink})`);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return sections.join("\n");
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Build the **Breaching Samples** markdown section — a table of
|
|
842
|
+
* timestamps, values, and any group-by attributes. Caps the row count
|
|
843
|
+
* so a 30-minute window at 1-second granularity doesn't dump thousands
|
|
844
|
+
* of lines onto the root-cause page; the caller can always drill in
|
|
845
|
+
* via the metric explorer link.
|
|
846
|
+
*
|
|
847
|
+
* Timestamps are emitted as inline code wrapping ISO 8601 strings so
|
|
848
|
+
* the client-side markdown viewer can localize them to the viewer's
|
|
849
|
+
* timezone (without losing the canonical instant).
|
|
850
|
+
*/
|
|
851
|
+
private static formatBreachingSamplesSection(input: {
|
|
852
|
+
samples: Array<MetricBreachingSample>;
|
|
853
|
+
totalSamples?: number | undefined;
|
|
854
|
+
unit: string | null;
|
|
855
|
+
metricName: string;
|
|
856
|
+
alias: string;
|
|
857
|
+
components: Array<MetricComponent>;
|
|
858
|
+
}): string {
|
|
859
|
+
const MAX_ROWS: number = 20;
|
|
860
|
+
|
|
861
|
+
// Sort chronologically and de-duplicate any accidental repeats
|
|
862
|
+
const sorted: Array<MetricBreachingSample> = [...input.samples].sort(
|
|
863
|
+
(a: MetricBreachingSample, b: MetricBreachingSample) => {
|
|
864
|
+
return (
|
|
865
|
+
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
866
|
+
);
|
|
867
|
+
},
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const displayedSamples: Array<MetricBreachingSample> = sorted.slice(
|
|
871
|
+
0,
|
|
872
|
+
MAX_ROWS,
|
|
873
|
+
);
|
|
874
|
+
|
|
875
|
+
// Collect attribute keys that appear on any displayed sample
|
|
876
|
+
const attrKeySet: Set<string> = new Set<string>();
|
|
877
|
+
for (const s of displayedSamples) {
|
|
878
|
+
for (const k of Object.keys(s.attributes || {})) {
|
|
879
|
+
attrKeySet.add(k);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
const attrKeys: Array<string> = Array.from(attrKeySet);
|
|
883
|
+
|
|
884
|
+
/*
|
|
885
|
+
* Escape pipe characters that could appear in the metric display
|
|
886
|
+
* name (formulas like "a | b" are unlikely but possible) so they
|
|
887
|
+
* don't break GitHub-flavored-markdown tables.
|
|
888
|
+
*/
|
|
889
|
+
const escapeCell: (value: string) => string = (value: string): string => {
|
|
890
|
+
return value.replace(/\|/g, "\\|");
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
/*
|
|
894
|
+
* Column layout:
|
|
895
|
+
* Timestamp | Metric | Alias | Value | <component_1> | ... | <attr_1> | ...
|
|
896
|
+
*
|
|
897
|
+
* The component columns let the reader see what each variable of a
|
|
898
|
+
* formula resolved to at the breach time — e.g. "when c = a + b
|
|
899
|
+
* breached 100, a was 55, b was 46". They are omitted for plain
|
|
900
|
+
* metric criteria.
|
|
901
|
+
*/
|
|
902
|
+
const headerCells: Array<string> = [
|
|
903
|
+
"Timestamp",
|
|
904
|
+
"Metric",
|
|
905
|
+
"Alias",
|
|
906
|
+
"Value",
|
|
907
|
+
];
|
|
908
|
+
|
|
909
|
+
for (const component of input.components) {
|
|
910
|
+
const label: string = component.unit
|
|
911
|
+
? `${component.alias} (${component.unit})`
|
|
912
|
+
: component.alias;
|
|
913
|
+
headerCells.push(label);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
headerCells.push(...attrKeys);
|
|
917
|
+
|
|
918
|
+
const unitSuffix: string = input.unit ? ` ${input.unit}` : "";
|
|
919
|
+
|
|
920
|
+
const headerRow: string = `| ${headerCells.join(" | ")} |`;
|
|
921
|
+
const dividerRow: string = `| ${headerCells
|
|
922
|
+
.map(() => {
|
|
923
|
+
return "---";
|
|
924
|
+
})
|
|
925
|
+
.join(" | ")} |`;
|
|
926
|
+
|
|
927
|
+
const metricCell: string = `\`${escapeCell(input.metricName)}\``;
|
|
928
|
+
const aliasCell: string = input.alias
|
|
929
|
+
? `\`${escapeCell(input.alias)}\``
|
|
930
|
+
: "-";
|
|
931
|
+
|
|
932
|
+
const dataRows: Array<string> = displayedSamples.map(
|
|
933
|
+
(s: MetricBreachingSample) => {
|
|
934
|
+
const timestampIso: string = new Date(s.timestamp).toISOString();
|
|
935
|
+
const cells: Array<string> = [
|
|
936
|
+
`\`${timestampIso}\``,
|
|
937
|
+
metricCell,
|
|
938
|
+
aliasCell,
|
|
939
|
+
`${MonitorCriteriaEvaluator.formatNumberForDisplay(s.value)}${unitSuffix}`,
|
|
940
|
+
];
|
|
941
|
+
|
|
942
|
+
for (const component of input.components) {
|
|
943
|
+
const match: MetricComponentValue | undefined = (
|
|
944
|
+
s.componentValues || []
|
|
945
|
+
).find((cv: MetricComponentValue) => {
|
|
946
|
+
return cv.alias === component.alias;
|
|
947
|
+
});
|
|
948
|
+
if (match && typeof match.value === "number") {
|
|
949
|
+
const componentUnitSuffix: string = component.unit
|
|
950
|
+
? ` ${component.unit}`
|
|
951
|
+
: "";
|
|
952
|
+
cells.push(
|
|
953
|
+
`${MonitorCriteriaEvaluator.formatNumberForDisplay(match.value)}${componentUnitSuffix}`,
|
|
954
|
+
);
|
|
955
|
+
} else {
|
|
956
|
+
cells.push("-");
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
for (const k of attrKeys) {
|
|
961
|
+
const v: unknown = (s.attributes as Record<string, unknown>)[k];
|
|
962
|
+
cells.push(v === undefined || v === null ? "-" : String(v));
|
|
963
|
+
}
|
|
964
|
+
return `| ${cells.join(" | ")} |`;
|
|
965
|
+
},
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
const lines: Array<string> = [
|
|
969
|
+
`**Breaching Samples**`,
|
|
970
|
+
MonitorCriteriaEvaluator.formatBreachingSamplesSummary({
|
|
971
|
+
breachingCount: sorted.length,
|
|
972
|
+
totalSamples: input.totalSamples,
|
|
973
|
+
}),
|
|
974
|
+
"",
|
|
975
|
+
headerRow,
|
|
976
|
+
dividerRow,
|
|
977
|
+
...dataRows,
|
|
978
|
+
];
|
|
979
|
+
|
|
980
|
+
if (sorted.length > displayedSamples.length) {
|
|
981
|
+
lines.push(
|
|
982
|
+
`\n_Showing the first ${displayedSamples.length} of ${sorted.length} breaching samples._`,
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return lines.join("\n");
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
private static formatBreachingSamplesSummary(input: {
|
|
990
|
+
breachingCount: number;
|
|
991
|
+
totalSamples?: number | undefined;
|
|
992
|
+
}): string {
|
|
993
|
+
if (
|
|
994
|
+
typeof input.totalSamples === "number" &&
|
|
995
|
+
input.totalSamples > 0 &&
|
|
996
|
+
input.totalSamples >= input.breachingCount
|
|
997
|
+
) {
|
|
998
|
+
return `${input.breachingCount} of ${input.totalSamples} samples breached the threshold.`;
|
|
999
|
+
}
|
|
1000
|
+
return `${input.breachingCount} sample${input.breachingCount === 1 ? "" : "s"} breached the threshold.`;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
private static formatNumberForDisplay(value: number): string {
|
|
1004
|
+
if (!Number.isFinite(value)) {
|
|
1005
|
+
return String(value);
|
|
1006
|
+
}
|
|
1007
|
+
if (Number.isInteger(value)) {
|
|
1008
|
+
return value.toString();
|
|
1009
|
+
}
|
|
1010
|
+
return Number(value.toFixed(2)).toString();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
private static buildMetricExplorerDeepLink(input: {
|
|
1014
|
+
monitor: Monitor;
|
|
1015
|
+
ctx: MetricCriteriaContext;
|
|
1016
|
+
}): string | null {
|
|
1017
|
+
const projectId: string | undefined = input.monitor.projectId?.toString();
|
|
1018
|
+
|
|
1019
|
+
if (!projectId) {
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/*
|
|
1024
|
+
* Metric explorer expects a JSON-encoded `metricQueries` param plus
|
|
1025
|
+
* optional start/end times. The shape is documented by
|
|
1026
|
+
* MetricExplorer.getMetricQueriesFromQuery(): it reads metricName,
|
|
1027
|
+
* attributes, and aggregationType (correctly spelled, unlike the
|
|
1028
|
+
* internal persisted field).
|
|
1029
|
+
*/
|
|
1030
|
+
const aggregationType: string | undefined =
|
|
1031
|
+
input.ctx.aggregationType || undefined;
|
|
1032
|
+
|
|
1033
|
+
const query: {
|
|
1034
|
+
metricName: string;
|
|
1035
|
+
attributes: JSONObject;
|
|
1036
|
+
aggregationType?: string;
|
|
1037
|
+
} = {
|
|
1038
|
+
metricName: input.ctx.metricName,
|
|
1039
|
+
attributes: input.ctx.filterAttributes || {},
|
|
1040
|
+
...(aggregationType ? { aggregationType } : {}),
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
// Time window: breach moment +- 15 minutes (or fall back to last hour).
|
|
1044
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
1045
|
+
const breachTime: Date | undefined = input.ctx.breachingSample?.timestamp;
|
|
1046
|
+
const startTime: Date = breachTime
|
|
1047
|
+
? OneUptimeDate.addRemoveMinutes(breachTime, -30)
|
|
1048
|
+
: OneUptimeDate.addRemoveHours(now, -1);
|
|
1049
|
+
const endTime: Date = breachTime
|
|
1050
|
+
? OneUptimeDate.addRemoveMinutes(breachTime, 15)
|
|
1051
|
+
: now;
|
|
1052
|
+
|
|
1053
|
+
const params: URLSearchParams = new URLSearchParams();
|
|
1054
|
+
params.set("metricQueries", JSON.stringify([query]));
|
|
1055
|
+
params.set("startTime", OneUptimeDate.toString(startTime));
|
|
1056
|
+
params.set("endTime", OneUptimeDate.toString(endTime));
|
|
1057
|
+
|
|
1058
|
+
/*
|
|
1059
|
+
* The route that actually reads these URL params is the metric
|
|
1060
|
+
* explorer at /metrics/view — the /metrics index is the metric list.
|
|
1061
|
+
*/
|
|
1062
|
+
return `${DashboardClientUrl.toString()}/${projectId}/metrics/view?${params.toString()}`;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
692
1065
|
private static async buildKubernetesRootCauseContext(input: {
|
|
693
1066
|
dataToProcess: DataToProcess;
|
|
694
1067
|
monitorStep: MonitorStep;
|