Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [RAC] Populate Observability alerts table with data from alerts indices (#96692) #97399

Merged
merged 1 commit into from
Apr 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/kbn-io-ts-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
export { jsonRt } from './json_rt';
export { mergeRt } from './merge_rt';
export { strictKeysRt } from './strict_keys_rt';
export { isoToEpochRt } from './iso_to_epoch_rt';
export { toNumberRt } from './to_number_rt';
export { toBooleanRt } from './to_boolean_rt';
Original file line number Diff line number Diff line change
@@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 { isoToEpochRt } from './index';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
Expand All @@ -17,9 +18,7 @@ export const isoToEpochRt = new t.Type<number, string, unknown>(
(input, context) =>
either.chain(t.string.validate(input, context), (str) => {
const epochDate = new Date(str).getTime();
return isNaN(epochDate)
? t.failure(input, context)
: t.success(epochDate);
return isNaN(epochDate) ? t.failure(input, context) : t.success(epochDate);
}),
(output) => new Date(output).toISOString()
);
Original file line number Diff line number Diff line change
@@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pageLoadAssetSize:
remoteClusters: 51327
reporting: 183418
rollup: 97204
ruleRegistry: 100000
savedObjects: 108518
savedObjectsManagement: 101836
savedObjectsTagging: 59482
Expand Down
25 changes: 25 additions & 0 deletions x-pack/plugins/apm/common/rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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.
*/

const plainApmRuleRegistrySettings = {
name: 'apm',
fieldMap: {
'service.environment': {
type: 'keyword',
},
'transaction.type': {
type: 'keyword',
},
'processor.event': {
type: 'keyword',
},
},
} as const;

type APMRuleRegistrySettings = typeof plainApmRuleRegistrySettings;

export const apmRuleRegistrySettings: APMRuleRegistrySettings = plainApmRuleRegistrySettings;
115 changes: 106 additions & 9 deletions x-pack/plugins/apm/public/components/alerting/register_apm_alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,39 @@

import { i18n } from '@kbn/i18n';
import { lazy } from 'react';
import { format } from 'url';
import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
import { asDuration, asPercent } from '../../../common/utils/formatters';
import { AlertType } from '../../../common/alert_types';
import { ApmPluginStartDeps } from '../../plugin';
import { ApmRuleRegistry } from '../../plugin';

export function registerApmAlerts(
alertTypeRegistry: ApmPluginStartDeps['triggersActionsUi']['alertTypeRegistry']
) {
alertTypeRegistry.register({
export function registerApmAlerts(apmRuleRegistry: ApmRuleRegistry) {
apmRuleRegistry.registerType({
id: AlertType.ErrorCount,
description: i18n.translate('xpack.apm.alertTypes.errorCount.description', {
defaultMessage:
'Alert when the number of errors in a service exceeds a defined threshold.',
}),
format: ({ alert }) => {
return {
reason: i18n.translate('xpack.apm.alertTypes.errorCount.reason', {
defaultMessage: `Error count is greater than {threshold} (current value is {measured}) for {serviceName}`,
values: {
threshold: alert['kibana.observability.evaluation.threshold'],
measured: alert['kibana.observability.evaluation.value'],
serviceName: alert['service.name']!,
},
}),
link: format({
pathname: `/app/apm/services/${alert['service.name']!}`,
query: {
...(alert['service.environment']
? { environment: alert['service.environment'] }
: { environment: ENVIRONMENT_ALL.value }),
},
}),
};
},
iconClass: 'bell',
documentationUrl(docLinks) {
return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/apm-alerts.html`;
Expand All @@ -41,7 +62,7 @@ export function registerApmAlerts(
),
});

alertTypeRegistry.register({
apmRuleRegistry.registerType({
id: AlertType.TransactionDuration,
description: i18n.translate(
'xpack.apm.alertTypes.transactionDuration.description',
Expand All @@ -50,6 +71,32 @@ export function registerApmAlerts(
'Alert when the latency of a specific transaction type in a service exceeds a defined threshold.',
}
),
format: ({ alert }) => ({
reason: i18n.translate(
'xpack.apm.alertTypes.transactionDuration.reason',
{
defaultMessage: `Latency is above {threshold} (current value is {measured}) for {serviceName}`,
values: {
threshold: asDuration(
alert['kibana.observability.evaluation.threshold']
),
measured: asDuration(
alert['kibana.observability.evaluation.value']
),
serviceName: alert['service.name']!,
},
}
),
link: format({
pathname: `/app/apm/services/${alert['service.name']!}`,
query: {
transactionType: alert['transaction.type']!,
...(alert['service.environment']
? { environment: alert['service.environment'] }
: { environment: ENVIRONMENT_ALL.value }),
},
}),
}),
iconClass: 'bell',
documentationUrl(docLinks) {
return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/apm-alerts.html`;
Expand All @@ -75,7 +122,7 @@ export function registerApmAlerts(
),
});

alertTypeRegistry.register({
apmRuleRegistry.registerType({
id: AlertType.TransactionErrorRate,
description: i18n.translate(
'xpack.apm.alertTypes.transactionErrorRate.description',
Expand All @@ -84,6 +131,34 @@ export function registerApmAlerts(
'Alert when the rate of transaction errors in a service exceeds a defined threshold.',
}
),
format: ({ alert }) => ({
reason: i18n.translate(
'xpack.apm.alertTypes.transactionErrorRate.reason',
{
defaultMessage: `Transaction error rate is greater than {threshold} (current value is {measured}) for {serviceName}`,
values: {
threshold: asPercent(
alert['kibana.observability.evaluation.threshold'],
100
),
measured: asPercent(
alert['kibana.observability.evaluation.value'],
100
),
serviceName: alert['service.name']!,
},
}
),
link: format({
pathname: `/app/apm/services/${alert['service.name']!}`,
query: {
transactionType: alert['transaction.type']!,
...(alert['service.environment']
? { environment: alert['service.environment'] }
: { environment: ENVIRONMENT_ALL.value }),
},
}),
}),
iconClass: 'bell',
documentationUrl(docLinks) {
return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/apm-alerts.html`;
Expand All @@ -109,14 +184,36 @@ export function registerApmAlerts(
),
});

alertTypeRegistry.register({
apmRuleRegistry.registerType({
id: AlertType.TransactionDurationAnomaly,
description: i18n.translate(
'xpack.apm.alertTypes.transactionDurationAnomaly.description',
{
defaultMessage: 'Alert when the latency of a service is abnormal.',
}
),
format: ({ alert }) => ({
reason: i18n.translate(
'xpack.apm.alertTypes.transactionDurationAnomaly.reason',
{
defaultMessage: `{severityLevel} anomaly detected for {serviceName} (score was {measured})`,
values: {
serviceName: alert['service.name'],
severityLevel: alert['kibana.rac.alert.severity.level'],
measured: alert['kibana.observability.evaluation.value'],
},
}
),
link: format({
pathname: `/app/apm/services/${alert['service.name']!}`,
query: {
transactionType: alert['transaction.type']!,
...(alert['service.environment']
? { environment: alert['service.environment'] }
: { environment: ENVIRONMENT_ALL.value }),
},
}),
}),
iconClass: 'bell',
documentationUrl(docLinks) {
return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/apm-alerts.html`;
Expand All @@ -137,7 +234,7 @@ export function registerApmAlerts(
- Type: \\{\\{context.transactionType\\}\\}
- Environment: \\{\\{context.environment\\}\\}
- Severity threshold: \\{\\{context.threshold\\}\\}
- Severity value: \\{\\{context.thresholdValue\\}\\}
- Severity value: \\{\\{context.triggerValue\\}\\}
`,
}
),
Expand Down
20 changes: 17 additions & 3 deletions x-pack/plugins/apm/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { ConfigSchema } from '.';
import {
FetchDataParams,
FormatterRuleRegistry,
HasDataParams,
ObservabilityPublicSetup,
} from '../../observability/public';
Expand Down Expand Up @@ -40,8 +41,11 @@ import { EmbeddableStart } from '../../../../src/plugins/embeddable/public';
import { registerApmAlerts } from './components/alerting/register_apm_alerts';
import { MlPluginSetup, MlPluginStart } from '../../ml/public';
import { MapsStartApi } from '../../maps/public';
import { apmRuleRegistrySettings } from '../common/rules';

export type ApmPluginSetup = ReturnType<ApmPlugin['setup']>;
export type ApmRuleRegistry = ApmPluginSetup['ruleRegistry'];

export type ApmPluginSetup = void;
export type ApmPluginStart = void;

export interface ApmPluginSetupDeps {
Expand All @@ -52,7 +56,7 @@ export interface ApmPluginSetupDeps {
home?: HomePublicPluginSetup;
licensing: LicensingPluginSetup;
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
observability?: ObservabilityPublicSetup;
observability: ObservabilityPublicSetup;
}

export interface ApmPluginStartDeps {
Expand Down Expand Up @@ -156,6 +160,13 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
},
});

const apmRuleRegistry = plugins.observability.ruleRegistry.create({
...apmRuleRegistrySettings,
ctor: FormatterRuleRegistry,
});

registerApmAlerts(apmRuleRegistry);

core.application.register({
id: 'ux',
title: 'User Experience',
Expand Down Expand Up @@ -196,9 +207,12 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
);
},
});

return {
ruleRegistry: apmRuleRegistry,
};
}
public start(core: CoreStart, plugins: ApmPluginStartDeps) {
toggleAppLinkInNav(core, this.initializerContext.config.get());
registerApmAlerts(plugins.triggersActionsUi.alertTypeRegistry);
}
}
12 changes: 9 additions & 3 deletions x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,14 @@ async function setIgnoreChanges() {
}
}

async function deleteApmTsConfig() {
await unlink(path.resolve(kibanaRoot, 'x-pack/plugins/apm', 'tsconfig.json'));
async function deleteTsConfigs() {
const toDelete = ['apm', 'observability', 'rule_registry'];

for (const app of toDelete) {
await unlink(
path.resolve(kibanaRoot, 'x-pack/plugins', app, 'tsconfig.json')
);
}
}

async function optimizeTsConfig() {
Expand All @@ -98,7 +104,7 @@ async function optimizeTsConfig() {

await addApmFilesToTestTsConfig();

await deleteApmTsConfig();
await deleteTsConfigs();

await setIgnoreChanges();
// eslint-disable-next-line no-console
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const filesToIgnore = [
path.resolve(kibanaRoot, 'tsconfig.json'),
path.resolve(kibanaRoot, 'tsconfig.base.json'),
path.resolve(kibanaRoot, 'x-pack/plugins/apm', 'tsconfig.json'),
path.resolve(kibanaRoot, 'x-pack/plugins/observability', 'tsconfig.json'),
path.resolve(kibanaRoot, 'x-pack/plugins/rule_registry', 'tsconfig.json'),
path.resolve(kibanaRoot, 'x-pack/test', 'tsconfig.json'),
];

Expand Down
11 changes: 7 additions & 4 deletions x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ import {
} from '../../../../../../typings/elasticsearch';
import { AlertServices } from '../../../../alerting/server';

export async function alertingEsClient<TParams extends ESSearchRequest>(
export async function alertingEsClient<TParams extends ESSearchRequest>({
scopedClusterClient,
params,
}: {
scopedClusterClient: AlertServices<
never,
never,
never
>['scopedClusterClient'],
params: TParams
): Promise<ESSearchResponse<unknown, TParams>> {
>['scopedClusterClient'];
params: TParams;
}): Promise<ESSearchResponse<unknown, TParams>> {
const response = await scopedClusterClient.asCurrentUser.search({
...params,
ignore_unavailable: true,
Expand Down
Loading