@oneuptime/common 10.0.70 → 10.0.72

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.
Files changed (144) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/Incident.ts +55 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +6 -4
  4. package/Models/DatabaseModels/Project.ts +5 -5
  5. package/Models/DatabaseModels/StatusPage.ts +80 -0
  6. package/Server/API/StatusPageAPI.ts +4 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +6 -3
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +17 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.ts +41 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.ts +25 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  12. package/Server/Services/AIBillingService.ts +2 -2
  13. package/Server/Services/AnalyticsDatabaseService.ts +17 -7
  14. package/Server/Services/BillingService.ts +116 -48
  15. package/Server/Services/NotificationService.ts +2 -2
  16. package/Server/Types/Database/QueryUtil.ts +13 -7
  17. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +175 -29
  18. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +71 -0
  19. package/Server/Utils/Monitor/MonitorAlert.ts +170 -7
  20. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +171 -2
  21. package/Server/Utils/Monitor/MonitorIncident.ts +212 -8
  22. package/Server/Utils/Monitor/MonitorMetricUtil.ts +423 -1
  23. package/Server/Utils/Monitor/MonitorResource.ts +2 -0
  24. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +99 -0
  25. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +268 -0
  26. package/Types/BaseDatabase/IncludesNone.ts +1 -4
  27. package/Types/Email.ts +50 -0
  28. package/Types/Infrastructure/BasicMetrics.ts +75 -0
  29. package/Types/Metrics/MetricQueryData.ts +11 -0
  30. package/Types/Monitor/CriteriaFilter.ts +10 -0
  31. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +11 -0
  32. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +10 -0
  33. package/Types/Monitor/MetricMonitor/MetricSeriesResult.ts +20 -0
  34. package/Types/Monitor/MonitorMetricType.ts +34 -0
  35. package/Types/Monitor/ServerMonitor/ServerMonitorResponse.ts +8 -0
  36. package/Types/Probe/ProbeApiIngestResponse.ts +25 -0
  37. package/Types/StatusPage/StatusPageLanguage.ts +29 -0
  38. package/UI/Components/Charts/Area/AreaChart.tsx +17 -12
  39. package/UI/Components/Charts/Bar/BarChart.tsx +16 -11
  40. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -0
  41. package/UI/Components/Charts/Line/LineChart.tsx +16 -11
  42. package/UI/Components/Filters/DateFilter.tsx +16 -8
  43. package/UI/Components/Filters/EntityFilter.tsx +33 -18
  44. package/UI/Components/Filters/FilterViewer.tsx +7 -5
  45. package/UI/Components/Filters/FiltersForm.tsx +27 -5
  46. package/UI/Components/Filters/NumberFilter.tsx +3 -2
  47. package/UI/Components/Filters/TextFilter.tsx +5 -4
  48. package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
  49. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +453 -0
  50. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +229 -0
  51. package/Utils/Metrics/MetricSeriesFingerprint.ts +97 -0
  52. package/Utils/Monitor/MonitorMetricType.ts +309 -19
  53. package/build/dist/Models/DatabaseModels/Alert.js +57 -0
  54. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/Incident.js +57 -0
  56. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  57. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +6 -4
  58. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  59. package/build/dist/Models/DatabaseModels/Project.js +5 -5
  60. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  61. package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
  62. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  63. package/build/dist/Server/API/StatusPageAPI.js +4 -0
  64. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  65. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +4 -2
  66. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -1
  67. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +12 -0
  68. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +1 -0
  69. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js +22 -0
  70. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776940714709-MigrationName.js.map +1 -0
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js +14 -0
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776971364783-AddStatusPageLanguageSettings.js.map +1 -0
  73. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  75. package/build/dist/Server/Services/AIBillingService.js +2 -2
  76. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  77. package/build/dist/Server/Services/AnalyticsDatabaseService.js +14 -4
  78. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  79. package/build/dist/Server/Services/BillingService.js +99 -39
  80. package/build/dist/Server/Services/BillingService.js.map +1 -1
  81. package/build/dist/Server/Services/NotificationService.js +2 -2
  82. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  83. package/build/dist/Server/Types/Database/QueryUtil.js +13 -7
  84. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  85. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +132 -30
  86. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  87. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +58 -7
  88. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
  89. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +134 -12
  90. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  91. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +112 -0
  92. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  93. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +159 -15
  94. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  95. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +373 -0
  96. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  97. package/build/dist/Server/Utils/Monitor/MonitorResource.js +2 -0
  98. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  99. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +65 -0
  100. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  101. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +199 -0
  102. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -1
  103. package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -1
  104. package/build/dist/Types/Email.js +42 -0
  105. package/build/dist/Types/Email.js.map +1 -1
  106. package/build/dist/Types/Monitor/CriteriaFilter.js +10 -0
  107. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  108. package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js +2 -0
  109. package/build/dist/Types/Monitor/MetricMonitor/MetricSeriesResult.js.map +1 -0
  110. package/build/dist/Types/Monitor/MonitorMetricType.js +28 -0
  111. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  112. package/build/dist/Types/StatusPage/StatusPageLanguage.js +21 -0
  113. package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -0
  114. package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -12
  115. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  116. package/build/dist/UI/Components/Charts/Bar/BarChart.js +12 -11
  117. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  118. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +11 -3
  119. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  120. package/build/dist/UI/Components/Charts/Line/LineChart.js +12 -11
  121. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  122. package/build/dist/UI/Components/Filters/DateFilter.js +1 -4
  123. package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
  124. package/build/dist/UI/Components/Filters/EntityFilter.js +21 -14
  125. package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
  126. package/build/dist/UI/Components/Filters/FilterViewer.js +1 -2
  127. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  128. package/build/dist/UI/Components/Filters/FiltersForm.js +7 -3
  129. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  130. package/build/dist/UI/Components/Filters/NumberFilter.js +0 -1
  131. package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
  132. package/build/dist/UI/Components/Filters/TextFilter.js +5 -4
  133. package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
  134. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +5 -3
  135. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  136. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +383 -0
  137. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -0
  138. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +109 -0
  139. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js.map +1 -0
  140. package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js +81 -0
  141. package/build/dist/Utils/Metrics/MetricSeriesFingerprint.js.map +1 -0
  142. package/build/dist/Utils/Monitor/MonitorMetricType.js +287 -19
  143. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  144. package/package.json +1 -1
