From 4d8ed7090832d76c2bb9beb5769b706a0e9c8636 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 7 Mar 2023 13:56:02 -0700 Subject: [PATCH 1/4] Implement HTTP API versioned types in stats route --- .../usage_collection/common/types/index.ts | 9 + .../common/types/stats/core_metrics.ts | 217 ++++++++++++++++++ .../common/types/stats/index.ts | 10 + .../common/types/stats/latest.ts | 9 + .../usage_collection/common/types/stats/v1.ts | 62 +++++ .../server/routes/stats/stats.ts | 43 ++-- src/plugins/usage_collection/tsconfig.json | 2 +- 7 files changed, 325 insertions(+), 27 deletions(-) create mode 100644 src/plugins/usage_collection/common/types/index.ts create mode 100644 src/plugins/usage_collection/common/types/stats/core_metrics.ts create mode 100644 src/plugins/usage_collection/common/types/stats/index.ts create mode 100644 src/plugins/usage_collection/common/types/stats/latest.ts create mode 100644 src/plugins/usage_collection/common/types/stats/v1.ts diff --git a/src/plugins/usage_collection/common/types/index.ts b/src/plugins/usage_collection/common/types/index.ts new file mode 100644 index 0000000000000..391ced0c509d1 --- /dev/null +++ b/src/plugins/usage_collection/common/types/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1Stats from './stats'; diff --git a/src/plugins/usage_collection/common/types/stats/core_metrics.ts b/src/plugins/usage_collection/common/types/stats/core_metrics.ts new file mode 100644 index 0000000000000..16b16076567b9 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/core_metrics.ts @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * The types are exact duplicates of the ops metrics types declared in core needed for reporting in the stats api. + * We use a copy of these to detect changes in the ops metrics reported from core + * See packages/core/metrics/core-metrics-server/index.ts + */ + +/** + * an IntervalHistogram object that samples and reports the event loop delay over time. + * The delays will be reported in milliseconds. + * See {@link IntervalHistogram} + * @public + */ +export interface IntervalHistogram { + // The first timestamp the interval timer kicked in for collecting data points. + fromTimestamp: string; + // Last timestamp the interval timer kicked in for collecting data points. + lastUpdatedAt: string; + // The minimum recorded event loop delay. + min: number; + // The maximum recorded event loop delay. + max: number; + // The mean of the recorded event loop delays. + mean: number; + // The number of times the event loop delay exceeded the maximum 1 hour event loop delay threshold. + exceeds: number; + // The standard deviation of the recorded event loop delays. + stddev: number; + // An object detailing the accumulated percentile distribution. + percentiles: { + // 50th percentile of delays of the collected data points. + 50: number; + // 75th percentile of delays of the collected data points. + 75: number; + // 95th percentile of delays of the collected data points. + 95: number; + // 99th percentile of delays of the collected data points. + 99: number; + }; +} + +/** + * See {@link ElasticsearchClientProtocol} + * Protocol(s) used by the Elasticsearch Client + * @public + */ + +export type ElasticsearchClientProtocol = 'none' | 'http' | 'https' | 'mixed'; + +/** + * See {@link ElasticsearchClientsMetrics} + * Metrics related to the elasticsearch clients + * @public + */ +export interface ElasticsearchClientsMetrics { + /** Total number of active sockets (all nodes, all connections) */ + totalActiveSockets: number; + /** Total number of available sockets (alive but idle, all nodes, all connections) */ + totalIdleSockets: number; + /** Total number of queued requests (all nodes, all connections) */ + totalQueuedRequests: number; +} + +/** + * See {@link OpsProcessMetrics} + * Process related metrics + * @public + */ +export interface OpsProcessMetrics { + /** pid of the kibana process */ + pid: number; + /** process memory usage */ + memory: { + /** heap memory usage */ + heap: { + /** total heap available */ + total_in_bytes: number; + /** used heap */ + used_in_bytes: number; + /** v8 heap size limit */ + size_limit: number; + }; + /** node rss */ + resident_set_size_in_bytes: number; + }; + /** mean event loop delay since last collection*/ + event_loop_delay: number; + /** node event loop delay histogram since last collection */ + event_loop_delay_histogram: IntervalHistogram; + /** uptime of the kibana process */ + uptime_in_millis: number; +} + +/** + * See {@link OpsOsMetrics} + * OS related metrics + * @public + */ +export interface OpsOsMetrics { + /** The os platform */ + platform: NodeJS.Platform; + /** The os platform release, prefixed by the platform name */ + platformRelease: string; + /** The os distrib. Only present for linux platforms */ + distro?: string; + /** The os distrib release, prefixed by the os distrib. Only present for linux platforms */ + distroRelease?: string; + /** cpu load metrics */ + load: { + /** load for last minute */ + '1m': number; + /** load for last 5 minutes */ + '5m': number; + /** load for last 15 minutes */ + '15m': number; + }; + /** system memory usage metrics */ + memory: { + /** total memory available */ + total_in_bytes: number; + /** current free memory */ + free_in_bytes: number; + /** current used memory */ + used_in_bytes: number; + }; + /** the OS uptime */ + uptime_in_millis: number; + + /** cpu accounting metrics, undefined when not running in a cgroup */ + cpuacct?: { + /** name of this process's cgroup */ + control_group: string; + /** cpu time used by this process's cgroup */ + usage_nanos: number; + }; + + /** cpu cgroup metrics, undefined when not running in a cgroup */ + cpu?: { + /** name of this process's cgroup */ + control_group: string; + /** the length of the cfs period */ + cfs_period_micros: number; + /** total available run-time within a cfs period */ + cfs_quota_micros: number; + /** current stats on the cfs periods */ + stat: { + /** number of cfs periods that elapsed */ + number_of_elapsed_periods: number; + /** number of times the cgroup has been throttled */ + number_of_times_throttled: number; + /** total amount of time the cgroup has been throttled for */ + time_throttled_nanos: number; + }; + }; +} + +/** + * server related metrics + * @public + */ +export interface OpsServerMetrics { + /** server response time stats */ + response_times: { + /** average response time */ + avg_in_millis: number; + /** maximum response time */ + max_in_millis: number; + }; + /** server requests stats */ + requests: { + /** number of disconnected requests since startup */ + disconnects: number; + /** total number of requests handled since startup */ + total: number; + /** number of request handled per response status code */ + statusCodes: Record; + }; + /** number of current concurrent connections to the server */ + concurrent_connections: number; +} + +/** + * Regroups metrics gathered by all the collectors. + * This contains metrics about the os/runtime, the kibana process and the http server. + * + * @public + */ +export interface OpsMetrics { + /** Time metrics were recorded at. */ + collected_at: Date; + /** + * Metrics related to the elasticsearch client + */ + elasticsearch_client: ElasticsearchClientsMetrics; + /** + * Process related metrics. + * @remarks processes field preferred + */ + process: OpsProcessMetrics; + /** Process related metrics. Reports an array of objects for each kibana pid.*/ + processes: OpsProcessMetrics[]; + /** OS related metrics */ + os: OpsOsMetrics; + /** server response time stats */ + response_times: OpsServerMetrics['response_times']; + /** server requests stats */ + requests: OpsServerMetrics['requests']; + /** number of current concurrent connections to the server */ + concurrent_connections: OpsServerMetrics['concurrent_connections']; +} diff --git a/src/plugins/usage_collection/common/types/stats/index.ts b/src/plugins/usage_collection/common/types/stats/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/stats/latest.ts b/src/plugins/usage_collection/common/types/stats/latest.ts new file mode 100644 index 0000000000000..5d3faf872d5d6 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/latest.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * as OpsMetricsCopy from './core_metrics'; +export * from './v1'; diff --git a/src/plugins/usage_collection/common/types/stats/v1.ts b/src/plugins/usage_collection/common/types/stats/v1.ts new file mode 100644 index 0000000000000..0ee5473438977 --- /dev/null +++ b/src/plugins/usage_collection/common/types/stats/v1.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as OpsMetricsCopy from './core_metrics'; + +/** v1 types Start */ +/** + * stats query v1 + * @remarks exclude_usage is always interpreted as true. query param retained to prevent breaking changes to existing consumers. + */ +export interface StatsHTTPQuery { + extended?: boolean | ''; + legacy?: boolean | ''; + exclude_usage?: boolean | ''; +} + +export interface UsageObject { + kibana?: UsageObject; + xpack?: UsageObject; + [key: string]: unknown | UsageObject; +} +/** + * Extended usage stats. + * Legacy implementation used to conditionally include kibana usage metrics + * as of https://github.com/elastic/kibana/pull/151082, usage is no longer reported + * and set to an empty object to prevent breaking changes to existing consumers. + */ +export interface ExtendedStats { + [key: string]: unknown; + clusterUuid?: string; // camel case if legacy === true + cluster_uuid?: string; // snake_case if legacy === false +} +/** + * OpsMetrics: aliased from a duplicate of core's OpsMetrics types + * @remarks the alternative to creating a local copy of the OpsMetrics types is to declare them as `unknown` and assume validation happens elsewhere. + * The disadvantage is that any changes made to the original OpsMetrics will be passed through without needing to update the API types. + */ +export type LastOpsMetrics = OpsMetricsCopy.OpsMetrics; + +export type KibanaServiceStatus = Record; +/** explicitly typed stats for kibana */ +export interface KibanaStats { + // kibana + kibana: { + uuid: string; + name: string; + index: string; + host: string; + locale: string; + transport_address: string; + version: string; + snapshot: boolean; + status: string; + }; +} +/** Stats response body */ +export type StatsHTTPBodyTyped = LastOpsMetrics | KibanaStats | ExtendedStats; diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 242e93a7554e3..401be6a0eb7c5 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -18,14 +18,9 @@ import { ServiceStatusLevels, } from '@kbn/core/server'; import { CollectorSet } from '../../collector'; +import { v1Stats } from '../../../common/types'; const SNAPSHOT_REGEX = /-snapshot/i; -interface UsageObject { - kibana?: UsageObject; - xpack?: UsageObject; - [key: string]: unknown | UsageObject; -} - export function registerStatsRoute({ router, config, @@ -73,28 +68,22 @@ export function registerStatsRoute({ }, }, async (context, req, res) => { - const isExtended = req.query.extended === '' || req.query.extended; - const isLegacy = req.query.legacy === '' || req.query.legacy; + const requestQuery: v1Stats.StatsHTTPQuery = req.query; + const isExtended = requestQuery.extended === '' || requestQuery.extended; + const isLegacy = requestQuery.legacy === '' || requestQuery.legacy; let extended; if (isExtended) { const core = await context.core; const { asCurrentUser } = core.elasticsearch.client; + // as of https://github.com/elastic/kibana/pull/151082, usage will always be an empty object. - const usage = {} as UsageObject; const clusterUuid = await getClusterUuid(asCurrentUser); - - // In an effort to make telemetry more easily augmented, we need to ensure - // we can passthrough the data without every part of the process needing - // to know about the change; however, to support legacy use cases where this - // wasn't true, we need to be backwards compatible with how the legacy data - // looked and support those use cases here. - extended = isLegacy - ? { usage, clusterUuid } - : collectorSet.toApiFieldNames({ - usage, - clusterUuid, - }); + const extendedClusterUuid = isLegacy ? { clusterUuid } : { cluster_uuid: clusterUuid }; + extended = { + usage: {}, + extendedClusterUuid, + }; } // Guaranteed to resolve immediately due to replay effect on getOpsMetrics$ @@ -120,17 +109,19 @@ export function registerStatsRoute({ collection_interval_in_millis: metrics.collectionInterval, }); + const body: v1Stats.StatsHTTPBodyTyped = { + ...kibanaStats, + ...extended, + }; + return res.ok({ - body: { - ...kibanaStats, - ...extended, - }, + body, }); } ); } -const ServiceStatusToLegacyState: Record = { +const ServiceStatusToLegacyState: v1Stats.KibanaServiceStatus = { [ServiceStatusLevels.critical.toString()]: 'red', [ServiceStatusLevels.unavailable.toString()]: 'red', [ServiceStatusLevels.degraded.toString()]: 'yellow', diff --git a/src/plugins/usage_collection/tsconfig.json b/src/plugins/usage_collection/tsconfig.json index f6e360d80b3ad..becc176c0451e 100644 --- a/src/plugins/usage_collection/tsconfig.json +++ b/src/plugins/usage_collection/tsconfig.json @@ -7,7 +7,7 @@ "include": [ "public/**/*", "server/**/*", - "common/*", + "common/**/*", "../../../typings/**/*" ], "kbn_references": [ From 069d8f776310881baf4784cbea1232a3f521d609 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 7 Mar 2023 15:39:43 -0700 Subject: [PATCH 2/4] Version ui_counters http api types --- .../usage_collection/common/types/index.ts | 4 +- .../common/types/ui_counters/index.ts | 10 ++++ .../common/types/ui_counters/latest.ts | 9 +++ .../common/types/ui_counters/v1.ts | 58 +++++++++++++++++++ .../common/types/usage_counters/index.ts | 10 ++++ .../common/types/usage_counters/latest.ts | 9 +++ .../common/types/usage_counters/v1.ts | 14 +++++ .../server/routes/stats/stats.ts | 8 +-- .../server/routes/ui_counters.ts | 15 +++-- .../usage_counters/usage_counter.test.ts | 5 +- .../server/usage_counters/usage_counter.ts | 18 +++--- 11 files changed, 140 insertions(+), 20 deletions(-) create mode 100644 src/plugins/usage_collection/common/types/ui_counters/index.ts create mode 100644 src/plugins/usage_collection/common/types/ui_counters/latest.ts create mode 100644 src/plugins/usage_collection/common/types/ui_counters/v1.ts create mode 100644 src/plugins/usage_collection/common/types/usage_counters/index.ts create mode 100644 src/plugins/usage_collection/common/types/usage_counters/latest.ts create mode 100644 src/plugins/usage_collection/common/types/usage_counters/v1.ts diff --git a/src/plugins/usage_collection/common/types/index.ts b/src/plugins/usage_collection/common/types/index.ts index 391ced0c509d1..285785a7ad952 100644 --- a/src/plugins/usage_collection/common/types/index.ts +++ b/src/plugins/usage_collection/common/types/index.ts @@ -6,4 +6,6 @@ * Side Public License, v 1. */ -export * as v1Stats from './stats'; +export * as Stats from './stats'; +export * as UiCounters from './ui_counters'; +export * as UsageCounters from './usage_counters'; diff --git a/src/plugins/usage_collection/common/types/ui_counters/index.ts b/src/plugins/usage_collection/common/types/ui_counters/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/ui_counters/latest.ts b/src/plugins/usage_collection/common/types/ui_counters/latest.ts new file mode 100644 index 0000000000000..f966e698fb59e --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/latest.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; diff --git a/src/plugins/usage_collection/common/types/ui_counters/v1.ts b/src/plugins/usage_collection/common/types/ui_counters/v1.ts new file mode 100644 index 0000000000000..fba90a4956666 --- /dev/null +++ b/src/plugins/usage_collection/common/types/ui_counters/v1.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * ui_counters query v1 + * @remarks + */ +export interface UiCountersRequestBody { + report: { + reportVersion?: 3; + userAgent?: Record< + string, + Readonly< + {} & { + key: string; + type: string; + appName: string; + userAgent: string; + } + > + >; + uiCounter?: Record< + string, + Readonly< + {} & { + key: string; + type: string; + appName: string; + eventName: string; + total: number; + } + > + >; + application_usage?: Record< + string, + Readonly<{ + minutesOnScreen: number; + numberOfClicks: number; + appId: string; + viewId: string; + }> + >; + }; +} +/** explicit response type for store report success. The status value is hardcoded. */ +export interface UiCountersResponseOk { + status: 'ok'; +} + +/** explicit response type for store report fail. The status value is hardcoded. */ +export interface UiCountersResponseFail { + status: 'fail'; +} diff --git a/src/plugins/usage_collection/common/types/usage_counters/index.ts b/src/plugins/usage_collection/common/types/usage_counters/index.ts new file mode 100644 index 0000000000000..f14c7c0580841 --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * as v1 from './v1'; +export * from './latest'; diff --git a/src/plugins/usage_collection/common/types/usage_counters/latest.ts b/src/plugins/usage_collection/common/types/usage_counters/latest.ts new file mode 100644 index 0000000000000..e9c79f0f50f93 --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/latest.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './v1'; diff --git a/src/plugins/usage_collection/common/types/usage_counters/v1.ts b/src/plugins/usage_collection/common/types/usage_counters/v1.ts new file mode 100644 index 0000000000000..f095de3fd802d --- /dev/null +++ b/src/plugins/usage_collection/common/types/usage_counters/v1.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface CounterMetric { + domainId: string; + counterName: string; + counterType: string; + incrementBy: number; +} diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 401be6a0eb7c5..a23a8fb34e855 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -18,7 +18,7 @@ import { ServiceStatusLevels, } from '@kbn/core/server'; import { CollectorSet } from '../../collector'; -import { v1Stats } from '../../../common/types'; +import { Stats } from '../../../common/types'; const SNAPSHOT_REGEX = /-snapshot/i; export function registerStatsRoute({ @@ -68,7 +68,7 @@ export function registerStatsRoute({ }, }, async (context, req, res) => { - const requestQuery: v1Stats.StatsHTTPQuery = req.query; + const requestQuery: Stats.v1.StatsHTTPQuery = req.query; const isExtended = requestQuery.extended === '' || requestQuery.extended; const isLegacy = requestQuery.legacy === '' || requestQuery.legacy; @@ -109,7 +109,7 @@ export function registerStatsRoute({ collection_interval_in_millis: metrics.collectionInterval, }); - const body: v1Stats.StatsHTTPBodyTyped = { + const body: Stats.v1.StatsHTTPBodyTyped = { ...kibanaStats, ...extended, }; @@ -121,7 +121,7 @@ export function registerStatsRoute({ ); } -const ServiceStatusToLegacyState: v1Stats.KibanaServiceStatus = { +const ServiceStatusToLegacyState: Stats.v1.KibanaServiceStatus = { [ServiceStatusLevels.critical.toString()]: 'red', [ServiceStatusLevels.unavailable.toString()]: 'red', [ServiceStatusLevels.degraded.toString()]: 'yellow', diff --git a/src/plugins/usage_collection/server/routes/ui_counters.ts b/src/plugins/usage_collection/server/routes/ui_counters.ts index f4d7385195012..9752afeae3ef9 100644 --- a/src/plugins/usage_collection/server/routes/ui_counters.ts +++ b/src/plugins/usage_collection/server/routes/ui_counters.ts @@ -10,6 +10,7 @@ import { schema } from '@kbn/config-schema'; import { IRouter, ISavedObjectsRepository } from '@kbn/core/server'; import { storeReport, reportSchema } from '../report'; import { UsageCounter } from '../usage_counters'; +import { UiCounters } from '../../common/types'; export function registerUiCountersRoute( router: IRouter, @@ -26,16 +27,22 @@ export function registerUiCountersRoute( }, }, async (context, req, res) => { - const { report } = req.body; + const requestBody: UiCounters.v1.UiCountersRequestBody = req.body; try { const internalRepository = getSavedObjects(); if (!internalRepository) { throw Error(`The saved objects client hasn't been initialised yet`); } - await storeReport(internalRepository, uiCountersUsageCounter, report); - return res.ok({ body: { status: 'ok' } }); + await storeReport(internalRepository, uiCountersUsageCounter, requestBody.report); + const bodyOk: UiCounters.v1.UiCountersResponseOk = { + status: 'ok', + }; + return res.ok({ body: bodyOk }); } catch (error) { - return res.ok({ body: { status: 'fail' } }); + const bodyFail: UiCounters.v1.UiCountersResponseFail = { + status: 'fail', + }; + return res.ok({ body: bodyFail }); } } ); diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts index 3602ff1a29376..e22423b2283cf 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.test.ts @@ -5,13 +5,14 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { UsageCounter, CounterMetric } from './usage_counter'; +import { UsageCounter } from './usage_counter'; +import type { UsageCounters } from '../../common/types'; import * as Rx from 'rxjs'; import * as rxOp from 'rxjs/operators'; describe('UsageCounter', () => { const domainId = 'test-domain-id'; - const counter$ = new Rx.Subject(); + const counter$ = new Rx.Subject(); const usageCounter = new UsageCounter({ domainId, counter$ }); afterAll(() => { diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts index b8057fdda8eb6..92fb633c6a612 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts @@ -7,17 +7,17 @@ */ import * as Rx from 'rxjs'; - -export interface CounterMetric { - domainId: string; - counterName: string; - counterType: string; - incrementBy: number; -} +import { UsageCounters } from '../../common/types'; +// export interface CounterMetric { +// domainId: string; +// counterName: string; +// counterType: string; +// incrementBy: number; +// } export interface UsageCounterDeps { domainId: string; - counter$: Rx.Subject; + counter$: Rx.Subject; } /** @@ -47,7 +47,7 @@ export interface IUsageCounter { export class UsageCounter implements IUsageCounter { private domainId: string; - private counter$: Rx.Subject; + private counter$: Rx.Subject; constructor({ domainId, counter$ }: UsageCounterDeps) { this.domainId = domainId; From 01e2d9e3212033ed545a87dd5496b5d62aa00f26 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 7 Mar 2023 16:30:29 -0700 Subject: [PATCH 3/4] version usage counters route types --- .../common/types/usage_counters/v1.ts | 12 ++++++++++ .../public/services/create_reporter.ts | 5 +++-- .../server/usage_counters/index.ts | 4 +++- .../usage_counters/saved_objects.test.ts | 6 +++-- .../server/usage_counters/saved_objects.ts | 4 ++-- .../server/usage_counters/usage_counter.ts | 22 ++----------------- .../usage_counters/usage_counters_service.ts | 15 ++++++++----- 7 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/plugins/usage_collection/common/types/usage_counters/v1.ts b/src/plugins/usage_collection/common/types/usage_counters/v1.ts index f095de3fd802d..fa53a9d9e93b0 100644 --- a/src/plugins/usage_collection/common/types/usage_counters/v1.ts +++ b/src/plugins/usage_collection/common/types/usage_counters/v1.ts @@ -12,3 +12,15 @@ export interface CounterMetric { counterType: string; incrementBy: number; } + +/** + * Details about the counter to be incremented + */ +export interface IncrementCounterParams { + /** The name of the counter **/ + counterName: string; + /** The counter type ("count" by default) **/ + counterType?: string; + /** Increment the counter by this number (1 if not specified) **/ + incrementBy?: number; +} diff --git a/src/plugins/usage_collection/public/services/create_reporter.ts b/src/plugins/usage_collection/public/services/create_reporter.ts index 95d5fff9cc943..1fbf17fe2e49a 100644 --- a/src/plugins/usage_collection/public/services/create_reporter.ts +++ b/src/plugins/usage_collection/public/services/create_reporter.ts @@ -8,6 +8,7 @@ import { Reporter, Storage } from '@kbn/analytics'; import { HttpSetup } from '@kbn/core/public'; +import { UiCounters } from '../../common/types'; interface AnalyicsReporterConfig { localStorage: Storage; @@ -27,8 +28,8 @@ export function createReporter(config: AnalyicsReporterConfig): Reporter { body: JSON.stringify({ report }), asSystemRequest: true, }); - - if (response.status !== 'ok') { + const okStatus: UiCounters.v1.UiCountersResponseOk = response.status; + if (response.status !== okStatus) { throw Error('Unable to store report.'); } return response; diff --git a/src/plugins/usage_collection/server/usage_counters/index.ts b/src/plugins/usage_collection/server/usage_counters/index.ts index cc1ee77ef3ea4..05d36aaef502f 100644 --- a/src/plugins/usage_collection/server/usage_counters/index.ts +++ b/src/plugins/usage_collection/server/usage_counters/index.ts @@ -5,10 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { UsageCounters } from '../../common/types'; +export type IncrementCounterParams = UsageCounters.v1.IncrementCounterParams; export type { UsageCountersServiceSetup } from './usage_counters_service'; export type { UsageCountersSavedObjectAttributes, UsageCountersSavedObject } from './saved_objects'; -export type { IUsageCounter as UsageCounter, IncrementCounterParams } from './usage_counter'; +export type { IUsageCounter as UsageCounter } from './usage_counter'; export { UsageCountersService } from './usage_counters_service'; export type { SerializeCounterParams } from './saved_objects'; diff --git a/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts b/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts index cb3c5e7683b16..fa40fd5a8d2be 100644 --- a/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts +++ b/src/plugins/usage_collection/server/usage_counters/saved_objects.test.ts @@ -8,7 +8,9 @@ import { serializeCounterKey, storeCounter } from './saved_objects'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; -import { CounterMetric } from './usage_counter'; + +import { UsageCounters } from '../../common/types'; + import moment from 'moment'; describe('counterKey', () => { @@ -38,7 +40,7 @@ describe('storeCounter', () => { }); it('stores counter in a saved object', async () => { - const counterMetric: CounterMetric = { + const counterMetric: UsageCounters.v1.CounterMetric = { domainId: 'a', counterName: 'b', counterType: 'c', diff --git a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts index 701bbba404c49..402dabb62b96b 100644 --- a/src/plugins/usage_collection/server/usage_counters/saved_objects.ts +++ b/src/plugins/usage_collection/server/usage_counters/saved_objects.ts @@ -12,7 +12,7 @@ import type { SavedObjectsServiceSetup, } from '@kbn/core/server'; import moment from 'moment'; -import type { CounterMetric } from './usage_counter'; +import { UsageCounters } from '../../common/types'; /** * The attributes stored in the UsageCounters' SavedObjects @@ -83,7 +83,7 @@ export const serializeCounterKey = ({ }; export const storeCounter = async ( - counterMetric: CounterMetric, + counterMetric: UsageCounters.v1.CounterMetric, internalRepository: Pick ) => { const { counterName, counterType, domainId, incrementBy } = counterMetric; diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts index 92fb633c6a612..80bd32ae4d1db 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counter.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counter.ts @@ -8,30 +8,12 @@ import * as Rx from 'rxjs'; import { UsageCounters } from '../../common/types'; -// export interface CounterMetric { -// domainId: string; -// counterName: string; -// counterType: string; -// incrementBy: number; -// } export interface UsageCounterDeps { domainId: string; counter$: Rx.Subject; } -/** - * Details about the counter to be incremented - */ -export interface IncrementCounterParams { - /** The name of the counter **/ - counterName: string; - /** The counter type ("count" by default) **/ - counterType?: string; - /** Increment the counter by this number (1 if not specified) **/ - incrementBy?: number; -} - /** * Usage Counter allows to keep track of any events that occur. * By calling {@link IUsageCounter.incrementCounter} devs can notify this @@ -42,7 +24,7 @@ export interface IUsageCounter { * Notifies the counter about a new event happening so it can increase the count internally. * @param params {@link IncrementCounterParams} */ - incrementCounter: (params: IncrementCounterParams) => void; + incrementCounter: (params: UsageCounters.v1.IncrementCounterParams) => void; } export class UsageCounter implements IUsageCounter { @@ -54,7 +36,7 @@ export class UsageCounter implements IUsageCounter { this.counter$ = counter$; } - public incrementCounter = (params: IncrementCounterParams) => { + public incrementCounter = (params: UsageCounters.v1.IncrementCounterParams) => { const { counterName, counterType = 'count', incrementBy = 1 } = params; this.counter$.next({ diff --git a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts index 095f314a8e80b..0218334b7e153 100644 --- a/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts +++ b/src/plugins/usage_collection/server/usage_counters/usage_counters_service.ts @@ -16,7 +16,8 @@ import { import type { Logger, LogMeta } from '@kbn/core/server'; import moment from 'moment'; -import { CounterMetric, UsageCounter } from './usage_counter'; +import { UsageCounter } from './usage_counter'; +import { UsageCounters } from '../../common/types'; import { registerUsageCountersSavedObjectType, storeCounter, @@ -54,7 +55,7 @@ export class UsageCountersService { private readonly bufferDurationMs: number; private readonly counterSets = new Map(); - private readonly source$ = new Rx.Subject(); + private readonly source$ = new Rx.Subject(); private readonly counter$ = this.source$.pipe(rxOp.multicast(new Rx.Subject()), rxOp.refCount()); private readonly flushCache$ = new Rx.Subject(); @@ -69,7 +70,7 @@ export class UsageCountersService { } public setup = (core: UsageCountersServiceSetupDeps): UsageCountersServiceSetup => { - const cache$ = new Rx.ReplaySubject(); + const cache$ = new Rx.ReplaySubject(); const storingCache$ = new Rx.BehaviorSubject(false); // flush cache data from cache -> source this.flushCache$ @@ -135,7 +136,7 @@ export class UsageCountersService { }; private storeDate$( - counters: CounterMetric[], + counters: UsageCounters.v1.CounterMetric[], internalRepository: Pick ) { return Rx.forkJoin( @@ -170,7 +171,9 @@ export class UsageCountersService { return this.counterSets.get(type); }; - private mergeCounters = (counters: CounterMetric[]): Record => { + private mergeCounters = ( + counters: UsageCounters.v1.CounterMetric[] + ): Record => { const date = moment.now(); return counters.reduce((acc, counter) => { const { counterName, domainId, counterType } = counter; @@ -188,6 +191,6 @@ export class UsageCountersService { incrementBy: existingCounter.incrementBy + counter.incrementBy, }, }; - }, {} as Record); + }, {} as Record); }; } From 5daf48baad531f933fd19328df21990c84adb653 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 8 Mar 2023 09:33:57 -0700 Subject: [PATCH 4/4] spread extended additions --- .../common/types/stats/core_metrics.ts | 4 +++- src/plugins/usage_collection/common/types/stats/v1.ts | 10 +++++++++- .../usage_collection/server/routes/stats/stats.ts | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/plugins/usage_collection/common/types/stats/core_metrics.ts b/src/plugins/usage_collection/common/types/stats/core_metrics.ts index 16b16076567b9..64e5ef34155d9 100644 --- a/src/plugins/usage_collection/common/types/stats/core_metrics.ts +++ b/src/plugins/usage_collection/common/types/stats/core_metrics.ts @@ -162,6 +162,7 @@ export interface OpsOsMetrics { } /** + * {@link OpsServerMetrics} * server related metrics * @public */ @@ -187,9 +188,10 @@ export interface OpsServerMetrics { } /** + * {@link OpsMetrics} * Regroups metrics gathered by all the collectors. * This contains metrics about the os/runtime, the kibana process and the http server. - * + * @public */ export interface OpsMetrics { diff --git a/src/plugins/usage_collection/common/types/stats/v1.ts b/src/plugins/usage_collection/common/types/stats/v1.ts index 0ee5473438977..97581a84cb813 100644 --- a/src/plugins/usage_collection/common/types/stats/v1.ts +++ b/src/plugins/usage_collection/common/types/stats/v1.ts @@ -24,14 +24,22 @@ export interface UsageObject { xpack?: UsageObject; [key: string]: unknown | UsageObject; } + +export interface ClusterUuidLegacy { + clusterUuid?: string; +} +export interface ClusterUuid { + cluster_uuid?: string; +} /** * Extended usage stats. + * @remarks * Legacy implementation used to conditionally include kibana usage metrics * as of https://github.com/elastic/kibana/pull/151082, usage is no longer reported * and set to an empty object to prevent breaking changes to existing consumers. */ export interface ExtendedStats { - [key: string]: unknown; + [key: string]: unknown | UsageObject; clusterUuid?: string; // camel case if legacy === true cluster_uuid?: string; // snake_case if legacy === false } diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index a23a8fb34e855..d98457d498244 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -82,7 +82,7 @@ export function registerStatsRoute({ const extendedClusterUuid = isLegacy ? { clusterUuid } : { cluster_uuid: clusterUuid }; extended = { usage: {}, - extendedClusterUuid, + ...extendedClusterUuid, }; }