From d911a311973cdb328f5c0c6b6e9fa2fce39d57cf Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Tue, 31 May 2022 16:13:44 +0900 Subject: [PATCH 01/16] Add otel metrics to alerting plugin --- package.json | 7 + .../alerting/server/monitoring/index.ts | 1 + .../server/monitoring/metrics.mock.ts | 13 + .../alerting/server/monitoring/metrics.ts | 22 ++ x-pack/plugins/alerting/server/plugin.ts | 11 +- .../server/rule_type_registry.test.ts | 3 + .../alerting/server/rule_type_registry.ts | 8 +- .../saved_objects/is_rule_exportable.test.ts | 3 + .../server/task_runner/task_runner.test.ts | 134 +++++--- .../server/task_runner/task_runner.ts | 12 +- .../task_runner/task_runner_cancel.test.ts | 14 +- .../task_runner/task_runner_factory.test.ts | 4 +- .../server/task_runner/task_runner_factory.ts | 7 +- .../monitoring_collection/server/config.ts | 12 + .../monitoring_collection/server/constants.ts | 2 + .../server/lib/prometheus_exporter.ts | 75 +++++ .../server/lib/prometheus_serializer.ts | 311 ++++++++++++++++++ .../monitoring_collection/server/plugin.ts | 87 ++++- .../server/routes/index.ts | 1 + .../server/routes/prometheus.test.ts | 29 ++ .../server/routes/prometheus.ts | 32 ++ yarn.lock | 169 +++++++++- 22 files changed, 891 insertions(+), 66 deletions(-) create mode 100644 x-pack/plugins/alerting/server/monitoring/metrics.mock.ts create mode 100644 x-pack/plugins/alerting/server/monitoring/metrics.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts create mode 100644 x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts create mode 100644 x-pack/plugins/monitoring_collection/server/routes/prometheus.test.ts create mode 100644 x-pack/plugins/monitoring_collection/server/routes/prometheus.ts diff --git a/package.json b/package.json index 4764f833786cb..e2c19700b73e7 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@emotion/css": "^11.9.0", "@emotion/react": "^11.9.0", "@emotion/serialize": "^1.0.3", + "@grpc/grpc-js": "^1.5.9", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.4", "@hapi/cookie": "^11.0.2", @@ -263,6 +264,12 @@ "@mapbox/mapbox-gl-draw": "1.3.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mapbox/vector-tile": "1.3.1", + "@opentelemetry/api-metrics": "0.29.2", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.29.2", + "@opentelemetry/exporter-prometheus": "^0.29.2", + "@opentelemetry/resources": "^1.3.1", + "@opentelemetry/sdk-metrics-base": "^0.29.2", + "@opentelemetry/semantic-conventions": "^1.3.1", "@reduxjs/toolkit": "^1.6.1", "@slack/webhook": "^5.0.4", "@turf/along": "6.0.1", diff --git a/x-pack/plugins/alerting/server/monitoring/index.ts b/x-pack/plugins/alerting/server/monitoring/index.ts index 5f298456554f0..3040758f6a4d7 100644 --- a/x-pack/plugins/alerting/server/monitoring/index.ts +++ b/x-pack/plugins/alerting/server/monitoring/index.ts @@ -8,3 +8,4 @@ export { registerNodeCollector } from './register_node_collector'; export { registerClusterCollector } from './register_cluster_collector'; export * from './types'; export * from './in_memory_metrics'; +export * from './metrics'; diff --git a/x-pack/plugins/alerting/server/monitoring/metrics.mock.ts b/x-pack/plugins/alerting/server/monitoring/metrics.mock.ts new file mode 100644 index 0000000000000..129298e63e90f --- /dev/null +++ b/x-pack/plugins/alerting/server/monitoring/metrics.mock.ts @@ -0,0 +1,13 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { metrics } from '@opentelemetry/api-metrics'; +import { Metrics } from './metrics'; + +export const metricsMock = { + create: () => new Metrics(metrics.getMeter('kibana.alerting.metrics.mock')), +}; diff --git a/x-pack/plugins/alerting/server/monitoring/metrics.ts b/x-pack/plugins/alerting/server/monitoring/metrics.ts new file mode 100644 index 0000000000000..b13fefa65b5c6 --- /dev/null +++ b/x-pack/plugins/alerting/server/monitoring/metrics.ts @@ -0,0 +1,22 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Counter, Histogram, Meter } from '@opentelemetry/api-metrics'; + +export class Metrics { + ruleExecutionsTotal: Counter; + ruleExecutions: Counter; + ruleFailures: Counter; + ruleDuration: Histogram; + + constructor(meter: Meter) { + this.ruleExecutionsTotal = meter.createCounter('ruleExecutionsTotal'); + this.ruleExecutions = meter.createCounter('ruleExecutions'); + this.ruleFailures = meter.createCounter('ruleFailures'); + this.ruleDuration = meter.createHistogram('ruleDuration'); + } +} diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 83d9c4f99ded8..3fe87ddae3344 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -49,6 +49,7 @@ import { import { PluginStartContract as FeaturesPluginStart } from '@kbn/features-plugin/server'; import { PluginStart as DataPluginStart } from '@kbn/data-plugin/server'; import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server'; +import { metrics } from '@opentelemetry/api-metrics'; import { RuleTypeRegistry } from './rule_type_registry'; import { TaskRunnerFactory } from './task_runner'; import { RulesClientFactory } from './rules_client_factory'; @@ -77,7 +78,12 @@ import { getHealth } from './health/get_health'; import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory'; import { AlertingAuthorization } from './authorization'; import { getSecurityHealth, SecurityHealth } from './lib/get_security_health'; -import { registerNodeCollector, registerClusterCollector, InMemoryMetrics } from './monitoring'; +import { + registerNodeCollector, + registerClusterCollector, + InMemoryMetrics, + Metrics, +} from './monitoring'; import { getRuleTaskTimeout } from './lib/get_rule_task_timeout'; import { getActionsConfigMap } from './lib/get_actions_config_map'; @@ -173,6 +179,7 @@ export class AlertingPlugin { private kibanaBaseUrl: string | undefined; private usageCounter: UsageCounter | undefined; private inMemoryMetrics: InMemoryMetrics; + private metrics: Metrics; constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); @@ -183,6 +190,7 @@ export class AlertingPlugin { this.telemetryLogger = initializerContext.logger.get('usage'); this.kibanaVersion = initializerContext.env.packageInfo.version; this.inMemoryMetrics = new InMemoryMetrics(initializerContext.logger.get('in_memory_metrics')); + this.metrics = new Metrics(metrics.getMeter('kibana.alerting')); } public setup( @@ -227,6 +235,7 @@ export class AlertingPlugin { licensing: plugins.licensing, minimumScheduleInterval: this.config.rules.minimumScheduleInterval, inMemoryMetrics: this.inMemoryMetrics, + metrics: this.metrics, }); this.ruleTypeRegistry = ruleTypeRegistry; diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index ed52ebf8b04da..daf8bd69332cc 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -14,6 +14,7 @@ import { licenseStateMock } from './lib/license_state.mock'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock'; +import { metricsMock } from './monitoring/metrics.mock'; const logger = loggingSystemMock.create().get(); let mockedLicenseState: jest.Mocked; @@ -21,6 +22,7 @@ let ruleTypeRegistryParams: ConstructorOptions; const taskManager = taskManagerMock.createSetup(); const inMemoryMetrics = inMemoryMetricsMock.create(); +const metrics = metricsMock.create(); beforeEach(() => { jest.resetAllMocks(); @@ -33,6 +35,7 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, + metrics, }; }); diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index 338450746781b..f6a164b423727 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -31,7 +31,7 @@ import { } from '../common'; import { ILicenseState } from './lib/license_state'; import { getRuleTypeFeatureUsageName } from './lib/get_rule_type_feature_usage_name'; -import { InMemoryMetrics } from './monitoring'; +import { InMemoryMetrics, Metrics } from './monitoring'; import { AlertingRulesConfig } from '.'; export interface ConstructorOptions { @@ -42,6 +42,7 @@ export interface ConstructorOptions { licensing: LicensingPluginSetup; minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; inMemoryMetrics: InMemoryMetrics; + metrics: Metrics; } export interface RegistryRuleType @@ -139,6 +140,7 @@ export class RuleTypeRegistry { private readonly minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; private readonly licensing: LicensingPluginSetup; private readonly inMemoryMetrics: InMemoryMetrics; + private readonly metrics: Metrics; constructor({ logger, @@ -148,6 +150,7 @@ export class RuleTypeRegistry { licensing, minimumScheduleInterval, inMemoryMetrics, + metrics, }: ConstructorOptions) { this.logger = logger; this.taskManager = taskManager; @@ -156,6 +159,7 @@ export class RuleTypeRegistry { this.licensing = licensing; this.minimumScheduleInterval = minimumScheduleInterval; this.inMemoryMetrics = inMemoryMetrics; + this.metrics = metrics; } public has(id: string) { @@ -274,7 +278,7 @@ export class RuleTypeRegistry { InstanceContext, ActionGroupIds, RecoveryActionGroupId | RecoveredActionGroupId - >(normalizedRuleType, context, this.inMemoryMetrics), + >(normalizedRuleType, context, this.inMemoryMetrics, this.metrics), }, }); // No need to notify usage on basic alert types diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index 0f2677dc49751..81992e1a50510 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -15,12 +15,14 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { isRuleExportable } from './is_rule_exportable'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { metricsMock } from '../monitoring/metrics.mock'; let ruleTypeRegistryParams: ConstructorOptions; let logger: MockedLogger; let mockedLicenseState: jest.Mocked; const taskManager = taskManagerMock.createSetup(); const inMemoryMetrics = inMemoryMetricsMock.create(); +const metrics = metricsMock.create(); beforeEach(() => { jest.resetAllMocks(); @@ -34,6 +36,7 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, + metrics, }; }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 5d2f6d7c1f659..d142cc385d035 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -43,6 +43,7 @@ import { omit } from 'lodash'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { ExecuteOptions } from '@kbn/actions-plugin/server/create_execute_function'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; +import { metricsMock } from '../monitoring/metrics.mock'; import moment from 'moment'; import { generateAlertOpts, @@ -123,6 +124,7 @@ describe('Task Runner', () => { const dataPlugin = dataPluginMock.createStartContract(); const uiSettingsService = uiSettingsServiceMock.createStartContract(); const inMemoryMetrics = inMemoryMetricsMock.create(); + const metrics = metricsMock.create(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -220,7 +222,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -321,7 +324,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -402,7 +406,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -523,7 +528,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -576,7 +582,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -655,7 +662,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -699,7 +707,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -756,7 +765,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -824,7 +834,8 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -905,7 +916,8 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -970,7 +982,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1082,7 +1095,8 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1194,7 +1208,8 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1285,7 +1300,8 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1376,7 +1392,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1439,7 +1456,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1462,7 +1480,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1492,7 +1511,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1524,7 +1544,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1561,7 +1582,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1596,7 +1618,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1625,7 +1648,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1654,7 +1678,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1684,7 +1709,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1717,7 +1743,8 @@ describe('Task Runner', () => { ruleType, legacyTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1755,7 +1782,8 @@ describe('Task Runner', () => { state: originalAlertSate, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1785,7 +1813,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1817,7 +1846,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1841,7 +1871,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1867,7 +1898,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1916,7 +1948,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2022,7 +2055,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2096,7 +2130,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2165,7 +2200,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2239,7 +2275,8 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2291,7 +2328,8 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, supportsEphemeralTasks: true, }, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2362,7 +2400,8 @@ describe('Task Runner', () => { state, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2391,7 +2430,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2406,7 +2446,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2434,7 +2475,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2469,7 +2511,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2554,7 +2597,8 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, actionsConfigMap, }, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2706,7 +2750,8 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, actionsConfigMap, }, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2776,7 +2821,8 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalled(); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 5f7e1299f7698..6a87822940a9f 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -55,7 +55,7 @@ import { } from '../../common'; import { NormalizedRuleType, UntypedNormalizedRuleType } from '../rule_type_registry'; import { getEsErrorMessage } from '../lib/errors'; -import { InMemoryMetrics, IN_MEMORY_METRICS } from '../monitoring'; +import { InMemoryMetrics, IN_MEMORY_METRICS, Metrics } from '../monitoring'; import { GenerateNewAndRecoveredAlertEventsParams, LogActiveAndRecoveredAlertsParams, @@ -111,6 +111,7 @@ export class TaskRunner< private readonly executionId: string; private readonly ruleTypeRegistry: RuleTypeRegistry; private readonly inMemoryMetrics: InMemoryMetrics; + private readonly metrics: Metrics; private alertingEventLogger: AlertingEventLogger; private usageCounter?: UsageCounter; private searchAbortController: AbortController; @@ -128,7 +129,8 @@ export class TaskRunner< >, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext, - inMemoryMetrics: InMemoryMetrics + inMemoryMetrics: InMemoryMetrics, + metrics: Metrics ) { this.context = context; this.logger = context.logger; @@ -141,6 +143,7 @@ export class TaskRunner< this.cancelled = false; this.executionId = uuid.v4(); this.inMemoryMetrics = inMemoryMetrics; + this.metrics = metrics; this.alertingEventLogger = new AlertingEventLogger(this.context.eventLogger); } @@ -676,6 +679,7 @@ export class TaskRunner< if (null != duration) { executionStatus.lastDuration = nanosToMillis(duration); monitoringHistory.duration = executionStatus.lastDuration; + this.metrics.ruleDuration.record(executionStatus.lastDuration); } // if executionStatus indicates an error, fill in fields in @@ -692,8 +696,12 @@ export class TaskRunner< if (!this.cancelled) { this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_EXECUTIONS); + // NOTE: Using the typesafe opentelemetry counter here directly + this.metrics.ruleExecutionsTotal.add(1); + this.metrics.ruleExecutions.add(1, { ruleType: this.ruleType.id }); if (executionStatus.error) { this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_FAILURES); + this.metrics.ruleFailures.add(1); } this.logger.debug( `Updating rule task for ${this.ruleType.id} rule with id ${ruleId} - ${JSON.stringify( diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index fb2d1be3a3872..f2d4578689f32 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -35,6 +35,7 @@ import { IEventLogger } from '@kbn/event-log-plugin/server'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; +import { metricsMock } from '../monitoring/metrics.mock'; import { AlertingEventLogger, RuleContextOpts, @@ -95,6 +96,7 @@ describe('Task Runner Cancel', () => { const uiSettingsService = uiSettingsServiceMock.createStartContract(); const dataPlugin = dataPluginMock.createStartContract(); const inMemoryMetrics = inMemoryMetricsMock.create(); + const metrics = metricsMock.create(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -176,7 +178,8 @@ describe('Task Runner Cancel', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -244,7 +247,8 @@ describe('Task Runner Cancel', () => { ...taskRunnerFactoryInitializerParams, cancelAlertsOnRuleTimeout: false, }, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -308,7 +312,8 @@ describe('Task Runner Cancel', () => { updatedRuleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -368,7 +373,8 @@ describe('Task Runner Cancel', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics + inMemoryMetrics, + metrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index e787617800356..ea6c1a0c63d81 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -26,8 +26,10 @@ import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; +import { metricsMock } from '../monitoring/metrics.mock'; const inMemoryMetrics = inMemoryMetricsMock.create(); +const metrics = metricsMock.create(); const executionContext = executionContextServiceMock.createSetupContract(); const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); @@ -115,7 +117,7 @@ describe('Task Runner Factory', () => { test(`throws an error if factory isn't initialized`, () => { const factory = new TaskRunnerFactory(); expect(() => - factory.create(ruleType, { taskInstance: mockedTaskInstance }, inMemoryMetrics) + factory.create(ruleType, { taskInstance: mockedTaskInstance }, inMemoryMetrics, metrics) ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory not initialized"`); }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index 09fe8cb030c40..8fae29b098345 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -32,7 +32,7 @@ import { } from '../types'; import { TaskRunner } from './task_runner'; import { NormalizedRuleType } from '../rule_type_registry'; -import { InMemoryMetrics } from '../monitoring'; +import { InMemoryMetrics, Metrics } from '../monitoring'; import { ActionsConfigMap } from '../lib/get_actions_config_map'; export interface TaskRunnerContext { @@ -89,7 +89,8 @@ export class TaskRunnerFactory { RecoveryActionGroupId >, { taskInstance }: RunContext, - inMemoryMetrics: InMemoryMetrics + inMemoryMetrics: InMemoryMetrics, + metrics: Metrics ) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); @@ -103,6 +104,6 @@ export class TaskRunnerFactory { InstanceContext, ActionGroupIds, RecoveryActionGroupId - >(ruleType, taskInstance, this.taskRunnerContext!, inMemoryMetrics); + >(ruleType, taskInstance, this.taskRunnerContext!, inMemoryMetrics, metrics); } } diff --git a/x-pack/plugins/monitoring_collection/server/config.ts b/x-pack/plugins/monitoring_collection/server/config.ts index 275d2f31e505d..6efe92986ddd5 100644 --- a/x-pack/plugins/monitoring_collection/server/config.ts +++ b/x-pack/plugins/monitoring_collection/server/config.ts @@ -9,6 +9,18 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), + opentelemetry: schema.object({ + metrics: schema.object({ + otlp: schema.object({ + url: schema.maybe(schema.string()), + headers: schema.maybe(schema.recordOf(schema.string(), schema.string())), + exportIntervalMillis: schema.number({ defaultValue: 10000 }), + }), + prometheus: schema.object({ + enabled: schema.boolean({ defaultValue: false }), + }), + }), + }), }); export type MonitoringCollectionConfig = ReturnType; diff --git a/x-pack/plugins/monitoring_collection/server/constants.ts b/x-pack/plugins/monitoring_collection/server/constants.ts index 86231dec6c6c2..4a698aa26f29b 100644 --- a/x-pack/plugins/monitoring_collection/server/constants.ts +++ b/x-pack/plugins/monitoring_collection/server/constants.ts @@ -5,3 +5,5 @@ * 2.0. */ export const TYPE_ALLOWLIST = ['node_rules', 'cluster_rules', 'node_actions', 'cluster_actions']; + +export const PROMETHEUS_ROUTE = '/api/monitoring_collection/v1/prometheus'; diff --git a/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts new file mode 100644 index 0000000000000..d4b21fbd9641f --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts @@ -0,0 +1,75 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/aabc5f6b89e3d9af6640fb854967212ca5b1a3b8/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts + +import { AggregationTemporality, MetricReader } from '@opentelemetry/sdk-metrics-base'; +import { PrometheusExporter as OpenTelemetryPrometheusExporter } from '@opentelemetry/exporter-prometheus'; +import { ExporterConfig } from '@opentelemetry/exporter-prometheus'; +import { KibanaResponseFactory } from '@kbn/core/server'; +import { Logger } from '@kbn/core/server'; +import { PrometheusSerializer } from './prometheus_serializer'; + +export class PrometheusExporter extends MetricReader { + private readonly _prefix?: string; + private readonly _appendTimestamp: boolean; + private _serializer: PrometheusSerializer; + + constructor(logger: Logger, config: ExporterConfig = {}) { + super(); + this._prefix = config.prefix || OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.prefix; + this._appendTimestamp = + typeof config.appendTimestamp === 'boolean' + ? config.appendTimestamp + : OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.appendTimestamp; + + this._serializer = new PrometheusSerializer(logger, this._prefix, this._appendTimestamp); + } + + selectAggregationTemporality(): AggregationTemporality { + return AggregationTemporality.CUMULATIVE; + } + + protected onForceFlush(): Promise { + return Promise.resolve(undefined); + } + + protected onShutdown(): Promise { + return Promise.resolve(undefined); + } + + /** + * Responds to incoming message with current state of all metrics. + */ + public exportMetrics(res: KibanaResponseFactory) { + // TODO: How can I type this return without requiring (forbidden path) KibanaReponse? + return this.collect().then( + (collectionResult) => { + const { resourceMetrics, errors } = collectionResult; + if (errors.length) { + return res.customError({ + statusCode: 500, + body: `PrometheusExporter: metrics collection errors ${errors}`, + }); + } + const result = this._serializer.serialize(resourceMetrics); + if (result === '') { + return res.noContent(); + } + return res.ok({ + body: result, + }); + }, + (err) => { + return res.customError({ + statusCode: 500, + body: `# Failed to export metrics ${err}`, + }); + } + ); + } +} diff --git a/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts b/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts new file mode 100644 index 0000000000000..d036a31673700 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts @@ -0,0 +1,311 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// From https://github.com/open-telemetry/opentelemetry-js/blob/97bc6321c0fe4da7414afb83038279b735a5ba65/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts +// Can be removed once https://github.com/open-telemetry/opentelemetry-js/issues/3033 is merged/released + +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger } from '@kbn/core/server'; +import { + ResourceMetrics, + InstrumentType, + DataPointType, + ScopeMetrics, + MetricData, + DataPoint, + Histogram, +} from '@opentelemetry/sdk-metrics-base'; +import type { MetricAttributes } from '@opentelemetry/api-metrics'; + +// From https://github.com/open-telemetry/opentelemetry-js/blob/28c9e8829488a7fa131803447b0511195ae1fdf0/packages/opentelemetry-core/src/common/time.ts#L148 +export function hrTimeToMilliseconds(time: [number, number]): number { + return Math.round(time[0] * 1e3 + time[1] / 1e6); +} + +type PrometheusDataTypeLiteral = 'counter' | 'gauge' | 'histogram' | 'summary' | 'untyped'; + +function escapeString(str: string) { + return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); +} + +function escapeAttributeValue(str: string) { + if (typeof str !== 'string') { + str = String(str); + } + return escapeString(str).replace(/"/g, '\\"'); +} + +const invalidCharacterRegex = /[^a-z0-9_]/gi; +/** + * Ensures metric names are valid Prometheus metric names by removing + * characters allowed by OpenTelemetry but disallowed by Prometheus. + * + * https://prometheus.io/docs/concepts/data_model/#metric-names-and-attributes + * + * 1. Names must match `[a-zA-Z_:][a-zA-Z0-9_:]*` + * + * 2. Colons are reserved for user defined recording rules. + * They should not be used by exporters or direct instrumentation. + * + * OpenTelemetry metric names are already validated in the Meter when they are created, + * and they match the format `[a-zA-Z][a-zA-Z0-9_.\-]*` which is very close to a valid + * prometheus metric name, so we only need to strip characters valid in OpenTelemetry + * but not valid in prometheus and replace them with '_'. + * + * @param name name to be sanitized + */ +function sanitizePrometheusMetricName(name: string): string { + return name.replace(invalidCharacterRegex, '_'); // replace all invalid characters with '_' +} + +/** + * @private + * + * Helper method which assists in enforcing the naming conventions for metric + * names in Prometheus + * @param name the name of the metric + * @param type the kind of metric + * @returns string + */ +function enforcePrometheusNamingConvention(name: string, type: InstrumentType): string { + // Prometheus requires that metrics of the Counter kind have "_total" suffix + if (!name.endsWith('_total') && type === InstrumentType.COUNTER) { + name = name + '_total'; + } + + return name; +} + +function valueString(value: number) { + if (Number.isNaN(value)) { + return 'Nan'; + } else if (!Number.isFinite(value)) { + if (value < 0) { + return '-Inf'; + } else { + return '+Inf'; + } + } else { + return `${value}`; + } +} + +function toPrometheusType( + instrumentType: InstrumentType, + dataPointType: DataPointType +): PrometheusDataTypeLiteral { + switch (dataPointType) { + case DataPointType.SINGULAR: + if ( + instrumentType === InstrumentType.COUNTER || + instrumentType === InstrumentType.OBSERVABLE_COUNTER + ) { + return 'counter'; + } + /** + * - HISTOGRAM + * - UP_DOWN_COUNTER + * - OBSERVABLE_GAUGE + * - OBSERVABLE_UP_DOWN_COUNTER + */ + return 'gauge'; + case DataPointType.HISTOGRAM: + return 'histogram'; + default: + return 'untyped'; + } +} + +function stringify( + metricName: string, + attributes: MetricAttributes, + value: number, + timestamp?: number, + additionalAttributes?: MetricAttributes +) { + let hasAttribute = false; + let attributesStr = ''; + + for (const [key, val] of Object.entries(attributes)) { + const sanitizedAttributeName = sanitizePrometheusMetricName(key); + hasAttribute = true; + attributesStr += `${ + attributesStr.length > 0 ? ',' : '' + }${sanitizedAttributeName}="${escapeAttributeValue(val)}"`; + } + if (additionalAttributes) { + for (const [key, val] of Object.entries(additionalAttributes)) { + const sanitizedAttributeName = sanitizePrometheusMetricName(key); + hasAttribute = true; + attributesStr += `${ + attributesStr.length > 0 ? ',' : '' + }${sanitizedAttributeName}="${escapeAttributeValue(val)}"`; + } + } + + if (hasAttribute) { + metricName += `{${attributesStr}}`; + } + + return `${metricName} ${valueString(value)}${ + timestamp !== undefined ? ' ' + String(timestamp) : '' + }\n`; +} + +export class PrometheusSerializer { + private logger: Logger; + private _prefix: string | undefined; + private _appendTimestamp: boolean; + + constructor(logger: Logger, prefix?: string, appendTimestamp = true) { + if (prefix) { + this._prefix = prefix + '_'; + } + this._appendTimestamp = appendTimestamp; + this.logger = logger; + } + + serialize(resourceMetrics: ResourceMetrics): string { + let str = ''; + for (const scopeMetrics of resourceMetrics.scopeMetrics) { + str += this.serializeScopeMetrics(scopeMetrics); + } + return str; + } + + serializeScopeMetrics(scopeMetrics: ScopeMetrics) { + let str = ''; + for (const metric of scopeMetrics.metrics) { + str += this.serializeMetricData(metric) + '\n'; + } + return str; + } + + serializeMetricData(metricData: MetricData) { + let name = sanitizePrometheusMetricName(escapeString(metricData.descriptor.name)); + if (this._prefix) { + name = `${this._prefix}${name}`; + } + const dataPointType = metricData.dataPointType; + + name = enforcePrometheusNamingConvention(name, metricData.descriptor.type); + + const help = `# HELP ${name} ${escapeString( + metricData.descriptor.description || 'description missing' + )}`; + const type = `# TYPE ${name} ${toPrometheusType(metricData.descriptor.type, dataPointType)}`; + + let results = ''; + switch (dataPointType) { + case DataPointType.SINGULAR: { + results = metricData.dataPoints + .map((it) => this.serializeSingularDataPoint(name, metricData.descriptor.type, it)) + .join(''); + break; + } + case DataPointType.HISTOGRAM: { + results = metricData.dataPoints + .map((it) => this.serializeHistogramDataPoint(name, metricData.descriptor.type, it)) + .join(''); + break; + } + default: { + this.logger.error(`Unrecognizable DataPointType: ${dataPointType} for metric "${name}"`); + } + } + + return `${help}\n${type}\n${results}`.trim(); + } + + serializeSingularDataPoint( + name: string, + type: InstrumentType, + dataPoint: DataPoint + ): string { + let results = ''; + + name = enforcePrometheusNamingConvention(name, type); + const { value, attributes } = dataPoint; + const timestamp = hrTimeToMilliseconds(dataPoint.endTime); + results += stringify( + name, + attributes, + value, + this._appendTimestamp ? timestamp : undefined, + undefined + ); + return results; + } + + serializeHistogramDataPoint( + name: string, + type: InstrumentType, + dataPoint: DataPoint + ): string { + let results = ''; + + name = enforcePrometheusNamingConvention(name, type); + const { value, attributes } = dataPoint; + const timestamp = hrTimeToMilliseconds(dataPoint.endTime); + /** Histogram["bucket"] is not typed with `number` */ + for (const key of ['count', 'sum'] as Array<'count' | 'sum'>) { + results += stringify( + name + '_' + key, + attributes, + value[key], + this._appendTimestamp ? timestamp : undefined, + undefined + ); + } + + let cumulativeSum = 0; + const countEntries = value.buckets.counts.entries(); + let infiniteBoundaryDefined = false; + for (const [idx, val] of countEntries) { + cumulativeSum += val; + const upperBound = value.buckets.boundaries[idx]; + /** HistogramAggregator is producing different boundary output - + * in one case not including infinity values, in other - + * full, e.g. [0, 100] and [0, 100, Infinity] + * we should consider that in export, if Infinity is defined, use it + * as boundary + */ + if (upperBound === undefined && infiniteBoundaryDefined) { + break; + } + if (upperBound === Infinity) { + infiniteBoundaryDefined = true; + } + results += stringify( + name + '_bucket', + attributes, + cumulativeSum, + this._appendTimestamp ? timestamp : undefined, + { + le: upperBound === undefined || upperBound === Infinity ? '+Inf' : String(upperBound), + } + ); + } + + return results; + } +} diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index e1c3a5064a579..7f42f99c5d95f 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -9,8 +9,16 @@ import { JsonObject } from '@kbn/utility-types'; import { CoreSetup, Plugin, PluginInitializerContext, Logger } from '@kbn/core/server'; import { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server'; import { ServiceStatus } from '@kbn/core/server'; -import { registerDynamicRoute } from './routes'; -import { TYPE_ALLOWLIST } from './constants'; +import { metrics } from '@opentelemetry/api-metrics'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; +import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import * as grpc from '@grpc/grpc-js'; +import { PrometheusExporter } from './lib/prometheus_exporter'; +import { MonitoringCollectionConfig } from './config'; +import { registerDynamicRoute, registerV1PrometheusRoute } from './routes'; +import { PROMETHEUS_ROUTE, TYPE_ALLOWLIST } from './constants'; export interface MonitoringCollectionSetup { registerMetric: (metric: Metric) => void; @@ -27,12 +35,16 @@ export interface Metric { export class MonitoringCollectionPlugin implements Plugin { private readonly initializerContext: PluginInitializerContext; private readonly logger: Logger; + private readonly config: MonitoringCollectionConfig; private metrics: Record> = {}; - constructor(initializerContext: PluginInitializerContext) { + private prometheusExporter?: PrometheusExporter; + + constructor(initializerContext: PluginInitializerContext) { this.initializerContext = initializerContext; this.logger = initializerContext.logger.get(); + this.config = initializerContext.config.get(); } async getMetric(type: string) { @@ -46,19 +58,28 @@ export class MonitoringCollectionPlugin implements Plugin; core.status.overall$.subscribe((newStatus) => { status = newStatus; }); + if (this.prometheusExporter) { + registerV1PrometheusRoute({ router, prometheusExporter: this.prometheusExporter }); + } + registerDynamicRoute({ router, config: { kibanaIndex, - kibanaVersion: this.initializerContext.env.packageInfo.version, - server: core.http.getServerInfo(), - uuid: this.initializerContext.env.instanceUuid, + kibanaVersion, + server, + uuid, }, getStatus: () => status, getMetric: async (type: string) => { @@ -85,6 +106,60 @@ export class MonitoringCollectionPlugin implements Plugin { + it('forwards the request to the prometheus exporter', async () => { + const router = httpServiceMock.createRouter(); + const prometheusExporter = { exportMetrics: jest.fn() } as unknown as PrometheusExporter; + registerV1PrometheusRoute({ router, prometheusExporter }); + + const [, handler] = router.get.mock.calls[0]; + + const context = {}; + const req = { params: {} } as KibanaRequest; + const factory: jest.Mocked = httpServerMock.createResponseFactory(); + + await handler(context, req, factory); + + expect(prometheusExporter.exportMetrics).toHaveBeenCalledWith(factory); + }); +}); diff --git a/x-pack/plugins/monitoring_collection/server/routes/prometheus.ts b/x-pack/plugins/monitoring_collection/server/routes/prometheus.ts new file mode 100644 index 0000000000000..40e783c3d8b46 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/prometheus.ts @@ -0,0 +1,32 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '@kbn/core/server'; +import { PROMETHEUS_ROUTE } from '../constants'; +import { PrometheusExporter } from '../lib/prometheus_exporter'; + +export function registerV1PrometheusRoute({ + router, + prometheusExporter, +}: { + router: IRouter; + prometheusExporter: PrometheusExporter; +}) { + router.get( + { + path: PROMETHEUS_ROUTE, + options: { + authRequired: true, + tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page + }, + validate: {}, + }, + async (context, req, res) => { + return prometheusExporter.exportMetrics(res); + } + ); +} diff --git a/yarn.lock b/yarn.lock index 57d384e8f4a02..0b94a0b7cf9a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1997,6 +1997,36 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@grpc/grpc-js@^1.5.9": + version "1.6.7" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.6.7.tgz#4c4fa998ff719fe859ac19fe977fdef097bb99aa" + integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== + dependencies: + "@grpc/proto-loader" "^0.6.4" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.6.4": + version "0.6.12" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.12.tgz#459b619b8b9b67794bf0d1cb819653a38c63e164" + integrity sha512-filTVbETFnxb9CyRX98zN18ilChTuf/C5scZ2xyaOTp0EHGq0/ufX8rjqXUcSb1Gpv7eZq4M2jDvbh9BogKnrg== + dependencies: + "@types/long" "^4.0.1" + lodash.camelcase "^4.3.0" + long "^4.0.0" + protobufjs "^6.10.0" + yargs "^16.2.0" + +"@grpc/proto-loader@^0.6.9": + version "0.6.13" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" + integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== + dependencies: + "@types/long" "^4.0.1" + lodash.camelcase "^4.3.0" + long "^4.0.0" + protobufjs "^6.11.3" + yargs "^16.2.0" + "@gulp-sourcemaps/identity-map@1.X": version "1.0.2" resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz#1e6fe5d8027b1f285dc0d31762f566bccd73d5a9" @@ -4347,11 +4377,120 @@ "@mattiasbuelens/web-streams-adapter" "~0.1.0" web-streams-polyfill "~3.0.3" -"@opentelemetry/api@^1.1.0": +"@opentelemetry/api-metrics@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.29.2.tgz#daa823e0965754222b49a6ae6133df8b39ff8fd2" + integrity sha512-yRdF5beqKuEdsPNoO7ijWCQ9HcyN0Tlgicf8RS6gzGOI54d6Hj7yKquJ6+X9XV+CSRbRWJYb+lOsXyso7uyX2g== + dependencies: + "@opentelemetry/api" "^1.0.0" + +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== +"@opentelemetry/core@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.3.1.tgz#6eef5c5efca9a4cd7daa0cd4c7ff28ca2317c8d7" + integrity sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA== + dependencies: + "@opentelemetry/semantic-conventions" "1.3.1" + +"@opentelemetry/exporter-metrics-otlp-grpc@^0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.29.2.tgz#d51352a703363237621d22793949cab4d69e1b01" + integrity sha512-OhZzSiHhn1RUdaM7XkDmZ6m6+T+TPbEJbiXPrswN8G6jKjokaP4QWM+iis1GQBqYcpMtFHhN0Mlr8mF9dzJ2pA== + dependencies: + "@grpc/grpc-js" "^1.5.9" + "@grpc/proto-loader" "^0.6.9" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/exporter-metrics-otlp-http" "0.29.2" + "@opentelemetry/otlp-grpc-exporter-base" "0.29.2" + "@opentelemetry/otlp-transformer" "0.29.2" + "@opentelemetry/resources" "1.3.1" + "@opentelemetry/sdk-metrics-base" "0.29.2" + +"@opentelemetry/exporter-metrics-otlp-http@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.29.2.tgz#4daf876d5f9cdabcd11fa0bcc3d4f73be89cd55f" + integrity sha512-DSNVeXMD9sx4CcDTEy1+DiZAdjqqFhSdQWaDI2cPyxDUfyAgrDJM+HUAgCCy87eamsOy0vNCu1Hq9Ygoq4ZwHQ== + dependencies: + "@opentelemetry/api-metrics" "0.29.2" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/otlp-exporter-base" "0.29.2" + "@opentelemetry/otlp-transformer" "0.29.2" + "@opentelemetry/resources" "1.3.1" + "@opentelemetry/sdk-metrics-base" "0.29.2" + +"@opentelemetry/exporter-prometheus@^0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.29.2.tgz#70ca7fb37655ca57a580387607d5465b47e27ac3" + integrity sha512-E5sRfUM4rzbvjxdpL1H6YRtjr8wY8+/2R4NjfxPEwrENLeeQk87V1E+YFLqAS7TfFLW7Zr4lmmamunwn5THvQA== + dependencies: + "@opentelemetry/api-metrics" "0.29.2" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/sdk-metrics-base" "0.29.2" + +"@opentelemetry/otlp-exporter-base@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.29.2.tgz#fc844cc00e1ad95444b85056c08b1ca3e8a40447" + integrity sha512-tTK+v2ER9Rv7YQXLrCvZpPdNdvZx8TGdZtlK7TKnzpyMRBIf7lqV1Jl0VaHFml+cgVJcGtow/ER6k5uJ5W4kUQ== + dependencies: + "@opentelemetry/core" "1.3.1" + +"@opentelemetry/otlp-grpc-exporter-base@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.29.2.tgz#bc01f79ce7a35f126c3ac0fd51ab68170b828b38" + integrity sha512-iNj1E7downme1PePb8itxg0/NF/l7SX3rGuDsR2+rCwwbd0NfmUN/6xwF1YupP6Z1/ThG0YE37eK/F1NZOt6mQ== + dependencies: + "@grpc/grpc-js" "^1.5.9" + "@grpc/proto-loader" "^0.6.9" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/otlp-exporter-base" "0.29.2" + +"@opentelemetry/otlp-transformer@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.29.2.tgz#61897d3d747182ab7e315a88a9a710a759c13390" + integrity sha512-Y6dJj+rhRGynxhLlgEJkdkXuLHdFG8igcSBv6oy3m3GHSSvZkyNV34dVjtZJ586mUXsbFuAf6uqjzteobewO1g== + dependencies: + "@opentelemetry/api-metrics" "0.29.2" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/resources" "1.3.1" + "@opentelemetry/sdk-metrics-base" "0.29.2" + "@opentelemetry/sdk-trace-base" "1.3.1" + +"@opentelemetry/resources@1.3.1", "@opentelemetry/resources@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.3.1.tgz#9fd85ac4ffeefc35441404b384d5c1db8b243121" + integrity sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA== + dependencies: + "@opentelemetry/core" "1.3.1" + "@opentelemetry/semantic-conventions" "1.3.1" + +"@opentelemetry/sdk-metrics-base@0.29.2", "@opentelemetry/sdk-metrics-base@^0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155" + integrity sha512-7hhhZ/6YRRgAXOUTeCsbe6SIk3wZAdAHnEwGGp7aiVH5AOyioHyHInw4EHtowlD6dbLxUWURjh6k+Geht2zbxg== + dependencies: + "@opentelemetry/api-metrics" "0.29.2" + "@opentelemetry/core" "1.3.1" + "@opentelemetry/resources" "1.3.1" + lodash.merge "4.6.2" + +"@opentelemetry/sdk-trace-base@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz#958083dbab928eefd17848959ac8810c787bec7f" + integrity sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ== + dependencies: + "@opentelemetry/core" "1.3.1" + "@opentelemetry/resources" "1.3.1" + "@opentelemetry/semantic-conventions" "1.3.1" + +"@opentelemetry/semantic-conventions@1.3.1", "@opentelemetry/semantic-conventions@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" + integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== + "@percy/agent@^0.28.6": version "0.28.6" resolved "https://registry.yarnpkg.com/@percy/agent/-/agent-0.28.6.tgz#b220fab6ddcf63ae4e6c343108ba6955a772ce1c" @@ -7195,6 +7334,11 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== +"@types/long@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + "@types/lru-cache@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" @@ -23827,6 +23971,25 @@ protobufjs@6.8.8: "@types/node" "^10.1.0" long "^4.0.0" +protobufjs@^6.10.0, protobufjs@^6.11.3: + version "6.11.3" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + protocol-buffers-schema@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz#00434f608b4e8df54c59e070efeefc37fb4bb859" @@ -30811,9 +30974,9 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0: +yargs@16.2.0, yargs@^16.2.0: version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" From 2638cbb93663a4467665daa9d332943081ae305c Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Fri, 8 Jul 2022 11:58:11 +0200 Subject: [PATCH 02/16] clean up otel poc --- package.json | 2 +- .../alerting/server/monitoring/index.ts | 1 - .../alerting/server/monitoring/metrics.ts | 22 -- x-pack/plugins/alerting/server/plugin.ts | 11 +- .../server/rule_type_registry.test.ts | 3 - .../alerting/server/rule_type_registry.ts | 8 +- .../saved_objects/is_rule_exportable.test.ts | 3 - .../server/task_runner/task_runner.test.ts | 134 +++----- .../server/task_runner/task_runner.ts | 12 +- .../task_runner/task_runner_cancel.test.ts | 14 +- .../task_runner/task_runner_factory.test.ts | 4 +- .../server/task_runner/task_runner_factory.ts | 7 +- .../plugins/monitoring_collection/README.md | 43 ++- .../monitoring_collection/server/constants.ts | 2 +- .../monitoring_collection/server/lib/index.ts | 1 + .../server/lib/prometheus_exporter.ts | 58 ++-- .../server/lib/prometheus_serializer.ts | 311 ------------------ .../monitoring_collection/server/plugin.ts | 28 +- .../v1/dynamic_route/index.test.ts} | 8 +- .../v1/dynamic_route/index.ts} | 7 +- .../server/routes/api/v1/index.ts} | 9 +- .../v1/prometheus/get_metrics.test.ts} | 19 +- .../v1/prometheus/get_metrics.ts} | 9 +- .../server/routes/api/v1/prometheus/index.ts | 7 + .../server/routes/index.ts | 3 +- yarn.lock | 53 ++- 26 files changed, 223 insertions(+), 556 deletions(-) delete mode 100644 x-pack/plugins/alerting/server/monitoring/metrics.ts delete mode 100644 x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts rename x-pack/plugins/monitoring_collection/server/routes/{dynamic_route.test.ts => api/v1/dynamic_route/index.test.ts} (94%) rename x-pack/plugins/monitoring_collection/server/routes/{dynamic_route.ts => api/v1/dynamic_route/index.ts} (86%) rename x-pack/plugins/{alerting/server/monitoring/metrics.mock.ts => monitoring_collection/server/routes/api/v1/index.ts} (55%) rename x-pack/plugins/monitoring_collection/server/routes/{prometheus.test.ts => api/v1/prometheus/get_metrics.test.ts} (58%) rename x-pack/plugins/monitoring_collection/server/routes/{prometheus.ts => api/v1/prometheus/get_metrics.ts} (72%) create mode 100644 x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/index.ts diff --git a/package.json b/package.json index e2c19700b73e7..d96dab6423a87 100644 --- a/package.json +++ b/package.json @@ -266,7 +266,7 @@ "@mapbox/vector-tile": "1.3.1", "@opentelemetry/api-metrics": "0.29.2", "@opentelemetry/exporter-metrics-otlp-grpc": "^0.29.2", - "@opentelemetry/exporter-prometheus": "^0.29.2", + "@opentelemetry/exporter-prometheus": "^0.30.0", "@opentelemetry/resources": "^1.3.1", "@opentelemetry/sdk-metrics-base": "^0.29.2", "@opentelemetry/semantic-conventions": "^1.3.1", diff --git a/x-pack/plugins/alerting/server/monitoring/index.ts b/x-pack/plugins/alerting/server/monitoring/index.ts index 3040758f6a4d7..5f298456554f0 100644 --- a/x-pack/plugins/alerting/server/monitoring/index.ts +++ b/x-pack/plugins/alerting/server/monitoring/index.ts @@ -8,4 +8,3 @@ export { registerNodeCollector } from './register_node_collector'; export { registerClusterCollector } from './register_cluster_collector'; export * from './types'; export * from './in_memory_metrics'; -export * from './metrics'; diff --git a/x-pack/plugins/alerting/server/monitoring/metrics.ts b/x-pack/plugins/alerting/server/monitoring/metrics.ts deleted file mode 100644 index b13fefa65b5c6..0000000000000 --- a/x-pack/plugins/alerting/server/monitoring/metrics.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Counter, Histogram, Meter } from '@opentelemetry/api-metrics'; - -export class Metrics { - ruleExecutionsTotal: Counter; - ruleExecutions: Counter; - ruleFailures: Counter; - ruleDuration: Histogram; - - constructor(meter: Meter) { - this.ruleExecutionsTotal = meter.createCounter('ruleExecutionsTotal'); - this.ruleExecutions = meter.createCounter('ruleExecutions'); - this.ruleFailures = meter.createCounter('ruleFailures'); - this.ruleDuration = meter.createHistogram('ruleDuration'); - } -} diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 3fe87ddae3344..83d9c4f99ded8 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -49,7 +49,6 @@ import { import { PluginStartContract as FeaturesPluginStart } from '@kbn/features-plugin/server'; import { PluginStart as DataPluginStart } from '@kbn/data-plugin/server'; import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server'; -import { metrics } from '@opentelemetry/api-metrics'; import { RuleTypeRegistry } from './rule_type_registry'; import { TaskRunnerFactory } from './task_runner'; import { RulesClientFactory } from './rules_client_factory'; @@ -78,12 +77,7 @@ import { getHealth } from './health/get_health'; import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory'; import { AlertingAuthorization } from './authorization'; import { getSecurityHealth, SecurityHealth } from './lib/get_security_health'; -import { - registerNodeCollector, - registerClusterCollector, - InMemoryMetrics, - Metrics, -} from './monitoring'; +import { registerNodeCollector, registerClusterCollector, InMemoryMetrics } from './monitoring'; import { getRuleTaskTimeout } from './lib/get_rule_task_timeout'; import { getActionsConfigMap } from './lib/get_actions_config_map'; @@ -179,7 +173,6 @@ export class AlertingPlugin { private kibanaBaseUrl: string | undefined; private usageCounter: UsageCounter | undefined; private inMemoryMetrics: InMemoryMetrics; - private metrics: Metrics; constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); @@ -190,7 +183,6 @@ export class AlertingPlugin { this.telemetryLogger = initializerContext.logger.get('usage'); this.kibanaVersion = initializerContext.env.packageInfo.version; this.inMemoryMetrics = new InMemoryMetrics(initializerContext.logger.get('in_memory_metrics')); - this.metrics = new Metrics(metrics.getMeter('kibana.alerting')); } public setup( @@ -235,7 +227,6 @@ export class AlertingPlugin { licensing: plugins.licensing, minimumScheduleInterval: this.config.rules.minimumScheduleInterval, inMemoryMetrics: this.inMemoryMetrics, - metrics: this.metrics, }); this.ruleTypeRegistry = ruleTypeRegistry; diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index daf8bd69332cc..ed52ebf8b04da 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -14,7 +14,6 @@ import { licenseStateMock } from './lib/license_state.mock'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock'; -import { metricsMock } from './monitoring/metrics.mock'; const logger = loggingSystemMock.create().get(); let mockedLicenseState: jest.Mocked; @@ -22,7 +21,6 @@ let ruleTypeRegistryParams: ConstructorOptions; const taskManager = taskManagerMock.createSetup(); const inMemoryMetrics = inMemoryMetricsMock.create(); -const metrics = metricsMock.create(); beforeEach(() => { jest.resetAllMocks(); @@ -35,7 +33,6 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, - metrics, }; }); diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index f6a164b423727..338450746781b 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -31,7 +31,7 @@ import { } from '../common'; import { ILicenseState } from './lib/license_state'; import { getRuleTypeFeatureUsageName } from './lib/get_rule_type_feature_usage_name'; -import { InMemoryMetrics, Metrics } from './monitoring'; +import { InMemoryMetrics } from './monitoring'; import { AlertingRulesConfig } from '.'; export interface ConstructorOptions { @@ -42,7 +42,6 @@ export interface ConstructorOptions { licensing: LicensingPluginSetup; minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; inMemoryMetrics: InMemoryMetrics; - metrics: Metrics; } export interface RegistryRuleType @@ -140,7 +139,6 @@ export class RuleTypeRegistry { private readonly minimumScheduleInterval: AlertingRulesConfig['minimumScheduleInterval']; private readonly licensing: LicensingPluginSetup; private readonly inMemoryMetrics: InMemoryMetrics; - private readonly metrics: Metrics; constructor({ logger, @@ -150,7 +148,6 @@ export class RuleTypeRegistry { licensing, minimumScheduleInterval, inMemoryMetrics, - metrics, }: ConstructorOptions) { this.logger = logger; this.taskManager = taskManager; @@ -159,7 +156,6 @@ export class RuleTypeRegistry { this.licensing = licensing; this.minimumScheduleInterval = minimumScheduleInterval; this.inMemoryMetrics = inMemoryMetrics; - this.metrics = metrics; } public has(id: string) { @@ -278,7 +274,7 @@ export class RuleTypeRegistry { InstanceContext, ActionGroupIds, RecoveryActionGroupId | RecoveredActionGroupId - >(normalizedRuleType, context, this.inMemoryMetrics, this.metrics), + >(normalizedRuleType, context, this.inMemoryMetrics), }, }); // No need to notify usage on basic alert types diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index 81992e1a50510..0f2677dc49751 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -15,14 +15,12 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { isRuleExportable } from './is_rule_exportable'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { metricsMock } from '../monitoring/metrics.mock'; let ruleTypeRegistryParams: ConstructorOptions; let logger: MockedLogger; let mockedLicenseState: jest.Mocked; const taskManager = taskManagerMock.createSetup(); const inMemoryMetrics = inMemoryMetricsMock.create(); -const metrics = metricsMock.create(); beforeEach(() => { jest.resetAllMocks(); @@ -36,7 +34,6 @@ beforeEach(() => { licensing: licensingMock.createSetup(), minimumScheduleInterval: { value: '1m', enforce: false }, inMemoryMetrics, - metrics, }; }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index d142cc385d035..5d2f6d7c1f659 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -43,7 +43,6 @@ import { omit } from 'lodash'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { ExecuteOptions } from '@kbn/actions-plugin/server/create_execute_function'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; -import { metricsMock } from '../monitoring/metrics.mock'; import moment from 'moment'; import { generateAlertOpts, @@ -124,7 +123,6 @@ describe('Task Runner', () => { const dataPlugin = dataPluginMock.createStartContract(); const uiSettingsService = uiSettingsServiceMock.createStartContract(); const inMemoryMetrics = inMemoryMetricsMock.create(); - const metrics = metricsMock.create(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -222,8 +220,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -324,8 +321,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -406,8 +402,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -528,8 +523,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -582,8 +576,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -662,8 +655,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -707,8 +699,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -765,8 +756,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -834,8 +824,7 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -916,8 +905,7 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -982,8 +970,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1095,8 +1082,7 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1208,8 +1194,7 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1300,8 +1285,7 @@ describe('Task Runner', () => { }, }, customTaskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1392,8 +1376,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1456,8 +1439,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1480,8 +1462,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1511,8 +1492,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1544,8 +1524,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1582,8 +1561,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1618,8 +1596,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1648,8 +1625,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1678,8 +1654,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1709,8 +1684,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1743,8 +1717,7 @@ describe('Task Runner', () => { ruleType, legacyTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1782,8 +1755,7 @@ describe('Task Runner', () => { state: originalAlertSate, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1813,8 +1785,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1846,8 +1817,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1871,8 +1841,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1898,8 +1867,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -1948,8 +1916,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2055,8 +2022,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2130,8 +2096,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2200,8 +2165,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2275,8 +2239,7 @@ describe('Task Runner', () => { }, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2328,8 +2291,7 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, supportsEphemeralTasks: true, }, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2400,8 +2362,7 @@ describe('Task Runner', () => { state, }, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2430,8 +2391,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2446,8 +2406,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2475,8 +2434,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2511,8 +2469,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2597,8 +2554,7 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, actionsConfigMap, }, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2750,8 +2706,7 @@ describe('Task Runner', () => { ...taskRunnerFactoryInitializerParams, actionsConfigMap, }, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); @@ -2821,8 +2776,7 @@ describe('Task Runner', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalled(); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 6a87822940a9f..5f7e1299f7698 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -55,7 +55,7 @@ import { } from '../../common'; import { NormalizedRuleType, UntypedNormalizedRuleType } from '../rule_type_registry'; import { getEsErrorMessage } from '../lib/errors'; -import { InMemoryMetrics, IN_MEMORY_METRICS, Metrics } from '../monitoring'; +import { InMemoryMetrics, IN_MEMORY_METRICS } from '../monitoring'; import { GenerateNewAndRecoveredAlertEventsParams, LogActiveAndRecoveredAlertsParams, @@ -111,7 +111,6 @@ export class TaskRunner< private readonly executionId: string; private readonly ruleTypeRegistry: RuleTypeRegistry; private readonly inMemoryMetrics: InMemoryMetrics; - private readonly metrics: Metrics; private alertingEventLogger: AlertingEventLogger; private usageCounter?: UsageCounter; private searchAbortController: AbortController; @@ -129,8 +128,7 @@ export class TaskRunner< >, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext, - inMemoryMetrics: InMemoryMetrics, - metrics: Metrics + inMemoryMetrics: InMemoryMetrics ) { this.context = context; this.logger = context.logger; @@ -143,7 +141,6 @@ export class TaskRunner< this.cancelled = false; this.executionId = uuid.v4(); this.inMemoryMetrics = inMemoryMetrics; - this.metrics = metrics; this.alertingEventLogger = new AlertingEventLogger(this.context.eventLogger); } @@ -679,7 +676,6 @@ export class TaskRunner< if (null != duration) { executionStatus.lastDuration = nanosToMillis(duration); monitoringHistory.duration = executionStatus.lastDuration; - this.metrics.ruleDuration.record(executionStatus.lastDuration); } // if executionStatus indicates an error, fill in fields in @@ -696,12 +692,8 @@ export class TaskRunner< if (!this.cancelled) { this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_EXECUTIONS); - // NOTE: Using the typesafe opentelemetry counter here directly - this.metrics.ruleExecutionsTotal.add(1); - this.metrics.ruleExecutions.add(1, { ruleType: this.ruleType.id }); if (executionStatus.error) { this.inMemoryMetrics.increment(IN_MEMORY_METRICS.RULE_FAILURES); - this.metrics.ruleFailures.add(1); } this.logger.debug( `Updating rule task for ${this.ruleType.id} rule with id ${ruleId} - ${JSON.stringify( diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index f2d4578689f32..fb2d1be3a3872 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -35,7 +35,6 @@ import { IEventLogger } from '@kbn/event-log-plugin/server'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; -import { metricsMock } from '../monitoring/metrics.mock'; import { AlertingEventLogger, RuleContextOpts, @@ -96,7 +95,6 @@ describe('Task Runner Cancel', () => { const uiSettingsService = uiSettingsServiceMock.createStartContract(); const dataPlugin = dataPluginMock.createStartContract(); const inMemoryMetrics = inMemoryMetricsMock.create(); - const metrics = metricsMock.create(); type TaskRunnerFactoryInitializerParamsType = jest.Mocked & { actionsPlugin: jest.Mocked; @@ -178,8 +176,7 @@ describe('Task Runner Cancel', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -247,8 +244,7 @@ describe('Task Runner Cancel', () => { ...taskRunnerFactoryInitializerParams, cancelAlertsOnRuleTimeout: false, }, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -312,8 +308,7 @@ describe('Task Runner Cancel', () => { updatedRuleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); @@ -373,8 +368,7 @@ describe('Task Runner Cancel', () => { ruleType, mockedTaskInstance, taskRunnerFactoryInitializerParams, - inMemoryMetrics, - metrics + inMemoryMetrics ); expect(AlertingEventLogger).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts index ea6c1a0c63d81..e787617800356 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.test.ts @@ -26,10 +26,8 @@ import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; -import { metricsMock } from '../monitoring/metrics.mock'; const inMemoryMetrics = inMemoryMetricsMock.create(); -const metrics = metricsMock.create(); const executionContext = executionContextServiceMock.createSetupContract(); const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test'); @@ -117,7 +115,7 @@ describe('Task Runner Factory', () => { test(`throws an error if factory isn't initialized`, () => { const factory = new TaskRunnerFactory(); expect(() => - factory.create(ruleType, { taskInstance: mockedTaskInstance }, inMemoryMetrics, metrics) + factory.create(ruleType, { taskInstance: mockedTaskInstance }, inMemoryMetrics) ).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory not initialized"`); }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index 8fae29b098345..09fe8cb030c40 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -32,7 +32,7 @@ import { } from '../types'; import { TaskRunner } from './task_runner'; import { NormalizedRuleType } from '../rule_type_registry'; -import { InMemoryMetrics, Metrics } from '../monitoring'; +import { InMemoryMetrics } from '../monitoring'; import { ActionsConfigMap } from '../lib/get_actions_config_map'; export interface TaskRunnerContext { @@ -89,8 +89,7 @@ export class TaskRunnerFactory { RecoveryActionGroupId >, { taskInstance }: RunContext, - inMemoryMetrics: InMemoryMetrics, - metrics: Metrics + inMemoryMetrics: InMemoryMetrics ) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); @@ -104,6 +103,6 @@ export class TaskRunnerFactory { InstanceContext, ActionGroupIds, RecoveryActionGroupId - >(ruleType, taskInstance, this.taskRunnerContext!, inMemoryMetrics, metrics); + >(ruleType, taskInstance, this.taskRunnerContext!, inMemoryMetrics); } } diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index 1f2d2984af886..cdd12b0701f77 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -2,4 +2,45 @@ ## Plugin -This plugin allows for other plugins to add data to Kibana stack monitoring documents. \ No newline at end of file +This plugin allows for other plugins to add data to Kibana stack monitoring documents. + +## OpenTelemetry Metrics + +TODO: explain how to instrument the code with `@opentelemetry/api-metrics` so that the steps below will work with metrics + +### Enable Prometheus endpoint with Elastic Agent Prometheus input + +1. Start [local setup with fleet](../fleet/README.md#running-fleet-server-locally-in-a-container) or a cloud cluster +2. Start Kibana +3. Set up a new agent policy and enroll a new agent in your local machine +4. Install the Prometheus Metrics package + a. Set **Hosts** with `localhost:5601` + b. Set **Metrics Path** with `/(BASEPATH)/api/monitoring_collection/v1/prometheus` + c. Remove the values from **Bearer Token File** and **SSL Certificate Authorities** + d. Set **Username** and **Password** with `elastic` and `changeme` +5. Add the following configuration to `kibana.dev.yml` + + ```yml + # Enable the prometheus exporter + monitoring_collection.opentelemetry.metrics: + prometheus.enabled: true + + ``` + +6. Set up a rule (I use "Create default rules" in the top "Alerts and rules" menu of Stack Monitoring UI) + +### Enable OpenTelemetry Metrics API exported as OpenTelemetry Protocol + +1. Start [local setup with fleet](../fleet/README.md#running-fleet-server-locally-in-a-container) or a cloud cluster +2. Start Kibana +3. Set up a new agent policy and enroll a new agent in your local machine +4. Install Elastic APM package listening on `localhost:8200` without authentication +5. Add the following configuration to `kibana.dev.yml` + + ```yml + # Enable the OTLP exporter + monitoring_collection.opentelemetry.metrics: + otlp.url: "http://127.0.0.1:8200" + ``` + +6. Set up a rule (I use "Create default rules" in the top "Alerts and rules" menu of Stack Monitoring UI) diff --git a/x-pack/plugins/monitoring_collection/server/constants.ts b/x-pack/plugins/monitoring_collection/server/constants.ts index 4a698aa26f29b..92b43a9d80e48 100644 --- a/x-pack/plugins/monitoring_collection/server/constants.ts +++ b/x-pack/plugins/monitoring_collection/server/constants.ts @@ -6,4 +6,4 @@ */ export const TYPE_ALLOWLIST = ['node_rules', 'cluster_rules', 'node_actions', 'cluster_actions']; -export const PROMETHEUS_ROUTE = '/api/monitoring_collection/v1/prometheus'; +export const MONITORING_COLLECTION_BASE_PATH = '/api/monitoring_collection'; diff --git a/x-pack/plugins/monitoring_collection/server/lib/index.ts b/x-pack/plugins/monitoring_collection/server/lib/index.ts index 0c39a62ab359c..34c1fce763bdc 100644 --- a/x-pack/plugins/monitoring_collection/server/lib/index.ts +++ b/x-pack/plugins/monitoring_collection/server/lib/index.ts @@ -7,3 +7,4 @@ export { getKibanaStats } from './get_kibana_stats'; export { getESClusterUuid } from './get_es_cluster_uuid'; +export { PrometheusExporter } from './prometheus_exporter'; diff --git a/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts index d4b21fbd9641f..f1d2d1ffd4fb3 100644 --- a/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts +++ b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts @@ -5,21 +5,20 @@ * 2.0. */ -// Adapted from https://github.com/open-telemetry/opentelemetry-js/blob/aabc5f6b89e3d9af6640fb854967212ca5b1a3b8/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusExporter.ts - import { AggregationTemporality, MetricReader } from '@opentelemetry/sdk-metrics-base'; -import { PrometheusExporter as OpenTelemetryPrometheusExporter } from '@opentelemetry/exporter-prometheus'; -import { ExporterConfig } from '@opentelemetry/exporter-prometheus'; +import { + PrometheusExporter as OpenTelemetryPrometheusExporter, + ExporterConfig, + PrometheusSerializer, +} from '@opentelemetry/exporter-prometheus'; import { KibanaResponseFactory } from '@kbn/core/server'; -import { Logger } from '@kbn/core/server'; -import { PrometheusSerializer } from './prometheus_serializer'; export class PrometheusExporter extends MetricReader { private readonly _prefix?: string; private readonly _appendTimestamp: boolean; private _serializer: PrometheusSerializer; - constructor(logger: Logger, config: ExporterConfig = {}) { + constructor(config: ExporterConfig = {}) { super(); this._prefix = config.prefix || OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.prefix; this._appendTimestamp = @@ -27,7 +26,7 @@ export class PrometheusExporter extends MetricReader { ? config.appendTimestamp : OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.appendTimestamp; - this._serializer = new PrometheusSerializer(logger, this._prefix, this._appendTimestamp); + this._serializer = new PrometheusSerializer(this._prefix, this._appendTimestamp); } selectAggregationTemporality(): AggregationTemporality { @@ -45,31 +44,30 @@ export class PrometheusExporter extends MetricReader { /** * Responds to incoming message with current state of all metrics. */ - public exportMetrics(res: KibanaResponseFactory) { - // TODO: How can I type this return without requiring (forbidden path) KibanaReponse? - return this.collect().then( - (collectionResult) => { - const { resourceMetrics, errors } = collectionResult; - if (errors.length) { - return res.customError({ - statusCode: 500, - body: `PrometheusExporter: metrics collection errors ${errors}`, - }); - } - const result = this._serializer.serialize(resourceMetrics); - if (result === '') { - return res.noContent(); - } - return res.ok({ - body: result, - }); - }, - (err) => { + public async exportMetrics(res: KibanaResponseFactory) { + try { + const collectionResult = await this.collect(); + const { resourceMetrics, errors } = collectionResult; + if (errors.length) { return res.customError({ statusCode: 500, - body: `# Failed to export metrics ${err}`, + body: `PrometheusExporter: Metrics collection errors ${errors}`, }); } - ); + const result = this._serializer.serialize(resourceMetrics); + if (result === '') { + return res.noContent(); + } + return res.ok({ + body: result, + }); + } catch (error) { + return res.customError({ + statusCode: 500, + body: { + message: `PrometheusExporter: Failed to export metrics ${error}`, + }, + }); + } } } diff --git a/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts b/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts deleted file mode 100644 index d036a31673700..0000000000000 --- a/x-pack/plugins/monitoring_collection/server/lib/prometheus_serializer.ts +++ /dev/null @@ -1,311 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// From https://github.com/open-telemetry/opentelemetry-js/blob/97bc6321c0fe4da7414afb83038279b735a5ba65/experimental/packages/opentelemetry-exporter-prometheus/src/PrometheusSerializer.ts -// Can be removed once https://github.com/open-telemetry/opentelemetry-js/issues/3033 is merged/released - -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Logger } from '@kbn/core/server'; -import { - ResourceMetrics, - InstrumentType, - DataPointType, - ScopeMetrics, - MetricData, - DataPoint, - Histogram, -} from '@opentelemetry/sdk-metrics-base'; -import type { MetricAttributes } from '@opentelemetry/api-metrics'; - -// From https://github.com/open-telemetry/opentelemetry-js/blob/28c9e8829488a7fa131803447b0511195ae1fdf0/packages/opentelemetry-core/src/common/time.ts#L148 -export function hrTimeToMilliseconds(time: [number, number]): number { - return Math.round(time[0] * 1e3 + time[1] / 1e6); -} - -type PrometheusDataTypeLiteral = 'counter' | 'gauge' | 'histogram' | 'summary' | 'untyped'; - -function escapeString(str: string) { - return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); -} - -function escapeAttributeValue(str: string) { - if (typeof str !== 'string') { - str = String(str); - } - return escapeString(str).replace(/"/g, '\\"'); -} - -const invalidCharacterRegex = /[^a-z0-9_]/gi; -/** - * Ensures metric names are valid Prometheus metric names by removing - * characters allowed by OpenTelemetry but disallowed by Prometheus. - * - * https://prometheus.io/docs/concepts/data_model/#metric-names-and-attributes - * - * 1. Names must match `[a-zA-Z_:][a-zA-Z0-9_:]*` - * - * 2. Colons are reserved for user defined recording rules. - * They should not be used by exporters or direct instrumentation. - * - * OpenTelemetry metric names are already validated in the Meter when they are created, - * and they match the format `[a-zA-Z][a-zA-Z0-9_.\-]*` which is very close to a valid - * prometheus metric name, so we only need to strip characters valid in OpenTelemetry - * but not valid in prometheus and replace them with '_'. - * - * @param name name to be sanitized - */ -function sanitizePrometheusMetricName(name: string): string { - return name.replace(invalidCharacterRegex, '_'); // replace all invalid characters with '_' -} - -/** - * @private - * - * Helper method which assists in enforcing the naming conventions for metric - * names in Prometheus - * @param name the name of the metric - * @param type the kind of metric - * @returns string - */ -function enforcePrometheusNamingConvention(name: string, type: InstrumentType): string { - // Prometheus requires that metrics of the Counter kind have "_total" suffix - if (!name.endsWith('_total') && type === InstrumentType.COUNTER) { - name = name + '_total'; - } - - return name; -} - -function valueString(value: number) { - if (Number.isNaN(value)) { - return 'Nan'; - } else if (!Number.isFinite(value)) { - if (value < 0) { - return '-Inf'; - } else { - return '+Inf'; - } - } else { - return `${value}`; - } -} - -function toPrometheusType( - instrumentType: InstrumentType, - dataPointType: DataPointType -): PrometheusDataTypeLiteral { - switch (dataPointType) { - case DataPointType.SINGULAR: - if ( - instrumentType === InstrumentType.COUNTER || - instrumentType === InstrumentType.OBSERVABLE_COUNTER - ) { - return 'counter'; - } - /** - * - HISTOGRAM - * - UP_DOWN_COUNTER - * - OBSERVABLE_GAUGE - * - OBSERVABLE_UP_DOWN_COUNTER - */ - return 'gauge'; - case DataPointType.HISTOGRAM: - return 'histogram'; - default: - return 'untyped'; - } -} - -function stringify( - metricName: string, - attributes: MetricAttributes, - value: number, - timestamp?: number, - additionalAttributes?: MetricAttributes -) { - let hasAttribute = false; - let attributesStr = ''; - - for (const [key, val] of Object.entries(attributes)) { - const sanitizedAttributeName = sanitizePrometheusMetricName(key); - hasAttribute = true; - attributesStr += `${ - attributesStr.length > 0 ? ',' : '' - }${sanitizedAttributeName}="${escapeAttributeValue(val)}"`; - } - if (additionalAttributes) { - for (const [key, val] of Object.entries(additionalAttributes)) { - const sanitizedAttributeName = sanitizePrometheusMetricName(key); - hasAttribute = true; - attributesStr += `${ - attributesStr.length > 0 ? ',' : '' - }${sanitizedAttributeName}="${escapeAttributeValue(val)}"`; - } - } - - if (hasAttribute) { - metricName += `{${attributesStr}}`; - } - - return `${metricName} ${valueString(value)}${ - timestamp !== undefined ? ' ' + String(timestamp) : '' - }\n`; -} - -export class PrometheusSerializer { - private logger: Logger; - private _prefix: string | undefined; - private _appendTimestamp: boolean; - - constructor(logger: Logger, prefix?: string, appendTimestamp = true) { - if (prefix) { - this._prefix = prefix + '_'; - } - this._appendTimestamp = appendTimestamp; - this.logger = logger; - } - - serialize(resourceMetrics: ResourceMetrics): string { - let str = ''; - for (const scopeMetrics of resourceMetrics.scopeMetrics) { - str += this.serializeScopeMetrics(scopeMetrics); - } - return str; - } - - serializeScopeMetrics(scopeMetrics: ScopeMetrics) { - let str = ''; - for (const metric of scopeMetrics.metrics) { - str += this.serializeMetricData(metric) + '\n'; - } - return str; - } - - serializeMetricData(metricData: MetricData) { - let name = sanitizePrometheusMetricName(escapeString(metricData.descriptor.name)); - if (this._prefix) { - name = `${this._prefix}${name}`; - } - const dataPointType = metricData.dataPointType; - - name = enforcePrometheusNamingConvention(name, metricData.descriptor.type); - - const help = `# HELP ${name} ${escapeString( - metricData.descriptor.description || 'description missing' - )}`; - const type = `# TYPE ${name} ${toPrometheusType(metricData.descriptor.type, dataPointType)}`; - - let results = ''; - switch (dataPointType) { - case DataPointType.SINGULAR: { - results = metricData.dataPoints - .map((it) => this.serializeSingularDataPoint(name, metricData.descriptor.type, it)) - .join(''); - break; - } - case DataPointType.HISTOGRAM: { - results = metricData.dataPoints - .map((it) => this.serializeHistogramDataPoint(name, metricData.descriptor.type, it)) - .join(''); - break; - } - default: { - this.logger.error(`Unrecognizable DataPointType: ${dataPointType} for metric "${name}"`); - } - } - - return `${help}\n${type}\n${results}`.trim(); - } - - serializeSingularDataPoint( - name: string, - type: InstrumentType, - dataPoint: DataPoint - ): string { - let results = ''; - - name = enforcePrometheusNamingConvention(name, type); - const { value, attributes } = dataPoint; - const timestamp = hrTimeToMilliseconds(dataPoint.endTime); - results += stringify( - name, - attributes, - value, - this._appendTimestamp ? timestamp : undefined, - undefined - ); - return results; - } - - serializeHistogramDataPoint( - name: string, - type: InstrumentType, - dataPoint: DataPoint - ): string { - let results = ''; - - name = enforcePrometheusNamingConvention(name, type); - const { value, attributes } = dataPoint; - const timestamp = hrTimeToMilliseconds(dataPoint.endTime); - /** Histogram["bucket"] is not typed with `number` */ - for (const key of ['count', 'sum'] as Array<'count' | 'sum'>) { - results += stringify( - name + '_' + key, - attributes, - value[key], - this._appendTimestamp ? timestamp : undefined, - undefined - ); - } - - let cumulativeSum = 0; - const countEntries = value.buckets.counts.entries(); - let infiniteBoundaryDefined = false; - for (const [idx, val] of countEntries) { - cumulativeSum += val; - const upperBound = value.buckets.boundaries[idx]; - /** HistogramAggregator is producing different boundary output - - * in one case not including infinity values, in other - - * full, e.g. [0, 100] and [0, 100, Infinity] - * we should consider that in export, if Infinity is defined, use it - * as boundary - */ - if (upperBound === undefined && infiniteBoundaryDefined) { - break; - } - if (upperBound === Infinity) { - infiniteBoundaryDefined = true; - } - results += stringify( - name + '_bucket', - attributes, - cumulativeSum, - this._appendTimestamp ? timestamp : undefined, - { - le: upperBound === undefined || upperBound === Infinity ? '+Inf' : String(upperBound), - } - ); - } - - return results; - } -} diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 7f42f99c5d95f..433b5a3d22e63 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -6,9 +6,14 @@ */ import { JsonObject } from '@kbn/utility-types'; -import { CoreSetup, Plugin, PluginInitializerContext, Logger } from '@kbn/core/server'; +import { + CoreSetup, + Plugin, + PluginInitializerContext, + Logger, + ServiceStatus, +} from '@kbn/core/server'; import { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server'; -import { ServiceStatus } from '@kbn/core/server'; import { metrics } from '@opentelemetry/api-metrics'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; @@ -17,8 +22,8 @@ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' import * as grpc from '@grpc/grpc-js'; import { PrometheusExporter } from './lib/prometheus_exporter'; import { MonitoringCollectionConfig } from './config'; -import { registerDynamicRoute, registerV1PrometheusRoute } from './routes'; -import { PROMETHEUS_ROUTE, TYPE_ALLOWLIST } from './constants'; +import { registerDynamicRoute, registerV1PrometheusRoute, PROMETHEUS_PATH } from './routes'; +import { TYPE_ALLOWLIST } from './constants'; export interface MonitoringCollectionSetup { registerMetric: (metric: Metric) => void; @@ -126,14 +131,9 @@ export class MonitoringCollectionPlugin implements Plugin { jest.resetAllMocks(); }); -jest.mock('../lib', () => ({ +jest.mock('../../../../lib', () => ({ getESClusterUuid: () => 'clusterA', getKibanaStats: () => ({ name: 'myKibana' }), })); @@ -42,7 +42,7 @@ describe('dynamic route', () => { const router = httpServiceMock.createRouter(); const getMetric = async () => { - return { foo: 1 }; + return Promise.resolve({ foo: 1 }); }; registerDynamicRoute({ router, @@ -81,7 +81,7 @@ describe('dynamic route', () => { const router = httpServiceMock.createRouter(); const getMetric = async () => { - return {}; + return Promise.resolve({}); }; registerDynamicRoute({ router, config: kibanaStatsConfig, getStatus, getMetric }); diff --git a/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts similarity index 86% rename from x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts rename to x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts index 944037dd17a7b..4d18eeb6ec922 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/dynamic_route.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts @@ -7,8 +7,9 @@ import { JsonObject } from '@kbn/utility-types'; import { schema } from '@kbn/config-schema'; import { IRouter, ServiceStatus } from '@kbn/core/server'; -import { getESClusterUuid, getKibanaStats } from '../lib'; -import { MetricResult } from '../plugin'; +import { getESClusterUuid, getKibanaStats } from '../../../../lib'; +import { MetricResult } from '../../../../plugin'; +import { MONITORING_COLLECTION_BASE_PATH } from '../../../../constants'; export function registerDynamicRoute({ router, @@ -34,7 +35,7 @@ export function registerDynamicRoute({ }) { router.get( { - path: `/api/monitoring_collection/{type}`, + path: `${MONITORING_COLLECTION_BASE_PATH}/{type}`, options: { authRequired: true, tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page diff --git a/x-pack/plugins/alerting/server/monitoring/metrics.mock.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/index.ts similarity index 55% rename from x-pack/plugins/alerting/server/monitoring/metrics.mock.ts rename to x-pack/plugins/monitoring_collection/server/routes/api/v1/index.ts index 129298e63e90f..e5a70f3f79abc 100644 --- a/x-pack/plugins/alerting/server/monitoring/metrics.mock.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/index.ts @@ -4,10 +4,5 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { metrics } from '@opentelemetry/api-metrics'; -import { Metrics } from './metrics'; - -export const metricsMock = { - create: () => new Metrics(metrics.getMeter('kibana.alerting.metrics.mock')), -}; +export { registerDynamicRoute } from './dynamic_route'; +export { registerV1PrometheusRoute, PROMETHEUS_PATH } from './prometheus'; diff --git a/x-pack/plugins/monitoring_collection/server/routes/prometheus.test.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.test.ts similarity index 58% rename from x-pack/plugins/monitoring_collection/server/routes/prometheus.test.ts rename to x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.test.ts index ec5d6d7bb44a4..b136d982992c4 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/prometheus.test.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.test.ts @@ -5,22 +5,25 @@ * 2.0. */ +import { RequestHandlerContext } from '@kbn/core/server'; import { httpServerMock, httpServiceMock } from '@kbn/core/server/mocks'; -import { registerV1PrometheusRoute } from './prometheus'; -import { KibanaRequest, KibanaResponseFactory } from '@kbn/core/server'; -import { PrometheusExporter } from '../lib/prometheus_exporter'; +import { registerV1PrometheusRoute } from '.'; +import { PrometheusExporter } from '../../../../lib'; -describe('prometheus route', () => { +describe('Prometheus route', () => { it('forwards the request to the prometheus exporter', async () => { const router = httpServiceMock.createRouter(); - const prometheusExporter = { exportMetrics: jest.fn() } as unknown as PrometheusExporter; + const prometheusExporter = { + exportMetrics: jest.fn(), + } as Partial as PrometheusExporter; + registerV1PrometheusRoute({ router, prometheusExporter }); const [, handler] = router.get.mock.calls[0]; - const context = {}; - const req = { params: {} } as KibanaRequest; - const factory: jest.Mocked = httpServerMock.createResponseFactory(); + const context = {} as jest.Mocked; + const req = httpServerMock.createKibanaRequest(); + const factory = httpServerMock.createResponseFactory(); await handler(context, req, factory); diff --git a/x-pack/plugins/monitoring_collection/server/routes/prometheus.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.ts similarity index 72% rename from x-pack/plugins/monitoring_collection/server/routes/prometheus.ts rename to x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.ts index 40e783c3d8b46..6977be155a4fb 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/prometheus.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/get_metrics.ts @@ -6,9 +6,10 @@ */ import { IRouter } from '@kbn/core/server'; -import { PROMETHEUS_ROUTE } from '../constants'; -import { PrometheusExporter } from '../lib/prometheus_exporter'; +import { MONITORING_COLLECTION_BASE_PATH } from '../../../../constants'; +import { PrometheusExporter } from '../../../../lib'; +export const PROMETHEUS_PATH = `${MONITORING_COLLECTION_BASE_PATH}/v1/prometheus`; export function registerV1PrometheusRoute({ router, prometheusExporter, @@ -18,14 +19,14 @@ export function registerV1PrometheusRoute({ }) { router.get( { - path: PROMETHEUS_ROUTE, + path: PROMETHEUS_PATH, options: { authRequired: true, tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page }, validate: {}, }, - async (context, req, res) => { + async (_context, _req, res) => { return prometheusExporter.exportMetrics(res); } ); diff --git a/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/index.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/index.ts new file mode 100644 index 0000000000000..5b99f51c94511 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/prometheus/index.ts @@ -0,0 +1,7 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export * from './get_metrics'; diff --git a/x-pack/plugins/monitoring_collection/server/routes/index.ts b/x-pack/plugins/monitoring_collection/server/routes/index.ts index c4a116cb3f84c..29cd177990593 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/index.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export { registerDynamicRoute } from './dynamic_route'; -export { registerV1PrometheusRoute } from './prometheus'; +export { registerV1PrometheusRoute, PROMETHEUS_PATH, registerDynamicRoute } from './api/v1'; diff --git a/yarn.lock b/yarn.lock index 0b94a0b7cf9a8..916e1d03d341e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4384,6 +4384,13 @@ dependencies: "@opentelemetry/api" "^1.0.0" +"@opentelemetry/api-metrics@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.30.0.tgz#b5defd10756e81d1c7ce8669ff8a8d2465ba0be8" + integrity sha512-jSb7iiYPY+DSUKIyzfGt0a5K1QGzWY5fSWtUB8Alfi27NhQGHBeuYYC5n9MaBP/HNWw5GpEIhXGEYCF9Pf8IEg== + dependencies: + "@opentelemetry/api" "^1.0.0" + "@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" @@ -4396,6 +4403,13 @@ dependencies: "@opentelemetry/semantic-conventions" "1.3.1" +"@opentelemetry/core@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.4.0.tgz#26839ab9e36583a174273a1e1c5b33336c163725" + integrity sha512-faq50VFEdyC7ICAOlhSi+yYZ+peznnGjTJToha9R63i9fVopzpKrkZt7AIdXUmz2+L2OqXrcJs7EIdN/oDyr5w== + dependencies: + "@opentelemetry/semantic-conventions" "1.4.0" + "@opentelemetry/exporter-metrics-otlp-grpc@^0.29.2": version "0.29.2" resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.29.2.tgz#d51352a703363237621d22793949cab4d69e1b01" @@ -4422,14 +4436,14 @@ "@opentelemetry/resources" "1.3.1" "@opentelemetry/sdk-metrics-base" "0.29.2" -"@opentelemetry/exporter-prometheus@^0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.29.2.tgz#70ca7fb37655ca57a580387607d5465b47e27ac3" - integrity sha512-E5sRfUM4rzbvjxdpL1H6YRtjr8wY8+/2R4NjfxPEwrENLeeQk87V1E+YFLqAS7TfFLW7Zr4lmmamunwn5THvQA== +"@opentelemetry/exporter-prometheus@^0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.30.0.tgz#f81322d3cb000170e716bc76820600d5649be538" + integrity sha512-y0SXvpzoKR+Tk/UL6F1f7vAcCzqpCDP/cTEa+Z7sX57aEG0HDXLQiLmAgK/BHqcEN5MFQMZ+MDVDsUrvpa6/Jw== dependencies: - "@opentelemetry/api-metrics" "0.29.2" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/sdk-metrics-base" "0.29.2" + "@opentelemetry/api-metrics" "0.30.0" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/sdk-metrics-base" "0.30.0" "@opentelemetry/otlp-exporter-base@0.29.2": version "0.29.2" @@ -4467,6 +4481,14 @@ "@opentelemetry/core" "1.3.1" "@opentelemetry/semantic-conventions" "1.3.1" +"@opentelemetry/resources@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.4.0.tgz#5e23b0d7976158861059dec17e0ee36a35a5ab85" + integrity sha512-Q3pI5+pCM+Ur7YwK9GbG89UBipwJbfmuzSPAXTw964ZHFzSrz+JAgrETC9rqsUOYdUlj/V7LbRMG5bo72xE0Xw== + dependencies: + "@opentelemetry/core" "1.4.0" + "@opentelemetry/semantic-conventions" "1.4.0" + "@opentelemetry/sdk-metrics-base@0.29.2", "@opentelemetry/sdk-metrics-base@^0.29.2": version "0.29.2" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155" @@ -4477,6 +4499,16 @@ "@opentelemetry/resources" "1.3.1" lodash.merge "4.6.2" +"@opentelemetry/sdk-metrics-base@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.30.0.tgz#242d9260a89a1ac2bf1e167b3fda758f3883c769" + integrity sha512-3BDg1MYDInDyGvy+bSH8OuCX5nsue7omH6Y2eidCGTTDYRPxDmq9tsRJxnTUepoMAvWX+1sTwZ4JqTFmc1z8Mw== + dependencies: + "@opentelemetry/api-metrics" "0.30.0" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/resources" "1.4.0" + lodash.merge "4.6.2" + "@opentelemetry/sdk-trace-base@1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz#958083dbab928eefd17848959ac8810c787bec7f" @@ -4491,6 +4523,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== +"@opentelemetry/semantic-conventions@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.4.0.tgz#facf2c67d6063b9918d5a5e3fdf25f3a30d547b6" + integrity sha512-Hzl8soGpmyzja9w3kiFFcYJ7n5HNETpplY6cb67KR4QPlxp4FTTresO06qXHgHDhyIInmbLJXuwARjjpsKYGuQ== + "@percy/agent@^0.28.6": version "0.28.6" resolved "https://registry.yarnpkg.com/@percy/agent/-/agent-0.28.6.tgz#b220fab6ddcf63ae4e6c343108ba6955a772ce1c" @@ -7506,7 +7543,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@12.20.24", "@types/node@16.11.41", "@types/node@>= 8", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10", "@types/node@^14.14.31": +"@types/node@*", "@types/node@12.20.24", "@types/node@16.11.41", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0", "@types/node@^10.1.0", "@types/node@^14.0.10", "@types/node@^14.14.31": version "16.11.41" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== From 218a4ffebb175e75d56d2d5fea53e07960c784a2 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 11 Jul 2022 10:38:02 +0200 Subject: [PATCH 03/16] Bump @opentelemetry/api-metrics and @opentelemetry/exporter-metrics-otlp-grpc versions to 0.30.0 --- package.json | 4 +- yarn.lock | 110 +++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index d96dab6423a87..58ca8725ea08f 100644 --- a/package.json +++ b/package.json @@ -264,8 +264,8 @@ "@mapbox/mapbox-gl-draw": "1.3.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mapbox/vector-tile": "1.3.1", - "@opentelemetry/api-metrics": "0.29.2", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.29.2", + "@opentelemetry/api-metrics": "0.30.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.30.0", "@opentelemetry/exporter-prometheus": "^0.30.0", "@opentelemetry/resources": "^1.3.1", "@opentelemetry/sdk-metrics-base": "^0.29.2", diff --git a/yarn.lock b/yarn.lock index 916e1d03d341e..c94a3f5473da1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4410,31 +4410,31 @@ dependencies: "@opentelemetry/semantic-conventions" "1.4.0" -"@opentelemetry/exporter-metrics-otlp-grpc@^0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.29.2.tgz#d51352a703363237621d22793949cab4d69e1b01" - integrity sha512-OhZzSiHhn1RUdaM7XkDmZ6m6+T+TPbEJbiXPrswN8G6jKjokaP4QWM+iis1GQBqYcpMtFHhN0Mlr8mF9dzJ2pA== +"@opentelemetry/exporter-metrics-otlp-grpc@^0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.30.0.tgz#4117d07b94302ef407dc7625a1b599de308c5476" + integrity sha512-02WEAA3X7A6qveCYISr6mvg8eKl9NeNdZytQiAexzAIItW/ncN3mxmbuf8VVZHNPBe6osisSzxhPpFH3G6Gh+w== dependencies: "@grpc/grpc-js" "^1.5.9" "@grpc/proto-loader" "^0.6.9" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/exporter-metrics-otlp-http" "0.29.2" - "@opentelemetry/otlp-grpc-exporter-base" "0.29.2" - "@opentelemetry/otlp-transformer" "0.29.2" - "@opentelemetry/resources" "1.3.1" - "@opentelemetry/sdk-metrics-base" "0.29.2" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/exporter-metrics-otlp-http" "0.30.0" + "@opentelemetry/otlp-grpc-exporter-base" "0.30.0" + "@opentelemetry/otlp-transformer" "0.30.0" + "@opentelemetry/resources" "1.4.0" + "@opentelemetry/sdk-metrics-base" "0.30.0" -"@opentelemetry/exporter-metrics-otlp-http@0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.29.2.tgz#4daf876d5f9cdabcd11fa0bcc3d4f73be89cd55f" - integrity sha512-DSNVeXMD9sx4CcDTEy1+DiZAdjqqFhSdQWaDI2cPyxDUfyAgrDJM+HUAgCCy87eamsOy0vNCu1Hq9Ygoq4ZwHQ== +"@opentelemetry/exporter-metrics-otlp-http@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.30.0.tgz#9d87e4c3e796e14109ac83e6d4ce5bad215c2a1e" + integrity sha512-2NFR/D9jih1TtEnEyD7oIMR47yb9Kuy5v2x+Fu19vv2gTf1HOhdA+LT4SpkxH+dUixEnDw8n11XBIa/uhNfq3Q== dependencies: - "@opentelemetry/api-metrics" "0.29.2" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/otlp-exporter-base" "0.29.2" - "@opentelemetry/otlp-transformer" "0.29.2" - "@opentelemetry/resources" "1.3.1" - "@opentelemetry/sdk-metrics-base" "0.29.2" + "@opentelemetry/api-metrics" "0.30.0" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/otlp-exporter-base" "0.30.0" + "@opentelemetry/otlp-transformer" "0.30.0" + "@opentelemetry/resources" "1.4.0" + "@opentelemetry/sdk-metrics-base" "0.30.0" "@opentelemetry/exporter-prometheus@^0.30.0": version "0.30.0" @@ -4445,33 +4445,33 @@ "@opentelemetry/core" "1.4.0" "@opentelemetry/sdk-metrics-base" "0.30.0" -"@opentelemetry/otlp-exporter-base@0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.29.2.tgz#fc844cc00e1ad95444b85056c08b1ca3e8a40447" - integrity sha512-tTK+v2ER9Rv7YQXLrCvZpPdNdvZx8TGdZtlK7TKnzpyMRBIf7lqV1Jl0VaHFml+cgVJcGtow/ER6k5uJ5W4kUQ== +"@opentelemetry/otlp-exporter-base@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.30.0.tgz#5f278b3529d38311dbdfc1ebcb764f5e5126e548" + integrity sha512-+dJnj2MSd3tsk+ooEw+0bF+dJs/NjGEVnCB3/FYxnUFaW9cCBbQQyt6X3YQYtYrEx4EEiTlwrW8pUpB1tsup7A== dependencies: - "@opentelemetry/core" "1.3.1" + "@opentelemetry/core" "1.4.0" -"@opentelemetry/otlp-grpc-exporter-base@0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.29.2.tgz#bc01f79ce7a35f126c3ac0fd51ab68170b828b38" - integrity sha512-iNj1E7downme1PePb8itxg0/NF/l7SX3rGuDsR2+rCwwbd0NfmUN/6xwF1YupP6Z1/ThG0YE37eK/F1NZOt6mQ== +"@opentelemetry/otlp-grpc-exporter-base@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.30.0.tgz#3fa07667ddf604a028583a2a138b8b4ba8fa9bb0" + integrity sha512-86fuhZ7Z2un3L5Kd7jbH1oEn92v9DD92teErnYRXqYB/qyO61OLxaY6WxH9KOjmbs5CgCdLQ5bvED3wWDe3r7w== dependencies: "@grpc/grpc-js" "^1.5.9" "@grpc/proto-loader" "^0.6.9" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/otlp-exporter-base" "0.29.2" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/otlp-exporter-base" "0.30.0" -"@opentelemetry/otlp-transformer@0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.29.2.tgz#61897d3d747182ab7e315a88a9a710a759c13390" - integrity sha512-Y6dJj+rhRGynxhLlgEJkdkXuLHdFG8igcSBv6oy3m3GHSSvZkyNV34dVjtZJ586mUXsbFuAf6uqjzteobewO1g== +"@opentelemetry/otlp-transformer@0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.30.0.tgz#d81e1ae68dfb31d66cd4ca03ca965cdaa2e2b288" + integrity sha512-BTLXyBPBlCQCG4tXYZjlso4pT+gGpnTjzkFYTPYs52fO5DMWvYHlV8ST/raOIqX7wsamiH2zeqJ9W91017MtdA== dependencies: - "@opentelemetry/api-metrics" "0.29.2" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/resources" "1.3.1" - "@opentelemetry/sdk-metrics-base" "0.29.2" - "@opentelemetry/sdk-trace-base" "1.3.1" + "@opentelemetry/api-metrics" "0.30.0" + "@opentelemetry/core" "1.4.0" + "@opentelemetry/resources" "1.4.0" + "@opentelemetry/sdk-metrics-base" "0.30.0" + "@opentelemetry/sdk-trace-base" "1.4.0" "@opentelemetry/resources@1.3.1", "@opentelemetry/resources@^1.3.1": version "1.3.1" @@ -4489,16 +4489,6 @@ "@opentelemetry/core" "1.4.0" "@opentelemetry/semantic-conventions" "1.4.0" -"@opentelemetry/sdk-metrics-base@0.29.2", "@opentelemetry/sdk-metrics-base@^0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155" - integrity sha512-7hhhZ/6YRRgAXOUTeCsbe6SIk3wZAdAHnEwGGp7aiVH5AOyioHyHInw4EHtowlD6dbLxUWURjh6k+Geht2zbxg== - dependencies: - "@opentelemetry/api-metrics" "0.29.2" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/resources" "1.3.1" - lodash.merge "4.6.2" - "@opentelemetry/sdk-metrics-base@0.30.0": version "0.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.30.0.tgz#242d9260a89a1ac2bf1e167b3fda758f3883c769" @@ -4509,14 +4499,24 @@ "@opentelemetry/resources" "1.4.0" lodash.merge "4.6.2" -"@opentelemetry/sdk-trace-base@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.3.1.tgz#958083dbab928eefd17848959ac8810c787bec7f" - integrity sha512-Or95QZ+9QyvAiwqj+K68z8bDDuyWF50c37w17D10GV1dWzg4Ezcectsu/GB61QcBxm3Y4br0EN5F5TpIFfFliQ== +"@opentelemetry/sdk-metrics-base@^0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155" + integrity sha512-7hhhZ/6YRRgAXOUTeCsbe6SIk3wZAdAHnEwGGp7aiVH5AOyioHyHInw4EHtowlD6dbLxUWURjh6k+Geht2zbxg== dependencies: + "@opentelemetry/api-metrics" "0.29.2" "@opentelemetry/core" "1.3.1" "@opentelemetry/resources" "1.3.1" - "@opentelemetry/semantic-conventions" "1.3.1" + lodash.merge "4.6.2" + +"@opentelemetry/sdk-trace-base@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.4.0.tgz#e54d09c1258cd53d3fe726053ed1cbda9d74f023" + integrity sha512-l7EEjcOgYlKWK0hfxz4Jtkkk2DuGiqBDWmRZf7g2Is9RVneF1IgcrbYZTKGaVfBKA7lPuVtUiQ2qTv3R+dKJrw== + dependencies: + "@opentelemetry/core" "1.4.0" + "@opentelemetry/resources" "1.4.0" + "@opentelemetry/semantic-conventions" "1.4.0" "@opentelemetry/semantic-conventions@1.3.1", "@opentelemetry/semantic-conventions@^1.3.1": version "1.3.1" From 70d0ed09d02d7e45b540f527d5dbf32e717e0577 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 11 Jul 2022 22:16:14 +0200 Subject: [PATCH 04/16] Add integration test for prometheus endpoint; improve reademe.md --- .github/CODEOWNERS | 1 + test/common/config.js | 2 + .../fixtures/plugins/otel_metrics/kibana.json | 15 +++++ .../plugins/otel_metrics/server/index.ts | 11 +++ .../otel_metrics/server/monitoring/metrics.ts | 19 ++++++ .../plugins/otel_metrics/server/plugin.ts | 28 ++++++++ .../server/routes/generate_otel_metrics.ts | 23 +++++++ .../otel_metrics/server/routes/index.ts | 9 +++ .../plugins/monitoring_collection/README.md | 67 +++++++++++++++++-- .../apis/monitoring_collection/index.ts | 14 ++++ .../apis/monitoring_collection/prometheus.ts | 24 +++++++ x-pack/test/api_integration/config.ts | 5 ++ 12 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 test/common/fixtures/plugins/otel_metrics/kibana.json create mode 100644 test/common/fixtures/plugins/otel_metrics/server/index.ts create mode 100644 test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts create mode 100644 test/common/fixtures/plugins/otel_metrics/server/plugin.ts create mode 100644 test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts create mode 100644 test/common/fixtures/plugins/otel_metrics/server/routes/index.ts create mode 100644 x-pack/test/api_integration/apis/monitoring_collection/index.ts create mode 100644 x-pack/test/api_integration/apis/monitoring_collection/prometheus.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3caf39ff52f3d..e1616360dd7fd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -115,6 +115,7 @@ /x-pack/plugins/monitoring/ @elastic/infra-monitoring-ui /x-pack/test/functional/apps/monitoring @elastic/infra-monitoring-ui /x-pack/test/api_integration/apis/monitoring @elastic/infra-monitoring-ui +/x-pack/test/api_integration/apis/monitoring-collection @elastic/infra-monitoring-ui # Fleet /fleet_packages.json @elastic/fleet diff --git a/test/common/config.js b/test/common/config.js index 048d032d0169a..6546c694f9d79 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -51,6 +51,8 @@ export default function () { `--server.maxPayload=1679958`, // newsfeed mock service `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'newsfeed')}`, + // otel mock service + `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'otel_metrics')}`, `--newsfeed.service.urlRoot=${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`, `--newsfeed.service.pathTemplate=/api/_newsfeed-FTS-external-service-simulators/kibana/v{VERSION}.json`, // code coverage reporting plugin diff --git a/test/common/fixtures/plugins/otel_metrics/kibana.json b/test/common/fixtures/plugins/otel_metrics/kibana.json new file mode 100644 index 0000000000000..f9cc773c1fe0a --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "openTelemetryInstrumentedPlugin", + "owner": { + "name": "Stack Monitoring", + "githubTeam": "stack-monitoring-ui" + }, + "version": "1.0.0", + "kibanaVersion": "kibana", + "requiredPlugins": [ + "monitoringCollection" + ], + "optionalPlugins": [], + "server": true, + "ui": false +} \ No newline at end of file diff --git a/test/common/fixtures/plugins/otel_metrics/server/index.ts b/test/common/fixtures/plugins/otel_metrics/server/index.ts new file mode 100644 index 0000000000000..eb5f587592cae --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/server/index.ts @@ -0,0 +1,11 @@ +/* + * 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 { OpenTelemetryUsageTest } from './plugin'; + +export const plugin = () => new OpenTelemetryUsageTest(); diff --git a/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts b/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts new file mode 100644 index 0000000000000..ce9742c6edfc3 --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts @@ -0,0 +1,19 @@ +/* + * 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 { Counter, Meter } from '@opentelemetry/api-metrics'; + +export class Metrics { + ruleExecutions: Counter; + + constructor(meter: Meter) { + this.ruleExecutions = meter.createCounter('request_count', { + description: 'Counts total number of requests', + }); + } +} diff --git a/test/common/fixtures/plugins/otel_metrics/server/plugin.ts b/test/common/fixtures/plugins/otel_metrics/server/plugin.ts new file mode 100644 index 0000000000000..65dec472b94f9 --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/server/plugin.ts @@ -0,0 +1,28 @@ +/* + * 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 { CoreSetup, Plugin } from '@kbn/core/server'; +import { metrics } from '@opentelemetry/api-metrics'; +import { generateOtelMetrics } from './routes'; +import { Metrics } from './monitoring/metrics'; + +export class OpenTelemetryUsageTest implements Plugin { + private metrics: Metrics; + + constructor() { + this.metrics = new Metrics(metrics.getMeter('dummyMetric')); + } + + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + generateOtelMetrics(router, this.metrics); + } + + public start() {} + public stop() {} +} diff --git a/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts b/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts new file mode 100644 index 0000000000000..807e7e852f54d --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts @@ -0,0 +1,23 @@ +/* + * 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 { IKibanaResponse, IRouter } from '@kbn/core/server'; +import { Metrics } from '../monitoring/metrics'; + +export const generateOtelMetrics = (router: IRouter, metrics: Metrics) => { + router.post( + { + path: '/api/generate_otel_metrics', + validate: {}, + }, + async function (_context, _req, res): Promise> { + metrics.ruleExecutions.add(1); + return res.ok({}); + } + ); +}; diff --git a/test/common/fixtures/plugins/otel_metrics/server/routes/index.ts b/test/common/fixtures/plugins/otel_metrics/server/routes/index.ts new file mode 100644 index 0000000000000..49ac53bcf5412 --- /dev/null +++ b/test/common/fixtures/plugins/otel_metrics/server/routes/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 * from './generate_otel_metrics'; diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index cdd12b0701f77..5bc6d377e91b5 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -6,8 +6,6 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu ## OpenTelemetry Metrics -TODO: explain how to instrument the code with `@opentelemetry/api-metrics` so that the steps below will work with metrics - ### Enable Prometheus endpoint with Elastic Agent Prometheus input 1. Start [local setup with fleet](../fleet/README.md#running-fleet-server-locally-in-a-container) or a cloud cluster @@ -27,8 +25,6 @@ TODO: explain how to instrument the code with `@opentelemetry/api-metrics` so th ``` -6. Set up a rule (I use "Create default rules" in the top "Alerts and rules" menu of Stack Monitoring UI) - ### Enable OpenTelemetry Metrics API exported as OpenTelemetry Protocol 1. Start [local setup with fleet](../fleet/README.md#running-fleet-server-locally-in-a-container) or a cloud cluster @@ -43,4 +39,65 @@ TODO: explain how to instrument the code with `@opentelemetry/api-metrics` so th otlp.url: "http://127.0.0.1:8200" ``` -6. Set up a rule (I use "Create default rules" in the top "Alerts and rules" menu of Stack Monitoring UI) +### Example of how to instrument the code + +* First, we need to define what metrics we want to instrument with OpenTelemetry + + ```ts + import { Counter, Meter } from '@opentelemetry/api-metrics'; + + export class FooApiMeters { + requestCount: Counter; + + constructor(meter: Meter) { + this.requestCount = meter.createCounter('request_count', { + description: 'Counts total number of requests', + }); + } + ``` + + In this example we're using a `Counter` metric, but [OpenTelemetry SDK](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api_metrics.Meter.html) provides there are other options to record metrics + +* Initialize meter in the plugin setup and pass it to the relevant components that will be instrumented. In this case, we want to intrument `FooApi` routes + + ```ts + import { IRouter } from '@kbn/core/server'; + import { FooApiMeters } from './foo_api_meters'; + import { metrics } from '@opentelemetry/api-metrics'; + + export class FooApiPlugin implements Plugin { + private metrics: Metrics; + private libs: { router: IRouter, metrics: FooApiMeters}; + + constructor() { + this.metrics = new Metrics(metrics.getMeter('kibana.fooApi')); + } + + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + + this.libs = { + router, + metrics: this.metrics + } + + initMetricsAPIRoute(this.libs); + } + } + ``` + + * Lastly we can use the `metrics` object to instrument the code + + ```ts + export const initMetricsAPIRoute = (libs: { router: IRouter, metrics: FooApiMeters}) => { + router.get( + { + path: '/api/foo', + validate: {}, + }, + async function (_context, _req, res) { + metrics.requestCount.add(1); + return res.ok({}); + } + ); + ``` diff --git a/x-pack/test/api_integration/apis/monitoring_collection/index.ts b/x-pack/test/api_integration/apis/monitoring_collection/index.ts new file mode 100644 index 0000000000000..e89bd44963c03 --- /dev/null +++ b/x-pack/test/api_integration/apis/monitoring_collection/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Monitoring Collection', function taskManagerSuite() { + loadTestFile(require.resolve('./prometheus')); + }); +} diff --git a/x-pack/test/api_integration/apis/monitoring_collection/prometheus.ts b/x-pack/test/api_integration/apis/monitoring_collection/prometheus.ts new file mode 100644 index 0000000000000..0ac13dda92cb5 --- /dev/null +++ b/x-pack/test/api_integration/apis/monitoring_collection/prometheus.ts @@ -0,0 +1,24 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Prometheus endpoint', () => { + it('returns prometheus scraped metrics', async () => { + await supertest.post('/api/generate_otel_metrics').set('kbn-xsrf', 'foo').expect(200); + const response = await supertest.get('/api/monitoring_collection/v1/prometheus').expect(200); + + expect(response.text.replace(/\s+/g, ' ')).to.match( + /^# HELP request_count_total Counts total number of requests # TYPE request_count_total counter request_count_total [0-9]/ + ); + }); + }); +} diff --git a/x-pack/test/api_integration/config.ts b/x-pack/test/api_integration/config.ts index 8cc5fb6f57d42..76f4a30e9c981 100644 --- a/x-pack/test/api_integration/config.ts +++ b/x-pack/test/api_integration/config.ts @@ -37,6 +37,11 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi '--xpack.ruleRegistry.write.cache.enabled=false', '--xpack.uptime.service.password=test', '--xpack.uptime.service.username=localKibanaIntegrationTestsUser', + `--monitoring_collection.opentelemetry.metrics=${JSON.stringify({ + prometheus: { + enabled: true, + }, + })}`, ], }, esTestCluster: { From bb77bc15b714b06e9ce3ae8b0d7076f3d9c1564f Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 11 Jul 2022 22:43:07 +0200 Subject: [PATCH 05/16] Fix tsconfig.base.json missing entries --- tsconfig.base.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tsconfig.base.json b/tsconfig.base.json index b9b634dd8a607..1d9596379888e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -199,6 +199,8 @@ "@kbn/coverage-fixtures-plugin/*": ["test/common/fixtures/plugins/coverage/*"], "@kbn/newsfeed-fixtures-plugin": ["test/common/fixtures/plugins/newsfeed"], "@kbn/newsfeed-fixtures-plugin/*": ["test/common/fixtures/plugins/newsfeed/*"], + "@kbn/open-telemetry-instrumented-plugin": ["test/common/fixtures/plugins/otel_metrics"], + "@kbn/open-telemetry-instrumented-plugin/*": ["test/common/fixtures/plugins/otel_metrics/*"], "@kbn/kbn-tp-run-pipeline-plugin": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline"], "@kbn/kbn-tp-run-pipeline-plugin/*": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline/*"], "@kbn/app-link-test-plugin": ["test/plugin_functional/plugins/app_link_test"], From 4ed979bc6b337712cba673e9c1b303d48c9b3c78 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 12 Jul 2022 10:30:18 +0200 Subject: [PATCH 06/16] Bump @opentelemetry/sdk-metrics-base; clean up --- package.json | 2 +- .../plugins/monitoring_collection/README.md | 11 +++++- x-pack/test/api_integration/apis/index.ts | 1 + x-pack/test/api_integration/config.ts | 6 +--- yarn.lock | 35 +++++-------------- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index ddf604113520c..c0ceabcb3cced 100644 --- a/package.json +++ b/package.json @@ -278,7 +278,7 @@ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.30.0", "@opentelemetry/exporter-prometheus": "^0.30.0", "@opentelemetry/resources": "^1.3.1", - "@opentelemetry/sdk-metrics-base": "^0.29.2", + "@opentelemetry/sdk-metrics-base": "^0.30.0", "@opentelemetry/semantic-conventions": "^1.3.1", "@reduxjs/toolkit": "^1.6.1", "@slack/webhook": "^5.0.4", diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index 5bc6d377e91b5..4cc0431bf23b8 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -86,7 +86,15 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu } ``` - * Lastly we can use the `metrics` object to instrument the code + `monitoring_collection` plugins has to be initialized before the plugin that will be instrumented. If for some reason the instrumentation doesn't record any metrics, make sure `monitoring_collection` is included in the list of `requiredPlugins`. e.g: + + ```json + "requiredPlugins": [ + "monitoringCollection" + ], + ``` + +* Lastly we can use the `metrics` object to instrument the code ```ts export const initMetricsAPIRoute = (libs: { router: IRouter, metrics: FooApiMeters}) => { @@ -101,3 +109,4 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu } ); ``` + diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 6bec2ebe80a13..46b10af2a52b3 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -36,5 +36,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./watcher')); loadTestFile(require.resolve('./logs_ui')); loadTestFile(require.resolve('./osquery')); + loadTestFile(require.resolve('./monitoring_collection')); }); } diff --git a/x-pack/test/api_integration/config.ts b/x-pack/test/api_integration/config.ts index 76f4a30e9c981..ca3795e812ee2 100644 --- a/x-pack/test/api_integration/config.ts +++ b/x-pack/test/api_integration/config.ts @@ -37,11 +37,7 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi '--xpack.ruleRegistry.write.cache.enabled=false', '--xpack.uptime.service.password=test', '--xpack.uptime.service.username=localKibanaIntegrationTestsUser', - `--monitoring_collection.opentelemetry.metrics=${JSON.stringify({ - prometheus: { - enabled: true, - }, - })}`, + '--monitoring_collection.opentelemetry.metrics.prometheus.enabled=true', ], }, esTestCluster: { diff --git a/yarn.lock b/yarn.lock index ded3a39134087..30a3882f6896b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4417,13 +4417,6 @@ "@mattiasbuelens/web-streams-adapter" "~0.1.0" web-streams-polyfill "~3.0.3" -"@opentelemetry/api-metrics@0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.29.2.tgz#daa823e0965754222b49a6ae6133df8b39ff8fd2" - integrity sha512-yRdF5beqKuEdsPNoO7ijWCQ9HcyN0Tlgicf8RS6gzGOI54d6Hj7yKquJ6+X9XV+CSRbRWJYb+lOsXyso7uyX2g== - dependencies: - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/api-metrics@0.30.0": version "0.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.30.0.tgz#b5defd10756e81d1c7ce8669ff8a8d2465ba0be8" @@ -4513,14 +4506,6 @@ "@opentelemetry/sdk-metrics-base" "0.30.0" "@opentelemetry/sdk-trace-base" "1.4.0" -"@opentelemetry/resources@1.3.1", "@opentelemetry/resources@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.3.1.tgz#9fd85ac4ffeefc35441404b384d5c1db8b243121" - integrity sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA== - dependencies: - "@opentelemetry/core" "1.3.1" - "@opentelemetry/semantic-conventions" "1.3.1" - "@opentelemetry/resources@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.4.0.tgz#5e23b0d7976158861059dec17e0ee36a35a5ab85" @@ -4529,7 +4514,15 @@ "@opentelemetry/core" "1.4.0" "@opentelemetry/semantic-conventions" "1.4.0" -"@opentelemetry/sdk-metrics-base@0.30.0": +"@opentelemetry/resources@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.3.1.tgz#9fd85ac4ffeefc35441404b384d5c1db8b243121" + integrity sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA== + dependencies: + "@opentelemetry/core" "1.3.1" + "@opentelemetry/semantic-conventions" "1.3.1" + +"@opentelemetry/sdk-metrics-base@0.30.0", "@opentelemetry/sdk-metrics-base@^0.30.0": version "0.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.30.0.tgz#242d9260a89a1ac2bf1e167b3fda758f3883c769" integrity sha512-3BDg1MYDInDyGvy+bSH8OuCX5nsue7omH6Y2eidCGTTDYRPxDmq9tsRJxnTUepoMAvWX+1sTwZ4JqTFmc1z8Mw== @@ -4539,16 +4532,6 @@ "@opentelemetry/resources" "1.4.0" lodash.merge "4.6.2" -"@opentelemetry/sdk-metrics-base@^0.29.2": - version "0.29.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.29.2.tgz#bd515455f1d90e211458dcf957f0ae937772b155" - integrity sha512-7hhhZ/6YRRgAXOUTeCsbe6SIk3wZAdAHnEwGGp7aiVH5AOyioHyHInw4EHtowlD6dbLxUWURjh6k+Geht2zbxg== - dependencies: - "@opentelemetry/api-metrics" "0.29.2" - "@opentelemetry/core" "1.3.1" - "@opentelemetry/resources" "1.3.1" - lodash.merge "4.6.2" - "@opentelemetry/sdk-trace-base@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.4.0.tgz#e54d09c1258cd53d3fe726053ed1cbda9d74f023" From f4340ed85ec0ef18c459b4bda77fd310f5bd5b7b Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 12 Jul 2022 10:40:30 +0200 Subject: [PATCH 07/16] Rename PrometheusExporter properties --- .../server/lib/prometheus_exporter.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts index f1d2d1ffd4fb3..fc4359609bf34 100644 --- a/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts +++ b/x-pack/plugins/monitoring_collection/server/lib/prometheus_exporter.ts @@ -14,19 +14,19 @@ import { import { KibanaResponseFactory } from '@kbn/core/server'; export class PrometheusExporter extends MetricReader { - private readonly _prefix?: string; - private readonly _appendTimestamp: boolean; - private _serializer: PrometheusSerializer; + private readonly prefix?: string; + private readonly appendTimestamp: boolean; + private serializer: PrometheusSerializer; constructor(config: ExporterConfig = {}) { super(); - this._prefix = config.prefix || OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.prefix; - this._appendTimestamp = + this.prefix = config.prefix || OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.prefix; + this.appendTimestamp = typeof config.appendTimestamp === 'boolean' ? config.appendTimestamp : OpenTelemetryPrometheusExporter.DEFAULT_OPTIONS.appendTimestamp; - this._serializer = new PrometheusSerializer(this._prefix, this._appendTimestamp); + this.serializer = new PrometheusSerializer(this.prefix, this.appendTimestamp); } selectAggregationTemporality(): AggregationTemporality { @@ -54,7 +54,7 @@ export class PrometheusExporter extends MetricReader { body: `PrometheusExporter: Metrics collection errors ${errors}`, }); } - const result = this._serializer.serialize(resourceMetrics); + const result = this.serializer.serialize(resourceMetrics); if (result === '') { return res.noContent(); } From 67e5a648ceeac632d8eebcd84a3b2cc59c7b0f06 Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Wed, 13 Jul 2022 14:40:21 +0900 Subject: [PATCH 08/16] Readme formatting tweaks --- .../plugins/monitoring_collection/README.md | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index 4cc0431bf23b8..04408532e5e91 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -12,17 +12,16 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu 2. Start Kibana 3. Set up a new agent policy and enroll a new agent in your local machine 4. Install the Prometheus Metrics package - a. Set **Hosts** with `localhost:5601` - b. Set **Metrics Path** with `/(BASEPATH)/api/monitoring_collection/v1/prometheus` - c. Remove the values from **Bearer Token File** and **SSL Certificate Authorities** - d. Set **Username** and **Password** with `elastic` and `changeme` + 1. Set **Hosts** with `localhost:5601` + 2. Set **Metrics Path** with `/(BASEPATH)/api/monitoring_collection/v1/prometheus` + 3. Remove the values from **Bearer Token File** and **SSL Certificate Authorities** + 4. Set **Username** and **Password** with `elastic` and `changeme` 5. Add the following configuration to `kibana.dev.yml` ```yml # Enable the prometheus exporter monitoring_collection.opentelemetry.metrics: prometheus.enabled: true - ``` ### Enable OpenTelemetry Metrics API exported as OpenTelemetry Protocol @@ -54,11 +53,12 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu description: 'Counts total number of requests', }); } + } ``` In this example we're using a `Counter` metric, but [OpenTelemetry SDK](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api_metrics.Meter.html) provides there are other options to record metrics -* Initialize meter in the plugin setup and pass it to the relevant components that will be instrumented. In this case, we want to intrument `FooApi` routes +* Initialize meter in the plugin setup and pass it to the relevant components that will be instrumented. In this case, we want to instrument `FooApi` routes. ```ts import { IRouter } from '@kbn/core/server'; @@ -86,13 +86,13 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu } ``` - `monitoring_collection` plugins has to be initialized before the plugin that will be instrumented. If for some reason the instrumentation doesn't record any metrics, make sure `monitoring_collection` is included in the list of `requiredPlugins`. e.g: + `monitoring_collection` plugins has to be initialized before the plugin that will be instrumented. If for some reason the instrumentation doesn't record any metrics, make sure `monitoring_collection` is included in the list of `requiredPlugins`. e.g: - ```json - "requiredPlugins": [ - "monitoringCollection" - ], - ``` + ```json + "requiredPlugins": [ + "monitoringCollection" + ], + ``` * Lastly we can use the `metrics` object to instrument the code @@ -108,5 +108,4 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu return res.ok({}); } ); - ``` - + ``` \ No newline at end of file From 6812f29cd4ff5fee62f7f47bad7fe4b3b98d8f0f Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Wed, 13 Jul 2022 14:56:13 +0900 Subject: [PATCH 09/16] Fix incorrect path --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a6094feb2e476..691b426eb9e6a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -116,7 +116,7 @@ /x-pack/plugins/monitoring/ @elastic/infra-monitoring-ui /x-pack/test/functional/apps/monitoring @elastic/infra-monitoring-ui /x-pack/test/api_integration/apis/monitoring @elastic/infra-monitoring-ui -/x-pack/test/api_integration/apis/monitoring-collection @elastic/infra-monitoring-ui +/x-pack/test/api_integration/apis/monitoring_collection @elastic/infra-monitoring-ui # Fleet /fleet_packages.json @elastic/fleet From b716f3a7bb42deae04ffb12e1408aac22187aa7b Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Wed, 13 Jul 2022 15:09:11 +0900 Subject: [PATCH 10/16] Remove grpc dependency --- package.json | 7 ++- .../monitoring_collection/server/plugin.ts | 15 +----- yarn.lock | 51 ++++--------------- 3 files changed, 14 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 603ca105b8956..97cc41e62c21b 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,6 @@ "@emotion/css": "^11.9.0", "@emotion/react": "^11.9.0", "@emotion/serialize": "^1.0.3", - "@grpc/grpc-js": "^1.5.9", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.4", "@hapi/cookie": "^11.0.2", @@ -275,12 +274,12 @@ "@mapbox/mapbox-gl-draw": "1.3.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mapbox/vector-tile": "1.3.1", - "@opentelemetry/api-metrics": "0.30.0", + "@opentelemetry/api-metrics": "^0.30.0", "@opentelemetry/exporter-metrics-otlp-grpc": "^0.30.0", "@opentelemetry/exporter-prometheus": "^0.30.0", - "@opentelemetry/resources": "^1.3.1", + "@opentelemetry/resources": "^1.4.0", "@opentelemetry/sdk-metrics-base": "^0.30.0", - "@opentelemetry/semantic-conventions": "^1.3.1", + "@opentelemetry/semantic-conventions": "^1.4.0", "@reduxjs/toolkit": "^1.6.1", "@slack/webhook": "^5.0.4", "@turf/along": "6.0.1", diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 433b5a3d22e63..598abff86eeb5 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -19,7 +19,6 @@ import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import * as grpc from '@grpc/grpc-js'; import { PrometheusExporter } from './lib/prometheus_exporter'; import { MonitoringCollectionConfig } from './config'; import { registerDynamicRoute, registerV1PrometheusRoute, PROMETHEUS_PATH } from './routes'; @@ -131,22 +130,10 @@ export class MonitoringCollectionPlugin implements Plugin=12.12.47" -"@grpc/proto-loader@^0.6.4": - version "0.6.12" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.12.tgz#459b619b8b9b67794bf0d1cb819653a38c63e164" - integrity sha512-filTVbETFnxb9CyRX98zN18ilChTuf/C5scZ2xyaOTp0EHGq0/ufX8rjqXUcSb1Gpv7eZq4M2jDvbh9BogKnrg== - dependencies: - "@types/long" "^4.0.1" - lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.10.0" - yargs "^16.2.0" - -"@grpc/proto-loader@^0.6.9": +"@grpc/proto-loader@^0.6.4", "@grpc/proto-loader@^0.6.9": version "0.6.13" resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== @@ -4421,7 +4410,7 @@ "@mattiasbuelens/web-streams-adapter" "~0.1.0" web-streams-polyfill "~3.0.3" -"@opentelemetry/api-metrics@0.30.0": +"@opentelemetry/api-metrics@0.30.0", "@opentelemetry/api-metrics@^0.30.0": version "0.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-metrics/-/api-metrics-0.30.0.tgz#b5defd10756e81d1c7ce8669ff8a8d2465ba0be8" integrity sha512-jSb7iiYPY+DSUKIyzfGt0a5K1QGzWY5fSWtUB8Alfi27NhQGHBeuYYC5n9MaBP/HNWw5GpEIhXGEYCF9Pf8IEg== @@ -4433,13 +4422,6 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== -"@opentelemetry/core@1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.3.1.tgz#6eef5c5efca9a4cd7daa0cd4c7ff28ca2317c8d7" - integrity sha512-k7lOC86N7WIyUZsUuSKZfFIrUtINtlauMGQsC1r7jNmcr0vVJGqK1ROBvt7WWMxLbpMnt1q2pXJO8tKu0b9auA== - dependencies: - "@opentelemetry/semantic-conventions" "1.3.1" - "@opentelemetry/core@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.4.0.tgz#26839ab9e36583a174273a1e1c5b33336c163725" @@ -4510,7 +4492,7 @@ "@opentelemetry/sdk-metrics-base" "0.30.0" "@opentelemetry/sdk-trace-base" "1.4.0" -"@opentelemetry/resources@1.4.0": +"@opentelemetry/resources@1.4.0", "@opentelemetry/resources@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.4.0.tgz#5e23b0d7976158861059dec17e0ee36a35a5ab85" integrity sha512-Q3pI5+pCM+Ur7YwK9GbG89UBipwJbfmuzSPAXTw964ZHFzSrz+JAgrETC9rqsUOYdUlj/V7LbRMG5bo72xE0Xw== @@ -4518,14 +4500,6 @@ "@opentelemetry/core" "1.4.0" "@opentelemetry/semantic-conventions" "1.4.0" -"@opentelemetry/resources@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.3.1.tgz#9fd85ac4ffeefc35441404b384d5c1db8b243121" - integrity sha512-X8bl3X0YjlsHWy0Iv0KUETtZuRUznX4yr1iScKCtfy8AoRfZFc2xxWKMDJ0TrqYwSapgeg4YwpmRzUKmmnrbeA== - dependencies: - "@opentelemetry/core" "1.3.1" - "@opentelemetry/semantic-conventions" "1.3.1" - "@opentelemetry/sdk-metrics-base@0.30.0", "@opentelemetry/sdk-metrics-base@^0.30.0": version "0.30.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics-base/-/sdk-metrics-base-0.30.0.tgz#242d9260a89a1ac2bf1e167b3fda758f3883c769" @@ -4545,12 +4519,7 @@ "@opentelemetry/resources" "1.4.0" "@opentelemetry/semantic-conventions" "1.4.0" -"@opentelemetry/semantic-conventions@1.3.1", "@opentelemetry/semantic-conventions@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" - integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== - -"@opentelemetry/semantic-conventions@1.4.0": +"@opentelemetry/semantic-conventions@1.4.0", "@opentelemetry/semantic-conventions@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.4.0.tgz#facf2c67d6063b9918d5a5e3fdf25f3a30d547b6" integrity sha512-Hzl8soGpmyzja9w3kiFFcYJ7n5HNETpplY6cb67KR4QPlxp4FTTresO06qXHgHDhyIInmbLJXuwARjjpsKYGuQ== @@ -6930,10 +6899,6 @@ version "0.0.0" uid "" -"@types/kbn__core-http-server@link:bazel-bin/packages/core/http/core-http-server/npm_module_types": - version "0.0.0" - uid "" - "@types/kbn__core-http-browser-internal@link:bazel-bin/packages/core/http/core-http-browser-internal/npm_module_types": version "0.0.0" uid "" @@ -6950,6 +6915,10 @@ version "0.0.0" uid "" +"@types/kbn__core-http-server@link:bazel-bin/packages/core/http/core-http-server/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__core-i18n-browser-internal@link:bazel-bin/packages/core/i18n/core-i18n-browser-internal/npm_module_types": version "0.0.0" uid "" @@ -24037,7 +24006,7 @@ protobufjs@6.8.8: "@types/node" "^10.1.0" long "^4.0.0" -protobufjs@^6.10.0, protobufjs@^6.11.3: +protobufjs@^6.11.3: version "6.11.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== @@ -31008,7 +30977,7 @@ yargs-unparser@2.0.0: yargs@16.2.0, yargs@^16.2.0: version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: cliui "^7.0.2" From 92a0862fe33d2b1a82fc982aebe2e5eef6c7bdf8 Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Wed, 13 Jul 2022 15:56:43 +0900 Subject: [PATCH 11/16] Add grpc back for handling auth headers --- package.json | 1 + .../plugins/monitoring_collection/server/plugin.ts | 13 +++++++++++-- yarn.lock | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 97cc41e62c21b..e9687d0f3e412 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@emotion/css": "^11.9.0", "@emotion/react": "^11.9.0", "@emotion/serialize": "^1.0.3", + "@grpc/grpc-js": "^1.6.7", "@hapi/accept": "^5.0.2", "@hapi/boom": "^9.1.4", "@hapi/cookie": "^11.0.2", diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 598abff86eeb5..35051b05c8076 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -19,6 +19,7 @@ import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import * as grpc from '@grpc/grpc-js'; import { PrometheusExporter } from './lib/prometheus_exporter'; import { MonitoringCollectionConfig } from './config'; import { registerDynamicRoute, registerV1PrometheusRoute, PROMETHEUS_PATH } from './routes'; @@ -128,12 +129,20 @@ export class MonitoringCollectionPlugin implements Plugin Date: Wed, 13 Jul 2022 15:58:41 +0900 Subject: [PATCH 12/16] Fix comment positioning --- x-pack/plugins/monitoring_collection/server/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 35051b05c8076..e0a0fc76a23ac 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -128,6 +128,7 @@ export class MonitoringCollectionPlugin implements Plugin Date: Wed, 13 Jul 2022 16:04:31 +0900 Subject: [PATCH 13/16] Include authenticated OTLP in readme --- .../plugins/monitoring_collection/README.md | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index 04408532e5e91..13b8396f01b3e 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -24,7 +24,7 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu prometheus.enabled: true ``` -### Enable OpenTelemetry Metrics API exported as OpenTelemetry Protocol +### Enable OpenTelemetry Metrics API exported as OpenTelemetry Protocol over GRPC 1. Start [local setup with fleet](../fleet/README.md#running-fleet-server-locally-in-a-container) or a cloud cluster 2. Start Kibana @@ -38,6 +38,26 @@ This plugin allows for other plugins to add data to Kibana stack monitoring docu otlp.url: "http://127.0.0.1:8200" ``` +You can also provide headers for OTLP endpoints that require authentication: + +```yml +# Enable the OTLP exporter to an authenticated APM endpoint +monitoring_collection.opentelemetry.metrics: + otlp: + url: "https://DEPLOYMENT.apm.REGION.PROVIDER.elastic-cloud.com" + headers: + Authorization: "Bearer SECRET_TOKEN" +``` + +For connection-level debug information you can set these variables: + +```bash +export GRPC_NODE_TRACE="xds_client,xds_resolver,cds_balancer,eds_balancer,priority,weighted_target,round_robin,resolving_load_balancer,subchannel,keepalive,dns_resolver,fault_injection,http_filter,csds" +export GRPC_NODE_VERBOSITY=DEBUG +``` + +See the [grpc-node docs](https://github.com/grpc/grpc-node/blob/master/doc/environment_variables.md) for details and other settings. + ### Example of how to instrument the code * First, we need to define what metrics we want to instrument with OpenTelemetry From 533bf453e99f6e09926cb7b633ff64b5d4f221c8 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Wed, 13 Jul 2022 10:03:05 +0200 Subject: [PATCH 14/16] Extract dynamic route into a new file --- .../otel_metrics/server/monitoring/metrics.ts | 4 +- .../server/routes/generate_otel_metrics.ts | 2 +- ...ex.test.ts => get_metrics_by_type.test.ts} | 4 +- .../v1/dynamic_route/get_metrics_by_type.ts | 67 +++++++++++++++++++ .../routes/api/v1/dynamic_route/index.ts | 62 +---------------- 5 files changed, 73 insertions(+), 66 deletions(-) rename x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/{index.test.ts => get_metrics_by_type.test.ts} (97%) create mode 100644 x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.ts diff --git a/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts b/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts index ce9742c6edfc3..044cd7bee5441 100644 --- a/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts +++ b/test/common/fixtures/plugins/otel_metrics/server/monitoring/metrics.ts @@ -9,10 +9,10 @@ import { Counter, Meter } from '@opentelemetry/api-metrics'; export class Metrics { - ruleExecutions: Counter; + requestCounter: Counter; constructor(meter: Meter) { - this.ruleExecutions = meter.createCounter('request_count', { + this.requestCounter = meter.createCounter('request_count', { description: 'Counts total number of requests', }); } diff --git a/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts b/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts index 807e7e852f54d..6809059ca1472 100644 --- a/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts +++ b/test/common/fixtures/plugins/otel_metrics/server/routes/generate_otel_metrics.ts @@ -16,7 +16,7 @@ export const generateOtelMetrics = (router: IRouter, metrics: Metrics) => { validate: {}, }, async function (_context, _req, res): Promise> { - metrics.ruleExecutions.add(1); + metrics.requestCounter.add(1); return res.ok({}); } ); diff --git a/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.test.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.test.ts similarity index 97% rename from x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.test.ts rename to x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.test.ts index c35f6880969c9..edc6c6cef7c8e 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.test.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.test.ts @@ -42,7 +42,7 @@ describe('dynamic route', () => { const router = httpServiceMock.createRouter(); const getMetric = async () => { - return Promise.resolve({ foo: 1 }); + return { foo: 1 }; }; registerDynamicRoute({ router, @@ -81,7 +81,7 @@ describe('dynamic route', () => { const router = httpServiceMock.createRouter(); const getMetric = async () => { - return Promise.resolve({}); + return {}; }; registerDynamicRoute({ router, config: kibanaStatsConfig, getStatus, getMetric }); diff --git a/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.ts new file mode 100644 index 0000000000000..4d18eeb6ec922 --- /dev/null +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/get_metrics_by_type.ts @@ -0,0 +1,67 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { JsonObject } from '@kbn/utility-types'; +import { schema } from '@kbn/config-schema'; +import { IRouter, ServiceStatus } from '@kbn/core/server'; +import { getESClusterUuid, getKibanaStats } from '../../../../lib'; +import { MetricResult } from '../../../../plugin'; +import { MONITORING_COLLECTION_BASE_PATH } from '../../../../constants'; + +export function registerDynamicRoute({ + router, + config, + getStatus, + getMetric, +}: { + router: IRouter; + config: { + kibanaIndex: string; + kibanaVersion: string; + uuid: string; + server: { + name: string; + hostname: string; + port: number; + }; + }; + getStatus: () => ServiceStatus | undefined; + getMetric: ( + type: string + ) => Promise> | MetricResult | undefined>; +}) { + router.get( + { + path: `${MONITORING_COLLECTION_BASE_PATH}/{type}`, + options: { + authRequired: true, + tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page + }, + validate: { + params: schema.object({ + type: schema.string(), + }), + }, + }, + async (context, req, res) => { + const type = req.params.type; + const esClient = (await context.core).elasticsearch.client; + const [data, clusterUuid, kibana] = await Promise.all([ + getMetric(type), + getESClusterUuid(esClient), + getKibanaStats({ config, getStatus }), + ]); + + return res.ok({ + body: { + [type]: data, + cluster_uuid: clusterUuid, + kibana, + }, + }); + } + ); +} diff --git a/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts index 4d18eeb6ec922..973d525b9a77b 100644 --- a/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts +++ b/x-pack/plugins/monitoring_collection/server/routes/api/v1/dynamic_route/index.ts @@ -4,64 +4,4 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { JsonObject } from '@kbn/utility-types'; -import { schema } from '@kbn/config-schema'; -import { IRouter, ServiceStatus } from '@kbn/core/server'; -import { getESClusterUuid, getKibanaStats } from '../../../../lib'; -import { MetricResult } from '../../../../plugin'; -import { MONITORING_COLLECTION_BASE_PATH } from '../../../../constants'; - -export function registerDynamicRoute({ - router, - config, - getStatus, - getMetric, -}: { - router: IRouter; - config: { - kibanaIndex: string; - kibanaVersion: string; - uuid: string; - server: { - name: string; - hostname: string; - port: number; - }; - }; - getStatus: () => ServiceStatus | undefined; - getMetric: ( - type: string - ) => Promise> | MetricResult | undefined>; -}) { - router.get( - { - path: `${MONITORING_COLLECTION_BASE_PATH}/{type}`, - options: { - authRequired: true, - tags: ['api'], // ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page - }, - validate: { - params: schema.object({ - type: schema.string(), - }), - }, - }, - async (context, req, res) => { - const type = req.params.type; - const esClient = (await context.core).elasticsearch.client; - const [data, clusterUuid, kibana] = await Promise.all([ - getMetric(type), - getESClusterUuid(esClient), - getKibanaStats({ config, getStatus }), - ]); - - return res.ok({ - body: { - [type]: data, - cluster_uuid: clusterUuid, - kibana, - }, - }); - } - ); -} +export * from './get_metrics_by_type'; From 9ab2f05fca24c0e4daad8c5391938823432ad531 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Wed, 13 Jul 2022 16:43:47 +0200 Subject: [PATCH 15/16] Enable otlp logging and compatibility with env vars --- package.json | 1 + .../plugins/monitoring_collection/README.md | 9 +++++++++ .../monitoring_collection/server/config.ts | 1 + .../monitoring_collection/server/plugin.ts | 20 ++++++++++++++++--- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e9687d0f3e412..99b9a6aa55da6 100644 --- a/package.json +++ b/package.json @@ -275,6 +275,7 @@ "@mapbox/mapbox-gl-draw": "1.3.0", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mapbox/vector-tile": "1.3.1", + "@opentelemetry/api": "^1.1.0", "@opentelemetry/api-metrics": "^0.30.0", "@opentelemetry/exporter-metrics-otlp-grpc": "^0.30.0", "@opentelemetry/exporter-prometheus": "^0.30.0", diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index 13b8396f01b3e..b5e8a38d16ad6 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -49,6 +49,15 @@ monitoring_collection.opentelemetry.metrics: Authorization: "Bearer SECRET_TOKEN" ``` +Alternatively, OTLP Exporter can be configured using environment variables `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`, `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. [See OTLP Exporter docs](https://opentelemetry.io/docs/reference/specification/protocol/exporter/) for details. + +It's possible to configure logging for the OTLP integration. If not informed, the default will be `info` + +```yml +monitoring_collection.opentelemetry.metrics: + logLevel: warn | info | debug | warn | none | verbose | all +``` + For connection-level debug information you can set these variables: ```bash diff --git a/x-pack/plugins/monitoring_collection/server/config.ts b/x-pack/plugins/monitoring_collection/server/config.ts index 6efe92986ddd5..5eda950ebe7f1 100644 --- a/x-pack/plugins/monitoring_collection/server/config.ts +++ b/x-pack/plugins/monitoring_collection/server/config.ts @@ -15,6 +15,7 @@ export const configSchema = schema.object({ url: schema.maybe(schema.string()), headers: schema.maybe(schema.recordOf(schema.string(), schema.string())), exportIntervalMillis: schema.number({ defaultValue: 10000 }), + logLevel: schema.string({ defaultValue: 'info' }), }), prometheus: schema.object({ enabled: schema.boolean({ defaultValue: false }), diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index e0a0fc76a23ac..818dd81a9c303 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -18,6 +18,7 @@ import { metrics } from '@opentelemetry/api-metrics'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-base'; import { Resource } from '@opentelemetry/resources'; +import { diag, DiagLogger, DiagLogLevel } from '@opentelemetry/api'; import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import * as grpc from '@grpc/grpc-js'; import { PrometheusExporter } from './lib/prometheus_exporter'; @@ -41,6 +42,7 @@ export class MonitoringCollectionPlugin implements Plugin> = {}; @@ -50,6 +52,14 @@ export class MonitoringCollectionPlugin implements Plugin this.logger.debug(message), + error: (message) => this.logger.error(message), + info: (message) => this.logger.info(message), + warn: (message) => this.logger.warn(message), + verbose: (message) => this.logger.trace(message), + }; } async getMetric(type: string) { @@ -127,11 +137,12 @@ export class MonitoringCollectionPlugin implements Plugin Date: Thu, 14 Jul 2022 10:15:00 +0200 Subject: [PATCH 16/16] Enable OTEL_EXPORTER_OTLP_ENDPOINT env var --- x-pack/plugins/monitoring_collection/README.md | 2 +- x-pack/plugins/monitoring_collection/server/plugin.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/monitoring_collection/README.md b/x-pack/plugins/monitoring_collection/README.md index b5e8a38d16ad6..1f9cadf40ee7b 100644 --- a/x-pack/plugins/monitoring_collection/README.md +++ b/x-pack/plugins/monitoring_collection/README.md @@ -49,7 +49,7 @@ monitoring_collection.opentelemetry.metrics: Authorization: "Bearer SECRET_TOKEN" ``` -Alternatively, OTLP Exporter can be configured using environment variables `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`, `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. [See OTLP Exporter docs](https://opentelemetry.io/docs/reference/specification/protocol/exporter/) for details. +Alternatively, OTLP Exporter can be configured using environment variables `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` and `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. [See OTLP Exporter docs](https://opentelemetry.io/docs/reference/specification/protocol/exporter/) for details. It's possible to configure logging for the OTLP integration. If not informed, the default will be `info` diff --git a/x-pack/plugins/monitoring_collection/server/plugin.ts b/x-pack/plugins/monitoring_collection/server/plugin.ts index 818dd81a9c303..1c30a8439cf3c 100644 --- a/x-pack/plugins/monitoring_collection/server/plugin.ts +++ b/x-pack/plugins/monitoring_collection/server/plugin.ts @@ -137,12 +137,15 @@ export class MonitoringCollectionPlugin implements Plugin