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

[SLO] Alerts embeddable #169910

Merged
merged 100 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
5da91f9
Create a dummy SLO alerts embeddable
mgiota Oct 26, 2023
81c9252
group slo embeddables
mgiota Oct 26, 2023
f183702
pass triggersActionsUi as a dependency
mgiota Oct 26, 2023
aa15c70
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Oct 26, 2023
4f6dbe7
attempt to use AlertsStateTable from triggersActionsUI (temp)
mgiota Oct 27, 2023
056ab91
pass required deps and context
mgiota Nov 5, 2023
65fe769
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Nov 5, 2023
8c3c95c
select multiple slos & calculate active alerts
mgiota Nov 10, 2023
7fb4777
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Nov 10, 2023
c723f2d
style embeddable alert
mgiota Nov 10, 2023
94ab9ef
Merge branch 'main' of github.com:elastic/kibana into slo_embeddable_…
mgiota Nov 10, 2023
5a59d64
set initial dimensions to the alerts panel
mgiota Nov 10, 2023
854f76f
add badges (need styling)
mgiota Nov 10, 2023
9d0487d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 10, 2023
87faa7f
unit tests for slo summary
mgiota Nov 12, 2023
b3ad651
more unit test scenarios
mgiota Nov 13, 2023
f27a8f6
refactor unit tests active alerts mocking
mgiota Nov 13, 2023
62d906a
refactor active alerts unit tests to use an ActiveAlertsMock class
mgiota Nov 13, 2023
2b69284
move ActiveAlerts in a separate file (is place ok?) so that I do not …
mgiota Nov 13, 2023
70b0f21
TODO, more unit tests to be added
mgiota Nov 13, 2023
ecc91f9
clean up
mgiota Nov 13, 2023
83f1f90
pass missing deps to embeddable in order for alerts table to be rende…
mgiota Nov 13, 2023
385bd08
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 14, 2023
68d66ad
use shareable alerts summary widget and try to render selected slos i…
mgiota Nov 14, 2023
dfc055d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 14, 2023
7d32548
render activity chart in the alerts embeddable
mgiota Nov 14, 2023
aea5896
proper filter selected SLOs in the alerts table
mgiota Nov 15, 2023
d047623
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 15, 2023
80995f2
edit slo alerts
mgiota Nov 15, 2023
8d19534
show selected slos in the slo configuration modal
mgiota Nov 15, 2023
2ee36aa
hide last updated column passing a custom configuration
mgiota Nov 15, 2023
e5e5c0c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 15, 2023
9f97e96
fix the loading state in the slo embeddable alerts table
mgiota Nov 15, 2023
899ff79
display rule name in the alerts table
mgiota Nov 15, 2023
2320c54
honor date picker
mgiota Nov 15, 2023
70bfa4d
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 16, 2023
cb49f89
calculate alertSummaryTimeRange
mgiota Nov 16, 2023
2623b68
Restructure
mgiota Nov 16, 2023
90d96d4
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Nov 16, 2023
5328bad
Merge branch 'main' of github.com:elastic/kibana into slo_embeddable_…
mgiota Nov 16, 2023
97996e5
Merge branch 'main' of github.com:elastic/kibana into slo_embeddable_…
mgiota Nov 16, 2023
9606eb5
clean up and fix types
mgiota Nov 17, 2023
feabba9
fix types
mgiota Nov 17, 2023
1a29174
alerts header (slos included & go to alerts)
mgiota Nov 19, 2023
b7cc03a
fix CI issues
mgiota Nov 20, 2023
cf20b6c
pass isServerless to the KibanaContextProvider
mgiota Nov 20, 2023
423238d
Merge branch 'main' into slo_embeddable_alerts_table
kibanamachine Nov 20, 2023
80dd64f
remove onBrushEnd
mgiota Nov 20, 2023
7f71d87
dispatch render complete when both summary and table have loaded
mgiota Nov 20, 2023
72d3be6
check for proper license
mgiota Nov 20, 2023
931da96
remove compact slo summary component
mgiota Nov 20, 2023
56e269e
Merge branch 'main' of github.com:elastic/kibana into slo_embeddable_…
mgiota Nov 21, 2023
beeb82f
remove unused prop
mgiota Nov 21, 2023
f16c003
Merge branch 'main' into slo_embeddable_alerts_table
kibanamachine Nov 21, 2023
7688768
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Nov 28, 2023
0145bab
fix imports
shahzad31 Nov 28, 2023
a36e199
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Nov 29, 2023
59d3d55
fix types
shahzad31 Nov 29, 2023
5d64d4c
use card color util
shahzad31 Nov 29, 2023
c56a012
use shared component
shahzad31 Nov 29, 2023
62d1de9
loading while loading
shahzad31 Nov 29, 2023
a536f46
fix i18n
shahzad31 Nov 29, 2023
f9158d0
change initial dimensions of SLO overview embeddable
mgiota Nov 30, 2023
62d4242
pass kibanaVersion
mgiota Nov 30, 2023
6e2366b
Merge branch 'main' into slo_embeddable_alerts_table
kibanamachine Dec 1, 2023
83caa3c
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Dec 1, 2023
4206117
handle embeddable state
shahzad31 Dec 1, 2023
32bdc03
fix types
mgiota Dec 1, 2023
daa44d1
remove instanceId * from alerts query to fix empty results
mgiota Dec 3, 2023
a1acde7
pass all startServices as deps to alerts embeddable
mgiota Dec 3, 2023
26fec24
Merge branch 'main' into slo_embeddable_alerts_table
kibanamachine Dec 4, 2023
aef25b5
call onLoaded in the use fetch alerts hook
mgiota Dec 4, 2023
01c11ab
change alerts table configuration id
mgiota Dec 4, 2023
a8b6a1a
wrap kuery in parenthesis
mgiota Dec 4, 2023
a1a4e26
refresh alerts table on refresh button
mgiota Dec 5, 2023
2c6a4af
update
shahzad31 Dec 5, 2023
f31d0d0
fix it
shahzad31 Dec 5, 2023
9c025b2
fix styles
shahzad31 Dec 5, 2023
f4a196c
fix date picker reload for alerts table
shahzad31 Dec 5, 2023
74a9797
resolve PR feedback
shahzad31 Dec 5, 2023
30a554f
common constant PR feedback
shahzad31 Dec 5, 2023
400404a
simplify query PR feedback
shahzad31 Dec 5, 2023
2ff0c2e
some PR feedback nits
shahzad31 Dec 5, 2023
aef588d
fix query
shahzad31 Dec 5, 2023
644fa72
update
shahzad31 Dec 5, 2023
6e0bb57
clean up
shahzad31 Dec 5, 2023
184defc
increase number of SLOs to 100
mgiota Dec 5, 2023
8616182
Merge branch 'slo_embeddable_alerts_table' of https://github.com/mgio…
shahzad31 Dec 5, 2023
1c4f446
update search
shahzad31 Dec 5, 2023
fc74b7e
fix unsub
shahzad31 Dec 5, 2023
072d6ca
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Dec 5, 2023
3432e02
fix queryn
shahzad31 Dec 5, 2023
4cabd19
account for changes to slos
shahzad31 Dec 5, 2023
efbd59e
use same query in summary
shahzad31 Dec 5, 2023
4a3e44f
wrap
shahzad31 Dec 5, 2023
5919347
improve styling
shahzad31 Dec 5, 2023
e0fca14
use common selector and update query
shahzad31 Dec 5, 2023
137ff37
enable single selection
shahzad31 Dec 5, 2023
80dc85d
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Dec 5, 2023
7aba99d
Merge branch 'main' of https://github.com/elastic/kibana into slo_emb…
shahzad31 Dec 5, 2023
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
1 change: 1 addition & 0 deletions x-pack/plugins/observability/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dataViews",
"dataViewEditor",
"embeddable",
"uiActions",
mgiota marked this conversation as resolved.
Show resolved Hide resolved
"exploratoryView",
"features",
"files",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ const getRenderValue = (mappedNonEcsValue: any) => {

if (!isEmpty(value)) {
if (typeof value === 'object') {
return JSON.stringify(value);
try {
return JSON.stringify(value);
} catch (e) {
return 'Error: Unable to parse JSON value.';
}
}
return value;
}

return '—';
return '—-';
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.
*/

/**
* We need to produce types and code transpilation at different folders during the build of the package.
* We have types and code at different imports because we don't want to import the whole package in the resulting webpack bundle for the plugin.
* This way plugins can do targeted imports to reduce the final code bundle
*/
import { ALERT_DURATION, ALERT_REASON, ALERT_STATUS, ALERT_RULE_NAME } from '@kbn/rule-data-utils';
import { EuiDataGridColumn } from '@elastic/eui';
import type { ColumnHeaderOptions } from '@kbn/timelines-plugin/common';
import { i18n } from '@kbn/i18n';

/**
* columns implements a subset of `EuiDataGrid`'s `EuiDataGridColumn` interface,
* plus additional TGrid column properties
*/
export const columns: Array<
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
> = [
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate(
'xpack.observability.sloAlertsEmbeddable.alertsTGrid.statusColumnDescription',
{
defaultMessage: 'Status',
}
),
id: ALERT_STATUS,
initialWidth: 110,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.durationColumnDescription', {
defaultMessage: 'Duration',
}),
id: ALERT_DURATION,
initialWidth: 116,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.sloColumnDescription', {
defaultMessage: 'Rule name',
}),
id: ALERT_RULE_NAME,
initialWidth: 110,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.reasonColumnDescription', {
defaultMessage: 'Reason',
}),
id: ALERT_REASON,
linkField: '*',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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 type { GetRenderCellValue } from '@kbn/triggers-actions-ui-plugin/public';
import { TIMESTAMP } from '@kbn/rule-data-utils';
import { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { AlertsTableConfigurationRegistry } from '@kbn/triggers-actions-ui-plugin/public/types';
import { casesFeatureId, observabilityFeatureId } from '../../../../common';
import { getRenderCellValue } from './render_cell_value';
import { columns } from './default_columns';
import { useGetAlertFlyoutComponents } from '../../alerts_flyout/use_get_alert_flyout_components';
import type { ObservabilityRuleTypeRegistry } from '../../../rules/create_observability_rule_type_registry';
import type { ConfigSchema } from '../../../plugin';
import type { TopAlert } from '../../../typings/alerts';
import { SLO_ALERTS_TABLE_CONFID } from '../../../embeddable/slo/constants';

export const getSloAlertsTableConfiguration = (
mgiota marked this conversation as resolved.
Show resolved Hide resolved
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry,
config: ConfigSchema
): AlertsTableConfigurationRegistry => ({
id: SLO_ALERTS_TABLE_CONFID,
cases: { featureId: casesFeatureId, owner: [observabilityFeatureId] },
columns,
getRenderCellValue: (({ setFlyoutAlert }: { setFlyoutAlert: (data: TopAlert) => void }) => {
return getRenderCellValue({ observabilityRuleTypeRegistry, setFlyoutAlert });
}) as unknown as GetRenderCellValue,
sort: [
{
[TIMESTAMP]: {
order: 'desc' as SortOrder,
},
},
],

useInternalFlyout: () => {
const { header, body, footer } = useGetAlertFlyoutComponents(observabilityRuleTypeRegistry);
return { header, body, footer };
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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 { EuiLink } from '@elastic/eui';
import React from 'react';
import {
ALERT_DURATION,
ALERT_RULE_NAME,
ALERT_STATUS,
ALERT_STATUS_ACTIVE,
ALERT_STATUS_RECOVERED,
ALERT_REASON,
TIMESTAMP,
} from '@kbn/rule-data-utils';
import { isEmpty } from 'lodash';
import type {
DeprecatedCellValueElementProps,
TimelineNonEcsData,
} from '@kbn/timelines-plugin/common';

import { asDuration } from '../../../../common/utils/formatters';
import { AlertStatusIndicator } from '../../alert_status_indicator';
import { TimestampTooltip } from '../timestamp_tooltip';
import { parseAlert } from '../../../pages/alerts/helpers/parse_alert';
import type { ObservabilityRuleTypeRegistry } from '../../../rules/create_observability_rule_type_registry';
import type { TopAlert } from '../../../typings/alerts';

export const getMappedNonEcsValue = ({
data,
fieldName,
}: {
data: TimelineNonEcsData[];
fieldName: string;
}): string[] | undefined => {
const item = data.find((d) => d.field === fieldName);
if (item != null && item.value != null) {
return item.value;
}
return undefined;
};

const getRenderValue = (mappedNonEcsValue: any) => {
// can be updated when working on https://github.com/elastic/kibana/issues/140819
const value = Array.isArray(mappedNonEcsValue) ? mappedNonEcsValue.join() : mappedNonEcsValue;

if (!isEmpty(value)) {
if (typeof value === 'object') {
return JSON.stringify(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should add a try/catch here since JSON.stringify can throw an error

}
return value;
}

return '—';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit -> normally we do that

Suggested change
return '';
return '--';

};

/**
* This implementation of `EuiDataGrid`'s `renderCellValue`
* accepts `EuiDataGridCellValueElementProps`, plus `data`
* from the TGrid
*/

export const getRenderCellValue = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@mgiota mgiota Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maryam-saeidi Yes you are right it is a copy, with a few differences. I wanted to customize the columns of the SLO alerts table and I created a separate alert table configuration for SLO embeddable. Here are the differences with current alert table configuration

  • added a rule name column
  • removed Last updated column

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maryam-saeidi @XavierM I'll create a separate issue to address nit feedback from Xavier. @maryam-saeidi We can discuss how we better share things between alerts table in the Dashboard app and O11y alerts page

Copy link
Member

@maryam-saeidi maryam-saeidi Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is useful to have a rule name column by default for the Alerts page as well. I created a draft PR to discuss it with Vinay. (It was related to this feedback)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I saw this one. Makes sense to have a rule column in both cases!

setFlyoutAlert,
observabilityRuleTypeRegistry,
}: {
setFlyoutAlert: (data: TopAlert) => void;
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry;
}) => {
return ({ columnId, data }: DeprecatedCellValueElementProps) => {
if (!data) return null;
const mappedNonEcsValue = getMappedNonEcsValue({
data,
fieldName: columnId,
});
const value = getRenderValue(mappedNonEcsValue);

switch (columnId) {
case ALERT_STATUS:
if (value !== ALERT_STATUS_ACTIVE && value !== ALERT_STATUS_RECOVERED) {
// NOTE: This should only be needed to narrow down the type.
// Status should be either "active" or "recovered".
return null;
}
return <AlertStatusIndicator alertStatus={value} />;
case TIMESTAMP:
return <TimestampTooltip time={new Date(value ?? '').getTime()} timeUnit="milliseconds" />;
case ALERT_DURATION:
return asDuration(Number(value));
case ALERT_RULE_NAME:
return value;
case ALERT_REASON:
const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {});
const alert = parseAlert(observabilityRuleTypeRegistry)(dataFieldEs);

return (
<EuiLink
data-test-subj="o11yGetRenderCellValueLink"
css={{ display: 'contents' }}
onClick={() => setFlyoutAlert && setFlyoutAlert(alert)}
>
{alert.reason}
</EuiLink>
);
default:
return <>{value}</>;
}
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 React, { useMemo } from 'react';
import type { TimeRange } from '@kbn/es-query';
import { useSloAlertsQuery } from './slo_alerts_table';
import { getAlertSummaryTimeRange } from '../../../../utils/alert_summary_widget';
import { observabilityAlertFeatureIds } from '../../../../../common/constants';
import { useTimeBuckets } from '../../../../hooks/use_time_buckets';
import { calculateTimeRangeBucketSize } from '../../../../pages/overview/helpers/calculate_bucket_size';
import { SloEmbeddableDeps } from '../slo_alerts_embeddable';
import { SloItem } from '../types';

const DEFAULT_INTERVAL = '60s';
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm';

interface Props {
deps: SloEmbeddableDeps;
slos: SloItem[];
timeRange: TimeRange;
onLoaded?: () => void;
}

export function SloAlertsSummary({ slos, deps, timeRange, onLoaded }: Props) {
const {
charts,
triggersActionsUi: { getAlertSummaryWidget: AlertSummaryWidget },
} = deps;

const esQuery = useSloAlertsQuery(slos, timeRange);
const timeBuckets = useTimeBuckets();
const bucketSize = useMemo(
() =>
calculateTimeRangeBucketSize(
{
from: timeRange.from,
to: timeRange.to,
},
timeBuckets
),
[timeRange.from, timeRange.to, timeBuckets]
);
const alertSummaryTimeRange = useMemo(
() =>
getAlertSummaryTimeRange(
{
from: timeRange.from,
to: timeRange.to,
},
bucketSize?.intervalString ?? DEFAULT_INTERVAL,
bucketSize?.dateFormat ?? DEFAULT_DATE_FORMAT
),
[timeRange.from, timeRange.to, bucketSize]
);

const chartProps = {
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
theme: charts.theme.useChartsTheme(),
baseTheme: charts.theme.useChartsBaseTheme(),
};
return (
<AlertSummaryWidget
featureIds={observabilityAlertFeatureIds}
filter={esQuery}
timeRange={alertSummaryTimeRange}
chartProps={chartProps}
fullSize
onLoaded={() => {
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
if (onLoaded) {
onLoaded();
}
}}
/>
);
}
Loading
Loading