mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Alert details page] Using alert summary widget as the history chart (#181824)
Resolves #181475
## Summary
This PR uses the alert summary widget as a history chart, here is how
they look side by side:

Now every rule's alert details page should have the history chart, and
the specific implementation for the following rules has been removed:
- APM Latency
- Log threshold
- Custom threshold
- SLO burn rate
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5cfb994571
commit
520f19d0a2
25 changed files with 256 additions and 1061 deletions
|
@ -12,13 +12,10 @@ import {
|
|||
ALERT_END,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_INSTANCE_ID,
|
||||
ALERT_RULE_TYPE_ID,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_START,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
|
@ -34,7 +31,6 @@ import { TimeRangeMetadataContextProvider } from '../../../../context/time_range
|
|||
import { getComparisonChartTheme } from '../../../shared/time_comparison/get_comparison_chart_theme';
|
||||
import FailedTransactionChart from './failed_transaction_chart';
|
||||
import { getAggsTypeFromRule } from './helpers';
|
||||
import { LatencyAlertsHistoryChart } from './latency_alerts_history_chart';
|
||||
import LatencyChart from './latency_chart';
|
||||
import ThroughputChart from './throughput_chart';
|
||||
import { AlertDetailsAppSectionProps } from './types';
|
||||
|
@ -125,12 +121,6 @@ export function AlertDetailsAppSection({
|
|||
const latencyAggregationType = getAggsTypeFromRule(params.aggregationType);
|
||||
const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]);
|
||||
const comparisonChartTheme = getComparisonChartTheme();
|
||||
const historicalRange = useMemo(() => {
|
||||
return {
|
||||
start: moment().subtract(30, 'days').toISOString(),
|
||||
end: moment().toISOString(),
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { from, to } = timeRange;
|
||||
if (!from || !to) {
|
||||
|
@ -206,20 +196,6 @@ export function AlertDetailsAppSection({
|
|||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LatencyAlertsHistoryChart
|
||||
ruleId={alert.fields[ALERT_RULE_UUID]}
|
||||
alertInstanceId={alert.fields[ALERT_INSTANCE_ID]}
|
||||
serviceName={serviceName}
|
||||
start={historicalRange.start}
|
||||
end={historicalRange.end}
|
||||
transactionType={transactionType}
|
||||
transactionName={transactionName}
|
||||
latencyAggregationType={latencyAggregationType}
|
||||
environment={environment}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ChartPointerEventContextProvider>
|
||||
</TimeRangeMetadataContextProvider>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -1,280 +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 { AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts';
|
||||
import {
|
||||
EuiBadge,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { convertTo } from '@kbn/observability-plugin/public';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import moment from 'moment';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useAlertsHistory } from '@kbn/observability-alert-details';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { ApmDocumentType } from '../../../../../common/document_type';
|
||||
import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types';
|
||||
import { getDurationFormatter } from '../../../../../common/utils/formatters';
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size';
|
||||
import { getLatencyChartSelector } from '../../../../selectors/latency_chart_selectors';
|
||||
import { filterNil } from '../../../shared/charts/latency_chart';
|
||||
import { TimeseriesChart } from '../../../shared/charts/timeseries_chart';
|
||||
import {
|
||||
getMaxY,
|
||||
getResponseTimeTickFormatter,
|
||||
} from '../../../shared/charts/transaction_charts/helper';
|
||||
import { CHART_ANNOTATION_RED_COLOR } from './constants';
|
||||
|
||||
interface LatencyAlertsHistoryChartProps {
|
||||
serviceName: string;
|
||||
start: string;
|
||||
end: string;
|
||||
transactionType?: string;
|
||||
transactionName?: string;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
environment: string;
|
||||
timeZone: string;
|
||||
ruleId: string;
|
||||
alertInstanceId?: string;
|
||||
}
|
||||
export function LatencyAlertsHistoryChart({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
transactionType,
|
||||
transactionName,
|
||||
latencyAggregationType,
|
||||
environment,
|
||||
timeZone,
|
||||
ruleId,
|
||||
alertInstanceId,
|
||||
}: LatencyAlertsHistoryChartProps) {
|
||||
const preferred = usePreferredDataSourceAndBucketSize({
|
||||
start,
|
||||
end,
|
||||
kuery: '',
|
||||
numBuckets: 100,
|
||||
// ServiceTransactionMetric does not have transactionName as a dimension, but it is faster than TransactionMetric
|
||||
// We use TransactionMetric only when there is a transactionName
|
||||
type: transactionName
|
||||
? ApmDocumentType.TransactionMetric
|
||||
: ApmDocumentType.ServiceTransactionMetric,
|
||||
});
|
||||
const { http, notifications } = useKibana().services;
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (serviceName && start && end && transactionType && latencyAggregationType && preferred) {
|
||||
return callApmApi(`GET /internal/apm/services/{serviceName}/transactions/charts/latency`, {
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query: {
|
||||
environment,
|
||||
kuery: '',
|
||||
start,
|
||||
end,
|
||||
transactionType,
|
||||
transactionName,
|
||||
latencyAggregationType,
|
||||
bucketSizeInSeconds: preferred.bucketSizeInSeconds,
|
||||
documentType: preferred.source.documentType,
|
||||
rollupInterval: preferred.source.rollupInterval,
|
||||
useDurationSummary:
|
||||
preferred.source.hasDurationSummaryField &&
|
||||
latencyAggregationType === LatencyAggregationType.avg,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
end,
|
||||
environment,
|
||||
latencyAggregationType,
|
||||
serviceName,
|
||||
start,
|
||||
transactionName,
|
||||
transactionType,
|
||||
preferred,
|
||||
]
|
||||
);
|
||||
const memoizedData = useMemo(
|
||||
() =>
|
||||
getLatencyChartSelector({
|
||||
latencyChart: data,
|
||||
latencyAggregationType,
|
||||
previousPeriodLabel: '',
|
||||
}),
|
||||
// It should only update when the data has changed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[data]
|
||||
);
|
||||
|
||||
const { currentPeriod, previousPeriod } = memoizedData;
|
||||
const timeseriesLatency = [currentPeriod, previousPeriod].filter(filterNil);
|
||||
const latencyMaxY = getMaxY(timeseriesLatency);
|
||||
const latencyFormatter = getDurationFormatter(latencyMaxY);
|
||||
const {
|
||||
data: { totalTriggeredAlerts, avgTimeToRecoverUS, histogramTriggeredAlerts },
|
||||
isError,
|
||||
isLoading,
|
||||
} = useAlertsHistory({
|
||||
http,
|
||||
featureIds: [AlertConsumers.APM],
|
||||
ruleId,
|
||||
dateRange: { from: start, to: end },
|
||||
instanceId: alertInstanceId,
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
notifications?.toasts.addDanger({
|
||||
title: i18n.translate('xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastTitle', {
|
||||
defaultMessage: 'Latency alerts history chart error',
|
||||
}),
|
||||
text: i18n.translate(
|
||||
'xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastDescription',
|
||||
{
|
||||
defaultMessage: `An error occurred when fetching latency alert history chart data for {serviceName}`,
|
||||
values: { serviceName },
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{serviceName}
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.chartTitle', {
|
||||
defaultMessage: ' latency alerts history',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.last30days', {
|
||||
defaultMessage: 'Last 30 days',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="danger">
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? <EuiLoadingSpinner size="s" /> : totalTriggeredAlerts || '-'}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.alertsTriggered', {
|
||||
defaultMessage: 'Alerts triggered',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : avgTimeToRecoverUS ? (
|
||||
convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.avgTimeToRecover', {
|
||||
defaultMessage: 'Avg time to recover',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<TimeseriesChart
|
||||
id="latencyChart"
|
||||
annotations={[
|
||||
<LineAnnotation
|
||||
id="annotations"
|
||||
key={'annotationsAlertHistory'}
|
||||
domainType={AnnotationDomainType.XDomain}
|
||||
dataValues={
|
||||
histogramTriggeredAlerts
|
||||
?.filter((annotation) => annotation.doc_count > 0)
|
||||
.map((annotation) => {
|
||||
return {
|
||||
dataValue: annotation.key,
|
||||
header: String(annotation.doc_count),
|
||||
details: moment(annotation.key_as_string).format('yyyy-MM-DD'),
|
||||
};
|
||||
}) || []
|
||||
}
|
||||
style={{
|
||||
line: {
|
||||
strokeWidth: 3,
|
||||
stroke: CHART_ANNOTATION_RED_COLOR,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
marker={<EuiIcon type="warning" color={CHART_ANNOTATION_RED_COLOR} />}
|
||||
markerBody={(annotationData) => (
|
||||
<>
|
||||
<EuiBadge color={CHART_ANNOTATION_RED_COLOR}>
|
||||
<EuiText size="xs" color="white">
|
||||
{annotationData.header}
|
||||
</EuiText>
|
||||
</EuiBadge>
|
||||
<EuiSpacer size="xs" />
|
||||
</>
|
||||
)}
|
||||
markerPosition={Position.Top}
|
||||
/>,
|
||||
]}
|
||||
height={200}
|
||||
comparisonEnabled={false}
|
||||
offset={''}
|
||||
fetchStatus={status}
|
||||
timeseries={timeseriesLatency}
|
||||
yLabelFormat={getResponseTimeTickFormatter(latencyFormatter)}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -1,205 +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 moment from 'moment';
|
||||
import React from 'react';
|
||||
import { Rule } from '@kbn/alerting-plugin/common';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiLoadingSpinner,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { convertTo } from '@kbn/observability-plugin/public';
|
||||
import { AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts';
|
||||
import { EuiIcon, EuiBadge } from '@elastic/eui';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import DateMath from '@kbn/datemath';
|
||||
import { useAlertsHistory } from '@kbn/observability-alert-details';
|
||||
import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana';
|
||||
import { type PartialCriterion } from '../../../../../../common/alerting/logs/log_threshold';
|
||||
import { CriterionPreview } from '../../expression_editor/criterion_preview_chart';
|
||||
import { PartialRuleParams } from '../../../../../../common/alerting/logs/log_threshold';
|
||||
import type { Group } from '../types';
|
||||
|
||||
const LogsHistoryChart = ({
|
||||
rule,
|
||||
instanceId,
|
||||
groups,
|
||||
}: {
|
||||
rule: Rule<PartialRuleParams>;
|
||||
instanceId?: string;
|
||||
groups?: Group[];
|
||||
}) => {
|
||||
const { http, notifications } = useKibanaContextForPlugin().services;
|
||||
// Show the Logs History Chart ONLY if we have one criteria
|
||||
// So always pull the first criteria
|
||||
const criteria = rule.params.criteria[0];
|
||||
|
||||
const dateRange = {
|
||||
from: 'now-30d',
|
||||
to: 'now',
|
||||
};
|
||||
const executionTimeRange = {
|
||||
gte: DateMath.parse(dateRange.from)!.valueOf(),
|
||||
lte: DateMath.parse(dateRange.to, { roundUp: true })!.valueOf(),
|
||||
buckets: 30,
|
||||
};
|
||||
|
||||
const {
|
||||
data: { histogramTriggeredAlerts, avgTimeToRecoverUS, totalTriggeredAlerts },
|
||||
isLoading,
|
||||
isError,
|
||||
} = useAlertsHistory({
|
||||
http,
|
||||
featureIds: [AlertConsumers.LOGS],
|
||||
ruleId: rule.id,
|
||||
dateRange,
|
||||
instanceId,
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
notifications?.toasts.addDanger({
|
||||
title: i18n.translate('xpack.infra.alertDetails.logsAlertHistoryChart.error.toastTitle', {
|
||||
defaultMessage: 'Logs alerts history chart error',
|
||||
}),
|
||||
text: i18n.translate(
|
||||
'xpack.infra.alertDetails.logsAlertHistoryChart.error.toastDescription',
|
||||
{
|
||||
defaultMessage: `An error occurred when fetching logs alert history chart data`,
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
const alertHistoryAnnotations =
|
||||
histogramTriggeredAlerts
|
||||
?.filter((annotation) => annotation.doc_count > 0)
|
||||
.map((annotation) => {
|
||||
return {
|
||||
dataValue: annotation.key,
|
||||
header: String(annotation.doc_count),
|
||||
// Only the date(without time) is needed here, uiSettings don't provide that
|
||||
details: moment(annotation.key_as_string).format('yyyy-MM-DD'),
|
||||
};
|
||||
}) || [];
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true} data-test-subj="logsHistoryChartAlertDetails">
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chartHistory.chartTitle', {
|
||||
defaultMessage: 'Logs threshold alerts history',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chartHistory.last30days', {
|
||||
defaultMessage: 'Last 30 days',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="danger">
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? <EuiLoadingSpinner size="s" /> : totalTriggeredAlerts || '-'}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chartHistory.alertsTriggered', {
|
||||
defaultMessage: 'Alerts triggered',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : avgTimeToRecoverUS ? (
|
||||
convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chartHistory.avgTimeToRecover', {
|
||||
defaultMessage: 'Avg time to recover',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<CriterionPreview
|
||||
annotations={[
|
||||
<LineAnnotation
|
||||
id="annotations"
|
||||
key={'annotationsAlertHistory'}
|
||||
domainType={AnnotationDomainType.XDomain}
|
||||
dataValues={alertHistoryAnnotations}
|
||||
style={{
|
||||
line: {
|
||||
strokeWidth: 3,
|
||||
stroke: euiThemeVars.euiColorDangerText,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
marker={<EuiIcon type="warning" color="danger" />}
|
||||
markerBody={(annotationData) => (
|
||||
<>
|
||||
<EuiBadge color="danger">
|
||||
<EuiText size="xs" color="white">
|
||||
{annotationData.header}
|
||||
</EuiText>
|
||||
</EuiBadge>
|
||||
<EuiSpacer size="xs" />
|
||||
</>
|
||||
)}
|
||||
markerPosition={Position.Top}
|
||||
/>,
|
||||
]}
|
||||
ruleParams={{ ...rule.params, timeSize: 1, timeUnit: 'd' }}
|
||||
logViewReference={rule.params.logView}
|
||||
chartCriterion={criteria as PartialCriterion}
|
||||
showThreshold={true}
|
||||
executionTimeRange={executionTimeRange}
|
||||
filterSeriesByGroupName={groups?.map((group) => group.value).join(', ')}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default LogsHistoryChart;
|
|
@ -12,7 +12,6 @@ import {
|
|||
ALERT_CONTEXT,
|
||||
ALERT_END,
|
||||
ALERT_EVALUATION_VALUE,
|
||||
ALERT_INSTANCE_ID,
|
||||
ALERT_START,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import moment from 'moment';
|
||||
|
@ -35,9 +34,7 @@ import { Threshold } from '../../../common/components/threshold';
|
|||
import { LogRateAnalysis } from './components/log_rate_analysis';
|
||||
import { LogThresholdCountChart, LogThresholdRatioChart } from './components/threhsold_chart';
|
||||
import { useLicense } from '../../../../hooks/use_license';
|
||||
import type { Group } from './types';
|
||||
|
||||
const LogsHistoryChart = React.lazy(() => import('./components/logs_history_chart'));
|
||||
const formatThreshold = (threshold: number) => String(threshold);
|
||||
|
||||
const AlertDetailsAppSection = ({
|
||||
|
@ -65,16 +62,6 @@ const AlertDetailsAppSection = ({
|
|||
.filter(identity)
|
||||
.join(' AND ')
|
||||
: '';
|
||||
const groups: Group[] | undefined = rule.params.groupBy
|
||||
? rule.params.groupBy.flatMap((field) => {
|
||||
const value: string = get(
|
||||
alert.fields[ALERT_CONTEXT],
|
||||
['groupByKeys', ...field.split('.')],
|
||||
null
|
||||
);
|
||||
return value ? { field, value } : [];
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const { derivedDataView } = useLogView({
|
||||
initialLogViewReference: rule.params.logView,
|
||||
|
@ -236,24 +223,6 @@ const AlertDetailsAppSection = ({
|
|||
} else return null;
|
||||
};
|
||||
|
||||
const getLogsHistoryChart = () => {
|
||||
return (
|
||||
rule &&
|
||||
rule.params.criteria.length === 1 && (
|
||||
<EuiFlexItem>
|
||||
<LogsHistoryChart
|
||||
rule={{
|
||||
...rule,
|
||||
params: { ...rule.params, timeSize: 12, timeUnit: 'h' },
|
||||
}}
|
||||
instanceId={alert.fields[ALERT_INSTANCE_ID]}
|
||||
groups={groups}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const getLogRateAnalysisSection = () => {
|
||||
return hasLicenseForLogRateAnalysis ? <LogRateAnalysis rule={rule} alert={alert} /> : null;
|
||||
};
|
||||
|
@ -263,7 +232,6 @@ const AlertDetailsAppSection = ({
|
|||
{getLogRatioChart()}
|
||||
{getLogCountChart()}
|
||||
{getLogRateAnalysisSection()}
|
||||
{getLogsHistoryChart()}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -107,7 +107,7 @@ describe('AlertDetailsAppSection', () => {
|
|||
it('should render rule and alert data', async () => {
|
||||
const result = renderComponent();
|
||||
|
||||
expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(7);
|
||||
expect((await result.findByTestId('thresholdAlertOverviewSection')).children.length).toBe(6);
|
||||
expect(result.getByTestId('thresholdRule-2000-2500')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -183,7 +183,7 @@ describe('AlertDetailsAppSection', () => {
|
|||
{ ['kibana.alert.end']: '2023-03-28T14:40:00.000Z' }
|
||||
);
|
||||
|
||||
expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(7);
|
||||
expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(6);
|
||||
expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ import type {
|
|||
import moment from 'moment';
|
||||
import { LOGS_EXPLORER_LOCATOR_ID, LogsExplorerLocatorParams } from '@kbn/deeplinks-observability';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { AlertHistoryChart } from './alert_history';
|
||||
import { useLicense } from '../../../../hooks/use_license';
|
||||
import { useKibana } from '../../../../utils/kibana_react';
|
||||
import { getGroupFilters } from '../../../../../common/custom_threshold_rule/helpers/get_group';
|
||||
|
@ -293,7 +292,6 @@ export default function AlertDetailsAppSection({
|
|||
{hasLogRateAnalysisLicense && (
|
||||
<LogRateAnalysis alert={alert} dataView={dataView} rule={rule} services={services} />
|
||||
)}
|
||||
<AlertHistoryChart alert={alert} dataView={dataView} rule={rule} />
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,224 +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 moment from 'moment';
|
||||
import React, { useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { RuleTypeParams } from '@kbn/alerting-plugin/common';
|
||||
import { EventAnnotationConfig } from '@kbn/event-annotation-common';
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiLoadingSpinner,
|
||||
useEuiTheme,
|
||||
EuiSelect,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_GROUP, ALERT_INSTANCE_ID, type AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { useAlertsHistory } from '@kbn/observability-alert-details';
|
||||
import { convertTo } from '../../../../../common/utils/formatters';
|
||||
import { getGroupFilters } from '../../../../../common/custom_threshold_rule/helpers/get_group';
|
||||
import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types';
|
||||
import { useKibana } from '../../../../utils/kibana_react';
|
||||
import { AlertParams } from '../../types';
|
||||
import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart';
|
||||
import { CustomThresholdAlert, CustomThresholdRule } from '../types';
|
||||
import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and_tooltip';
|
||||
|
||||
const DEFAULT_INTERVAL = '1d';
|
||||
const SERIES_TYPE = 'bar_stacked';
|
||||
|
||||
interface Props {
|
||||
alert: CustomThresholdAlert;
|
||||
rule: CustomThresholdRule;
|
||||
dataView?: DataView;
|
||||
}
|
||||
|
||||
const dateRange = {
|
||||
from: 'now-30d',
|
||||
to: 'now+1d',
|
||||
};
|
||||
|
||||
export function AlertHistoryChart({ rule, dataView, alert }: Props) {
|
||||
const { http, notifications } = useKibana().services;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const ruleParams = rule.params as RuleTypeParams & AlertParams;
|
||||
const groups = alert.fields[ALERT_GROUP];
|
||||
const instanceId = alert.fields[ALERT_INSTANCE_ID];
|
||||
const featureIds = [rule.consumer as AlertConsumers];
|
||||
const options = rule.params.criteria.map((criterion, index) => {
|
||||
const { title, tooltip } = generateChartTitleAndTooltip(criterion, 27);
|
||||
return {
|
||||
text: title,
|
||||
title: tooltip,
|
||||
};
|
||||
});
|
||||
const [selectedCriterion, setSelectedCriterion] = useState<CustomMetricExpressionParams>(
|
||||
rule.params.criteria[0]
|
||||
);
|
||||
|
||||
const {
|
||||
data: { histogramTriggeredAlerts, avgTimeToRecoverUS, totalTriggeredAlerts },
|
||||
isLoading,
|
||||
isError,
|
||||
} = useAlertsHistory({
|
||||
http,
|
||||
featureIds,
|
||||
ruleId: rule.id,
|
||||
dateRange,
|
||||
instanceId,
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
notifications?.toasts.addDanger({
|
||||
title: i18n.translate('xpack.observability.customThreshold.alertHistory.error.toastTitle', {
|
||||
defaultMessage: 'Alerts history chart error',
|
||||
}),
|
||||
text: i18n.translate(
|
||||
'xpack.observability.customThreshold.alertHistory.error.toastDescription',
|
||||
{
|
||||
defaultMessage: `An error occurred when fetching alert history chart data`,
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const annotations: EventAnnotationConfig[] =
|
||||
histogramTriggeredAlerts
|
||||
?.filter((annotation) => annotation.doc_count > 0)
|
||||
.map((annotation) => {
|
||||
return {
|
||||
type: 'manual',
|
||||
id: uuidv4(),
|
||||
label: String(annotation.doc_count),
|
||||
key: {
|
||||
type: 'point_in_time',
|
||||
timestamp: moment(new Date(annotation.key_as_string!))
|
||||
.startOf('day')
|
||||
.add(12, 'h')
|
||||
.toISOString(),
|
||||
},
|
||||
lineWidth: 2,
|
||||
color: euiTheme.colors.danger,
|
||||
icon: 'alert',
|
||||
textVisibility: true,
|
||||
};
|
||||
}) || [];
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true} data-test-subj="AlertDetails">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate('xpack.observability.customThreshold.alertHistory.chartTitle', {
|
||||
defaultMessage: 'Alerts history',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.observability.customThreshold.alertHistory.last30days', {
|
||||
defaultMessage: 'Last 30 days',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{rule.params.criteria.length > 1 && (
|
||||
<EuiSelect
|
||||
data-test-subj="o11yAlertHistoryChartSelect"
|
||||
options={options}
|
||||
onChange={(e) =>
|
||||
setSelectedCriterion(
|
||||
rule.params.criteria[
|
||||
options.map((option) => option.text).indexOf(e.target.value) ?? 0
|
||||
]
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="danger">
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? <EuiLoadingSpinner size="s" /> : totalTriggeredAlerts || '-'}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate(
|
||||
'xpack.observability.customThreshold.alertHistory.alertsTriggered',
|
||||
{
|
||||
defaultMessage: 'Alerts triggered',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : avgTimeToRecoverUS ? (
|
||||
convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.observability.customThreshold.alertHistory.avgTimeToRecover', {
|
||||
defaultMessage: 'Avg time to recover',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<RuleConditionChart
|
||||
additionalFilters={getGroupFilters(groups)}
|
||||
annotations={annotations}
|
||||
chartOptions={{
|
||||
// For alert details page, the series type needs to be changed to 'bar_stacked'
|
||||
// due to https://github.com/elastic/elastic-charts/issues/2323
|
||||
seriesType: SERIES_TYPE,
|
||||
interval: DEFAULT_INTERVAL,
|
||||
}}
|
||||
dataView={dataView}
|
||||
groupBy={ruleParams.groupBy}
|
||||
metricExpression={selectedCriterion}
|
||||
searchConfiguration={ruleParams.searchConfiguration}
|
||||
timeRange={dateRange}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -14,12 +14,12 @@ import {
|
|||
type LogRateAnalysisType,
|
||||
} from '@kbn/aiops-log-rate-analysis/log_rate_analysis_type';
|
||||
import { LogRateAnalysisContent, type LogRateAnalysisResultsData } from '@kbn/aiops-plugin/public';
|
||||
import { Rule } from '@kbn/alerting-plugin/common';
|
||||
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { Message } from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import { ALERT_END } from '@kbn/rule-data-utils';
|
||||
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { CustomThresholdRuleTypeParams } from '../../types';
|
||||
import { TopAlert } from '../../../..';
|
||||
import { Color, colorTransformer } from '../../../../../common/custom_threshold_rule/color_palette';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Rule } from '@kbn/alerting-plugin/common';
|
||||
import type { Rule } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { TopAlert } from '../../..';
|
||||
import { CustomThresholdAlertFields, CustomThresholdRuleTypeParams } from '../types';
|
||||
|
||||
|
|
|
@ -16,12 +16,11 @@ export const buildCustomThresholdRule = (
|
|||
rule: Partial<CustomThresholdRule> = {}
|
||||
): CustomThresholdRule => {
|
||||
return {
|
||||
alertTypeId: 'metrics.alert.threshold',
|
||||
ruleTypeId: 'metrics.alert.threshold',
|
||||
createdBy: 'admin',
|
||||
updatedBy: 'admin',
|
||||
createdAt: new Date('2023-02-20T15:25:32.125Z'),
|
||||
updatedAt: new Date('2023-03-02T16:24:41.177Z'),
|
||||
apiKey: 'apiKey',
|
||||
apiKeyOwner: 'admin',
|
||||
notifyWhen: null,
|
||||
muteAll: false,
|
||||
|
|
|
@ -53,7 +53,7 @@ const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createSt
|
|||
const mockKibana = () => {
|
||||
useKibanaMock.mockReturnValue({
|
||||
services: {
|
||||
...kibanaStartMock.startContract(),
|
||||
...kibanaStartMock.startContract().services,
|
||||
cases: casesPluginMock.createStartContract(),
|
||||
application: { currentAppId$: from('mockedApp') },
|
||||
http: {
|
||||
|
@ -63,9 +63,6 @@ const mockKibana = () => {
|
|||
},
|
||||
observabilityAIAssistant: mockObservabilityAIAssistant,
|
||||
theme: {},
|
||||
triggersActionsUi: {
|
||||
ruleTypeRegistry,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -78,6 +75,7 @@ jest.mock('../../hooks/use_fetch_rule', () => {
|
|||
rule: {
|
||||
id: 'ruleId',
|
||||
name: 'ruleName',
|
||||
consumer: 'logs',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
@ -139,7 +137,7 @@ describe('Alert details', () => {
|
|||
expect(alertDetails.queryByTestId('alertDetailsError')).toBeFalsy();
|
||||
expect(alertDetails.queryByTestId('alertDetailsPageTitle')).toBeTruthy();
|
||||
expect(alertDetails.queryByTestId('alertDetailsTabbedContent')).toBeTruthy();
|
||||
expect(alertDetails.queryByTestId('alert-summary-container')).toBeTruthy();
|
||||
expect(alertDetails.queryByTestId('alert-summary-container')).toBeFalsy();
|
||||
expect(alertDetails.queryByTestId('overviewTab')).toBeTruthy();
|
||||
expect(alertDetails.queryByTestId('metadataTab')).toBeTruthy();
|
||||
});
|
||||
|
|
|
@ -44,7 +44,9 @@ import { observabilityFeatureId } from '../../../common';
|
|||
import { paths } from '../../../common/locators/paths';
|
||||
import { HeaderMenu } from '../overview/components/header_menu/header_menu';
|
||||
import { AlertOverview } from '../../components/alert_overview/alert_overview';
|
||||
import { CustomThresholdRule } from '../../components/custom_threshold/components/types';
|
||||
import { AlertDetailContextualInsights } from './alert_details_contextual_insights';
|
||||
import { AlertHistoryChart } from './components/alert_history';
|
||||
|
||||
interface AlertDetailsPathParams {
|
||||
alertId: string;
|
||||
|
@ -77,8 +79,9 @@ export function AlertDetails() {
|
|||
const [ruleTypeModel, setRuleTypeModel] = useState<RuleTypeModel | null>(null);
|
||||
const CasesContext = getCasesContext();
|
||||
const userCasesPermissions = canUseCases([observabilityFeatureId]);
|
||||
const ruleId = alertDetail?.formatted.fields[ALERT_RULE_UUID];
|
||||
const { rule } = useFetchRule({
|
||||
ruleId: alertDetail?.formatted.fields[ALERT_RULE_UUID],
|
||||
ruleId,
|
||||
});
|
||||
const [summaryFields, setSummaryFields] = useState<AlertSummaryField[]>();
|
||||
const [alertStatus, setAlertStatus] = useState<AlertStatus>();
|
||||
|
@ -165,8 +168,8 @@ export function AlertDetails() {
|
|||
const overviewTab = alertDetail ? (
|
||||
AlertDetailsAppSection &&
|
||||
/*
|
||||
when feature flag is enabled, show alert details page with customized overview tab,
|
||||
otherwise show default overview tab
|
||||
when feature flag is enabled, show alert details page with customized overview tab,
|
||||
otherwise show default overview tab
|
||||
*/
|
||||
isAlertDetailsEnabledPerApp(alertDetail.formatted, config) ? (
|
||||
<>
|
||||
|
@ -175,13 +178,20 @@ export function AlertDetails() {
|
|||
<AlertDetailContextualInsights alert={alertDetail} />
|
||||
<EuiSpacer size="l" />
|
||||
{rule && alertDetail.formatted && (
|
||||
<AlertDetailsAppSection
|
||||
alert={alertDetail.formatted}
|
||||
rule={rule}
|
||||
timeZone={timeZone}
|
||||
setAlertSummaryFields={setSummaryFields}
|
||||
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
|
||||
/>
|
||||
<>
|
||||
<AlertDetailsAppSection
|
||||
alert={alertDetail.formatted}
|
||||
rule={rule}
|
||||
timeZone={timeZone}
|
||||
setAlertSummaryFields={setSummaryFields}
|
||||
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
|
||||
/>
|
||||
<EuiSpacer size="l" />
|
||||
<AlertHistoryChart
|
||||
alert={alertDetail.formatted}
|
||||
rule={rule as unknown as CustomThresholdRule}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
@ -278,7 +288,7 @@ export function getScreenDescription(alertDetail: AlertData) {
|
|||
Use the following alert fields as background information for generating a response. Do not list them as bullet points in the response.
|
||||
${Object.entries(getRelevantAlertFields(alertDetail))
|
||||
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
|
||||
.join('\n')}
|
||||
.join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { ALERTING_FEATURE_ID } from '@kbn/alerting-plugin/common';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiLoadingSpinner,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_INSTANCE_ID, ALERT_RULE_UUID, type AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { useAlertsHistory } from '@kbn/observability-alert-details';
|
||||
import type { Rule } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { convertTo } from '../../../../common/utils/formatters';
|
||||
import { useFetchRuleTypes } from '../../../hooks/use_fetch_rule_types';
|
||||
import { useGetFilteredRuleTypes } from '../../../hooks/use_get_filtered_rule_types';
|
||||
import { useKibana } from '../../../utils/kibana_react';
|
||||
import { TopAlert } from '../../..';
|
||||
import { getDefaultAlertSummaryTimeRange } from '../../../utils/alert_summary_widget';
|
||||
|
||||
interface Props {
|
||||
alert: TopAlert;
|
||||
rule: Rule;
|
||||
}
|
||||
|
||||
const dateRange = {
|
||||
from: 'now-30d',
|
||||
to: 'now+1d',
|
||||
};
|
||||
|
||||
export function AlertHistoryChart({ rule, alert }: Props) {
|
||||
const {
|
||||
http,
|
||||
notifications,
|
||||
triggersActionsUi: { getAlertSummaryWidget: AlertSummaryWidget },
|
||||
} = useKibana().services;
|
||||
const instanceId = alert.fields[ALERT_INSTANCE_ID];
|
||||
const filteredRuleTypes = useGetFilteredRuleTypes();
|
||||
const { ruleTypes } = useFetchRuleTypes({
|
||||
filterByRuleTypeIds: filteredRuleTypes,
|
||||
});
|
||||
const ruleType = ruleTypes?.find((type) => type.id === rule?.ruleTypeId);
|
||||
const featureIds =
|
||||
rule?.consumer === ALERTING_FEATURE_ID && ruleType?.producer
|
||||
? [ruleType.producer as AlertConsumers]
|
||||
: rule
|
||||
? [rule.consumer as AlertConsumers]
|
||||
: [];
|
||||
const ruleId = alert.fields[ALERT_RULE_UUID];
|
||||
|
||||
const {
|
||||
data: { avgTimeToRecoverUS, totalTriggeredAlerts },
|
||||
isLoading,
|
||||
isError,
|
||||
} = useAlertsHistory({
|
||||
http,
|
||||
featureIds,
|
||||
ruleId: rule.id,
|
||||
dateRange,
|
||||
instanceId,
|
||||
});
|
||||
|
||||
if (isError) {
|
||||
notifications?.toasts.addDanger({
|
||||
title: i18n.translate('xpack.observability.alertDetailsPage.alertHistory.error.toastTitle', {
|
||||
defaultMessage: 'Alerts history chart error',
|
||||
}),
|
||||
text: i18n.translate(
|
||||
'xpack.observability.alertDetailsPage.alertHistory.error.toastDescription',
|
||||
{
|
||||
defaultMessage: `An error occurred when fetching alert history chart data`,
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true} data-test-subj="AlertDetails">
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate('xpack.observability.alertDetailsPage.alertHistory.chartTitle', {
|
||||
defaultMessage: 'Alerts history',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.observability.alertDetailsPage.alertHistory.last30days', {
|
||||
defaultMessage: 'Last 30 days',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="danger">
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? <EuiLoadingSpinner size="s" /> : totalTriggeredAlerts || '-'}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate(
|
||||
'xpack.observability.alertDetailsPage.alertHistory.alertsTriggered',
|
||||
{
|
||||
defaultMessage: 'Alerts triggered',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : avgTimeToRecoverUS ? (
|
||||
convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate(
|
||||
'xpack.observability.alertDetailsPage.alertHistory.avgTimeToRecover',
|
||||
{
|
||||
defaultMessage: 'Avg time to recover',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<AlertSummaryWidget
|
||||
featureIds={featureIds}
|
||||
timeRange={getDefaultAlertSummaryTimeRange()}
|
||||
fullSize
|
||||
hideStats
|
||||
filter={{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
[ALERT_RULE_UUID]: ruleId,
|
||||
},
|
||||
},
|
||||
...(instanceId && instanceId !== '*'
|
||||
? [
|
||||
{
|
||||
term: {
|
||||
[ALERT_INSTANCE_ID]: instanceId,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -10,7 +10,6 @@ import React, { useEffect } from 'react';
|
|||
import { AlertSummaryField } from '@kbn/observability-plugin/public';
|
||||
import { useKibana } from '../../../../utils/kibana_react';
|
||||
import { useFetchSloDetails } from '../../../../hooks/use_fetch_slo_details';
|
||||
import { AlertsHistoryPanel } from './components/alerts_history/alerts_history_panel';
|
||||
import { ErrorRatePanel } from './components/error_rate/error_rate_panel';
|
||||
import { CustomAlertDetailsPanel } from './components/custom_panels/custom_panels';
|
||||
import { BurnRateAlert, BurnRateRule } from './types';
|
||||
|
@ -69,7 +68,6 @@ export default function AlertDetailsAppSection({
|
|||
<EuiFlexGroup direction="column" data-test-subj="overviewSection">
|
||||
<ErrorRatePanel alert={alert} slo={slo} isLoading={isLoading} />
|
||||
<CustomAlertDetailsPanel alert={alert} slo={slo} rule={rule} />
|
||||
<AlertsHistoryPanel alert={alert} rule={rule} slo={slo} isLoading={isLoading} />
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,208 +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 {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
EuiLoadingChart,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiStat,
|
||||
EuiText,
|
||||
EuiTextColor,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useAlertsHistory } from '@kbn/observability-alert-details';
|
||||
import rison from '@kbn/rison';
|
||||
import { ALERT_INSTANCE_ID, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
|
||||
import { GetSLOResponse } from '@kbn/slo-schema';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { convertTo } from '@kbn/observability-plugin/public';
|
||||
import { useKibana } from '../../../../../../utils/kibana_react';
|
||||
import { WindowSchema } from '../../../../../../typings';
|
||||
import { ErrorRateChart } from '../../../../error_rate_chart';
|
||||
import { BurnRateAlert, BurnRateRule } from '../../types';
|
||||
import { getActionGroupFromReason } from '../../utils/alert';
|
||||
|
||||
interface Props {
|
||||
slo?: GetSLOResponse;
|
||||
alert: BurnRateAlert;
|
||||
rule: BurnRateRule;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export function AlertsHistoryPanel({ rule, slo, alert, isLoading }: Props) {
|
||||
const {
|
||||
services: { http },
|
||||
} = useKibana();
|
||||
const { isLoading: isAlertsHistoryLoading, data } = useAlertsHistory({
|
||||
featureIds: ['slo'],
|
||||
ruleId: rule.id,
|
||||
dateRange: {
|
||||
from: 'now-30d',
|
||||
to: 'now',
|
||||
},
|
||||
http,
|
||||
instanceId: alert.fields[ALERT_INSTANCE_ID],
|
||||
});
|
||||
|
||||
const actionGroup = getActionGroupFromReason(alert.reason);
|
||||
const actionGroupWindow = (
|
||||
(alert.fields[ALERT_RULE_PARAMETERS]?.windows ?? []) as WindowSchema[]
|
||||
).find((window: WindowSchema) => window.actionGroup === actionGroup);
|
||||
const dataTimeRange = {
|
||||
from: moment().subtract(30, 'day').toDate(),
|
||||
to: new Date(),
|
||||
};
|
||||
|
||||
function getAlertsLink() {
|
||||
const kuery = `kibana.alert.rule.uuid:"${rule.id}"`;
|
||||
return http.basePath.prepend(`/app/observability/alerts?_a=${rison.encode({ kuery })}`);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <EuiLoadingChart size="m" mono data-test-subj="loading" />;
|
||||
}
|
||||
|
||||
if (!slo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPanel paddingSize="m" color="transparent" hasBorder data-test-subj="alertsHistoryPanel">
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexGroup direction="row" justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate(
|
||||
'xpack.slo.burnRateRule.alertDetailsAppSection.alertsHistory.title',
|
||||
{ defaultMessage: '{sloName} alerts history', values: { sloName: slo.name } }
|
||||
)}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLink color="text" href={getAlertsLink()} data-test-subj="alertsLink">
|
||||
<EuiIcon type="sortRight" style={{ marginRight: '4px' }} />
|
||||
<FormattedMessage
|
||||
id="xpack.slo.burnRateRule.alertDetailsAppSection.alertsHistory.alertsLink"
|
||||
defaultMessage="View alerts"
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
<span>
|
||||
{i18n.translate(
|
||||
'xpack.slo.burnRateRule.alertDetailsAppSection.alertsHistory.subtitle',
|
||||
{
|
||||
defaultMessage: 'Last 30 days',
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup direction="row" gutterSize="m" justifyContent="flexStart">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiStat
|
||||
title={
|
||||
isAlertsHistoryLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : data.totalTriggeredAlerts ? (
|
||||
data.totalTriggeredAlerts
|
||||
) : (
|
||||
'-'
|
||||
)
|
||||
}
|
||||
titleColor="danger"
|
||||
titleSize="m"
|
||||
textAlign="left"
|
||||
isLoading={isLoading}
|
||||
data-test-subj="alertsTriggeredStats"
|
||||
reverse
|
||||
description={
|
||||
<EuiTextColor color="default">
|
||||
<span>
|
||||
{i18n.translate(
|
||||
'xpack.slo.burnRateRule.alertDetailsAppSection.alertsHistory.triggeredAlertsStatsTitle',
|
||||
{ defaultMessage: 'Alerts triggered' }
|
||||
)}
|
||||
</span>
|
||||
</EuiTextColor>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiStat
|
||||
title={
|
||||
isAlertsHistoryLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : data.avgTimeToRecoverUS ? (
|
||||
convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: data.avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
) : (
|
||||
'-'
|
||||
)
|
||||
}
|
||||
titleColor="default"
|
||||
titleSize="m"
|
||||
textAlign="left"
|
||||
isLoading={isLoading}
|
||||
data-test-subj="avgTimeToRecoverStat"
|
||||
reverse
|
||||
description={
|
||||
<EuiTextColor color="default">
|
||||
<span>
|
||||
{i18n.translate(
|
||||
'xpack.slo.burnRateRule.alertDetailsAppSection.alertsHistory.avgTimeToRecoverStatsTitle',
|
||||
{ defaultMessage: 'Avg time to recover' }
|
||||
)}
|
||||
</span>
|
||||
</EuiTextColor>
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiFlexGroup direction="row" gutterSize="m" justifyContent="flexStart">
|
||||
<EuiFlexItem>
|
||||
{isAlertsHistoryLoading ? (
|
||||
<EuiLoadingSpinner size="s" />
|
||||
) : (
|
||||
<ErrorRateChart
|
||||
slo={slo}
|
||||
dataTimeRange={dataTimeRange}
|
||||
threshold={actionGroupWindow!.burnRateThreshold}
|
||||
annotations={data.histogramTriggeredAlerts
|
||||
.filter((a) => a.doc_count > 0)
|
||||
.map((a) => ({
|
||||
date: new Date(a.key_as_string!),
|
||||
total: a.doc_count,
|
||||
}))}
|
||||
showErrorRateAsLine
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -29,7 +29,6 @@
|
|||
"@kbn/kibana-utils-plugin",
|
||||
"@kbn/slo-schema",
|
||||
"@kbn/alerting-plugin",
|
||||
"@kbn/observability-alert-details",
|
||||
"@kbn/rison",
|
||||
"@kbn/embeddable-plugin",
|
||||
"@kbn/lens-plugin",
|
||||
|
|
|
@ -8679,7 +8679,6 @@
|
|||
"xpack.apm.agentExplorerInstanceTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1 version} other {# versions}}",
|
||||
"xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "Vous pouvez configurer le nom du nœud de service via {seeDocs}.",
|
||||
"xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1 version} other {# versions}}",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastDescription": "Une erreur s'est produite lors de la récupération des données graphiques de l'historique d'alertes de latence pour {serviceName}",
|
||||
"xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "score {value} {value, select, critical {} other {et plus}}",
|
||||
"xpack.apm.alerts.timeLabelForData": "Dernières {lookback} {timeLabel} de données, ({displayedGroups} groupes affichés sur {totalGroups}",
|
||||
"xpack.apm.alertTypes.errorCount.reason": "Le nombre d'erreurs est {measured} au cours des derniers/dernières {interval} pour {group}. Alerte lorsque > {threshold}.",
|
||||
|
@ -9056,7 +9055,6 @@
|
|||
"xpack.apm.aggregatedTransactions.fallback.tooltip": "Cette page utilise les données d'événements de transactions lorsqu'aucun événement d'indicateur n'a été trouvé dans la plage temporelle actuelle, ou lorsqu'un filtre a été appliqué en fonction des champs indisponibles dans les documents des événements d'indicateurs.",
|
||||
"xpack.apm.alertDetails.error.toastDescription": "Impossible de charger les graphiques de la page de détails d’alerte. Veuillez essayer d’actualiser la page si l’alerte vient d’être créée",
|
||||
"xpack.apm.alertDetails.error.toastTitle": "Une erreur s’est produite lors de l’identification de la plage temporelle de l’alerte.",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastTitle": "Erreur du graphique d’historique des alertes de latence",
|
||||
"xpack.apm.alerting.fields.environment": "Environnement",
|
||||
"xpack.apm.alerting.fields.error.group.id": "Clé du groupe d'erreurs",
|
||||
"xpack.apm.alerting.fields.service": "Service",
|
||||
|
@ -9535,10 +9533,6 @@
|
|||
"xpack.apm.labs.description": "Essayez les fonctionnalités APM qui sont en version d'évaluation technique et en cours de progression.",
|
||||
"xpack.apm.labs.feedbackButtonLabel": "Dites-nous ce que vous pensez !",
|
||||
"xpack.apm.labs.reload": "Recharger pour appliquer les modifications",
|
||||
"xpack.apm.latencyChartHistory.alertsTriggered": "Alertes déclenchées",
|
||||
"xpack.apm.latencyChartHistory.avgTimeToRecover": "Temps moyen de récupération",
|
||||
"xpack.apm.latencyChartHistory.chartTitle": " historique des alertes de latence",
|
||||
"xpack.apm.latencyChartHistory.last30days": "30 derniers jours",
|
||||
"xpack.apm.latencyCorrelations.licenseCheckText": "Pour utiliser les corrélations de latence, vous devez disposer d'une licence Elastic Platinum. Elle vous permettra de découvrir quels champs sont corrélés à de faibles performances.",
|
||||
"xpack.apm.license.button": "Commencer l'essai",
|
||||
"xpack.apm.license.title": "Commencer un essai gratuit de 30 jours",
|
||||
|
@ -21189,8 +21183,6 @@
|
|||
"xpack.infra.waffle.customMetrics.editMode.deleteAriaLabel": "Supprimer l'indicateur personnalisé pour {name}",
|
||||
"xpack.infra.waffle.customMetrics.editMode.editButtonAriaLabel": "Modifier l'indicateur personnalisé pour {name}",
|
||||
"xpack.infra.waffle.unableToSelectGroupErrorMessage": "Impossible de sélectionner les options de regroupement pour {nodeType}",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastDescription": "Une erreur s’est produite lors de la récupération des données graphiques de l’historique d’alertes de logs",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastTitle": "Erreur graphique dans l’historique d’alertes de logs",
|
||||
"xpack.infra.alerting.alertDropdownTitle": "Alertes et règles",
|
||||
"xpack.infra.alerting.alertFlyout.groupBy.placeholder": "Rien (non groupé)",
|
||||
"xpack.infra.alerting.alertFlyout.groupByLabel": "Regrouper par",
|
||||
|
@ -21418,10 +21410,6 @@
|
|||
"xpack.infra.logEntryExampleMessageHeaders.logColumnHeader.messageLabel": "Message",
|
||||
"xpack.infra.logs.alertDetails.chart.ratioTitle": "Ratio de Requête A à Requête B",
|
||||
"xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "Alerte démarrée",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "Alertes déclenchées",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.avgTimeToRecover": "Temps moyen de récupération",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.chartTitle": "Historique des alertes de seuil de logs",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.last30days": "30 derniers jours",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysis.sectionTitle": "Analyse du taux de log",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysisTitle": "Causes possibles et résolutions",
|
||||
"xpack.infra.logs.alertDropdown.inlineLogViewCreateAlertContent": "La création d'alertes n'est pas prise en charge avec les vues de log en ligne.",
|
||||
|
@ -30032,12 +30020,6 @@
|
|||
"xpack.observability.customThreshold.alertChartTitle": "Résultat de l'équation pour ",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysis.sectionTitle": "Analyse du taux de log",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysisTitle": "Causes possibles et résolutions",
|
||||
"xpack.observability.customThreshold.alertHistory.alertsTriggered": "Alertes déclenchées",
|
||||
"xpack.observability.customThreshold.alertHistory.avgTimeToRecover": "Temps moyen de récupération",
|
||||
"xpack.observability.customThreshold.alertHistory.chartTitle": "Historique des alertes",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastDescription": "Une erreur s'est produite lors de la récupération des données graphiques de l'historique des alertes",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastTitle": "Erreur lors de la récupération des données graphiques de l'historique des alertes",
|
||||
"xpack.observability.customThreshold.alertHistory.last30days": "30 derniers jours",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.description": "Vérifiez l'équation de la règle.",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.title": "Une erreur s'est produite lors de l'affichage du graphique",
|
||||
"xpack.observability.customThreshold.rule..charts.errorMessage": "Oups, un problème est survenu",
|
||||
|
|
|
@ -8666,7 +8666,6 @@
|
|||
"xpack.apm.agentExplorerInstanceTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1バージョン} other {# バージョン}}",
|
||||
"xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "{seeDocs}を使用してサービスノード名を構成できます。",
|
||||
"xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1バージョン} other {# バージョン}}",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastDescription": "{serviceName}のレイテンシアラート履歴グラフデータを取得するときに、エラーが発生しました。",
|
||||
"xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "スコア {value} {value, select, critical {} other {以上}}",
|
||||
"xpack.apm.alerts.timeLabelForData": "過去{lookback} {timeLabel}のデータ({displayedGroups}/{totalGroups}個のグループを表示)",
|
||||
"xpack.apm.alertTypes.minimumWindowSize.description": "推奨される最小値は{sizeValue} {sizeUnit}です。これにより、アラートに評価する十分なデータがあることが保証されます。低すぎる値を選択した場合、アラートが想定通りに実行されない可能性があります。",
|
||||
|
@ -9037,7 +9036,6 @@
|
|||
"xpack.apm.aggregatedTransactions.fallback.tooltip": "メトリックイベントが現在の時間範囲にないか、メトリックイベントドキュメントにないフィールドに基づいてフィルターが適用されたため、このページはトランザクションイベントデータを使用しています。",
|
||||
"xpack.apm.alertDetails.error.toastDescription": "アラート詳細ページのグラフを読み込めません。アラートが新しく作成された場合は、ページを更新してください",
|
||||
"xpack.apm.alertDetails.error.toastTitle": "アラート時間範囲を特定するときにエラーが発生しました。",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastTitle": "レイテンシアラート履歴グラフエラー",
|
||||
"xpack.apm.alerting.fields.environment": "環境",
|
||||
"xpack.apm.alerting.fields.error.group.id": "エラーグループキー",
|
||||
"xpack.apm.alerting.fields.service": "サービス",
|
||||
|
@ -9516,10 +9514,6 @@
|
|||
"xpack.apm.labs.description": "現在テクニカルプレビュー中のAPM機能をお試しください。",
|
||||
"xpack.apm.labs.feedbackButtonLabel": "ご意見をお聞かせください。",
|
||||
"xpack.apm.labs.reload": "変更を適用するには、再読み込みしてください",
|
||||
"xpack.apm.latencyChartHistory.alertsTriggered": "アラートがトリガーされました",
|
||||
"xpack.apm.latencyChartHistory.avgTimeToRecover": "回復までの平均時間",
|
||||
"xpack.apm.latencyChartHistory.chartTitle": " レイテンシアラート履歴",
|
||||
"xpack.apm.latencyChartHistory.last30days": "過去30日間",
|
||||
"xpack.apm.latencyCorrelations.licenseCheckText": "遅延の相関関係を使用するには、Elastic Platinumライセンスのサブスクリプションが必要です。使用すると、パフォーマンスの低下に関連しているフィールドを検出できます。",
|
||||
"xpack.apm.license.button": "トライアルを開始",
|
||||
"xpack.apm.license.title": "無料の 30 日トライアルを開始",
|
||||
|
@ -21152,8 +21146,6 @@
|
|||
"xpack.infra.waffle.customMetrics.editMode.deleteAriaLabel": "{name} のカスタムメトリックを削除",
|
||||
"xpack.infra.waffle.customMetrics.editMode.editButtonAriaLabel": "{name} のカスタムメトリックを編集",
|
||||
"xpack.infra.waffle.unableToSelectGroupErrorMessage": "{nodeType} のオプションでグループを選択できません",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastDescription": "ログアラート履歴グラフデータを取得するときに、エラーが発生しました。",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastTitle": "ログアラート履歴グラフエラー",
|
||||
"xpack.infra.alerting.alertDropdownTitle": "アラートとルール",
|
||||
"xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし(グループなし)",
|
||||
"xpack.infra.alerting.alertFlyout.groupByLabel": "グループ分けの条件",
|
||||
|
@ -21392,10 +21384,6 @@
|
|||
"xpack.infra.logEntryExampleMessageHeaders.logColumnHeader.messageLabel": "メッセージ",
|
||||
"xpack.infra.logs.alertDetails.chart.ratioTitle": "クエリAとクエリBの比率",
|
||||
"xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "アラートが開始しました",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "アラートがトリガーされました",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.avgTimeToRecover": "回復までの平均時間",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.chartTitle": "ログしきい値アラート履歴",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.last30days": "過去30日間",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysis.sectionTitle": "ログレート分析",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysisTitle": "考えられる原因と修正方法",
|
||||
"xpack.infra.logs.alertDropdown.inlineLogViewCreateAlertContent": "インラインログビューではアラートの作成がサポートされていません",
|
||||
|
@ -30004,12 +29992,6 @@
|
|||
"xpack.observability.customThreshold.alertChartTitle": "式の結果 ",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysis.sectionTitle": "ログレート分析",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysisTitle": "考えられる原因と修正方法",
|
||||
"xpack.observability.customThreshold.alertHistory.alertsTriggered": "アラートがトリガーされました",
|
||||
"xpack.observability.customThreshold.alertHistory.avgTimeToRecover": "回復までの平均時間",
|
||||
"xpack.observability.customThreshold.alertHistory.chartTitle": "アラート履歴",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastDescription": "アラート履歴グラフデータを取得するときに、エラーが発生しました",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastTitle": "アラート履歴グラフエラー",
|
||||
"xpack.observability.customThreshold.alertHistory.last30days": "過去30日間",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.description": "ルール式を確認してください。",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.title": "グラフの表示中にエラーが発生しました",
|
||||
"xpack.observability.customThreshold.rule..charts.errorMessage": "問題が発生しました",
|
||||
|
|
|
@ -8682,7 +8682,6 @@
|
|||
"xpack.apm.agentExplorerInstanceTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1 个版本} other {# 个版本}}",
|
||||
"xpack.apm.agentExplorerInstanceTable.noServiceNodeName.tooltip.linkToDocs": "您可以通过 {seeDocs} 配置服务节点名称。",
|
||||
"xpack.apm.agentExplorerTable.agentVersionColumnLabel.multipleVersions": "{versionsCount, plural, one {1 个版本} other {# 个版本}}",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastDescription": "提取 {serviceName} 的延迟告警历史记录图表数据时出错",
|
||||
"xpack.apm.alerts.anomalySeverity.scoreDetailsDescription": "分数 {value} {value, select, critical {} other {及以上}}",
|
||||
"xpack.apm.alerts.timeLabelForData": "过去 {lookback} {timeLabel}的数据,显示{displayedGroups}/{totalGroups} 个组",
|
||||
"xpack.apm.alertTypes.errorCount.reason": "对于 {group},过去 {interval}的错误计数为 {measured}。超出 {threshold} 时告警。",
|
||||
|
@ -9059,7 +9058,6 @@
|
|||
"xpack.apm.aggregatedTransactions.fallback.tooltip": "此页面正在使用事务事件数据,因为当前时间范围内未找到任何指标事件,或者已根据指标事件文档中不可用的字段应用了筛选。",
|
||||
"xpack.apm.alertDetails.error.toastDescription": "无法加载告警详情页面的图表。如果告警为新建告警,请尝试刷新该页面",
|
||||
"xpack.apm.alertDetails.error.toastTitle": "识别告警时间范围时出错。",
|
||||
"xpack.apm.alertDetails.latencyAlertHistoryChart.error.toastTitle": "延迟告警历史记录图表错误",
|
||||
"xpack.apm.alerting.fields.environment": "环境",
|
||||
"xpack.apm.alerting.fields.error.group.id": "错误分组密钥",
|
||||
"xpack.apm.alerting.fields.service": "服务",
|
||||
|
@ -9538,10 +9536,6 @@
|
|||
"xpack.apm.labs.description": "试用正处于技术预览状态和开发中的 APM 功能。",
|
||||
"xpack.apm.labs.feedbackButtonLabel": "告诉我们您的看法!",
|
||||
"xpack.apm.labs.reload": "重新加载以应用更改",
|
||||
"xpack.apm.latencyChartHistory.alertsTriggered": "已触发告警",
|
||||
"xpack.apm.latencyChartHistory.avgTimeToRecover": "恢复的平均时间",
|
||||
"xpack.apm.latencyChartHistory.chartTitle": " 延迟告警历史记录",
|
||||
"xpack.apm.latencyChartHistory.last30days": "过去 30 天",
|
||||
"xpack.apm.latencyCorrelations.licenseCheckText": "要使用延迟相关性,必须订阅 Elastic 白金级许可证。使用相关性,将能够发现哪些字段与性能差相关。",
|
||||
"xpack.apm.license.button": "开始试用",
|
||||
"xpack.apm.license.title": "开始为期 30 天的免费试用",
|
||||
|
@ -21195,8 +21189,6 @@
|
|||
"xpack.infra.waffle.customMetrics.editMode.deleteAriaLabel": "删除 {name} 的定制指标",
|
||||
"xpack.infra.waffle.customMetrics.editMode.editButtonAriaLabel": "编辑 {name} 的定制指标",
|
||||
"xpack.infra.waffle.unableToSelectGroupErrorMessage": "无法选择 {nodeType} 的分组依据选项",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastDescription": "提取日志告警历史记录图表数据时出错",
|
||||
"xpack.infra.alertDetails.logsAlertHistoryChart.error.toastTitle": "日志告警历史记录图表错误",
|
||||
"xpack.infra.alerting.alertDropdownTitle": "告警和规则",
|
||||
"xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容(未分组)",
|
||||
"xpack.infra.alerting.alertFlyout.groupByLabel": "分组依据",
|
||||
|
@ -21424,10 +21416,6 @@
|
|||
"xpack.infra.logEntryExampleMessageHeaders.logColumnHeader.messageLabel": "消息",
|
||||
"xpack.infra.logs.alertDetails.chart.ratioTitle": "查询 A 到查询 B 的比率",
|
||||
"xpack.infra.logs.alertDetails.chartAnnotation.alertStarted": "已启动告警",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.alertsTriggered": "已触发告警",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.avgTimeToRecover": "恢复的平均时间",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.chartTitle": "日志阈值告警历史记录",
|
||||
"xpack.infra.logs.alertDetails.chartHistory.last30days": "过去 30 天",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysis.sectionTitle": "日志速率分析",
|
||||
"xpack.infra.logs.alertDetails.logRateAnalysisTitle": "可能的原因和补救措施",
|
||||
"xpack.infra.logs.alertDropdown.inlineLogViewCreateAlertContent": "不支持通过内联日志视图创建告警",
|
||||
|
@ -30044,12 +30032,6 @@
|
|||
"xpack.observability.customThreshold.alertChartTitle": "方程结果用于 ",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysis.sectionTitle": "日志速率分析",
|
||||
"xpack.observability.customThreshold.alertDetails.logRateAnalysisTitle": "可能的原因和补救措施",
|
||||
"xpack.observability.customThreshold.alertHistory.alertsTriggered": "已触发告警",
|
||||
"xpack.observability.customThreshold.alertHistory.avgTimeToRecover": "恢复的平均时间",
|
||||
"xpack.observability.customThreshold.alertHistory.chartTitle": "告警历史记录",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastDescription": "提取告警历史记录图表数据时出错",
|
||||
"xpack.observability.customThreshold.alertHistory.error.toastTitle": "告警历史记录图表错误",
|
||||
"xpack.observability.customThreshold.alertHistory.last30days": "过去 30 天",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.description": "检查规则方程。",
|
||||
"xpack.observability.customThreshold.rule..charts.error_equation.title": "渲染图表时出错",
|
||||
"xpack.observability.customThreshold.rule..charts.errorMessage": "哇哦,出问题了",
|
||||
|
|
|
@ -24,6 +24,7 @@ export const AlertSummaryWidget = ({
|
|||
onClick = () => {},
|
||||
timeRange,
|
||||
hideChart,
|
||||
hideStats,
|
||||
onLoaded,
|
||||
dependencies: { charts },
|
||||
}: AlertSummaryWidgetProps & AlertSummaryWidgetDependencies) => {
|
||||
|
@ -63,6 +64,7 @@ export const AlertSummaryWidget = ({
|
|||
dateFormat={timeRange.dateFormat}
|
||||
recoveredAlertCount={recoveredAlertCount}
|
||||
hideChart={hideChart}
|
||||
hideStats={hideStats}
|
||||
dependencyProps={dependencyProps}
|
||||
/>
|
||||
) : null
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LIGHT_THEME } from '@elastic/charts';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { AlertSummaryWidgetCompact as Component } from './alert_summary_widget_compact';
|
||||
import { mockedAlertSummaryResponse, mockedChartProps } from '../../../mock/alert_summary_widget';
|
||||
|
@ -20,5 +21,9 @@ export const Compact = {
|
|||
chartProps: mockedChartProps,
|
||||
timeRangeTitle: 'Last 30 days',
|
||||
onClick: action('clicked'),
|
||||
dependencyProps: {
|
||||
baseTheme: LIGHT_THEME,
|
||||
sparklineTheme: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LIGHT_THEME } from '@elastic/charts';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { AlertSummaryWidgetFullSize as Component } from './alert_summary_widget_full_size';
|
||||
import { mockedAlertSummaryResponse, mockedChartProps } from '../../../mock/alert_summary_widget';
|
||||
|
@ -18,9 +19,13 @@ export const FullSize = {
|
|||
args: {
|
||||
...mockedAlertSummaryResponse,
|
||||
hideChart: false,
|
||||
hideStats: false,
|
||||
chartProps: {
|
||||
...mockedChartProps,
|
||||
onBrushEnd: action('brushEvent'),
|
||||
},
|
||||
dependencyProps: {
|
||||
baseTheme: LIGHT_THEME,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -75,4 +75,15 @@ describe('AlertSummaryWidgetFullSize', () => {
|
|||
alertSummaryWidget.queryByTestId('alertSummaryWidgetFullSizeChartContainer')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render AlertSummaryWidgetFullSize without stats', async () => {
|
||||
const alertSummaryWidget = renderComponent({
|
||||
hideStats: true,
|
||||
});
|
||||
|
||||
expect(alertSummaryWidget.queryByTestId('alertSummaryWidgetFullSize')).toBeTruthy();
|
||||
expect(
|
||||
alertSummaryWidget.queryByTestId('alertSummaryWidgetFullSizeStats')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -30,6 +30,7 @@ export interface AlertSummaryWidgetFullSizeProps {
|
|||
recoveredAlertCount: number;
|
||||
dateFormat?: string;
|
||||
hideChart?: boolean;
|
||||
hideStats?: boolean;
|
||||
dependencyProps: DependencyProps;
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,7 @@ export const AlertSummaryWidgetFullSize = ({
|
|||
dateFormat,
|
||||
recoveredAlertCount,
|
||||
hideChart,
|
||||
hideStats,
|
||||
dependencyProps: { baseTheme },
|
||||
}: AlertSummaryWidgetFullSizeProps) => {
|
||||
const chartData = activeAlerts.map((alert) => alert.doc_count);
|
||||
|
@ -55,12 +57,14 @@ export const AlertSummaryWidgetFullSize = ({
|
|||
hasShadow={false}
|
||||
paddingSize="none"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<AlertCounts
|
||||
activeAlertCount={activeAlertCount}
|
||||
recoveredAlertCount={recoveredAlertCount}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{!hideStats && (
|
||||
<EuiFlexItem data-test-subj="alertSummaryWidgetFullSizeStats">
|
||||
<AlertCounts
|
||||
activeAlertCount={activeAlertCount}
|
||||
recoveredAlertCount={recoveredAlertCount}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{!hideChart && (
|
||||
<div data-test-subj="alertSummaryWidgetFullSizeChartContainer">
|
||||
<EuiSpacer size="l" />
|
||||
|
|
|
@ -53,5 +53,6 @@ export interface AlertSummaryWidgetProps {
|
|||
timeRange: AlertSummaryTimeRange;
|
||||
chartProps?: ChartProps;
|
||||
hideChart?: boolean;
|
||||
hideStats?: boolean;
|
||||
onLoaded?: (alertsCount?: AlertsCount) => void;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue