@oneuptime/common 10.0.31 → 10.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/ExceptionInstance.ts +29 -4
- package/Models/AnalyticsModels/Log.ts +110 -4
- package/Models/AnalyticsModels/Metric.ts +16 -9
- package/Models/AnalyticsModels/MonitorLog.ts +4 -2
- package/Models/AnalyticsModels/Span.ts +79 -6
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/LogDropFilter.ts +480 -0
- package/Models/DatabaseModels/LogPipeline.ts +412 -0
- package/Models/DatabaseModels/LogPipelineProcessor.ts +430 -0
- package/Models/DatabaseModels/LogScrubRule.ts +516 -0
- package/Server/API/TelemetryAPI.ts +261 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.ts +57 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/WhatsAppAuthorization.ts +87 -0
- package/Server/Services/AnalyticsDatabaseService.ts +61 -0
- package/Server/Services/LogAggregationService.ts +238 -1
- package/Server/Services/LogDropFilterService.ts +10 -0
- package/Server/Services/LogPipelineProcessorService.ts +10 -0
- package/Server/Services/LogPipelineService.ts +10 -0
- package/Server/Services/LogScrubRuleService.ts +10 -0
- package/Server/Services/TelemetryAttributeService.ts +4 -6
- package/Server/Utils/AnalyticsDatabase/Statement.ts +15 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +138 -11
- package/Tests/Server/Services/LogAggregationService.test.ts +3 -2
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +9 -0
- package/Types/AnalyticsDatabase/TableColumnType.ts +4 -0
- package/Types/Date.ts +22 -0
- package/Types/Log/LogDropFilterAction.ts +6 -0
- package/Types/Log/LogPipelineProcessorType.ts +44 -0
- package/Types/Log/LogScrubAction.ts +7 -0
- package/Types/Log/LogScrubPatternType.ts +10 -0
- package/Types/Permission.ts +174 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +152 -4
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +92 -0
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +332 -117
- package/UI/Components/LogsViewer/components/LogSearchBar.tsx +294 -274
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +513 -234
- package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +37 -29
- package/UI/Components/LogsViewer/components/LogsTable.tsx +6 -1
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +106 -0
- package/UI/Utils/LogExport.ts +160 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +28 -4
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +97 -4
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +16 -9
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MonitorLog.js +4 -2
- package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +73 -6
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +508 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogPipeline.js +438 -0
- package/build/dist/Models/DatabaseModels/LogPipeline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogPipelineProcessor.js +452 -0
- package/build/dist/Models/DatabaseModels/LogPipelineProcessor.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogScrubRule.js +545 -0
- package/build/dist/Models/DatabaseModels/LogScrubRule.js.map +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +155 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.js +52 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773402621107-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773676206197-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js +58 -0
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +30 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +188 -1
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogDropFilterService.js +9 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js +9 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -0
- package/build/dist/Server/Services/LogPipelineService.js +9 -0
- package/build/dist/Server/Services/LogPipelineService.js.map +1 -0
- package/build/dist/Server/Services/LogScrubRuleService.js +9 -0
- package/build/dist/Server/Services/LogScrubRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js +4 -6
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +13 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +98 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js +3 -2
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js.map +1 -1
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +10 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js +4 -0
- package/build/dist/Types/AnalyticsDatabase/TableColumnType.js.map +1 -1
- package/build/dist/Types/Date.js +16 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Log/LogDropFilterAction.js +7 -0
- package/build/dist/Types/Log/LogDropFilterAction.js.map +1 -0
- package/build/dist/Types/Log/LogPipelineProcessorType.js +9 -0
- package/build/dist/Types/Log/LogPipelineProcessorType.js.map +1 -0
- package/build/dist/Types/Log/LogScrubAction.js +8 -0
- package/build/dist/Types/Log/LogScrubAction.js.map +1 -0
- package/build/dist/Types/Log/LogScrubPatternType.js +11 -0
- package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -0
- package/build/dist/Types/Permission.js +152 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +124 -11
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +36 -0
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +114 -4
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js +17 -5
- package/build/dist/UI/Components/LogsViewer/components/LogSearchBar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +229 -122
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +5 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +4 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +28 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Utils/LogExport.js +129 -0
- package/build/dist/UI/Utils/LogExport.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExpressResponse,
|
|
3
|
+
NextFunction,
|
|
4
|
+
OneUptimeRequest,
|
|
5
|
+
} from "../Utils/Express";
|
|
6
|
+
import Response from "../Utils/Response";
|
|
7
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
8
|
+
import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
|
|
9
|
+
import GlobalConfigService from "../Services/GlobalConfigService";
|
|
10
|
+
import ObjectID from "../../Types/ObjectID";
|
|
11
|
+
import crypto from "crypto";
|
|
12
|
+
import logger from "../Utils/Logger";
|
|
13
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
14
|
+
|
|
15
|
+
export default class WhatsAppAuthorization {
|
|
16
|
+
@CaptureSpan()
|
|
17
|
+
public static async isAuthorizedWhatsAppRequest(
|
|
18
|
+
req: OneUptimeRequest,
|
|
19
|
+
res: ExpressResponse,
|
|
20
|
+
next: NextFunction,
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
logger.debug("Starting WhatsApp webhook signature verification");
|
|
23
|
+
|
|
24
|
+
const signature: string | undefined = req.headers["x-hub-signature-256"] as
|
|
25
|
+
| string
|
|
26
|
+
| undefined;
|
|
27
|
+
|
|
28
|
+
if (!signature) {
|
|
29
|
+
logger.error(
|
|
30
|
+
"WhatsApp webhook request missing X-Hub-Signature-256 header.",
|
|
31
|
+
);
|
|
32
|
+
return Response.sendErrorResponse(
|
|
33
|
+
req,
|
|
34
|
+
res,
|
|
35
|
+
new BadDataException("Missing X-Hub-Signature-256 header."),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const globalConfig: GlobalConfig | null =
|
|
40
|
+
await GlobalConfigService.findOneBy({
|
|
41
|
+
query: {
|
|
42
|
+
_id: ObjectID.getZeroObjectID().toString(),
|
|
43
|
+
},
|
|
44
|
+
props: {
|
|
45
|
+
isRoot: true,
|
|
46
|
+
},
|
|
47
|
+
select: {
|
|
48
|
+
metaWhatsAppAppSecret: true,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const appSecret: string | undefined =
|
|
53
|
+
globalConfig?.metaWhatsAppAppSecret?.trim() || undefined;
|
|
54
|
+
|
|
55
|
+
if (!appSecret) {
|
|
56
|
+
logger.error(
|
|
57
|
+
"Meta WhatsApp App Secret is not configured. Cannot verify webhook signature.",
|
|
58
|
+
);
|
|
59
|
+
return Response.sendErrorResponse(
|
|
60
|
+
req,
|
|
61
|
+
res,
|
|
62
|
+
new BadDataException("Meta WhatsApp App Secret is not configured."),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const rawBody: string = req.rawBody || "";
|
|
67
|
+
|
|
68
|
+
const expectedSignature: string = `sha256=${crypto.createHmac("sha256", appSecret).update(rawBody).digest("hex")}`;
|
|
69
|
+
|
|
70
|
+
if (
|
|
71
|
+
!crypto.timingSafeEqual(
|
|
72
|
+
Buffer.from(expectedSignature) as Uint8Array,
|
|
73
|
+
Buffer.from(signature) as Uint8Array,
|
|
74
|
+
)
|
|
75
|
+
) {
|
|
76
|
+
logger.error("WhatsApp webhook signature verification failed.");
|
|
77
|
+
return Response.sendErrorResponse(
|
|
78
|
+
req,
|
|
79
|
+
res,
|
|
80
|
+
new BadDataException("WhatsApp webhook signature verification failed."),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
logger.debug("WhatsApp webhook signature verified successfully");
|
|
85
|
+
next();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -238,6 +238,13 @@ export default class AnalyticsDatabaseService<
|
|
|
238
238
|
const statement: Statement =
|
|
239
239
|
this.statementGenerator.toAddColumnStatement(column);
|
|
240
240
|
await this.execute(statement);
|
|
241
|
+
|
|
242
|
+
// Add skip index separately (ClickHouse requires ADD INDEX as a separate ALTER statement)
|
|
243
|
+
const indexStatement: Statement | null =
|
|
244
|
+
this.statementGenerator.toAddSkipIndexStatement(column);
|
|
245
|
+
if (indexStatement) {
|
|
246
|
+
await this.execute(indexStatement);
|
|
247
|
+
}
|
|
241
248
|
}
|
|
242
249
|
|
|
243
250
|
@CaptureSpan()
|
|
@@ -247,6 +254,60 @@ export default class AnalyticsDatabaseService<
|
|
|
247
254
|
);
|
|
248
255
|
}
|
|
249
256
|
|
|
257
|
+
public async doesColumnExist(columnName: string): Promise<boolean> {
|
|
258
|
+
const tableName: string = this.model.tableName;
|
|
259
|
+
const result: { data: Array<JSONObject> } = await (
|
|
260
|
+
await this.executeQuery(
|
|
261
|
+
`SELECT count() as cnt FROM system.columns WHERE database = currentDatabase() AND table = '${tableName}' AND name = '${columnName}'`,
|
|
262
|
+
)
|
|
263
|
+
).json();
|
|
264
|
+
|
|
265
|
+
const rows: Array<JSONObject> = result.data || [];
|
|
266
|
+
|
|
267
|
+
return rows.length > 0 && Number(rows[0]!["cnt"]) > 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public async getColumnCodec(columnName: string): Promise<string> {
|
|
271
|
+
const tableName: string = this.model.tableName;
|
|
272
|
+
const result: { data: Array<JSONObject> } = await (
|
|
273
|
+
await this.executeQuery(
|
|
274
|
+
`SELECT compression_codec FROM system.columns WHERE database = currentDatabase() AND table = '${tableName}' AND name = '${columnName}'`,
|
|
275
|
+
)
|
|
276
|
+
).json();
|
|
277
|
+
|
|
278
|
+
const rows: Array<JSONObject> = result.data || [];
|
|
279
|
+
|
|
280
|
+
if (rows.length === 0) {
|
|
281
|
+
return "";
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return (rows[0]!["compression_codec"] as string) || "";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public async setColumnCodecIfNotSet(data: {
|
|
288
|
+
columnName: string;
|
|
289
|
+
columnType: string;
|
|
290
|
+
codec: string;
|
|
291
|
+
expectedCodecValue: string;
|
|
292
|
+
}): Promise<void> {
|
|
293
|
+
const tableName: string = this.model.tableName;
|
|
294
|
+
const currentCodec: string = await this.getColumnCodec(data.columnName);
|
|
295
|
+
|
|
296
|
+
if (currentCodec === data.expectedCodecValue) {
|
|
297
|
+
logger.info(
|
|
298
|
+
`${tableName}.${data.columnName} already has ${data.expectedCodecValue}, skipping`,
|
|
299
|
+
);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await this.execute(
|
|
304
|
+
`ALTER TABLE ${tableName} MODIFY COLUMN ${data.columnName} ${data.columnType} CODEC(${data.codec}) SETTINGS mutations_sync=0`,
|
|
305
|
+
);
|
|
306
|
+
logger.info(
|
|
307
|
+
`Applied ${data.codec} codec to ${tableName}.${data.columnName} (async)`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
250
311
|
@CaptureSpan()
|
|
251
312
|
public async findBy(findBy: FindBy<TBaseModel>): Promise<Array<TBaseModel>> {
|
|
252
313
|
return await this._findBy(findBy);
|
|
@@ -5,6 +5,7 @@ import { JSONObject } from "../../Types/JSON";
|
|
|
5
5
|
import ObjectID from "../../Types/ObjectID";
|
|
6
6
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
7
7
|
import Includes from "../../Types/BaseDatabase/Includes";
|
|
8
|
+
import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
8
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
9
10
|
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
10
11
|
|
|
@@ -82,7 +83,7 @@ export interface AnalyticsTableRow {
|
|
|
82
83
|
|
|
83
84
|
export class LogAggregationService {
|
|
84
85
|
private static readonly DEFAULT_FACET_LIMIT: number = 500;
|
|
85
|
-
private static readonly TABLE_NAME: string =
|
|
86
|
+
private static readonly TABLE_NAME: string = AnalyticsTableName.Log;
|
|
86
87
|
private static readonly TOP_LEVEL_COLUMNS: Set<string> = new Set([
|
|
87
88
|
"severityText",
|
|
88
89
|
"serviceId",
|
|
@@ -641,6 +642,242 @@ export class LogAggregationService {
|
|
|
641
642
|
}
|
|
642
643
|
}
|
|
643
644
|
|
|
645
|
+
@CaptureSpan()
|
|
646
|
+
public static async getExportLogs(request: {
|
|
647
|
+
projectId: ObjectID;
|
|
648
|
+
startTime: Date;
|
|
649
|
+
endTime: Date;
|
|
650
|
+
limit: number;
|
|
651
|
+
serviceIds?: Array<ObjectID> | undefined;
|
|
652
|
+
severityTexts?: Array<string> | undefined;
|
|
653
|
+
bodySearchText?: string | undefined;
|
|
654
|
+
traceIds?: Array<string> | undefined;
|
|
655
|
+
spanIds?: Array<string> | undefined;
|
|
656
|
+
}): Promise<Array<JSONObject>> {
|
|
657
|
+
const maxLimit: number = Math.min(request.limit || 10000, 10000);
|
|
658
|
+
|
|
659
|
+
const statement: Statement = SQL`
|
|
660
|
+
SELECT
|
|
661
|
+
time,
|
|
662
|
+
serviceId,
|
|
663
|
+
severityText,
|
|
664
|
+
severityNumber,
|
|
665
|
+
body,
|
|
666
|
+
traceId,
|
|
667
|
+
spanId,
|
|
668
|
+
attributes
|
|
669
|
+
FROM ${LogAggregationService.TABLE_NAME}
|
|
670
|
+
WHERE projectId = ${{
|
|
671
|
+
type: TableColumnType.ObjectID,
|
|
672
|
+
value: request.projectId,
|
|
673
|
+
}}
|
|
674
|
+
AND time >= ${{
|
|
675
|
+
type: TableColumnType.Date,
|
|
676
|
+
value: request.startTime,
|
|
677
|
+
}}
|
|
678
|
+
AND time <= ${{
|
|
679
|
+
type: TableColumnType.Date,
|
|
680
|
+
value: request.endTime,
|
|
681
|
+
}}
|
|
682
|
+
`;
|
|
683
|
+
|
|
684
|
+
LogAggregationService.appendCommonFilters(statement, request);
|
|
685
|
+
|
|
686
|
+
statement.append(
|
|
687
|
+
SQL` ORDER BY time DESC LIMIT ${{
|
|
688
|
+
type: TableColumnType.Number,
|
|
689
|
+
value: maxLimit,
|
|
690
|
+
}}`,
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
const dbResult: Results = await LogDatabaseService.executeQuery(statement);
|
|
694
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
695
|
+
data?: Array<JSONObject>;
|
|
696
|
+
}>();
|
|
697
|
+
|
|
698
|
+
return response.data || [];
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
@CaptureSpan()
|
|
702
|
+
public static async getLogContext(request: {
|
|
703
|
+
projectId: ObjectID;
|
|
704
|
+
serviceId: ObjectID;
|
|
705
|
+
time: Date;
|
|
706
|
+
logId: string;
|
|
707
|
+
count: number;
|
|
708
|
+
}): Promise<{ before: Array<JSONObject>; after: Array<JSONObject> }> {
|
|
709
|
+
const count: number = Math.min(request.count || 5, 20);
|
|
710
|
+
|
|
711
|
+
const beforeStatement: Statement = SQL`
|
|
712
|
+
SELECT
|
|
713
|
+
_id,
|
|
714
|
+
time,
|
|
715
|
+
timeUnixNano,
|
|
716
|
+
serviceId,
|
|
717
|
+
severityText,
|
|
718
|
+
severityNumber,
|
|
719
|
+
body,
|
|
720
|
+
traceId,
|
|
721
|
+
spanId,
|
|
722
|
+
attributes
|
|
723
|
+
FROM ${LogAggregationService.TABLE_NAME}
|
|
724
|
+
WHERE projectId = ${{
|
|
725
|
+
type: TableColumnType.ObjectID,
|
|
726
|
+
value: request.projectId,
|
|
727
|
+
}}
|
|
728
|
+
AND serviceId = ${{
|
|
729
|
+
type: TableColumnType.ObjectID,
|
|
730
|
+
value: request.serviceId,
|
|
731
|
+
}}
|
|
732
|
+
AND time <= ${{
|
|
733
|
+
type: TableColumnType.Date,
|
|
734
|
+
value: request.time,
|
|
735
|
+
}}
|
|
736
|
+
AND _id != ${{
|
|
737
|
+
type: TableColumnType.Text,
|
|
738
|
+
value: request.logId,
|
|
739
|
+
}}
|
|
740
|
+
ORDER BY time DESC, timeUnixNano DESC
|
|
741
|
+
LIMIT ${{
|
|
742
|
+
type: TableColumnType.Number,
|
|
743
|
+
value: count,
|
|
744
|
+
}}
|
|
745
|
+
`;
|
|
746
|
+
|
|
747
|
+
const afterStatement: Statement = SQL`
|
|
748
|
+
SELECT
|
|
749
|
+
_id,
|
|
750
|
+
time,
|
|
751
|
+
timeUnixNano,
|
|
752
|
+
serviceId,
|
|
753
|
+
severityText,
|
|
754
|
+
severityNumber,
|
|
755
|
+
body,
|
|
756
|
+
traceId,
|
|
757
|
+
spanId,
|
|
758
|
+
attributes
|
|
759
|
+
FROM ${LogAggregationService.TABLE_NAME}
|
|
760
|
+
WHERE projectId = ${{
|
|
761
|
+
type: TableColumnType.ObjectID,
|
|
762
|
+
value: request.projectId,
|
|
763
|
+
}}
|
|
764
|
+
AND serviceId = ${{
|
|
765
|
+
type: TableColumnType.ObjectID,
|
|
766
|
+
value: request.serviceId,
|
|
767
|
+
}}
|
|
768
|
+
AND time >= ${{
|
|
769
|
+
type: TableColumnType.Date,
|
|
770
|
+
value: request.time,
|
|
771
|
+
}}
|
|
772
|
+
AND _id != ${{
|
|
773
|
+
type: TableColumnType.Text,
|
|
774
|
+
value: request.logId,
|
|
775
|
+
}}
|
|
776
|
+
ORDER BY time ASC, timeUnixNano ASC
|
|
777
|
+
LIMIT ${{
|
|
778
|
+
type: TableColumnType.Number,
|
|
779
|
+
value: count,
|
|
780
|
+
}}
|
|
781
|
+
`;
|
|
782
|
+
|
|
783
|
+
const [beforeResult, afterResult] = await Promise.all([
|
|
784
|
+
LogDatabaseService.executeQuery(beforeStatement),
|
|
785
|
+
LogDatabaseService.executeQuery(afterStatement),
|
|
786
|
+
]);
|
|
787
|
+
|
|
788
|
+
const beforeResponse: DbJSONResponse = await beforeResult.json<{
|
|
789
|
+
data?: Array<JSONObject>;
|
|
790
|
+
}>();
|
|
791
|
+
const afterResponse: DbJSONResponse = await afterResult.json<{
|
|
792
|
+
data?: Array<JSONObject>;
|
|
793
|
+
}>();
|
|
794
|
+
|
|
795
|
+
const beforeRows: Array<JSONObject> = (beforeResponse.data || []).reverse();
|
|
796
|
+
const afterRows: Array<JSONObject> = afterResponse.data || [];
|
|
797
|
+
|
|
798
|
+
return { before: beforeRows, after: afterRows };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
@CaptureSpan()
|
|
802
|
+
public static async getDropFilterEstimate(request: {
|
|
803
|
+
projectId: ObjectID;
|
|
804
|
+
startTime: Date;
|
|
805
|
+
endTime: Date;
|
|
806
|
+
filterQuery: string;
|
|
807
|
+
serviceIds?: Array<ObjectID> | undefined;
|
|
808
|
+
severityTexts?: Array<string> | undefined;
|
|
809
|
+
bodySearchText?: string | undefined;
|
|
810
|
+
}): Promise<{
|
|
811
|
+
totalLogs: number;
|
|
812
|
+
matchingLogs: number;
|
|
813
|
+
estimatedReductionPercent: number;
|
|
814
|
+
}> {
|
|
815
|
+
// Get total count
|
|
816
|
+
const totalStatement: Statement = SQL`
|
|
817
|
+
SELECT count() AS cnt
|
|
818
|
+
FROM ${LogAggregationService.TABLE_NAME}
|
|
819
|
+
WHERE projectId = ${{
|
|
820
|
+
type: TableColumnType.ObjectID,
|
|
821
|
+
value: request.projectId,
|
|
822
|
+
}}
|
|
823
|
+
AND time >= ${{
|
|
824
|
+
type: TableColumnType.Date,
|
|
825
|
+
value: request.startTime,
|
|
826
|
+
}}
|
|
827
|
+
AND time <= ${{
|
|
828
|
+
type: TableColumnType.Date,
|
|
829
|
+
value: request.endTime,
|
|
830
|
+
}}
|
|
831
|
+
`;
|
|
832
|
+
|
|
833
|
+
LogAggregationService.appendCommonFilters(totalStatement, request);
|
|
834
|
+
|
|
835
|
+
// Get matching count using the filter query as body search
|
|
836
|
+
const matchStatement: Statement = SQL`
|
|
837
|
+
SELECT count() AS cnt
|
|
838
|
+
FROM ${LogAggregationService.TABLE_NAME}
|
|
839
|
+
WHERE projectId = ${{
|
|
840
|
+
type: TableColumnType.ObjectID,
|
|
841
|
+
value: request.projectId,
|
|
842
|
+
}}
|
|
843
|
+
AND time >= ${{
|
|
844
|
+
type: TableColumnType.Date,
|
|
845
|
+
value: request.startTime,
|
|
846
|
+
}}
|
|
847
|
+
AND time <= ${{
|
|
848
|
+
type: TableColumnType.Date,
|
|
849
|
+
value: request.endTime,
|
|
850
|
+
}}
|
|
851
|
+
`;
|
|
852
|
+
|
|
853
|
+
LogAggregationService.appendCommonFilters(matchStatement, {
|
|
854
|
+
...request,
|
|
855
|
+
bodySearchText: request.filterQuery,
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
const [totalResult, matchResult] = await Promise.all([
|
|
859
|
+
LogDatabaseService.executeQuery(totalStatement),
|
|
860
|
+
LogDatabaseService.executeQuery(matchStatement),
|
|
861
|
+
]);
|
|
862
|
+
|
|
863
|
+
const totalResponse: DbJSONResponse = await totalResult.json<{
|
|
864
|
+
data?: Array<JSONObject>;
|
|
865
|
+
}>();
|
|
866
|
+
const matchResponse: DbJSONResponse = await matchResult.json<{
|
|
867
|
+
data?: Array<JSONObject>;
|
|
868
|
+
}>();
|
|
869
|
+
|
|
870
|
+
const totalData: Array<JSONObject> = totalResponse.data || [];
|
|
871
|
+
const matchData: Array<JSONObject> = matchResponse.data || [];
|
|
872
|
+
|
|
873
|
+
const totalLogs: number = Number(totalData[0]?.["cnt"] || 0);
|
|
874
|
+
const matchingLogs: number = Number(matchData[0]?.["cnt"] || 0);
|
|
875
|
+
const estimatedReductionPercent: number =
|
|
876
|
+
totalLogs > 0 ? Math.round((matchingLogs / totalLogs) * 100) : 0;
|
|
877
|
+
|
|
878
|
+
return { totalLogs, matchingLogs, estimatedReductionPercent };
|
|
879
|
+
}
|
|
880
|
+
|
|
644
881
|
private static isTopLevelColumn(key: string): boolean {
|
|
645
882
|
return LogAggregationService.TOP_LEVEL_COLUMNS.has(key);
|
|
646
883
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/LogPipelineProcessor";
|
|
3
|
+
|
|
4
|
+
export class Service extends DatabaseService<Model> {
|
|
5
|
+
public constructor() {
|
|
6
|
+
super(Model);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default new Service();
|
|
@@ -227,8 +227,8 @@ export class TelemetryAttributeService {
|
|
|
227
227
|
WITH filtered AS (
|
|
228
228
|
SELECT arrayJoin(
|
|
229
229
|
if(
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
empty(${data.attributeKeysColumn}),
|
|
231
|
+
mapKeys(${data.attributesColumn}),
|
|
232
232
|
${data.attributeKeysColumn}
|
|
233
233
|
)
|
|
234
234
|
) AS attribute
|
|
@@ -238,10 +238,8 @@ export class TelemetryAttributeService {
|
|
|
238
238
|
value: data.projectId,
|
|
239
239
|
}}
|
|
240
240
|
AND (
|
|
241
|
-
${data.attributeKeysColumn}
|
|
242
|
-
|
|
243
|
-
${data.attributesColumn} != ''
|
|
244
|
-
)
|
|
241
|
+
NOT empty(${data.attributeKeysColumn}) OR
|
|
242
|
+
NOT empty(${data.attributesColumn})
|
|
245
243
|
)
|
|
246
244
|
AND ${data.timeColumn} >= ${{
|
|
247
245
|
type: TableColumnType.Date,
|
|
@@ -120,7 +120,11 @@ export class Statement implements BaseQueryParams {
|
|
|
120
120
|
finalValue = v.value.values;
|
|
121
121
|
}
|
|
122
122
|
} else if (v.value instanceof Date) {
|
|
123
|
-
|
|
123
|
+
if (typeof v !== "string" && v.type === TableColumnType.DateTime64) {
|
|
124
|
+
finalValue = OneUptimeDate.toClickhouseDateTime64(v.value);
|
|
125
|
+
} else {
|
|
126
|
+
finalValue = OneUptimeDate.toClickhouseDateTime(v.value);
|
|
127
|
+
}
|
|
124
128
|
} else {
|
|
125
129
|
finalValue = v.value;
|
|
126
130
|
}
|
|
@@ -136,6 +140,15 @@ export class Statement implements BaseQueryParams {
|
|
|
136
140
|
finalValue = OneUptimeDate.toClickhouseDateTime(finalValue);
|
|
137
141
|
}
|
|
138
142
|
|
|
143
|
+
if (
|
|
144
|
+
typeof v !== "string" &&
|
|
145
|
+
v.type === TableColumnType.DateTime64 &&
|
|
146
|
+
!(v.value instanceof Date)
|
|
147
|
+
) {
|
|
148
|
+
finalValue = OneUptimeDate.fromString(finalValue as string);
|
|
149
|
+
finalValue = OneUptimeDate.toClickhouseDateTime64(finalValue);
|
|
150
|
+
}
|
|
151
|
+
|
|
139
152
|
return finalValue;
|
|
140
153
|
}
|
|
141
154
|
|
|
@@ -176,6 +189,7 @@ export class Statement implements BaseQueryParams {
|
|
|
176
189
|
[TableColumnType.Number]: "Int32",
|
|
177
190
|
[TableColumnType.Decimal]: "Double",
|
|
178
191
|
[TableColumnType.Date]: "DateTime",
|
|
192
|
+
[TableColumnType.DateTime64]: "DateTime64(9)",
|
|
179
193
|
[TableColumnType.JSON]: "JSON",
|
|
180
194
|
[TableColumnType.ArrayNumber]: "Array(Int32)",
|
|
181
195
|
[TableColumnType.ArrayText]: "Array(String)",
|