@@ -10,7 +10,9 @@ import {
10
10
  ServiceType,
11
11
  } from "../../../Models/AnalyticsModels/Metric";
12
12
  import MetricType from "../../../Models/DatabaseModels/MetricType";
13
- import BasicInfrastructureMetrics from "../../../Types/Infrastructure/BasicMetrics";
13
+ import BasicInfrastructureMetrics, {
14
+ NetworkInterfaceMetrics,
15
+ } from "../../../Types/Infrastructure/BasicMetrics";
14
16
  import Dictionary from "../../../Types/Dictionary";
15
17
  import { JSONObject } from "../../../Types/JSON";
16
18
  import CapturedMetric from "../../../Types/Monitor/CustomCodeMonitor/CapturedMetric";
@@ -159,6 +161,73 @@ export default class MonitorMetricUtil {
159
161
  } as JSONObject;
160
162
  }
161
163
 
164
+ /*
165
+ * Helper that collapses the "build attributes → build row → push → register MetricType"
166
+ * pattern used repeatedly below. Silently skips emission when value is missing/non-finite
167
+ * so callers can pass optional agent fields directly.
168
+ */
169
+ private static async pushMonitorMetric(data: {
170
+ projectId: ObjectID;
171
+ monitorId: ObjectID;
172
+ monitorName: string | undefined;
173
+ probeName: string | undefined;
174
+ metricName: string;
175
+ value: number | null | undefined;
176
+ description: string;
177
+ unit: string;
178
+ extraAttributes?: JSONObject;
179
+ metricPointType?: MetricPointType;
180
+ metricRows: Array<JSONObject>;
181
+ metricNameServiceNameMap: Dictionary<MetricType>;
182
+ }): Promise<void> {
183
+ if (
184
+ data.value === undefined ||
185
+ data.value === null ||
186
+ typeof data.value !== "number" ||
187
+ !isFinite(data.value)
188
+ ) {
189
+ return;
190
+ }
191
+
192
+ const attributeInput: {
193
+ monitorId: ObjectID;
194
+ projectId: ObjectID;
195
+ monitorName?: string | undefined;
196
+ probeName?: string | undefined;
197
+ extraAttributes?: JSONObject;
198
+ } = {
199
+ monitorId: data.monitorId,
200
+ projectId: data.projectId,
201
+ monitorName: data.monitorName,
202
+ probeName: data.probeName,
203
+ };
204
+
205
+ if (data.extraAttributes) {
206
+ attributeInput.extraAttributes = data.extraAttributes;
207
+ }
208
+
209
+ const attributes: JSONObject =
210
+ this.buildMonitorMetricAttributes(attributeInput);
211
+
212
+ const metricRow: JSONObject = await this.buildMonitorMetricRow({
213
+ projectId: data.projectId,
214
+ monitorId: data.monitorId,
215
+ metricName: data.metricName,
216
+ value: data.value,
217
+ attributes: attributes,
218
+ metricPointType: data.metricPointType || MetricPointType.Sum,
219
+ });
220
+
221
+ data.metricRows.push(metricRow);
222
+
223
+ const metricType: MetricType = new MetricType();
224
+ metricType.name = data.metricName;
225
+ metricType.description = data.description;
226
+ metricType.unit = data.unit;
227
+
228
+ data.metricNameServiceNameMap[data.metricName] = metricType;
229
+ }
230
+
162
231
  @CaptureSpan()
