@oneuptime/common 10.4.10 → 10.4.12
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 +47 -1
- package/Models/AnalyticsModels/Log.ts +47 -1
- package/Models/AnalyticsModels/Metric.ts +1 -7
- package/Models/AnalyticsModels/Profile.ts +47 -1
- package/Models/AnalyticsModels/ProfileSample.ts +47 -1
- package/Models/AnalyticsModels/Span.ts +47 -1
- package/Models/DatabaseModels/Alert.ts +332 -0
- package/Models/DatabaseModels/DockerHost.ts +83 -0
- package/Models/DatabaseModels/Host.ts +83 -0
- package/Models/DatabaseModels/Incident.ts +332 -0
- package/Models/DatabaseModels/Index.ts +0 -2
- package/Models/DatabaseModels/KubernetesCluster.ts +83 -0
- package/Models/DatabaseModels/RunbookAgent.ts +16 -2
- package/Server/EnvironmentConfig.ts +16 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +14 -0
- package/Server/Infrastructure/ClickhouseDatabase.ts +20 -3
- package/Server/Infrastructure/InMemoryTTLCache.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779199346010-AddTelemetryRetentionConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779277271302-DropServiceDependencyTable.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779282769946-AddTelemetryRetentionToHostDockerKubernetes.ts +50 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.ts +253 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.ts +60 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/TelemetryIngest.ts +6 -38
- package/Server/Middleware/UserAuthorization.ts +1 -11
- package/Server/Services/AlertService.ts +2 -4
- package/Server/Services/AnalyticsDatabaseService.ts +33 -1
- package/Server/Services/IncidentService.ts +2 -4
- package/Server/Services/Index.ts +0 -2
- package/Server/Services/LogAggregationService.ts +54 -6
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +6 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +1 -0
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +17 -0
- package/Server/Services/OpenTelemetryIngestService.ts +132 -0
- package/Server/Services/TelemetryIngestionKeyService.ts +90 -1
- package/Server/Services/TraceAggregationService.ts +83 -8
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +2 -4
- package/Server/Utils/Telemetry/Telemetry.ts +38 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +5 -7
- package/Types/Dashboard/DashboardComponentType.ts +0 -1
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +2 -0
- package/Types/Dashboard/DashboardComponents/DashboardTableComponent.ts +74 -1
- package/Types/Dashboard/DashboardTemplates.ts +164 -51
- package/Types/Monitor/MonitorType.ts +1 -1
- package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +36 -9
- package/Types/Permission.ts +0 -46
- package/Types/Telemetry/ServiceType.ts +19 -0
- package/UI/Components/Forms/Validation.ts +2 -2
- package/UI/Components/LogsViewer/LogsViewer.tsx +135 -17
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +84 -1
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -1
- package/Utils/Dashboard/Components/DashboardTableComponent.ts +80 -17
- package/Utils/Dashboard/Components/Index.ts +0 -7
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +41 -1
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +41 -1
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +0 -7
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Profile.js +41 -1
- package/build/dist/Models/AnalyticsModels/Profile.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ProfileSample.js +41 -1
- package/build/dist/Models/AnalyticsModels/ProfileSample.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +41 -1
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Alert.js +324 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +84 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +84 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +324 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +0 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +84 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookAgent.js +16 -2
- package/build/dist/Models/DatabaseModels/RunbookAgent.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +8 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +9 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +12 -3
- package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
- package/build/dist/Server/Infrastructure/InMemoryTTLCache.js +49 -0
- package/build/dist/Server/Infrastructure/InMemoryTTLCache.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779199346010-AddTelemetryRetentionConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779277271302-DropServiceDependencyTable.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779277271302-DropServiceDependencyTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779282769946-AddTelemetryRetentionToHostDockerKubernetes.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779282769946-AddTelemetryRetentionToHostDockerKubernetes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js +100 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +2 -26
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +1 -7
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +2 -1
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +23 -2
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +2 -1
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +0 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +46 -4
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +28 -24
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +18 -2
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +103 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +83 -0
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +66 -6
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +2 -1
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +26 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js +3 -7
- package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +0 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js +13 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +142 -42
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +1 -1
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +27 -7
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +0 -40
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +20 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -0
- package/build/dist/UI/Components/Forms/Validation.js +2 -2
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +106 -16
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +67 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js +68 -16
- package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/Index.js +0 -4
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -2
- package/Models/DatabaseModels/ServiceDependency.ts +0 -529
- package/Server/Services/ServiceDependencyService.ts +0 -48
- package/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.ts +0 -27
- package/Typings/elkjs.d.ts +0 -30
- package/UI/Components/Graphs/ServiceDependencyGraph.tsx +0 -286
- package/Utils/Dashboard/Components/DashboardHostMetricChartComponent.ts +0 -132
- package/build/dist/Models/DatabaseModels/ServiceDependency.js +0 -545
- package/build/dist/Models/DatabaseModels/ServiceDependency.js.map +0 -1
- package/build/dist/Server/Services/ServiceDependencyService.js +0 -47
- package/build/dist/Server/Services/ServiceDependencyService.js.map +0 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js +0 -11
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js.map +0 -1
- package/build/dist/UI/Components/Graphs/ServiceDependencyGraph.js +0 -206
- package/build/dist/UI/Components/Graphs/ServiceDependencyGraph.js.map +0 -1
- package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js +0 -113
- package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js.map +0 -1
|
@@ -8,6 +8,7 @@ import Includes from "../../Types/BaseDatabase/Includes";
|
|
|
8
8
|
import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName";
|
|
9
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
10
10
|
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
11
|
+
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
11
12
|
|
|
12
13
|
export interface HistogramBucket {
|
|
13
14
|
time: string;
|
|
@@ -92,6 +93,19 @@ export class LogAggregationService {
|
|
|
92
93
|
"traceId",
|
|
93
94
|
"spanId",
|
|
94
95
|
]);
|
|
96
|
+
/*
|
|
97
|
+
* Virtual facet keys that don't correspond to real ClickHouse columns —
|
|
98
|
+
* they all read out of `serviceId` filtered by `serviceType`. The
|
|
99
|
+
* discriminator was added so host / docker host / k8s cluster telemetry
|
|
100
|
+
* could reuse the `serviceId` slot instead of synthesising phantom
|
|
101
|
+
* Service rows; these facets surface each resource type independently.
|
|
102
|
+
*/
|
|
103
|
+
private static readonly RESOURCE_FACET_KEYS: Map<string, ServiceType> =
|
|
104
|
+
new Map([
|
|
105
|
+
["hostId", ServiceType.Host],
|
|
106
|
+
["dockerHostId", ServiceType.DockerHost],
|
|
107
|
+
["kubernetesClusterId", ServiceType.KubernetesCluster],
|
|
108
|
+
]);
|
|
95
109
|
private static readonly ATTRIBUTE_KEY_PATTERN: RegExp = /^[a-zA-Z0-9._:/-]+$/;
|
|
96
110
|
private static readonly MAX_FACET_KEY_LENGTH: number = 256;
|
|
97
111
|
|
|
@@ -192,13 +206,25 @@ export class LogAggregationService {
|
|
|
192
206
|
|
|
193
207
|
LogAggregationService.validateFacetKey(request.facetKey);
|
|
194
208
|
|
|
195
|
-
const
|
|
196
|
-
request.facetKey
|
|
197
|
-
|
|
209
|
+
const resourceServiceType: ServiceType | undefined =
|
|
210
|
+
LogAggregationService.RESOURCE_FACET_KEYS.get(request.facetKey);
|
|
211
|
+
const isResourceFacet: boolean = resourceServiceType !== undefined;
|
|
212
|
+
const isTopLevelColumn: boolean =
|
|
213
|
+
isResourceFacet ||
|
|
214
|
+
LogAggregationService.isTopLevelColumn(request.facetKey);
|
|
198
215
|
|
|
199
216
|
const statement: Statement = new Statement();
|
|
200
217
|
|
|
201
|
-
if (
|
|
218
|
+
if (isResourceFacet) {
|
|
219
|
+
/*
|
|
220
|
+
* Virtual facet — group serviceId values whose row carries the
|
|
221
|
+
* matching ServiceType discriminator (Host / DockerHost /
|
|
222
|
+
* KubernetesCluster).
|
|
223
|
+
*/
|
|
224
|
+
statement.append(
|
|
225
|
+
SQL`SELECT toString(serviceId) AS val, count() AS cnt FROM ${LogAggregationService.TABLE_NAME}`,
|
|
226
|
+
);
|
|
227
|
+
} else if (isTopLevelColumn) {
|
|
202
228
|
statement.append(
|
|
203
229
|
SQL`SELECT toString(${request.facetKey}) AS val, count() AS cnt FROM ${LogAggregationService.TABLE_NAME}`,
|
|
204
230
|
);
|
|
@@ -224,7 +250,26 @@ export class LogAggregationService {
|
|
|
224
250
|
}}`,
|
|
225
251
|
);
|
|
226
252
|
|
|
227
|
-
if (
|
|
253
|
+
if (isResourceFacet) {
|
|
254
|
+
statement.append(
|
|
255
|
+
SQL` AND serviceType = ${{
|
|
256
|
+
type: TableColumnType.Text,
|
|
257
|
+
value: resourceServiceType as string,
|
|
258
|
+
}}`,
|
|
259
|
+
);
|
|
260
|
+
} else if (request.facetKey === "serviceId") {
|
|
261
|
+
/*
|
|
262
|
+
* Constrain the canonical Services facet to rows that actually
|
|
263
|
+
* belong to a Service. NULL / empty serviceType covers legacy
|
|
264
|
+
* rows ingested before the discriminator existed.
|
|
265
|
+
*/
|
|
266
|
+
statement.append(
|
|
267
|
+
SQL` AND (serviceType = '' OR serviceType = ${{
|
|
268
|
+
type: TableColumnType.Text,
|
|
269
|
+
value: ServiceType.OpenTelemetry as string,
|
|
270
|
+
}})`,
|
|
271
|
+
);
|
|
272
|
+
} else if (!isTopLevelColumn) {
|
|
228
273
|
statement.append(
|
|
229
274
|
SQL` AND JSONHas(attributes, ${{
|
|
230
275
|
type: TableColumnType.Text,
|
|
@@ -944,7 +989,10 @@ export class LogAggregationService {
|
|
|
944
989
|
throw new BadDataException("Invalid facetKey");
|
|
945
990
|
}
|
|
946
991
|
|
|
947
|
-
if (
|
|
992
|
+
if (
|
|
993
|
+
LogAggregationService.isTopLevelColumn(facetKey) ||
|
|
994
|
+
LogAggregationService.RESOURCE_FACET_KEYS.has(facetKey)
|
|
995
|
+
) {
|
|
948
996
|
return;
|
|
949
997
|
}
|
|
950
998
|
|
|
@@ -77,6 +77,9 @@ export class Service extends DatabaseService<Model> {
|
|
|
77
77
|
const userOnSchedule: ObjectID | null =
|
|
78
78
|
await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
|
|
79
79
|
createdModel.onCallDutyPolicyScheduleId,
|
|
80
|
+
{
|
|
81
|
+
onCallDutyPolicyId: createdModel.onCallDutyPolicy?.id || undefined,
|
|
82
|
+
},
|
|
80
83
|
);
|
|
81
84
|
|
|
82
85
|
if (!userOnSchedule) {
|
|
@@ -277,6 +280,9 @@ export class Service extends DatabaseService<Model> {
|
|
|
277
280
|
const userOnSchedule: ObjectID | null =
|
|
278
281
|
await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
|
|
279
282
|
deletedItem.onCallDutyPolicyScheduleId!,
|
|
283
|
+
{
|
|
284
|
+
onCallDutyPolicyId: deletedItem.onCallDutyPolicy?.id || undefined,
|
|
285
|
+
},
|
|
280
286
|
);
|
|
281
287
|
|
|
282
288
|
if (!userOnSchedule) {
|
|
@@ -460,6 +460,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
460
460
|
const userIdInSchedule: ObjectID | null =
|
|
461
461
|
await OnCallDutyPolicyScheduleService.getCurrentUserIdInSchedule(
|
|
462
462
|
scheduleRule.onCallDutyPolicyScheduleId!,
|
|
463
|
+
{ onCallDutyPolicyId: options.onCallPolicyId },
|
|
463
464
|
);
|
|
464
465
|
|
|
465
466
|
if (!userIdInSchedule) {
|
|
@@ -1052,17 +1052,28 @@ export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
|
1052
1052
|
scheduleUserIds: Array<ObjectID>;
|
|
1053
1053
|
windowStart: Date;
|
|
1054
1054
|
windowEnd: Date;
|
|
1055
|
+
currentOnCallDutyPolicyId?: ObjectID | undefined;
|
|
1055
1056
|
}): Promise<Array<UserOverrideRecord>> {
|
|
1056
1057
|
if (data.scheduleUserIds.length === 0) {
|
|
1057
1058
|
return [];
|
|
1058
1059
|
}
|
|
1059
1060
|
|
|
1061
|
+
/*
|
|
1062
|
+
* When a policy context is provided, include overrides scoped to that
|
|
1063
|
+
* policy plus global overrides. Without a policy context (e.g. schedule
|
|
1064
|
+
* roster refresh, dashboard preview, incoming-call routing), only global
|
|
1065
|
+
* overrides apply — a policy-specific override from one policy must not
|
|
1066
|
+
* affect schedule resolution for a different policy.
|
|
1067
|
+
*/
|
|
1060
1068
|
const overrides: Array<OnCallDutyPolicyUserOverride> =
|
|
1061
1069
|
await OnCallDutyPolicyUserOverrideService.findBy({
|
|
1062
1070
|
query: {
|
|
1063
1071
|
projectId: data.projectId,
|
|
1064
1072
|
startsAt: QueryHelper.lessThanEqualTo(data.windowEnd),
|
|
1065
1073
|
endsAt: QueryHelper.greaterThanEqualTo(data.windowStart),
|
|
1074
|
+
onCallDutyPolicyId: data.currentOnCallDutyPolicyId
|
|
1075
|
+
? QueryHelper.equalToOrNull(data.currentOnCallDutyPolicyId)
|
|
1076
|
+
: QueryHelper.isNull(),
|
|
1066
1077
|
},
|
|
1067
1078
|
select: {
|
|
1068
1079
|
startsAt: true,
|
|
@@ -1106,6 +1117,7 @@ export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
|
1106
1117
|
public async getEventByIndexInSchedule(data: {
|
|
1107
1118
|
scheduleId: ObjectID;
|
|
1108
1119
|
getNumberOfEvents: number; // which event would you like to get. First event, second event, etc.
|
|
1120
|
+
onCallDutyPolicyId?: ObjectID | undefined;
|
|
1109
1121
|
}): Promise<Array<CalendarEvent>> {
|
|
1110
1122
|
logger.debug(
|
|
1111
1123
|
"getEventByIndexInSchedule called with data: " + JSON.stringify(data),
|
|
@@ -1169,12 +1181,14 @@ export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
|
1169
1181
|
scheduleUserIds,
|
|
1170
1182
|
windowStart: currentStartTime,
|
|
1171
1183
|
windowEnd: currentEndTime,
|
|
1184
|
+
currentOnCallDutyPolicyId: data.onCallDutyPolicyId,
|
|
1172
1185
|
});
|
|
1173
1186
|
|
|
1174
1187
|
if (overrides.length > 0) {
|
|
1175
1188
|
events = UserOverrideUtil.applyOverridesToEvents({
|
|
1176
1189
|
events,
|
|
1177
1190
|
overrides,
|
|
1191
|
+
currentOnCallDutyPolicyId: data.onCallDutyPolicyId?.toString(),
|
|
1178
1192
|
});
|
|
1179
1193
|
}
|
|
1180
1194
|
}
|
|
@@ -1189,6 +1203,7 @@ export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
|
1189
1203
|
@CaptureSpan()
|
|
1190
1204
|
public async getCurrentUserIdInSchedule(
|
|
1191
1205
|
scheduleId: ObjectID,
|
|
1206
|
+
options?: { onCallDutyPolicyId?: ObjectID | undefined } | undefined,
|
|
1192
1207
|
): Promise<ObjectID | null> {
|
|
1193
1208
|
const { layerProps, projectId, scheduleUserIds } =
|
|
1194
1209
|
await this.getScheduleLayerProps({
|
|
@@ -1223,12 +1238,14 @@ export class Service extends DatabaseService<OnCallDutyPolicySchedule> {
|
|
|
1223
1238
|
scheduleUserIds,
|
|
1224
1239
|
windowStart: currentStartTime,
|
|
1225
1240
|
windowEnd: currentEndTime,
|
|
1241
|
+
currentOnCallDutyPolicyId: options?.onCallDutyPolicyId,
|
|
1226
1242
|
});
|
|
1227
1243
|
|
|
1228
1244
|
if (overrides.length > 0) {
|
|
1229
1245
|
events = UserOverrideUtil.applyOverridesToEvents({
|
|
1230
1246
|
events,
|
|
1231
1247
|
overrides,
|
|
1248
|
+
currentOnCallDutyPolicyId: options?.onCallDutyPolicyId?.toString(),
|
|
1232
1249
|
});
|
|
1233
1250
|
}
|
|
1234
1251
|
}
|
|
@@ -16,6 +16,13 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
|
16
16
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
17
17
|
import logger from "../Utils/Logger";
|
|
18
18
|
import TelemetryRetentionConfig from "../../Types/Telemetry/TelemetryRetentionConfig";
|
|
19
|
+
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
20
|
+
import Host from "../../Models/DatabaseModels/Host";
|
|
21
|
+
import DockerHost from "../../Models/DatabaseModels/DockerHost";
|
|
22
|
+
import KubernetesCluster from "../../Models/DatabaseModels/KubernetesCluster";
|
|
23
|
+
import HostService from "./HostService";
|
|
24
|
+
import DockerHostService from "./DockerHostService";
|
|
25
|
+
import KubernetesClusterService from "./KubernetesClusterService";
|
|
19
26
|
|
|
20
27
|
export enum OtelAggregationTemporality {
|
|
21
28
|
Cumulative = "AGGREGATION_TEMPORALITY_CUMULATIVE",
|
|
@@ -25,6 +32,14 @@ export enum OtelAggregationTemporality {
|
|
|
25
32
|
export interface TelemetryServiceMetadata {
|
|
26
33
|
serviceName: string;
|
|
27
34
|
serviceId: ObjectID;
|
|
35
|
+
/*
|
|
36
|
+
* Discriminator stamped on every analytics row so the read side
|
|
37
|
+
* knows which Postgres table the `serviceId` actually points at
|
|
38
|
+
* (real Service, Host, DockerHost, KubernetesCluster, Monitor, …).
|
|
39
|
+
* Defaults to OpenTelemetry for legacy ingest paths that go through
|
|
40
|
+
* `telemetryServiceFromName`.
|
|
41
|
+
*/
|
|
42
|
+
serviceType: ServiceType;
|
|
28
43
|
dataRententionInDays: number;
|
|
29
44
|
serviceRetentionConfig: TelemetryRetentionConfig | null;
|
|
30
45
|
serviceRetentionInDays: number | null;
|
|
@@ -166,6 +181,7 @@ export default class OTelIngestService {
|
|
|
166
181
|
return {
|
|
167
182
|
serviceName: data.serviceName,
|
|
168
183
|
serviceId: svc.id!,
|
|
184
|
+
serviceType: ServiceType.OpenTelemetry,
|
|
169
185
|
dataRententionInDays:
|
|
170
186
|
serviceLevelRetention || projectContext.projectRetentionInDays,
|
|
171
187
|
serviceRetentionConfig: svc.telemetryRetentionConfig ?? null,
|
|
@@ -225,6 +241,122 @@ export default class OTelIngestService {
|
|
|
225
241
|
|
|
226
242
|
return buildMetadata(service);
|
|
227
243
|
}
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
* Builds a TelemetryServiceMetadata for a non-Service resource —
|
|
247
|
+
* Host, DockerHost, KubernetesCluster, Monitor. These resources
|
|
248
|
+
* own telemetry directly via their own Postgres id (stamped into
|
|
249
|
+
* the analytics row's `serviceId` column) and do not have a paired
|
|
250
|
+
* Service row. Per-resource retention overrides (when set on the
|
|
251
|
+
* Host / DockerHost / KubernetesCluster row) are honoured here so
|
|
252
|
+
* the resource owner can keep host telemetry longer/shorter than
|
|
253
|
+
* the project default. Monitor / unknown resource types fall back
|
|
254
|
+
* to the project retention.
|
|
255
|
+
*/
|
|
256
|
+
@CaptureSpan()
|
|
257
|
+
public static async buildResourceMetadataForNonService(data: {
|
|
258
|
+
serviceName: string;
|
|
259
|
+
resourceId: ObjectID;
|
|
260
|
+
serviceType: ServiceType;
|
|
261
|
+
projectId: ObjectID;
|
|
262
|
+
}): Promise<TelemetryServiceMetadata> {
|
|
263
|
+
const projectContext: ProjectRetentionContext =
|
|
264
|
+
await this.getProjectRetentionContext(data.projectId);
|
|
265
|
+
|
|
266
|
+
const resourceRetention: {
|
|
267
|
+
retainTelemetryDataForDays: number | null;
|
|
268
|
+
telemetryRetentionConfig: TelemetryRetentionConfig | null;
|
|
269
|
+
} = await this.getResourceRetention(data.resourceId, data.serviceType);
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
serviceName: data.serviceName,
|
|
273
|
+
serviceId: data.resourceId,
|
|
274
|
+
serviceType: data.serviceType,
|
|
275
|
+
dataRententionInDays:
|
|
276
|
+
resourceRetention.retainTelemetryDataForDays ||
|
|
277
|
+
projectContext.projectRetentionInDays,
|
|
278
|
+
serviceRetentionConfig: resourceRetention.telemetryRetentionConfig,
|
|
279
|
+
serviceRetentionInDays: resourceRetention.retainTelemetryDataForDays,
|
|
280
|
+
projectRetentionConfig: projectContext.projectRetentionConfig,
|
|
281
|
+
projectRetentionInDays: projectContext.projectRetentionInDays,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/*
|
|
286
|
+
* Look up per-resource retention overrides for non-Service telemetry.
|
|
287
|
+
* One small SELECT per batch — the caller caches the resulting
|
|
288
|
+
* TelemetryServiceMetadata under `serviceDictionary[serviceName]`
|
|
289
|
+
* so steady-state ingest skips this lookup after the first row.
|
|
290
|
+
*/
|
|
291
|
+
@CaptureSpan()
|
|
292
|
+
private static async getResourceRetention(
|
|
293
|
+
resourceId: ObjectID,
|
|
294
|
+
serviceType: ServiceType,
|
|
295
|
+
): Promise<{
|
|
296
|
+
retainTelemetryDataForDays: number | null;
|
|
297
|
+
telemetryRetentionConfig: TelemetryRetentionConfig | null;
|
|
298
|
+
}> {
|
|
299
|
+
try {
|
|
300
|
+
if (serviceType === ServiceType.Host) {
|
|
301
|
+
const host: Host | null = await HostService.findOneById({
|
|
302
|
+
id: resourceId,
|
|
303
|
+
select: {
|
|
304
|
+
retainTelemetryDataForDays: true,
|
|
305
|
+
telemetryRetentionConfig: true,
|
|
306
|
+
},
|
|
307
|
+
props: { isRoot: true },
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
retainTelemetryDataForDays: host?.retainTelemetryDataForDays ?? null,
|
|
311
|
+
telemetryRetentionConfig: host?.telemetryRetentionConfig ?? null,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (serviceType === ServiceType.DockerHost) {
|
|
315
|
+
const dockerHost: DockerHost | null =
|
|
316
|
+
await DockerHostService.findOneById({
|
|
317
|
+
id: resourceId,
|
|
318
|
+
select: {
|
|
319
|
+
retainTelemetryDataForDays: true,
|
|
320
|
+
telemetryRetentionConfig: true,
|
|
321
|
+
},
|
|
322
|
+
props: { isRoot: true },
|
|
323
|
+
});
|
|
324
|
+
return {
|
|
325
|
+
retainTelemetryDataForDays:
|
|
326
|
+
dockerHost?.retainTelemetryDataForDays ?? null,
|
|
327
|
+
telemetryRetentionConfig:
|
|
328
|
+
dockerHost?.telemetryRetentionConfig ?? null,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (serviceType === ServiceType.KubernetesCluster) {
|
|
332
|
+
const cluster: KubernetesCluster | null =
|
|
333
|
+
await KubernetesClusterService.findOneById({
|
|
334
|
+
id: resourceId,
|
|
335
|
+
select: {
|
|
336
|
+
retainTelemetryDataForDays: true,
|
|
337
|
+
telemetryRetentionConfig: true,
|
|
338
|
+
},
|
|
339
|
+
props: { isRoot: true },
|
|
340
|
+
});
|
|
341
|
+
return {
|
|
342
|
+
retainTelemetryDataForDays:
|
|
343
|
+
cluster?.retainTelemetryDataForDays ?? null,
|
|
344
|
+
telemetryRetentionConfig: cluster?.telemetryRetentionConfig ?? null,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
} catch (err) {
|
|
348
|
+
logger.warn(
|
|
349
|
+
`Per-resource retention lookup failed for ${serviceType} ${resourceId.toString()}: ${
|
|
350
|
+
err instanceof Error ? err.message : String(err)
|
|
351
|
+
}`,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
retainTelemetryDataForDays: null,
|
|
356
|
+
telemetryRetentionConfig: null,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
228
360
|
@CaptureSpan()
|
|
229
361
|
public static getMetricFromDatapoint(data: {
|
|
230
362
|
dbMetric: Metric;
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
-
import
|
|
2
|
+
import DeleteBy from "../Types/Database/DeleteBy";
|
|
3
|
+
import UpdateBy from "../Types/Database/UpdateBy";
|
|
4
|
+
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
3
5
|
import DatabaseService from "./DatabaseService";
|
|
4
6
|
import ObjectID from "../../Types/ObjectID";
|
|
5
7
|
import Model from "../../Models/DatabaseModels/TelemetryIngestionKey";
|
|
6
8
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
9
|
+
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
|
|
10
|
+
import { createHash } from "crypto";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* 60s is the worst-case staleness on any single API node after a key is
|
|
14
|
+
* revoked from the dashboard. We invalidate in-process immediately on
|
|
15
|
+
* delete/update; this TTL is the upper bound for *other* processes.
|
|
16
|
+
*/
|
|
17
|
+
const POSITIVE_TTL_MS: number = 60 * 1000;
|
|
18
|
+
/*
|
|
19
|
+
* Short TTL on misses so an invalid-token flood can't pin entries in the
|
|
20
|
+
* bounded cache for long while still absorbing repeat hits.
|
|
21
|
+
*/
|
|
22
|
+
const NEGATIVE_TTL_MS: number = 10 * 1000;
|
|
7
23
|
|
|
8
24
|
export class Service extends DatabaseService<Model> {
|
|
25
|
+
private projectIdCache: InMemoryTTLCache<string | null> =
|
|
26
|
+
new InMemoryTTLCache(10_000);
|
|
27
|
+
|
|
9
28
|
public constructor() {
|
|
10
29
|
super(Model);
|
|
11
30
|
}
|
|
@@ -20,6 +39,76 @@ export class Service extends DatabaseService<Model> {
|
|
|
20
39
|
|
|
21
40
|
return { createBy, carryForward: null };
|
|
22
41
|
}
|
|
42
|
+
|
|
43
|
+
@CaptureSpan()
|
|
44
|
+
protected override async onBeforeDelete(
|
|
45
|
+
deleteBy: DeleteBy<Model>,
|
|
46
|
+
): Promise<OnDelete<Model>> {
|
|
47
|
+
/*
|
|
48
|
+
* We don't know which secretKey(s) are being deleted without an extra
|
|
49
|
+
* query; clear the whole cache. Key deletes are rare so this is cheap.
|
|
50
|
+
*/
|
|
51
|
+
this.projectIdCache.clear();
|
|
52
|
+
return { deleteBy, carryForward: null };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@CaptureSpan()
|
|
56
|
+
protected override async onBeforeUpdate(
|
|
57
|
+
updateBy: UpdateBy<Model>,
|
|
58
|
+
): Promise<OnUpdate<Model>> {
|
|
59
|
+
/*
|
|
60
|
+
* Same reasoning as onBeforeDelete. secretKey is not user-editable today
|
|
61
|
+
* but projectId could change, so be conservative.
|
|
62
|
+
*/
|
|
63
|
+
this.projectIdCache.clear();
|
|
64
|
+
return { updateBy, carryForward: null };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve an ingestion token to its projectId, with a short-lived
|
|
69
|
+
* in-process cache to keep the hot ingest path off Postgres. Returns null
|
|
70
|
+
* for unknown, malformed, or revoked tokens (also cached, for a shorter
|
|
71
|
+
* TTL).
|
|
72
|
+
*/
|
|
73
|
+
@CaptureSpan()
|
|
74
|
+
public async getProjectIdFromSecretKey(
|
|
75
|
+
secretKey: string,
|
|
76
|
+
): Promise<ObjectID | null> {
|
|
77
|
+
const cacheKey: string = createHash("sha256")
|
|
78
|
+
.update(secretKey)
|
|
79
|
+
.digest("hex");
|
|
80
|
+
|
|
81
|
+
const cached: string | null | undefined = this.projectIdCache.get(cacheKey);
|
|
82
|
+
if (cached !== undefined) {
|
|
83
|
+
return cached === null ? null : new ObjectID(cached);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let secretKeyObjectId: ObjectID;
|
|
87
|
+
try {
|
|
88
|
+
secretKeyObjectId = new ObjectID(secretKey);
|
|
89
|
+
} catch {
|
|
90
|
+
this.projectIdCache.set(cacheKey, null, NEGATIVE_TTL_MS);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const token: Model | null = await this.findOneBy({
|
|
95
|
+
query: { secretKey: secretKeyObjectId },
|
|
96
|
+
select: { projectId: true },
|
|
97
|
+
props: { isRoot: true },
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const projectId: ObjectID | undefined = token?.projectId as
|
|
101
|
+
| ObjectID
|
|
102
|
+
| undefined;
|
|
103
|
+
|
|
104
|
+
if (!projectId) {
|
|
105
|
+
this.projectIdCache.set(cacheKey, null, NEGATIVE_TTL_MS);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.projectIdCache.set(cacheKey, projectId.toString(), POSITIVE_TTL_MS);
|
|
110
|
+
return projectId;
|
|
111
|
+
}
|
|
23
112
|
}
|
|
24
113
|
|
|
25
114
|
export default new Service();
|
|
@@ -9,6 +9,7 @@ import AnalyticsTableName from "../../Types/AnalyticsDatabase/AnalyticsTableName
|
|
|
9
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
10
10
|
import { DbJSONResponse, Results } from "./AnalyticsDatabaseService";
|
|
11
11
|
import logger from "../Utils/Logger";
|
|
12
|
+
import ServiceType from "../../Types/Telemetry/ServiceType";
|
|
12
13
|
|
|
13
14
|
export interface HistogramBucket {
|
|
14
15
|
time: string;
|
|
@@ -69,6 +70,17 @@ export class TraceAggregationService {
|
|
|
69
70
|
"statusCode",
|
|
70
71
|
"isRootSpan",
|
|
71
72
|
]);
|
|
73
|
+
/*
|
|
74
|
+
* Virtual facet keys — same scheme as LogAggregationService. The
|
|
75
|
+
* `serviceId` slot is reused for host / docker host / k8s cluster ids,
|
|
76
|
+
* disambiguated by the `serviceType` discriminator.
|
|
77
|
+
*/
|
|
78
|
+
private static readonly RESOURCE_FACET_KEYS: Map<string, ServiceType> =
|
|
79
|
+
new Map([
|
|
80
|
+
["hostId", ServiceType.Host],
|
|
81
|
+
["dockerHostId", ServiceType.DockerHost],
|
|
82
|
+
["kubernetesClusterId", ServiceType.KubernetesCluster],
|
|
83
|
+
]);
|
|
72
84
|
private static readonly ATTRIBUTE_KEY_PATTERN: RegExp = /^[a-zA-Z0-9._:/-]+$/;
|
|
73
85
|
private static readonly MAX_FACET_KEY_LENGTH: number = 256;
|
|
74
86
|
|
|
@@ -156,14 +168,25 @@ export class TraceAggregationService {
|
|
|
156
168
|
TraceAggregationService.validateFacetKey(facetKey);
|
|
157
169
|
}
|
|
158
170
|
|
|
171
|
+
const resourceKeys: Array<string> = request.facetKeys.filter(
|
|
172
|
+
(key: string): boolean => {
|
|
173
|
+
return TraceAggregationService.RESOURCE_FACET_KEYS.has(key);
|
|
174
|
+
},
|
|
175
|
+
);
|
|
159
176
|
const topLevelKeys: Array<string> = request.facetKeys.filter(
|
|
160
177
|
(key: string): boolean => {
|
|
161
|
-
return
|
|
178
|
+
return (
|
|
179
|
+
TraceAggregationService.isTopLevelColumn(key) &&
|
|
180
|
+
!TraceAggregationService.RESOURCE_FACET_KEYS.has(key)
|
|
181
|
+
);
|
|
162
182
|
},
|
|
163
183
|
);
|
|
164
184
|
const attributeKeys: Array<string> = request.facetKeys.filter(
|
|
165
185
|
(key: string): boolean => {
|
|
166
|
-
return
|
|
186
|
+
return (
|
|
187
|
+
!TraceAggregationService.isTopLevelColumn(key) &&
|
|
188
|
+
!TraceAggregationService.RESOURCE_FACET_KEYS.has(key)
|
|
189
|
+
);
|
|
167
190
|
},
|
|
168
191
|
);
|
|
169
192
|
|
|
@@ -171,6 +194,13 @@ export class TraceAggregationService {
|
|
|
171
194
|
if (topLevelKeys.length > 0) {
|
|
172
195
|
selectColumns.push(...topLevelKeys);
|
|
173
196
|
}
|
|
197
|
+
if (resourceKeys.length > 0) {
|
|
198
|
+
// Virtual facets read out of serviceId disambiguated by serviceType.
|
|
199
|
+
if (!selectColumns.includes("serviceId")) {
|
|
200
|
+
selectColumns.push("serviceId");
|
|
201
|
+
}
|
|
202
|
+
selectColumns.push("serviceType");
|
|
203
|
+
}
|
|
174
204
|
if (attributeKeys.length > 0) {
|
|
175
205
|
selectColumns.push("attributes");
|
|
176
206
|
}
|
|
@@ -244,6 +274,27 @@ export class TraceAggregationService {
|
|
|
244
274
|
map.set(value, (map.get(value) || 0) + 1);
|
|
245
275
|
}
|
|
246
276
|
|
|
277
|
+
if (resourceKeys.length > 0) {
|
|
278
|
+
const rowServiceType: string =
|
|
279
|
+
row["serviceType"] === undefined || row["serviceType"] === null
|
|
280
|
+
? ""
|
|
281
|
+
: String(row["serviceType"]);
|
|
282
|
+
const rowServiceId: unknown = row["serviceId"];
|
|
283
|
+
if (rowServiceId !== undefined && rowServiceId !== null) {
|
|
284
|
+
const value: string = String(rowServiceId);
|
|
285
|
+
if (value.length > 0) {
|
|
286
|
+
for (const key of resourceKeys) {
|
|
287
|
+
const expected: ServiceType | undefined =
|
|
288
|
+
TraceAggregationService.RESOURCE_FACET_KEYS.get(key);
|
|
289
|
+
if (expected && rowServiceType === expected) {
|
|
290
|
+
const map: Map<string, number> = counts[key]!;
|
|
291
|
+
map.set(value, (map.get(value) || 0) + 1);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
247
298
|
if (attributeKeys.length > 0) {
|
|
248
299
|
const attrs: unknown = row["attributes"];
|
|
249
300
|
let parsed: Record<string, unknown> | null = null;
|
|
@@ -368,13 +419,20 @@ export class TraceAggregationService {
|
|
|
368
419
|
|
|
369
420
|
TraceAggregationService.validateFacetKey(request.facetKey);
|
|
370
421
|
|
|
371
|
-
const
|
|
372
|
-
request.facetKey
|
|
373
|
-
|
|
422
|
+
const resourceServiceType: ServiceType | undefined =
|
|
423
|
+
TraceAggregationService.RESOURCE_FACET_KEYS.get(request.facetKey);
|
|
424
|
+
const isResourceFacet: boolean = resourceServiceType !== undefined;
|
|
425
|
+
const isTopLevelColumn: boolean =
|
|
426
|
+
isResourceFacet ||
|
|
427
|
+
TraceAggregationService.isTopLevelColumn(request.facetKey);
|
|
374
428
|
|
|
375
429
|
const statement: Statement = new Statement();
|
|
376
430
|
|
|
377
|
-
if (
|
|
431
|
+
if (isResourceFacet) {
|
|
432
|
+
statement.append(
|
|
433
|
+
SQL`SELECT toString(serviceId) AS val, count() AS cnt FROM ${TraceAggregationService.TABLE_NAME}`,
|
|
434
|
+
);
|
|
435
|
+
} else if (isTopLevelColumn) {
|
|
378
436
|
statement.append(
|
|
379
437
|
SQL`SELECT toString(${request.facetKey}) AS val, count() AS cnt FROM ${TraceAggregationService.TABLE_NAME}`,
|
|
380
438
|
);
|
|
@@ -400,7 +458,21 @@ export class TraceAggregationService {
|
|
|
400
458
|
}}`,
|
|
401
459
|
);
|
|
402
460
|
|
|
403
|
-
if (
|
|
461
|
+
if (isResourceFacet) {
|
|
462
|
+
statement.append(
|
|
463
|
+
SQL` AND serviceType = ${{
|
|
464
|
+
type: TableColumnType.Text,
|
|
465
|
+
value: resourceServiceType as string,
|
|
466
|
+
}}`,
|
|
467
|
+
);
|
|
468
|
+
} else if (request.facetKey === "serviceId") {
|
|
469
|
+
statement.append(
|
|
470
|
+
SQL` AND (serviceType = '' OR serviceType = ${{
|
|
471
|
+
type: TableColumnType.Text,
|
|
472
|
+
value: ServiceType.OpenTelemetry as string,
|
|
473
|
+
}})`,
|
|
474
|
+
);
|
|
475
|
+
} else if (!isTopLevelColumn) {
|
|
404
476
|
statement.append(
|
|
405
477
|
SQL` AND JSONHas(attributes, ${{
|
|
406
478
|
type: TableColumnType.Text,
|
|
@@ -539,7 +611,10 @@ export class TraceAggregationService {
|
|
|
539
611
|
throw new BadDataException("Invalid facetKey");
|
|
540
612
|
}
|
|
541
613
|
|
|
542
|
-
if (
|
|
614
|
+
if (
|
|
615
|
+
TraceAggregationService.isTopLevelColumn(facetKey) ||
|
|
616
|
+
TraceAggregationService.RESOURCE_FACET_KEYS.has(facetKey)
|
|
617
|
+
) {
|
|
543
618
|
return;
|
|
544
619
|
}
|
|
545
620
|
|
|
@@ -5,10 +5,8 @@ import MetricService from "../../Services/MetricService";
|
|
|
5
5
|
import GlobalConfigService from "../../Services/GlobalConfigService";
|
|
6
6
|
import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
|
|
7
7
|
import DataToProcess from "./DataToProcess";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
ServiceType,
|
|
11
|
-
} from "../../../Models/AnalyticsModels/Metric";
|
|
8
|
+
import { MetricPointType } from "../../../Models/AnalyticsModels/Metric";
|
|
9
|
+
import ServiceType from "../../../Types/Telemetry/ServiceType";
|
|
12
10
|
import MetricType from "../../../Models/DatabaseModels/MetricType";
|
|
13
11
|
import BasicInfrastructureMetrics, {
|
|
14
12
|
NetworkInterfaceMetrics,
|
|
@@ -135,6 +135,44 @@ export default class TelemetryUtil {
|
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/*
|
|
139
|
+
* Cross-cutting resource stamps. Spread alongside the service stamp on
|
|
140
|
+
* every analytics row so a single ClickHouse query can find "all
|
|
141
|
+
* telemetry from host X" / "from docker host Y" / "from cluster Z"
|
|
142
|
+
* regardless of which Service the row primarily belongs to. Without
|
|
143
|
+
* these, host context would only be queryable as the raw OTel
|
|
144
|
+
* `resource.host.name` string with no stable id link.
|
|
145
|
+
*/
|
|
146
|
+
public static getAttributesForHostIdAndHostName(data: {
|
|
147
|
+
hostId: ObjectID;
|
|
148
|
+
hostName: string;
|
|
149
|
+
}): Dictionary<AttributeType> {
|
|
150
|
+
return {
|
|
151
|
+
"oneuptime.host.id": data.hostId.toString(),
|
|
152
|
+
"oneuptime.host.name": data.hostName,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public static getAttributesForDockerHostIdAndHostName(data: {
|
|
157
|
+
dockerHostId: ObjectID;
|
|
158
|
+
hostName: string;
|
|
159
|
+
}): Dictionary<AttributeType> {
|
|
160
|
+
return {
|
|
161
|
+
"oneuptime.docker.host.id": data.dockerHostId.toString(),
|
|
162
|
+
"oneuptime.docker.host.name": data.hostName,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public static getAttributesForKubernetesClusterIdAndName(data: {
|
|
167
|
+
kubernetesClusterId: ObjectID;
|
|
168
|
+
clusterName: string;
|
|
169
|
+
}): Dictionary<AttributeType> {
|
|
170
|
+
return {
|
|
171
|
+
"oneuptime.kubernetes.cluster.id": data.kubernetesClusterId.toString(),
|
|
172
|
+
"oneuptime.kubernetes.cluster.name": data.clusterName,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
138
176
|
@CaptureSpan()
|
|
139
177
|
public static getAttributes(data: {
|
|
140
178
|
prefixKeysWithString: string;
|