@oneuptime/common 10.0.26 → 10.0.28
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/GlobalConfig.ts +18 -0
- package/Server/API/TelemetryAPI.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1772350000000-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/LogAggregationService.ts +1 -1
- package/Server/Utils/AnalyticsDatabase/Statement.ts +3 -1
- package/Server/Utils/Monitor/MonitorLogUtil.ts +92 -23
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +75 -12
- package/UI/Components/LogsViewer/LogsViewer.tsx +32 -26
- package/UI/Components/LogsViewer/components/FacetSection.tsx +45 -7
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +19 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +1 -1
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1772350000000-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1772350000000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +1 -1
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +3 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +69 -15
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +60 -12
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +22 -16
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +26 -6
- package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
- package/package.json +1 -1
|
@@ -605,4 +605,22 @@ export default class GlobalConfig extends GlobalConfigModel {
|
|
|
605
605
|
unique: true,
|
|
606
606
|
})
|
|
607
607
|
public monitorLogRetentionInDays?: number = undefined;
|
|
608
|
+
|
|
609
|
+
@ColumnAccessControl({
|
|
610
|
+
create: [],
|
|
611
|
+
read: [],
|
|
612
|
+
update: [],
|
|
613
|
+
})
|
|
614
|
+
@TableColumn({
|
|
615
|
+
type: TableColumnType.Number,
|
|
616
|
+
title: "Monitor Metric Retention Days",
|
|
617
|
+
description:
|
|
618
|
+
"Number of days to retain monitor metrics. Monitor metrics older than this will be automatically deleted. Default is 1 day.",
|
|
619
|
+
})
|
|
620
|
+
@Column({
|
|
621
|
+
type: ColumnType.Number,
|
|
622
|
+
nullable: true,
|
|
623
|
+
unique: true,
|
|
624
|
+
})
|
|
625
|
+
public monitorMetricRetentionInDays?: number = undefined;
|
|
608
626
|
}
|
|
@@ -212,7 +212,7 @@ router.post(
|
|
|
212
212
|
? OneUptimeDate.fromString(body["endTime"] as string)
|
|
213
213
|
: OneUptimeDate.getCurrentDate();
|
|
214
214
|
|
|
215
|
-
const limit: number = (body["limit"] as number) ||
|
|
215
|
+
const limit: number = (body["limit"] as number) || 500;
|
|
216
216
|
|
|
217
217
|
const serviceIds: Array<ObjectID> | undefined = body["serviceIds"]
|
|
218
218
|
? (body["serviceIds"] as Array<string>).map((id: string) => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1772350000000 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1772350000000";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "GlobalConfig" ADD "monitorMetricRetentionInDays" integer`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "GlobalConfig" ADD CONSTRAINT "UQ_monitor_metric_retention" UNIQUE ("monitorMetricRetentionInDays")`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "GlobalConfig" DROP CONSTRAINT "UQ_monitor_metric_retention"`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "GlobalConfig" DROP COLUMN "monitorMetricRetentionInDays"`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -261,6 +261,7 @@ import { MigrationName1770834237090 } from "./1770834237090-MigrationName";
|
|
|
261
261
|
import { MigrationName1770834237091 } from "./1770834237091-MigrationName";
|
|
262
262
|
import { MigrationName1772111896988 } from "./1772111896988-MigrationName";
|
|
263
263
|
import { MigrationName1772280000000 } from "./1772280000000-MigrationName";
|
|
264
|
+
import { MigrationName1772350000000 } from "./1772350000000-MigrationName";
|
|
264
265
|
|
|
265
266
|
export default [
|
|
266
267
|
InitialMigration,
|
|
@@ -526,4 +527,5 @@ export default [
|
|
|
526
527
|
MigrationName1770834237091,
|
|
527
528
|
MigrationName1772111896988,
|
|
528
529
|
MigrationName1772280000000,
|
|
530
|
+
MigrationName1772350000000,
|
|
529
531
|
];
|
|
@@ -45,7 +45,7 @@ export interface FacetRequest {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export class LogAggregationService {
|
|
48
|
-
private static readonly DEFAULT_FACET_LIMIT: number =
|
|
48
|
+
private static readonly DEFAULT_FACET_LIMIT: number = 500;
|
|
49
49
|
private static readonly TABLE_NAME: string = "LogItem";
|
|
50
50
|
private static readonly TOP_LEVEL_COLUMNS: Set<string> = new Set([
|
|
51
51
|
"severityText",
|
|
@@ -9,6 +9,7 @@ import LessThan from "../../../Types/BaseDatabase/LessThan";
|
|
|
9
9
|
import LessThanOrEqual from "../../../Types/BaseDatabase/LessThanOrEqual";
|
|
10
10
|
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
|
|
11
11
|
import GreaterThanOrNull from "../../../Types/BaseDatabase/GreaterThanOrNull";
|
|
12
|
+
import NotEqual from "../../../Types/BaseDatabase/NotEqual";
|
|
12
13
|
import Search from "../../../Types/BaseDatabase/Search";
|
|
13
14
|
import OneUptimeDate from "../../../Types/Date";
|
|
14
15
|
import Dictionary from "../../../Types/Dictionary";
|
|
@@ -103,7 +104,8 @@ export class Statement implements BaseQueryParams {
|
|
|
103
104
|
v.value instanceof GreaterThan ||
|
|
104
105
|
v.value instanceof GreaterThanOrEqual ||
|
|
105
106
|
v.value instanceof LessThanOrNull ||
|
|
106
|
-
v.value instanceof GreaterThanOrNull
|
|
107
|
+
v.value instanceof GreaterThanOrNull ||
|
|
108
|
+
v.value instanceof NotEqual
|
|
107
109
|
) {
|
|
108
110
|
finalValue = v.value.value;
|
|
109
111
|
} else if (v.value instanceof Includes) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import MonitorLogService from "../../Services/MonitorLogService";
|
|
2
|
+
import GlobalConfigService from "../../Services/GlobalConfigService";
|
|
3
|
+
import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
|
|
2
4
|
import logger from "../Logger";
|
|
3
5
|
import OneUptimeDate from "../../../Types/Date";
|
|
4
6
|
import ObjectID from "../../../Types/ObjectID";
|
|
@@ -6,6 +8,64 @@ import { JSONObject } from "../../../Types/JSON";
|
|
|
6
8
|
import DataToProcess from "./DataToProcess";
|
|
7
9
|
|
|
8
10
|
export default class MonitorLogUtil {
|
|
11
|
+
// Default retention in days if GlobalConfig is not set
|
|
12
|
+
private static readonly DEFAULT_RETENTION_DAYS: number = 1;
|
|
13
|
+
|
|
14
|
+
// Cached retention value to avoid querying GlobalConfig on every monitor check
|
|
15
|
+
private static cachedRetentionDays: number | null = null;
|
|
16
|
+
private static lastCacheRefresh: Date | null = null;
|
|
17
|
+
private static readonly CACHE_TTL_MS: number = 5 * 60 * 1000; // 5 minutes
|
|
18
|
+
|
|
19
|
+
private static async getRetentionDays(): Promise<number> {
|
|
20
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
21
|
+
|
|
22
|
+
// Return cached value if still fresh
|
|
23
|
+
if (
|
|
24
|
+
this.cachedRetentionDays !== null &&
|
|
25
|
+
this.lastCacheRefresh !== null &&
|
|
26
|
+
now.getTime() - this.lastCacheRefresh.getTime() < this.CACHE_TTL_MS
|
|
27
|
+
) {
|
|
28
|
+
return this.cachedRetentionDays;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const globalConfig: GlobalConfig | null =
|
|
33
|
+
await GlobalConfigService.findOneBy({
|
|
34
|
+
query: {
|
|
35
|
+
_id: ObjectID.getZeroObjectID().toString(),
|
|
36
|
+
},
|
|
37
|
+
props: {
|
|
38
|
+
isRoot: true,
|
|
39
|
+
},
|
|
40
|
+
select: {
|
|
41
|
+
monitorLogRetentionInDays: true,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
globalConfig &&
|
|
47
|
+
globalConfig.monitorLogRetentionInDays !== undefined &&
|
|
48
|
+
globalConfig.monitorLogRetentionInDays !== null &&
|
|
49
|
+
globalConfig.monitorLogRetentionInDays > 0
|
|
50
|
+
) {
|
|
51
|
+
this.cachedRetentionDays = globalConfig.monitorLogRetentionInDays;
|
|
52
|
+
} else {
|
|
53
|
+
this.cachedRetentionDays = this.DEFAULT_RETENTION_DAYS;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.lastCacheRefresh = now;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error(
|
|
59
|
+
"Error fetching monitor log retention config, using default:",
|
|
60
|
+
);
|
|
61
|
+
logger.error(error);
|
|
62
|
+
this.cachedRetentionDays = this.DEFAULT_RETENTION_DAYS;
|
|
63
|
+
this.lastCacheRefresh = now;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.cachedRetentionDays;
|
|
67
|
+
}
|
|
68
|
+
|
|
9
69
|
public static saveMonitorLog(data: {
|
|
10
70
|
monitorId: ObjectID;
|
|
11
71
|
projectId: ObjectID;
|
|
@@ -23,28 +83,37 @@ export default class MonitorLogUtil {
|
|
|
23
83
|
return;
|
|
24
84
|
}
|
|
25
85
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
86
|
+
// Fire-and-forget: fetch retention config then insert
|
|
87
|
+
this.getRetentionDays()
|
|
88
|
+
.then((retentionDays: number) => {
|
|
89
|
+
const logIngestionDate: Date = OneUptimeDate.getCurrentDate();
|
|
90
|
+
const logTimestamp: string =
|
|
91
|
+
OneUptimeDate.toClickhouseDateTime(logIngestionDate);
|
|
92
|
+
|
|
93
|
+
const retentionDate: Date = OneUptimeDate.addRemoveDays(
|
|
94
|
+
logIngestionDate,
|
|
95
|
+
retentionDays,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const monitorLogRow: JSONObject = {
|
|
99
|
+
_id: ObjectID.generate().toString(),
|
|
100
|
+
createdAt: logTimestamp,
|
|
101
|
+
updatedAt: logTimestamp,
|
|
102
|
+
projectId: data.projectId.toString(),
|
|
103
|
+
monitorId: data.monitorId.toString(),
|
|
104
|
+
time: logTimestamp,
|
|
105
|
+
logBody: JSON.parse(JSON.stringify(data.dataToProcess)),
|
|
106
|
+
retentionDate: OneUptimeDate.toClickhouseDateTime(retentionDate),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
MonitorLogService.insertJsonRows([monitorLogRow]).catch(
|
|
110
|
+
(err: Error) => {
|
|
111
|
+
logger.error(err);
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
})
|
|
115
|
+
.catch((err: Error) => {
|
|
116
|
+
logger.error(err);
|
|
117
|
+
});
|
|
49
118
|
}
|
|
50
119
|
}
|
|
@@ -2,6 +2,8 @@ import logger from "../Logger";
|
|
|
2
2
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
3
3
|
import TelemetryUtil from "../Telemetry/Telemetry";
|
|
4
4
|
import MetricService from "../../Services/MetricService";
|
|
5
|
+
import GlobalConfigService from "../../Services/GlobalConfigService";
|
|
6
|
+
import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
|
|
5
7
|
import DataToProcess from "./DataToProcess";
|
|
6
8
|
import {
|
|
7
9
|
MetricPointType,
|
|
@@ -20,6 +22,63 @@ import ObjectID from "../../../Types/ObjectID";
|
|
|
20
22
|
import OneUptimeDate from "../../../Types/Date";
|
|
21
23
|
|
|
22
24
|
export default class MonitorMetricUtil {
|
|
25
|
+
// Default retention in days if GlobalConfig is not set
|
|
26
|
+
private static readonly DEFAULT_RETENTION_DAYS: number = 1;
|
|
27
|
+
|
|
28
|
+
// Cached retention value to avoid querying GlobalConfig on every monitor check
|
|
29
|
+
private static cachedRetentionDays: number | null = null;
|
|
30
|
+
private static lastCacheRefresh: Date | null = null;
|
|
31
|
+
private static readonly CACHE_TTL_MS: number = 5 * 60 * 1000; // 5 minutes
|
|
32
|
+
|
|
33
|
+
private static async getRetentionDays(): Promise<number> {
|
|
34
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
35
|
+
|
|
36
|
+
// Return cached value if still fresh
|
|
37
|
+
if (
|
|
38
|
+
this.cachedRetentionDays !== null &&
|
|
39
|
+
this.lastCacheRefresh !== null &&
|
|
40
|
+
now.getTime() - this.lastCacheRefresh.getTime() < this.CACHE_TTL_MS
|
|
41
|
+
) {
|
|
42
|
+
return this.cachedRetentionDays;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const globalConfig: GlobalConfig | null =
|
|
47
|
+
await GlobalConfigService.findOneBy({
|
|
48
|
+
query: {
|
|
49
|
+
_id: ObjectID.getZeroObjectID().toString(),
|
|
50
|
+
},
|
|
51
|
+
props: {
|
|
52
|
+
isRoot: true,
|
|
53
|
+
},
|
|
54
|
+
select: {
|
|
55
|
+
monitorMetricRetentionInDays: true,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
globalConfig &&
|
|
61
|
+
globalConfig.monitorMetricRetentionInDays !== undefined &&
|
|
62
|
+
globalConfig.monitorMetricRetentionInDays !== null &&
|
|
63
|
+
globalConfig.monitorMetricRetentionInDays > 0
|
|
64
|
+
) {
|
|
65
|
+
this.cachedRetentionDays = globalConfig.monitorMetricRetentionInDays;
|
|
66
|
+
} else {
|
|
67
|
+
this.cachedRetentionDays = this.DEFAULT_RETENTION_DAYS;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.lastCacheRefresh = now;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error(
|
|
73
|
+
"Error fetching monitor metric retention config, using default:",
|
|
74
|
+
);
|
|
75
|
+
logger.error(error);
|
|
76
|
+
this.cachedRetentionDays = this.DEFAULT_RETENTION_DAYS;
|
|
77
|
+
this.lastCacheRefresh = now;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return this.cachedRetentionDays;
|
|
81
|
+
}
|
|
23
82
|
private static buildMonitorMetricAttributes(data: {
|
|
24
83
|
monitorId: ObjectID;
|
|
25
84
|
projectId: ObjectID;
|
|
@@ -47,14 +106,14 @@ export default class MonitorMetricUtil {
|
|
|
47
106
|
return attributes;
|
|
48
107
|
}
|
|
49
108
|
|
|
50
|
-
private static buildMonitorMetricRow(data: {
|
|
109
|
+
private static async buildMonitorMetricRow(data: {
|
|
51
110
|
projectId: ObjectID;
|
|
52
111
|
monitorId: ObjectID;
|
|
53
112
|
metricName: string;
|
|
54
113
|
value: number | null | undefined;
|
|
55
114
|
attributes: JSONObject;
|
|
56
115
|
metricPointType?: MetricPointType;
|
|
57
|
-
}): JSONObject {
|
|
116
|
+
}): Promise<JSONObject> {
|
|
58
117
|
const ingestionDate: Date = OneUptimeDate.getCurrentDate();
|
|
59
118
|
const ingestionTimestamp: string =
|
|
60
119
|
OneUptimeDate.toClickhouseDateTime(ingestionDate);
|
|
@@ -65,7 +124,11 @@ export default class MonitorMetricUtil {
|
|
|
65
124
|
const attributeKeys: Array<string> =
|
|
66
125
|
TelemetryUtil.getAttributeKeys(attributes);
|
|
67
126
|
|
|
68
|
-
const
|
|
127
|
+
const retentionDays: number = await this.getRetentionDays();
|
|
128
|
+
const retentionDate: Date = OneUptimeDate.addRemoveDays(
|
|
129
|
+
ingestionDate,
|
|
130
|
+
retentionDays,
|
|
131
|
+
);
|
|
69
132
|
|
|
70
133
|
return {
|
|
71
134
|
_id: ObjectID.generate().toString(),
|
|
@@ -149,7 +212,7 @@ export default class MonitorMetricUtil {
|
|
|
149
212
|
probeName: data.probeName,
|
|
150
213
|
});
|
|
151
214
|
|
|
152
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
215
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
153
216
|
projectId: data.projectId,
|
|
154
217
|
monitorId: data.monitorId,
|
|
155
218
|
metricName: MonitorMetricType.IsOnline,
|
|
@@ -186,7 +249,7 @@ export default class MonitorMetricUtil {
|
|
|
186
249
|
probeName: data.probeName,
|
|
187
250
|
});
|
|
188
251
|
|
|
189
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
252
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
190
253
|
projectId: data.projectId,
|
|
191
254
|
monitorId: data.monitorId,
|
|
192
255
|
metricName: MonitorMetricType.CPUUsagePercent,
|
|
@@ -214,7 +277,7 @@ export default class MonitorMetricUtil {
|
|
|
214
277
|
probeName: data.probeName,
|
|
215
278
|
});
|
|
216
279
|
|
|
217
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
280
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
218
281
|
projectId: data.projectId,
|
|
219
282
|
monitorId: data.monitorId,
|
|
220
283
|
metricName: MonitorMetricType.MemoryUsagePercent,
|
|
@@ -250,7 +313,7 @@ export default class MonitorMetricUtil {
|
|
|
250
313
|
extraAttributes: extraAttributes,
|
|
251
314
|
});
|
|
252
315
|
|
|
253
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
316
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
254
317
|
projectId: data.projectId,
|
|
255
318
|
monitorId: data.monitorId,
|
|
256
319
|
metricName: MonitorMetricType.DiskUsagePercent,
|
|
@@ -288,7 +351,7 @@ export default class MonitorMetricUtil {
|
|
|
288
351
|
extraAttributes: extraAttributes,
|
|
289
352
|
});
|
|
290
353
|
|
|
291
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
354
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
292
355
|
projectId: data.projectId,
|
|
293
356
|
monitorId: data.monitorId,
|
|
294
357
|
metricName: MonitorMetricType.ExecutionTime,
|
|
@@ -345,7 +408,7 @@ export default class MonitorMetricUtil {
|
|
|
345
408
|
extraAttributes: extraAttributes,
|
|
346
409
|
});
|
|
347
410
|
|
|
348
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
411
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
349
412
|
projectId: data.projectId,
|
|
350
413
|
monitorId: data.monitorId,
|
|
351
414
|
metricName: MonitorMetricType.ExecutionTime,
|
|
@@ -380,7 +443,7 @@ export default class MonitorMetricUtil {
|
|
|
380
443
|
extraAttributes: extraAttributes,
|
|
381
444
|
});
|
|
382
445
|
|
|
383
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
446
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
384
447
|
projectId: data.projectId,
|
|
385
448
|
monitorId: data.monitorId,
|
|
386
449
|
metricName: MonitorMetricType.ResponseTime,
|
|
@@ -415,7 +478,7 @@ export default class MonitorMetricUtil {
|
|
|
415
478
|
extraAttributes: extraAttributes,
|
|
416
479
|
});
|
|
417
480
|
|
|
418
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
481
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
419
482
|
projectId: data.projectId,
|
|
420
483
|
monitorId: data.monitorId,
|
|
421
484
|
metricName: MonitorMetricType.IsOnline,
|
|
@@ -449,7 +512,7 @@ export default class MonitorMetricUtil {
|
|
|
449
512
|
extraAttributes: extraAttributes,
|
|
450
513
|
});
|
|
451
514
|
|
|
452
|
-
const metricRow: JSONObject = this.buildMonitorMetricRow({
|
|
515
|
+
const metricRow: JSONObject = await this.buildMonitorMetricRow({
|
|
453
516
|
projectId: data.projectId,
|
|
454
517
|
monitorId: data.monitorId,
|
|
455
518
|
metricName: MonitorMetricType.ResponseStatusCode,
|
|
@@ -465,31 +465,10 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
465
465
|
});
|
|
466
466
|
}, [props.activeFilters, serviceMap]);
|
|
467
467
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (pageError) {
|
|
473
|
-
return <ErrorMessage message={pageError} />;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const toolbarProps: LogsViewerToolbarProps = {
|
|
477
|
-
resultCount: totalItems,
|
|
478
|
-
currentPage,
|
|
479
|
-
totalPages,
|
|
480
|
-
...(props.liveOptions ? { liveOptions: props.liveOptions } : {}),
|
|
481
|
-
...(props.timeRange && props.onTimeRangeChange
|
|
482
|
-
? {
|
|
483
|
-
timeRange: props.timeRange,
|
|
484
|
-
onTimeRangeChange: props.onTimeRangeChange,
|
|
485
|
-
}
|
|
486
|
-
: {}),
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
const showSidebar: boolean =
|
|
490
|
-
props.showFacetSidebar !== false && Boolean(props.facetData);
|
|
491
|
-
|
|
492
|
-
// Replace serviceId UUIDs with human-readable names in value suggestions
|
|
468
|
+
/*
|
|
469
|
+
* Replace serviceId UUIDs with human-readable names in value suggestions
|
|
470
|
+
* Must be before early returns to maintain consistent hook call order.
|
|
471
|
+
*/
|
|
493
472
|
const resolvedValueSuggestions: Record<string, Array<string>> | undefined =
|
|
494
473
|
useMemo(() => {
|
|
495
474
|
if (!props.valueSuggestions) {
|
|
@@ -512,7 +491,10 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
512
491
|
return suggestions;
|
|
513
492
|
}, [props.valueSuggestions, serviceMap]);
|
|
514
493
|
|
|
515
|
-
|
|
494
|
+
/*
|
|
495
|
+
* Wrap onFieldValueSelect to resolve service names back to UUIDs
|
|
496
|
+
* Must be before early returns to maintain consistent hook call order.
|
|
497
|
+
*/
|
|
516
498
|
const handleFieldValueSelectWithServiceResolve:
|
|
517
499
|
| ((fieldKey: string, value: string) => void)
|
|
518
500
|
| undefined = useMemo(() => {
|
|
@@ -537,6 +519,30 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
537
519
|
};
|
|
538
520
|
}, [props.onFieldValueSelect, serviceMap]);
|
|
539
521
|
|
|
522
|
+
if (isPageLoading) {
|
|
523
|
+
return <PageLoader isVisible={true} />;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (pageError) {
|
|
527
|
+
return <ErrorMessage message={pageError} />;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const toolbarProps: LogsViewerToolbarProps = {
|
|
531
|
+
resultCount: totalItems,
|
|
532
|
+
currentPage,
|
|
533
|
+
totalPages,
|
|
534
|
+
...(props.liveOptions ? { liveOptions: props.liveOptions } : {}),
|
|
535
|
+
...(props.timeRange && props.onTimeRangeChange
|
|
536
|
+
? {
|
|
537
|
+
timeRange: props.timeRange,
|
|
538
|
+
onTimeRangeChange: props.onTimeRangeChange,
|
|
539
|
+
}
|
|
540
|
+
: {}),
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const showSidebar: boolean =
|
|
544
|
+
props.showFacetSidebar !== false && Boolean(props.facetData);
|
|
545
|
+
|
|
540
546
|
return (
|
|
541
547
|
<div className="space-y-2">
|
|
542
548
|
{props.showFilters && (
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
FunctionComponent,
|
|
3
|
+
ReactElement,
|
|
4
|
+
useState,
|
|
5
|
+
useMemo,
|
|
6
|
+
} from "react";
|
|
2
7
|
import { FacetValue } from "../types";
|
|
3
8
|
import FacetValueRow from "./FacetValueRow";
|
|
4
9
|
import Icon from "../../Icon/Icon";
|
|
@@ -17,21 +22,40 @@ export interface FacetSectionProps {
|
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
const DEFAULT_VISIBLE_COUNT: number = 5;
|
|
25
|
+
const SEARCH_THRESHOLD: number = 6;
|
|
20
26
|
|
|
21
27
|
const FacetSection: FunctionComponent<FacetSectionProps> = (
|
|
22
28
|
props: FacetSectionProps,
|
|
23
29
|
): ReactElement => {
|
|
24
30
|
const [isExpanded, setIsExpanded] = useState<boolean>(true);
|
|
25
31
|
const [showAll, setShowAll] = useState<boolean>(false);
|
|
32
|
+
const [searchText, setSearchText] = useState<string>("");
|
|
33
|
+
|
|
34
|
+
const showSearch: boolean = props.values.length >= SEARCH_THRESHOLD;
|
|
35
|
+
|
|
36
|
+
const filteredValues: Array<FacetValue> = useMemo(() => {
|
|
37
|
+
if (!searchText.trim()) {
|
|
38
|
+
return props.values;
|
|
39
|
+
}
|
|
40
|
+
const query: string = searchText.toLowerCase().trim();
|
|
41
|
+
return props.values.filter((facet: FacetValue) => {
|
|
42
|
+
const displayName: string =
|
|
43
|
+
props.valueDisplayMap?.[facet.value] ?? facet.value;
|
|
44
|
+
return displayName.toLowerCase().includes(query);
|
|
45
|
+
});
|
|
46
|
+
}, [props.values, props.valueDisplayMap, searchText]);
|
|
26
47
|
|
|
27
48
|
const visibleCount: number =
|
|
28
49
|
props.initialVisibleCount ?? DEFAULT_VISIBLE_COUNT;
|
|
29
50
|
|
|
30
|
-
const displayedValues: Array<FacetValue> =
|
|
31
|
-
?
|
|
32
|
-
:
|
|
51
|
+
const displayedValues: Array<FacetValue> = searchText.trim()
|
|
52
|
+
? filteredValues
|
|
53
|
+
: showAll
|
|
54
|
+
? filteredValues
|
|
55
|
+
: filteredValues.slice(0, visibleCount);
|
|
33
56
|
|
|
34
|
-
const hasMore: boolean =
|
|
57
|
+
const hasMore: boolean =
|
|
58
|
+
!searchText.trim() && filteredValues.length > visibleCount;
|
|
35
59
|
|
|
36
60
|
const maxCount: number =
|
|
37
61
|
props.values.length > 0
|
|
@@ -71,6 +95,20 @@ const FacetSection: FunctionComponent<FacetSectionProps> = (
|
|
|
71
95
|
|
|
72
96
|
{isExpanded && (
|
|
73
97
|
<div className="mt-1 px-1">
|
|
98
|
+
{showSearch && (
|
|
99
|
+
<div className="mb-1 px-1">
|
|
100
|
+
<input
|
|
101
|
+
type="text"
|
|
102
|
+
placeholder={`Search ${props.title.toLowerCase()}...`}
|
|
103
|
+
value={searchText}
|
|
104
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
105
|
+
setSearchText(e.target.value);
|
|
106
|
+
}}
|
|
107
|
+
className="w-full rounded border border-gray-200 bg-gray-50 px-2 py-1 text-[11px] text-gray-700 placeholder-gray-400 outline-none focus:border-indigo-300 focus:bg-white focus:ring-1 focus:ring-indigo-200"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
|
|
74
112
|
{displayedValues.map((facet: FacetValue) => {
|
|
75
113
|
return (
|
|
76
114
|
<FacetValueRow
|
|
@@ -91,9 +129,9 @@ const FacetSection: FunctionComponent<FacetSectionProps> = (
|
|
|
91
129
|
);
|
|
92
130
|
})}
|
|
93
131
|
|
|
94
|
-
{
|
|
132
|
+
{displayedValues.length === 0 && (
|
|
95
133
|
<p className="px-1 py-2 text-[11px] text-gray-400">
|
|
96
|
-
No values found
|
|
134
|
+
{searchText.trim() ? "No matches found" : "No values found"}
|
|
97
135
|
</p>
|
|
98
136
|
)}
|
|
99
137
|
|
|
@@ -65,6 +65,7 @@ let GlobalConfig = class GlobalConfig extends GlobalConfigModel {
|
|
|
65
65
|
this.enterpriseLicenseExpiresAt = undefined;
|
|
66
66
|
this.enterpriseLicenseToken = undefined;
|
|
67
67
|
this.monitorLogRetentionInDays = undefined;
|
|
68
|
+
this.monitorMetricRetentionInDays = undefined;
|
|
68
69
|
}
|
|
69
70
|
};
|
|
70
71
|
__decorate([
|
|
@@ -654,6 +655,24 @@ __decorate([
|
|
|
654
655
|
}),
|
|
655
656
|
__metadata("design:type", Number)
|
|
656
657
|
], GlobalConfig.prototype, "monitorLogRetentionInDays", void 0);
|
|
658
|
+
__decorate([
|
|
659
|
+
ColumnAccessControl({
|
|
660
|
+
create: [],
|
|
661
|
+
read: [],
|
|
662
|
+
update: [],
|
|
663
|
+
}),
|
|
664
|
+
TableColumn({
|
|
665
|
+
type: TableColumnType.Number,
|
|
666
|
+
title: "Monitor Metric Retention Days",
|
|
667
|
+
description: "Number of days to retain monitor metrics. Monitor metrics older than this will be automatically deleted. Default is 1 day.",
|
|
668
|
+
}),
|
|
669
|
+
Column({
|
|
670
|
+
type: ColumnType.Number,
|
|
671
|
+
nullable: true,
|
|
672
|
+
unique: true,
|
|
673
|
+
}),
|
|
674
|
+
__metadata("design:type", Number)
|
|
675
|
+
], GlobalConfig.prototype, "monitorMetricRetentionInDays", void 0);
|
|
657
676
|
GlobalConfig = __decorate([
|
|
658
677
|
TableMetadata({
|
|
659
678
|
tableName: "GlobalConfig",
|