163
232
  public static async saveMonitorMetrics(data: {
164
233
  monitorId: ObjectID;
@@ -333,6 +402,359 @@ export default class MonitorMetricUtil {
333
402
  metricNameServiceNameMap[MonitorMetricType.DiskUsagePercent] =
334
403
  metricType;
335
404
  }
405
+
406
+ // Per-disk I/O counters (cumulative since boot).
407
+ for (const diskMetric of basicMetrics.diskMetrics) {
408
+ const diskAttrs: JSONObject = {};
409
+ if (diskMetric.diskPath) {
410
+ diskAttrs["diskPath"] = diskMetric.diskPath;
411
+ }
412
+ if (diskMetric.device) {
413
+ diskAttrs["device"] = diskMetric.device;
414
+ }
415
+ if (diskMetric.fstype) {
416
+ diskAttrs["fstype"] = diskMetric.fstype;
417
+ }
418
+
419
+ await this.pushMonitorMetric({
420
+ projectId: data.projectId,
421
+ monitorId: data.monitorId,
422
+ monitorName: data.monitorName,
423
+ probeName: data.probeName,
424
+ metricName: MonitorMetricType.DiskReadBytesTotal,
425
+ value: diskMetric.readBytes,
426
+ description: "Total bytes read from disk since boot",
427
+ unit: "bytes",
428
+ extraAttributes: diskAttrs,
429
+ metricRows,
430
+ metricNameServiceNameMap,
431
+ });
432
+
433
+ await this.pushMonitorMetric({
434
+ projectId: data.projectId,
435
+ monitorId: data.monitorId,
436
+ monitorName: data.monitorName,
437
+ probeName: data.probeName,
438
+ metricName: MonitorMetricType.DiskWriteBytesTotal,
439
+ value: diskMetric.writeBytes,
440
+ description: "Total bytes written to disk since boot",
441
+ unit: "bytes",
442
+ extraAttributes: diskAttrs,
443
+ metricRows,
444
+ metricNameServiceNameMap,
445
+ });
446
+
447
+ await this.pushMonitorMetric({
448
+ projectId: data.projectId,
449
+ monitorId: data.monitorId,
450
+ monitorName: data.monitorName,
451
+ probeName: data.probeName,
452
+ metricName: MonitorMetricType.DiskReadOpsTotal,
453
+ value: diskMetric.readCount,
454
+ description: "Total disk read operations since boot",
455
+ unit: "ops",
456
+ extraAttributes: diskAttrs,
457
+ metricRows,
458
+ metricNameServiceNameMap,
459
+ });
460
+
461
+ await this.pushMonitorMetric({
462
+ projectId: data.projectId,
463
+ monitorId: data.monitorId,
464
+ monitorName: data.monitorName,
465
+ probeName: data.probeName,
466
+ metricName: MonitorMetricType.DiskWriteOpsTotal,
467
+ value: diskMetric.writeCount,
468
+ description: "Total disk write operations since boot",
469
+ unit: "ops",
470
+ extraAttributes: diskAttrs,
471
+ metricRows,
472
+ metricNameServiceNameMap,
473
+ });
474
+ }
475
+ }
476
+
477
+ // Load average (1/5/15 min). Emitted only when the agent provides it.
478
+ if (basicMetrics.loadMetrics) {
479
+ await this.pushMonitorMetric({
480
+ projectId: data.projectId,
481
+ monitorId: data.monitorId,
482
+ monitorName: data.monitorName,
483
+ probeName: data.probeName,
484
+ metricName: MonitorMetricType.LoadAverage1Min,
485
+ value: basicMetrics.loadMetrics.load1,
486
+ description: "1-minute load average",
487
+ unit: "",
488
+ metricRows,
489
+ metricNameServiceNameMap,
490
+ });
491
+ await this.pushMonitorMetric({
492
+ projectId: data.projectId,
493
+ monitorId: data.monitorId,
494
+ monitorName: data.monitorName,
495
+ probeName: data.probeName,
496
+ metricName: MonitorMetricType.LoadAverage5Min,
497
+ value: basicMetrics.loadMetrics.load5,
498
+ description: "5-minute load average",
499
+ unit: "",
500
+ metricRows,
501
+ metricNameServiceNameMap,
502
+ });
503
+ await this.pushMonitorMetric({
504
+ projectId: data.projectId,
505
+ monitorId: data.monitorId,
506
+ monitorName: data.monitorName,
507
+ probeName: data.probeName,
508
+ metricName: MonitorMetricType.LoadAverage15Min,
509
+ value: basicMetrics.loadMetrics.load15,
510
+ description: "15-minute load average",
511
+ unit: "",
512
+ metricRows,
513
+ metricNameServiceNameMap,
514
+ });
515
+ }
516
+
517
+ // Memory extras: swap + available.
518
+ if (basicMetrics.memoryMetrics) {
519
+ await this.pushMonitorMetric({
520
+ projectId: data.projectId,
521
+ monitorId: data.monitorId,
522
+ monitorName: data.monitorName,
523
+ probeName: data.probeName,
524
+ metricName: MonitorMetricType.SwapUsagePercent,
525
+ value: basicMetrics.memoryMetrics.swapPercentUsed,
526
+ description: "Swap memory usage percentage",
527
+ unit: "%",
528
+ metricRows,
529
+ metricNameServiceNameMap,
530
+ });
531
+ await this.pushMonitorMetric({
532
+ projectId: data.projectId,
533
+ monitorId: data.monitorId,
534
+ monitorName: data.monitorName,
535
+ probeName: data.probeName,
536
+ metricName: MonitorMetricType.MemoryAvailableBytes,
537
+ value: basicMetrics.memoryMetrics.available,
538
+ description: "Memory available to new allocations",
539
+ unit: "bytes",
540
+ metricRows,
541
+ metricNameServiceNameMap,
542
+ });
543
+ }
544
+
545
+ // CPU time breakdown — drills down into what the CPU was doing.
546
+ if (basicMetrics.cpuMetrics) {
547
+ await this.pushMonitorMetric({
548
+ projectId: data.projectId,
549
+ monitorId: data.monitorId,
550
+ monitorName: data.monitorName,
551
+ probeName: data.probeName,
552
+ metricName: MonitorMetricType.CPUTimeUserPercent,
553
+ value: basicMetrics.cpuMetrics.timeUserPercent,
554
+ description: "CPU time spent in user space",
555
+ unit: "%",
556
+ metricRows,
557
+ metricNameServiceNameMap,
558
+ });
559
+ await this.pushMonitorMetric({
560
+ projectId: data.projectId,
561
+ monitorId: data.monitorId,
562
+ monitorName: data.monitorName,
563
+ probeName: data.probeName,
564
+ metricName: MonitorMetricType.CPUTimeSystemPercent,
565
+ value: basicMetrics.cpuMetrics.timeSystemPercent,
566
+ description: "CPU time spent in kernel space",
567
+ unit: "%",
568
+ metricRows,
569
+ metricNameServiceNameMap,
570
+ });
571
+ await this.pushMonitorMetric({
572
+ projectId: data.projectId,
573
+ monitorId: data.monitorId,
574
+ monitorName: data.monitorName,
575
+ probeName: data.probeName,
576
+ metricName: MonitorMetricType.CPUTimeIoWaitPercent,
577
+ value: basicMetrics.cpuMetrics.timeIoWaitPercent,
578
+ description: "CPU time spent waiting on I/O",
579
+ unit: "%",
580
+ metricRows,
581
+ metricNameServiceNameMap,
582
+ });
583
+ await this.pushMonitorMetric({
584
+ projectId: data.projectId,
585
+ monitorId: data.monitorId,
586
+ monitorName: data.monitorName,
587
+ probeName: data.probeName,
588
+ metricName: MonitorMetricType.CPUTimeIdlePercent,
589
+ value: basicMetrics.cpuMetrics.timeIdlePercent,
590
+ description: "CPU time spent idle",
591
+ unit: "%",
592
+ metricRows,
593
+ metricNameServiceNameMap,
594
+ });
595
+ await this.pushMonitorMetric({
596
+ projectId: data.projectId,
597
+ monitorId: data.monitorId,
598
+ monitorName: data.monitorName,
599
+ probeName: data.probeName,
600
+ metricName: MonitorMetricType.CPUTimeStealPercent,
601
+ value: basicMetrics.cpuMetrics.timeStealPercent,
602
+ description: "CPU time stolen by the hypervisor",
603
+ unit: "%",
604
+ metricRows,
605
+ metricNameServiceNameMap,
606
+ });
607
+ }
608
+
609
+ // Network counters — both per-interface and aggregate.
610
+ if (basicMetrics.networkMetrics) {
611
+ const net: typeof basicMetrics.networkMetrics =
612
+ basicMetrics.networkMetrics;
613
+
614
+ for (const iface of net.interfaces || []) {
615
+ const ifaceAttrs: JSONObject = {
616
+ interfaceName: (iface as NetworkInterfaceMetrics).interfaceName,
617
+ };
618
+
619
+ await this.pushMonitorMetric({
620
+ projectId: data.projectId,
621
+ monitorId: data.monitorId,
622
+ monitorName: data.monitorName,
623
+ probeName: data.probeName,
624
+ metricName: MonitorMetricType.NetworkBytesReceivedTotal,
625
+ value: iface.bytesReceived,
626
+ description: "Network bytes received since boot (per-interface)",
627
+ unit: "bytes",
628
+ extraAttributes: ifaceAttrs,
629
+ metricRows,
630
+ metricNameServiceNameMap,
631
+ });
632
+ await this.pushMonitorMetric({
633
+ projectId: data.projectId,
634
+ monitorId: data.monitorId,
635
+ monitorName: data.monitorName,
636
+ probeName: data.probeName,
637
+ metricName: MonitorMetricType.NetworkBytesSentTotal,
638
+ value: iface.bytesSent,
639
+ description: "Network bytes sent since boot (per-interface)",
640
+ unit: "bytes",
641
+ extraAttributes: ifaceAttrs,
642
+ metricRows,
643
+ metricNameServiceNameMap,
644
+ });
645
+ await this.pushMonitorMetric({
646
+ projectId: data.projectId,
647
+ monitorId: data.monitorId,
648
+ monitorName: data.monitorName,
649
+ probeName: data.probeName,
650
+ metricName: MonitorMetricType.NetworkPacketsReceivedTotal,
651
+ value: iface.packetsReceived,
652
+ description: "Network packets received since boot (per-interface)",
653
+ unit: "packets",
654
+ extraAttributes: ifaceAttrs,
655
+ metricRows,
656
+ metricNameServiceNameMap,
657
+ });
658
+ await this.pushMonitorMetric({
659
+ projectId: data.projectId,
660
+ monitorId: data.monitorId,
661
+ monitorName: data.monitorName,
662
+ probeName: data.probeName,
663
+ metricName: MonitorMetricType.NetworkPacketsSentTotal,
664
+ value: iface.packetsSent,
665
+ description: "Network packets sent since boot (per-interface)",
666
+ unit: "packets",
667
+ extraAttributes: ifaceAttrs,
668
+ metricRows,
669
+ metricNameServiceNameMap,
670
+ });
671
+ await this.pushMonitorMetric({
672
+ projectId: data.projectId,
673
+ monitorId: data.monitorId,
674
+ monitorName: data.monitorName,
675
+ probeName: data.probeName,
676
+ metricName: MonitorMetricType.NetworkErrorsIn,
677
+ value: iface.errorsIn,
678
+ description: "Network receive errors since boot (per-interface)",
679
+ unit: "errors",
680
+ extraAttributes: ifaceAttrs,
681
+ metricRows,
682
+ metricNameServiceNameMap,
683
+ });
684
+ await this.pushMonitorMetric({
685
+ projectId: data.projectId,
686
+ monitorId: data.monitorId,
687
+ monitorName: data.monitorName,
688
+ probeName: data.probeName,
689
+ metricName: MonitorMetricType.NetworkErrorsOut,
690
+ value: iface.errorsOut,
691
+ description: "Network transmit errors since boot (per-interface)",
692
+ unit: "errors",
693
+ extraAttributes: ifaceAttrs,
694
+ metricRows,
695
+ metricNameServiceNameMap,
696
+ });
697
+ }
698
+
699
+ await this.pushMonitorMetric({
700
+ projectId: data.projectId,
701
+ monitorId: data.monitorId,
702
+ monitorName: data.monitorName,
703
+ probeName: data.probeName,
704
+ metricName: MonitorMetricType.NetworkConnectionsEstablished,
705
+ value: net.connectionsEstablished,
706
+ description: "Count of ESTABLISHED network connections",
707
+ unit: "connections",
708
+ metricRows,
709
+ metricNameServiceNameMap,
710
+ });
711
+ await this.pushMonitorMetric({
712
+ projectId: data.projectId,
713
+ monitorId: data.monitorId,
714
+ monitorName: data.monitorName,
715
+ probeName: data.probeName,
716
+ metricName: MonitorMetricType.NetworkConnectionsListen,
717
+ value: net.connectionsListen,
718
+ description: "Count of LISTENing network sockets",
719
+ unit: "connections",
720
+ metricRows,
721
+ metricNameServiceNameMap,
722
+ });
723
+ }
724
+
725
+ // Host uptime.
726
+ if (basicMetrics.hostMetrics) {
727
+ await this.pushMonitorMetric({
728
+ projectId: data.projectId,
729
+ monitorId: data.monitorId,
730
+ monitorName: data.monitorName,
731
+ probeName: data.probeName,
732
+ metricName: MonitorMetricType.HostUptimeSeconds,
733
+ value: basicMetrics.hostMetrics.uptimeSeconds,
734
+ description: "Host uptime in seconds",
735
+ unit: "seconds",
736
+ metricRows,
737
+ metricNameServiceNameMap,
738
+ });
739
+ }
740
+
741
+ // Process count — simple scalar derived from the processes array.
742
+ const serverProcesses: Array<unknown> | undefined = (
743
+ data.dataToProcess as ServerMonitorResponse
744
+ ).processes;
745
+ if (serverProcesses && serverProcesses.length >= 0) {
746
+ await this.pushMonitorMetric({
747
+ projectId: data.projectId,
748
+ monitorId: data.monitorId,
749
+ monitorName: data.monitorName,
750
+ probeName: data.probeName,
751
+ metricName: MonitorMetricType.ProcessCountTotal,
752
+ value: serverProcesses.length,
753
+ description: "Total running processes on the server",
754
+ unit: "processes",
755
+ metricRows,
756
+ metricNameServiceNameMap,
757
+ });
336
758
  }
337
759
  }
