@oneuptime/common 10.0.55 → 10.0.57
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/DockerHost.ts +662 -0
- package/Models/DatabaseModels/GlobalConfig.ts +112 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Server/API/TelemetryAPI.ts +360 -16
- package/Server/Infrastructure/ClickhouseConfig.ts +9 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1774000000002-MigrationName.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1775766676723-MigrationName.ts +133 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1775900000000-AddGlobalSmtpOAuth.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/DockerHostService.ts +173 -0
- package/Server/Services/ExceptionAggregationService.ts +335 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LogAggregationService.ts +17 -0
- package/Server/Services/MonitorService.ts +21 -21
- package/Server/Services/TraceAggregationService.ts +514 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +73 -1
- package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
- package/Tests/__mocks__/mermaid.js +18 -0
- package/Tests/__mocks__/react-markdown.js +17 -0
- package/Tests/__mocks__/react-syntax-highlighter.js +19 -0
- package/Tests/__mocks__/remark-gfm.js +8 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/Monitor/DockerAlertTemplates.ts +507 -0
- package/Types/Monitor/DockerMetricCatalog.ts +226 -0
- package/Types/Monitor/MonitorStep.ts +33 -0
- package/Types/Monitor/MonitorStepDockerMonitor.ts +38 -0
- package/Types/Monitor/MonitorType.ts +15 -1
- package/Types/Permission.ts +38 -0
- package/UI/Components/Icon/Icon.tsx +87 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +7 -132
- package/UI/Components/ModelDetail/CardModelDetail.tsx +11 -1
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +285 -0
- package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +85 -0
- package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +156 -0
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +160 -0
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +85 -0
- package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +102 -0
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +280 -0
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +125 -0
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +114 -0
- package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +378 -0
- package/UI/Components/TelemetryViewer/components/TelemetrySearchHelp.tsx +78 -0
- package/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.tsx +64 -0
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +193 -0
- package/UI/Components/TelemetryViewer/types.ts +67 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js +686 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +117 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +240 -16
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +9 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774000000002-MigrationName.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774000000002-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1775766676723-MigrationName.js +52 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1775766676723-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1775900000000-AddGlobalSmtpOAuth.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1775900000000-AddGlobalSmtpOAuth.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +162 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -0
- package/build/dist/Server/Services/ExceptionAggregationService.js +224 -0
- package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -0
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +11 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +19 -17
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +364 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +46 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js +2 -2
- package/build/dist/Tests/Server/Services/LogAggregationService.test.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Monitor/DockerAlertTemplates.js +410 -0
- package/build/dist/Types/Monitor/DockerAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/DockerMetricCatalog.js +192 -0
- package/build/dist/Types/Monitor/DockerMetricCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStep.js +23 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDockerMonitor.js +21 -0
- package/build/dist/Types/Monitor/MonitorStepDockerMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +14 -1
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Permission.js +36 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +13 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +7 -75
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +8 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +71 -0
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +39 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +61 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +66 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +41 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +35 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +132 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +65 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +52 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +224 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchHelp.js +35 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchHelp.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js +27 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchSuggestions.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +97 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/types.js +6 -0
- package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -0
- package/jest.config.json +6 -1
- package/package.json +1 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
|
|
2
|
+
import ExceptionInstanceService from "./ExceptionInstanceService";
|
|
3
|
+
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
|
|
4
|
+
import { JSONObject } from "../../Types/JSON";
|
|
5
|
+
import ObjectID from "../../Types/ObjectID";
|
|
6
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
7
|
+
import Includes from "../../Types/BaseDatabase/Includes";
|
|
8
|
+
import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
9
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
10
|
+
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
11
|
+
|
|
12
|
+
export interface HistogramBucket {
|
|
13
|
+
time: string;
|
|
14
|
+
series: string;
|
|
15
|
+
count: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ExceptionFilters {
|
|
19
|
+
serviceIds?: Array<ObjectID> | undefined;
|
|
20
|
+
exceptionTypes?: Array<string> | undefined;
|
|
21
|
+
environments?: Array<string> | undefined;
|
|
22
|
+
fingerprints?: Array<string> | undefined;
|
|
23
|
+
traceIds?: Array<string> | undefined;
|
|
24
|
+
escaped?: boolean | undefined;
|
|
25
|
+
messageSearchText?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HistogramRequest extends ExceptionFilters {
|
|
29
|
+
projectId: ObjectID;
|
|
30
|
+
startTime: Date;
|
|
31
|
+
endTime: Date;
|
|
32
|
+
bucketSizeInMinutes: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FacetValue {
|
|
36
|
+
value: string;
|
|
37
|
+
count: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface FacetRequest extends ExceptionFilters {
|
|
41
|
+
projectId: ObjectID;
|
|
42
|
+
startTime: Date;
|
|
43
|
+
endTime: Date;
|
|
44
|
+
facetKey: string;
|
|
45
|
+
limit?: number | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class ExceptionAggregationService {
|
|
49
|
+
private static readonly DEFAULT_FACET_LIMIT: number = 500;
|
|
50
|
+
private static readonly TABLE_NAME: string =
|
|
51
|
+
AnalyticsTableName.ExceptionInstance;
|
|
52
|
+
private static readonly TOP_LEVEL_COLUMNS: Set<string> = new Set([
|
|
53
|
+
"serviceId",
|
|
54
|
+
"exceptionType",
|
|
55
|
+
"environment",
|
|
56
|
+
"fingerprint",
|
|
57
|
+
"traceId",
|
|
58
|
+
"spanId",
|
|
59
|
+
"escaped",
|
|
60
|
+
"release",
|
|
61
|
+
]);
|
|
62
|
+
private static readonly ATTRIBUTE_KEY_PATTERN: RegExp = /^[a-zA-Z0-9._:/-]+$/;
|
|
63
|
+
private static readonly MAX_FACET_KEY_LENGTH: number = 256;
|
|
64
|
+
|
|
65
|
+
@CaptureSpan()
|
|
66
|
+
public static async getHistogram(
|
|
67
|
+
request: HistogramRequest,
|
|
68
|
+
): Promise<Array<HistogramBucket>> {
|
|
69
|
+
const statement: Statement =
|
|
70
|
+
ExceptionAggregationService.buildHistogramStatement(request);
|
|
71
|
+
|
|
72
|
+
const dbResult: Results =
|
|
73
|
+
await ExceptionInstanceService.executeQuery(statement);
|
|
74
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
75
|
+
data?: Array<JSONObject>;
|
|
76
|
+
}>();
|
|
77
|
+
|
|
78
|
+
const rows: Array<JSONObject> = response.data || [];
|
|
79
|
+
|
|
80
|
+
return rows.map((row: JSONObject): HistogramBucket => {
|
|
81
|
+
return {
|
|
82
|
+
time: String(row["bucket"] || ""),
|
|
83
|
+
series: ExceptionAggregationService.mapEscapedToSeries(row["escaped"]),
|
|
84
|
+
count: Number(row["cnt"] || 0),
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@CaptureSpan()
|
|
90
|
+
public static async getFacetValues(
|
|
91
|
+
request: FacetRequest,
|
|
92
|
+
): Promise<Array<FacetValue>> {
|
|
93
|
+
const statement: Statement =
|
|
94
|
+
ExceptionAggregationService.buildFacetStatement(request);
|
|
95
|
+
|
|
96
|
+
const dbResult: Results =
|
|
97
|
+
await ExceptionInstanceService.executeQuery(statement);
|
|
98
|
+
const response: DbJSONResponse = await dbResult.json<{
|
|
99
|
+
data?: Array<JSONObject>;
|
|
100
|
+
}>();
|
|
101
|
+
|
|
102
|
+
const rows: Array<JSONObject> = response.data || [];
|
|
103
|
+
|
|
104
|
+
return rows
|
|
105
|
+
.map((row: JSONObject): FacetValue => {
|
|
106
|
+
return {
|
|
107
|
+
value: String(row["val"] || ""),
|
|
108
|
+
count: Number(row["cnt"] || 0),
|
|
109
|
+
};
|
|
110
|
+
})
|
|
111
|
+
.filter((facet: FacetValue): boolean => {
|
|
112
|
+
return facet.value.length > 0;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private static mapEscapedToSeries(raw: unknown): string {
|
|
117
|
+
// ClickHouse returns booleans as 0/1
|
|
118
|
+
if (raw === true || raw === 1 || raw === "1" || raw === "true") {
|
|
119
|
+
return "unhandled";
|
|
120
|
+
}
|
|
121
|
+
return "handled";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private static buildHistogramStatement(request: HistogramRequest): Statement {
|
|
125
|
+
const intervalSeconds: number = request.bucketSizeInMinutes * 60;
|
|
126
|
+
|
|
127
|
+
const statement: Statement = SQL`
|
|
128
|
+
SELECT
|
|
129
|
+
toStartOfInterval(time, INTERVAL ${{
|
|
130
|
+
type: TableColumnType.Number,
|
|
131
|
+
value: intervalSeconds,
|
|
132
|
+
}} SECOND) AS bucket,
|
|
133
|
+
escaped,
|
|
134
|
+
count() AS cnt
|
|
135
|
+
FROM ${ExceptionAggregationService.TABLE_NAME}
|
|
136
|
+
WHERE projectId = ${{
|
|
137
|
+
type: TableColumnType.ObjectID,
|
|
138
|
+
value: request.projectId,
|
|
139
|
+
}}
|
|
140
|
+
AND time >= ${{
|
|
141
|
+
type: TableColumnType.Date,
|
|
142
|
+
value: request.startTime,
|
|
143
|
+
}}
|
|
144
|
+
AND time <= ${{
|
|
145
|
+
type: TableColumnType.Date,
|
|
146
|
+
value: request.endTime,
|
|
147
|
+
}}
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
ExceptionAggregationService.appendCommonFilters(statement, request);
|
|
151
|
+
|
|
152
|
+
statement.append(" GROUP BY bucket, escaped ORDER BY bucket ASC");
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* Defense in depth: cap histogram runtime below nginx's 60s
|
|
156
|
+
* proxy_read_timeout. 'break' returns partial aggregated results
|
|
157
|
+
* rather than throwing, which is acceptable for a density viz.
|
|
158
|
+
*/
|
|
159
|
+
statement.append(
|
|
160
|
+
" SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return statement;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private static buildFacetStatement(request: FacetRequest): Statement {
|
|
167
|
+
const limit: number =
|
|
168
|
+
request.limit ?? ExceptionAggregationService.DEFAULT_FACET_LIMIT;
|
|
169
|
+
|
|
170
|
+
ExceptionAggregationService.validateFacetKey(request.facetKey);
|
|
171
|
+
|
|
172
|
+
const isTopLevelColumn: boolean =
|
|
173
|
+
ExceptionAggregationService.isTopLevelColumn(request.facetKey);
|
|
174
|
+
|
|
175
|
+
const statement: Statement = new Statement();
|
|
176
|
+
|
|
177
|
+
if (isTopLevelColumn) {
|
|
178
|
+
statement.append(
|
|
179
|
+
SQL`SELECT toString(${request.facetKey}) AS val, count() AS cnt FROM ${ExceptionAggregationService.TABLE_NAME}`,
|
|
180
|
+
);
|
|
181
|
+
} else {
|
|
182
|
+
statement.append(
|
|
183
|
+
SQL`SELECT JSONExtractRaw(attributes, ${{
|
|
184
|
+
type: TableColumnType.Text,
|
|
185
|
+
value: request.facetKey,
|
|
186
|
+
}}) AS val, count() AS cnt FROM ${ExceptionAggregationService.TABLE_NAME}`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
statement.append(
|
|
191
|
+
SQL` WHERE projectId = ${{
|
|
192
|
+
type: TableColumnType.ObjectID,
|
|
193
|
+
value: request.projectId,
|
|
194
|
+
}} AND time >= ${{
|
|
195
|
+
type: TableColumnType.Date,
|
|
196
|
+
value: request.startTime,
|
|
197
|
+
}} AND time <= ${{
|
|
198
|
+
type: TableColumnType.Date,
|
|
199
|
+
value: request.endTime,
|
|
200
|
+
}}`,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (!isTopLevelColumn) {
|
|
204
|
+
statement.append(
|
|
205
|
+
SQL` AND JSONHas(attributes, ${{
|
|
206
|
+
type: TableColumnType.Text,
|
|
207
|
+
value: request.facetKey,
|
|
208
|
+
}}) = 1`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
ExceptionAggregationService.appendCommonFilters(statement, request);
|
|
213
|
+
|
|
214
|
+
statement.append(
|
|
215
|
+
SQL` GROUP BY val ORDER BY cnt DESC LIMIT ${{
|
|
216
|
+
type: TableColumnType.Number,
|
|
217
|
+
value: limit,
|
|
218
|
+
}}`,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
* Defense in depth: cap individual facet query runtime below nginx's
|
|
223
|
+
* 60s proxy_read_timeout so a slow facet never starves the endpoint.
|
|
224
|
+
*/
|
|
225
|
+
statement.append(
|
|
226
|
+
" SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return statement;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private static appendCommonFilters(
|
|
233
|
+
statement: Statement,
|
|
234
|
+
request: ExceptionFilters,
|
|
235
|
+
): void {
|
|
236
|
+
if (request.serviceIds && request.serviceIds.length > 0) {
|
|
237
|
+
statement.append(
|
|
238
|
+
SQL` AND serviceId IN (${{
|
|
239
|
+
type: TableColumnType.ObjectID,
|
|
240
|
+
value: new Includes(
|
|
241
|
+
request.serviceIds.map((id: ObjectID) => {
|
|
242
|
+
return id.toString();
|
|
243
|
+
}),
|
|
244
|
+
),
|
|
245
|
+
}})`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (request.exceptionTypes && request.exceptionTypes.length > 0) {
|
|
250
|
+
statement.append(
|
|
251
|
+
SQL` AND exceptionType IN (${{
|
|
252
|
+
type: TableColumnType.Text,
|
|
253
|
+
value: new Includes(request.exceptionTypes),
|
|
254
|
+
}})`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (request.environments && request.environments.length > 0) {
|
|
259
|
+
statement.append(
|
|
260
|
+
SQL` AND environment IN (${{
|
|
261
|
+
type: TableColumnType.Text,
|
|
262
|
+
value: new Includes(request.environments),
|
|
263
|
+
}})`,
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (request.fingerprints && request.fingerprints.length > 0) {
|
|
268
|
+
statement.append(
|
|
269
|
+
SQL` AND fingerprint IN (${{
|
|
270
|
+
type: TableColumnType.Text,
|
|
271
|
+
value: new Includes(request.fingerprints),
|
|
272
|
+
}})`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (request.traceIds && request.traceIds.length > 0) {
|
|
277
|
+
statement.append(
|
|
278
|
+
SQL` AND traceId IN (${{
|
|
279
|
+
type: TableColumnType.Text,
|
|
280
|
+
value: new Includes(request.traceIds),
|
|
281
|
+
}})`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (request.escaped !== undefined) {
|
|
286
|
+
statement.append(
|
|
287
|
+
SQL` AND escaped = ${{
|
|
288
|
+
type: TableColumnType.Boolean,
|
|
289
|
+
value: request.escaped,
|
|
290
|
+
}}`,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
request.messageSearchText &&
|
|
296
|
+
request.messageSearchText.trim().length > 0
|
|
297
|
+
) {
|
|
298
|
+
statement.append(
|
|
299
|
+
SQL` AND message ILIKE ${{
|
|
300
|
+
type: TableColumnType.Text,
|
|
301
|
+
value: `%${request.messageSearchText.trim()}%`,
|
|
302
|
+
}}`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private static isTopLevelColumn(key: string): boolean {
|
|
308
|
+
return ExceptionAggregationService.TOP_LEVEL_COLUMNS.has(key);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private static validateFacetKey(
|
|
312
|
+
facetKey: unknown,
|
|
313
|
+
): asserts facetKey is string {
|
|
314
|
+
if (typeof facetKey !== "string") {
|
|
315
|
+
throw new BadDataException("Invalid facetKey");
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (
|
|
319
|
+
facetKey.length === 0 ||
|
|
320
|
+
facetKey.length > ExceptionAggregationService.MAX_FACET_KEY_LENGTH
|
|
321
|
+
) {
|
|
322
|
+
throw new BadDataException("Invalid facetKey");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (ExceptionAggregationService.isTopLevelColumn(facetKey)) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!ExceptionAggregationService.ATTRIBUTE_KEY_PATTERN.test(facetKey)) {
|
|
330
|
+
throw new BadDataException("Invalid facetKey");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export default ExceptionAggregationService;
|
package/Server/Services/Index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import IncidentStateTimelineService from "./IncidentStateTimelineService";
|
|
|
34
34
|
//Labels.
|
|
35
35
|
import LabelService from "./LabelService";
|
|
36
36
|
import KubernetesClusterService from "./KubernetesClusterService";
|
|
37
|
+
import DockerHostService from "./DockerHostService";
|
|
37
38
|
import LlmProviderService from "./LlmProviderService";
|
|
38
39
|
import LogService from "./LogService";
|
|
39
40
|
import MailService from "./MailService";
|
|
@@ -258,6 +259,7 @@ const services: Array<BaseService> = [
|
|
|
258
259
|
|
|
259
260
|
LabelService,
|
|
260
261
|
KubernetesClusterService,
|
|
262
|
+
DockerHostService,
|
|
261
263
|
LlmProviderService,
|
|
262
264
|
|
|
263
265
|
MailService,
|
|
@@ -174,6 +174,15 @@ export class LogAggregationService {
|
|
|
174
174
|
|
|
175
175
|
statement.append(" GROUP BY bucket, severityText ORDER BY bucket ASC");
|
|
176
176
|
|
|
177
|
+
/*
|
|
178
|
+
* Defense in depth: cap histogram runtime below nginx's 60s
|
|
179
|
+
* proxy_read_timeout. 'break' returns partial aggregated results
|
|
180
|
+
* rather than throwing, which is acceptable for a density viz.
|
|
181
|
+
*/
|
|
182
|
+
statement.append(
|
|
183
|
+
" SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
|
|
184
|
+
);
|
|
185
|
+
|
|
177
186
|
return statement;
|
|
178
187
|
}
|
|
179
188
|
|
|
@@ -233,6 +242,14 @@ export class LogAggregationService {
|
|
|
233
242
|
}}`,
|
|
234
243
|
);
|
|
235
244
|
|
|
245
|
+
/*
|
|
246
|
+
* Defense in depth: cap individual facet query runtime below nginx's
|
|
247
|
+
* 60s proxy_read_timeout so a slow facet never starves the endpoint.
|
|
248
|
+
*/
|
|
249
|
+
statement.append(
|
|
250
|
+
" SETTINGS max_execution_time = 45, timeout_overflow_mode = 'break'",
|
|
251
|
+
);
|
|
252
|
+
|
|
236
253
|
return statement;
|
|
237
254
|
}
|
|
238
255
|
|
|
@@ -71,7 +71,6 @@ import ExceptionMessages from "../../Types/Exception/ExceptionMessages";
|
|
|
71
71
|
import Project from "../../Models/DatabaseModels/Project";
|
|
72
72
|
import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
|
|
73
73
|
import { WhatsAppMessagePayload } from "../../Types/WhatsApp/WhatsAppMessage";
|
|
74
|
-
import MetricService from "./MetricService";
|
|
75
74
|
|
|
76
75
|
export interface MonitorDestinationInfo {
|
|
77
76
|
monitorDestination: string;
|
|
@@ -284,27 +283,28 @@ export class Service extends DatabaseService<Model> {
|
|
|
284
283
|
onDelete: OnDelete<Model>,
|
|
285
284
|
_itemIdsBeforeDelete: ObjectID[],
|
|
286
285
|
): Promise<OnDelete<Model>> {
|
|
286
|
+
/*
|
|
287
|
+
* The monitor has already been deleted from the database at this point.
|
|
288
|
+
* Any failure in the post-delete side effects below (e.g. billing
|
|
289
|
+
* reporting) must NOT propagate up to the caller as a 500 — otherwise the
|
|
290
|
+
* client sees "500 Internal Server Error" even though the delete actually
|
|
291
|
+
* succeeded. Log and swallow instead.
|
|
292
|
+
*
|
|
293
|
+
* Note: we intentionally do NOT delete Metric rows for this monitor here.
|
|
294
|
+
* The Metric table has a ClickHouse TTL on retentionDate (set at ingest
|
|
295
|
+
* from GlobalConfig.monitorMetricRetentionInDays) that auto-drops rows.
|
|
296
|
+
* A synchronous ALTER TABLE … DELETE on every monitor deletion is both
|
|
297
|
+
* redundant and expensive.
|
|
298
|
+
*/
|
|
287
299
|
if (onDelete.deleteBy.props.tenantId && IsBillingEnabled) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
await MetricService.deleteBy({
|
|
300
|
-
query: {
|
|
301
|
-
projectId: monitor.projectId,
|
|
302
|
-
serviceId: monitor.id,
|
|
303
|
-
},
|
|
304
|
-
props: {
|
|
305
|
-
isRoot: true,
|
|
306
|
-
},
|
|
307
|
-
});
|
|
300
|
+
try {
|
|
301
|
+
await ActiveMonitoringMeteredPlan.reportQuantityToBillingProvider(
|
|
302
|
+
onDelete.deleteBy.props.tenantId,
|
|
303
|
+
);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
logger.error(
|
|
306
|
+
`Error while reporting active monitor quantity to billing provider for project ${onDelete.deleteBy.props.tenantId?.toString()}: ${error}`,
|
|
307
|
+
);
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|