338
760
 
@@ -709,6 +709,7 @@ export default class MonitorResourceUtil {
709
709
  props: {
710
710
  telemetryQuery: telemetryQuery,
711
711
  },
712
+ matchesPerSeries: response.perSeriesMatches,
712
713
  });
713
714
 
714
715
  await MonitorAlert.criteriaMetCreateAlertsAndUpdateMonitorStatus({
@@ -721,6 +722,7 @@ export default class MonitorResourceUtil {
721
722
  props: {
722
723
  telemetryQuery: telemetryQuery,
723
724
  },
725
+ matchesPerSeries: response.perSeriesMatches,
724
726
  });
725
727
  } else if (
726
728
  !response.criteriaMetId &&
@@ -1,4 +1,5 @@
1
1
  import MonitorType from "../../../Types/Monitor/MonitorType";
2
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
2
3
  import { JSONObject } from "../../../Types/JSON";
3
4
  import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
4
5
  import IncomingMonitorRequest from "../../../Types/Monitor/IncomingMonitor/IncomingMonitorRequest";
@@ -21,6 +22,7 @@ import DomainMonitorResponse from "../../../Types/Monitor/DomainMonitor/DomainMo
21
22
  import ExternalStatusPageMonitorResponse, {
22
23
  ExternalStatusPageComponentStatus,
23
24
  } from "../../../Types/Monitor/ExternalStatusPageMonitor/ExternalStatusPageMonitorResponse";
25
+ import MetricMonitorResponse from "../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
24
26
  import Typeof from "../../../Types/Typeof";
25
27
  import VMUtil from "../VM/VMAPI";
26
28
  import DataToProcess from "./DataToProcess";
@@ -37,6 +39,21 @@ export default class MonitorTemplateUtil {
37
39
  public static buildTemplateStorageMap(data: {
38
40
  monitorType: MonitorType;
39
41
  dataToProcess: DataToProcess;
42
+ /**
43
+ * The monitor that fired this criterion. Used to expose identity
44
+ * fields (`{{monitorName}}`, `{{monitorId}}`, etc.) to incident
45
+ * and alert title/description templates. Optional for backwards
46
+ * compatibility with existing callers.
47
+ */
48
+ monitor?: Monitor | undefined;
49
+ /**
50
+ * When set, the attribute values identifying the specific series
51
+ * this template is being rendered for. Each label is exposed to
52
+ * the template under its own key (so `{{host.name}}` works) and
53
+ * also collected under a `seriesLabels` object for iteration.
54
+ * Only populated when a metric monitor fires per-series.
55
+ */
56
+ seriesLabels?: JSONObject | undefined;
40
57
  }): JSONObject {
41
58
  let storageMap: JSONObject = {};
42
59
 
@@ -315,6 +332,31 @@ export default class MonitorTemplateUtil {
315
332
  } as JSONObject;
316
333
  }
317
334
 
335
+ if (
336
+ data.monitorType === MonitorType.Metrics ||
337
+ data.monitorType === MonitorType.Kubernetes ||
338
+ data.monitorType === MonitorType.Docker
339
+ ) {
340
+ const metricResponse: MetricMonitorResponse =
341
+ data.dataToProcess as MetricMonitorResponse;
342
+
343
+ const queryConfigs: Array<unknown> =
344
+ metricResponse.metricViewConfig?.queryConfigs || [];
345
+
346
+ const firstQuery: unknown = queryConfigs[0];
347
+ const metricName: string | undefined = (
348
+ firstQuery as
349
+ | {
350
+ metricQueryData?: { filterData?: { metricName?: string } };
351
+ }
352
+ | undefined
353
+ )?.metricQueryData?.filterData?.metricName;
354
+
355
+ storageMap = {
356
+ metricName: metricName || "",
357
+ } as JSONObject;
358
+ }
359
+
318
360
  if (data.monitorType === MonitorType.ExternalStatusPage) {
319
361
  const externalStatusPageResponse:
320
362
  | ExternalStatusPageMonitorResponse
@@ -347,6 +389,63 @@ export default class MonitorTemplateUtil {
347
389
  logger.error(err);
348
390
  }
349
391
 
392
+ /*
393
+ * Fold series labels onto the storage map so templates like
394
+ * `{{host.name}}` or `{{resource.k8s.container.name}}` resolve at
395
+ * render time. The template engine walks dotted paths as nested
396
+ * property access (`host` → `.name`), so for each dotted label
397
+ * key we build up a nested object rather than storing the flat
398
+ * key. Also expose the full label map under `seriesLabels` for
399
+ * iteration-style templates.
400
+ */
401
+ if (data.seriesLabels && Object.keys(data.seriesLabels).length > 0) {
402
+ for (const key of Object.keys(data.seriesLabels)) {
403
+ const value: unknown = data.seriesLabels[key];
404
+ if (value === undefined || value === null) {
405
+ continue;
406
+ }
407
+ const parts: Array<string> = key.split(".");
408
+ let cursor: JSONObject = storageMap;
409
+ for (let i: number = 0; i < parts.length - 1; i++) {
410
+ const part: string = parts[i]!;
411
+ const existing: unknown = cursor[part];
412
+ if (
413
+ !existing ||
414
+ typeof existing !== "object" ||
415
+ Array.isArray(existing)
416
+ ) {
417
+ cursor[part] = {};
418
+ }
419
+ cursor = cursor[part] as JSONObject;
420
+ }
421
+ cursor[parts[parts.length - 1]!] = value as JSONObject[string];
422
+ }
423
+ storageMap["seriesLabels"] = data.seriesLabels;
424
+ }
425
+
426
+ /*
427
+ * Monitor identity fields. Always exposed (when a monitor is provided),
428
+ * independent of monitorType, so templates like `{{monitorName}}` work
429
+ * uniformly across Server/VM, Probe, Synthetic, Metric monitors, etc.
430
+ */
431
+ if (data.monitor) {
432
+ if (data.monitor.name) {
433
+ storageMap["monitorName"] = data.monitor.name;
434
+ }
435
+ if (data.monitor.id) {
436
+ storageMap["monitorId"] = data.monitor.id.toString();
437
+ }
438
+ if (data.monitor.description) {
439
+ storageMap["monitorDescription"] = data.monitor.description;
440
+ }
441
+ if (data.monitor.slug) {
442
+ storageMap["monitorSlug"] = data.monitor.slug;
443
+ }
444
+ if (data.monitor.monitorType) {
445
+ storageMap["monitorType"] = data.monitor.monitorType;
446
+ }
447
+ }
448
+
350
449
  logger.debug(`Storage Map: ${JSON.stringify(storageMap, null, 2)}`);
351
450
 
352
451
  return storageMap;