mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Alerting] Fix the charts on Log Threshold Rule Alert Detail page (#160321)
## Summary This PR fixes #160320 by changing the chart from the `CriterionPreview` chart, borrowed from the Log Threshold Rule, to an embedded Lens visualization that represents the correct document count in one chart. I also took the liberty of changing the ratio chart to use the same technique for consistency sake. ## Count with multiple conditions ### Before <img width="736" alt="image" src="6c6a27ea
-f8e4-491f-8a12-261d0ed13dcb"> ### After <img width="736" alt="image" src="9b18ebe9
-e911-4e40-8911-bee55cd7d245"> ## Count with multiple conditions and a group by ### Before <img width="736" alt="image" src="7b9462da
-55b2-4f54-ba09-3c55b372ae2c"> ### After <img width="736" alt="image" src="b268caed
-242f-430a-ade0-14bf491ec899"> ## Ratio with multiple conditions ### Before <img width="736" alt="image" src="55b8dfa2
-7789-433b-bffd-e412bdb08b3f"> ### After <img width="736" alt="image" src="a029bf8a
-3ba1-4e16-87bd-097ebc526a4e"> ## Ratio with multiple conditions and a group by ### Before <img width="736" alt="image" src="61ddf1e9
-c5ad-4546-a539-15a51ee563c0"> ### After <img width="736" alt="image" src="15b0aaa3
-4ef9-47f6-baba-24869feae77e">
This commit is contained in:
parent
58b3c3298f
commit
a8322d2711
10 changed files with 688 additions and 369 deletions
|
@ -8,9 +8,8 @@
|
|||
import moment from 'moment';
|
||||
|
||||
export interface TimeRange {
|
||||
from?: string;
|
||||
to?: string;
|
||||
interval?: string;
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export const getPaddedAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => {
|
||||
|
|
|
@ -1,238 +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 {
|
||||
Chart,
|
||||
BarSeries,
|
||||
ScaleType,
|
||||
LineAnnotation,
|
||||
RectAnnotation,
|
||||
Axis,
|
||||
Settings,
|
||||
Position,
|
||||
AnnotationDomainType,
|
||||
Tooltip,
|
||||
} from '@elastic/charts';
|
||||
import React, { ReactElement, useEffect, useMemo } from 'react';
|
||||
import { useIsDarkMode } from '../../../../../hooks/use_is_dark_mode';
|
||||
import { ExecutionTimeRange } from '../../../../../types';
|
||||
import { decodeOrThrow } from '../../../../../../common/runtime_types';
|
||||
import {
|
||||
GetLogAlertsChartPreviewDataAlertParamsSubset,
|
||||
getLogAlertsChartPreviewDataAlertParamsSubsetRT,
|
||||
} from '../../../../../../common/http_api';
|
||||
import {
|
||||
Comparator,
|
||||
PartialRuleParams,
|
||||
Threshold,
|
||||
} from '../../../../../../common/alerting/logs/log_threshold';
|
||||
import { PersistedLogViewReference } from '../../../../../../common/log_views';
|
||||
import { useKibanaTimeZoneSetting } from '../../../../../hooks/use_kibana_time_zone_setting';
|
||||
import { getChartTheme } from '../../../../../utils/get_chart_theme';
|
||||
import {
|
||||
yAxisFormatter,
|
||||
tooltipProps,
|
||||
getDomain,
|
||||
useDateFormatter,
|
||||
LoadingState,
|
||||
ErrorState,
|
||||
NoDataState,
|
||||
ChartContainer,
|
||||
} from '../../../../common/criterion_preview_chart/criterion_preview_chart';
|
||||
import { Color, colorTransformer } from '../../../../../../common/color_palette';
|
||||
import { useChartPreviewData } from '../../expression_editor/hooks/use_chart_preview_data';
|
||||
|
||||
interface ChartProps {
|
||||
buckets: number;
|
||||
logViewReference: PersistedLogViewReference;
|
||||
ruleParams: PartialRuleParams;
|
||||
threshold?: Threshold;
|
||||
showThreshold: boolean;
|
||||
executionTimeRange?: ExecutionTimeRange;
|
||||
filterSeriesByGroupName?: string;
|
||||
annotations?: Array<ReactElement<typeof RectAnnotation | typeof LineAnnotation>>;
|
||||
}
|
||||
|
||||
const LogsRatioChart: React.FC<ChartProps> = ({
|
||||
buckets,
|
||||
ruleParams,
|
||||
logViewReference,
|
||||
threshold,
|
||||
showThreshold,
|
||||
executionTimeRange,
|
||||
filterSeriesByGroupName,
|
||||
annotations,
|
||||
}) => {
|
||||
const chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset | null = useMemo(() => {
|
||||
const params = {
|
||||
criteria: ruleParams.criteria,
|
||||
count: {
|
||||
comparator: ruleParams.count.comparator,
|
||||
value: ruleParams.count.value,
|
||||
},
|
||||
timeSize: ruleParams.timeSize,
|
||||
timeUnit: ruleParams.timeUnit,
|
||||
groupBy: ruleParams.groupBy,
|
||||
};
|
||||
|
||||
try {
|
||||
return decodeOrThrow(getLogAlertsChartPreviewDataAlertParamsSubsetRT)(params);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}, [
|
||||
ruleParams.criteria,
|
||||
ruleParams.count.comparator,
|
||||
ruleParams.count.value,
|
||||
ruleParams.timeSize,
|
||||
ruleParams.timeUnit,
|
||||
ruleParams.groupBy,
|
||||
]);
|
||||
const {
|
||||
getChartPreviewData,
|
||||
isLoading,
|
||||
hasError,
|
||||
chartPreviewData: series,
|
||||
} = useChartPreviewData({
|
||||
logViewReference,
|
||||
ruleParams: chartAlertParams,
|
||||
buckets,
|
||||
executionTimeRange,
|
||||
filterSeriesByGroupName,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
getChartPreviewData();
|
||||
}, [getChartPreviewData]);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const timezone = useKibanaTimeZoneSetting();
|
||||
const { yMin, yMax, xMin, xMax } = getDomain(series, false);
|
||||
const dateFormatter = useDateFormatter(xMin, xMax);
|
||||
const hasData = series.length > 0;
|
||||
const THRESHOLD_OPACITY = 0.3;
|
||||
const chartDomain = {
|
||||
max: showThreshold && threshold ? Math.max(yMax, threshold.value) * 1.1 : yMax * 1.1, // Add 10% headroom.
|
||||
min: showThreshold && threshold ? Math.min(yMin, threshold.value) : yMin,
|
||||
};
|
||||
const isAbove =
|
||||
showThreshold && threshold && threshold.comparator
|
||||
? [Comparator.GT, Comparator.GT_OR_EQ].includes(threshold.comparator)
|
||||
: false;
|
||||
|
||||
const isBelow =
|
||||
showThreshold && threshold && threshold.comparator
|
||||
? [Comparator.LT, Comparator.LT_OR_EQ].includes(threshold.comparator)
|
||||
: false;
|
||||
const barSeries = useMemo(() => {
|
||||
return series.flatMap(({ points, id }) => points.map((point) => ({ ...point, groupBy: id })));
|
||||
}, [series]);
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingState />;
|
||||
} else if (hasError) {
|
||||
return <ErrorState />;
|
||||
} else if (!hasData) {
|
||||
return <NoDataState />;
|
||||
}
|
||||
return (
|
||||
<ChartContainer>
|
||||
<Chart>
|
||||
<BarSeries
|
||||
id="criterion-preview"
|
||||
xScaleType={ScaleType.Time}
|
||||
yScaleType={ScaleType.Linear}
|
||||
xAccessor="timestamp"
|
||||
yAccessors={['value']}
|
||||
splitSeriesAccessors={['groupBy']}
|
||||
data={barSeries}
|
||||
barSeriesStyle={{
|
||||
rectBorder: {
|
||||
stroke: colorTransformer(Color.color0),
|
||||
strokeWidth: 1,
|
||||
visible: true,
|
||||
},
|
||||
rect: {
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
color={colorTransformer(Color.color0)}
|
||||
timeZone={timezone}
|
||||
/>
|
||||
{showThreshold && threshold ? (
|
||||
<LineAnnotation
|
||||
id={`threshold-line`}
|
||||
domainType={AnnotationDomainType.YDomain}
|
||||
dataValues={[{ dataValue: threshold.value }]}
|
||||
style={{
|
||||
line: {
|
||||
strokeWidth: 2,
|
||||
stroke: colorTransformer(Color.color1),
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{showThreshold && threshold && isBelow ? (
|
||||
<RectAnnotation
|
||||
id="below-threshold"
|
||||
style={{
|
||||
fill: colorTransformer(Color.color1),
|
||||
opacity: THRESHOLD_OPACITY,
|
||||
}}
|
||||
dataValues={[
|
||||
{
|
||||
coordinates: {
|
||||
x0: xMin,
|
||||
x1: xMax,
|
||||
y0: chartDomain.min,
|
||||
y1: threshold.value,
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{annotations}
|
||||
{showThreshold && threshold && isAbove ? (
|
||||
<RectAnnotation
|
||||
id="above-threshold"
|
||||
style={{
|
||||
fill: colorTransformer(Color.color1),
|
||||
opacity: THRESHOLD_OPACITY,
|
||||
}}
|
||||
dataValues={[
|
||||
{
|
||||
coordinates: {
|
||||
x0: xMin,
|
||||
x1: xMax,
|
||||
y0: threshold.value,
|
||||
y1: chartDomain.max,
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
<Axis
|
||||
id={'timestamp'}
|
||||
position={Position.Bottom}
|
||||
showOverlappingTicks={true}
|
||||
tickFormat={dateFormatter}
|
||||
/>
|
||||
<Axis
|
||||
id={'values'}
|
||||
position={Position.Left}
|
||||
tickFormat={yAxisFormatter}
|
||||
domain={chartDomain}
|
||||
/>
|
||||
<Settings theme={getChartTheme(isDarkMode)} />
|
||||
<Tooltip {...tooltipProps} />
|
||||
</Chart>
|
||||
</ChartContainer>
|
||||
);
|
||||
};
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default LogsRatioChart;
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* 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 { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
import { EuiThemeComputed, transparentize } from '@elastic/eui';
|
||||
|
||||
export interface IndexPattern {
|
||||
pattern: string;
|
||||
timestampField: string;
|
||||
}
|
||||
|
||||
export interface Threshold {
|
||||
value: number;
|
||||
fill: 'above' | 'below';
|
||||
}
|
||||
|
||||
export interface Timerange {
|
||||
from: number;
|
||||
to?: number;
|
||||
}
|
||||
|
||||
function createBaseLensDefinition<D extends {}>(
|
||||
index: IndexPattern,
|
||||
euiTheme: EuiThemeComputed,
|
||||
threshold: Threshold,
|
||||
alertRange: Timerange,
|
||||
layerDef: D,
|
||||
filter?: string
|
||||
) {
|
||||
return {
|
||||
title: 'Threshold Chart',
|
||||
visualizationType: 'lnsXY',
|
||||
type: 'lens',
|
||||
references: [],
|
||||
state: {
|
||||
visualization: {
|
||||
legend: {
|
||||
isVisible: false,
|
||||
position: 'right',
|
||||
},
|
||||
valueLabels: 'hide',
|
||||
fittingFunction: 'None',
|
||||
axisTitlesVisibilitySettings: {
|
||||
x: false,
|
||||
yLeft: false,
|
||||
yRight: false,
|
||||
},
|
||||
tickLabelsVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
labelsOrientation: {
|
||||
x: 0,
|
||||
yLeft: 0,
|
||||
yRight: 0,
|
||||
},
|
||||
gridlinesVisibilitySettings: {
|
||||
x: true,
|
||||
yLeft: true,
|
||||
yRight: true,
|
||||
},
|
||||
preferredSeriesType: 'bar_stacked',
|
||||
layers: [
|
||||
{
|
||||
layerId: 'e6f553a0-9e36-4eea-8ecf-8261523c6f44',
|
||||
accessors: ['607b2253-ed20-4f0a-bf62-07a1f846cca4'],
|
||||
position: 'top',
|
||||
seriesType: 'bar_stacked',
|
||||
showGridlines: false,
|
||||
layerType: 'data',
|
||||
xAccessor: '8ed7d473-ff48-4c90-be2c-ae46f3a11030',
|
||||
yConfig: [
|
||||
{
|
||||
forAccessor: '607b2253-ed20-4f0a-bf62-07a1f846cca4',
|
||||
color: '#6092c0',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
layerId: '62dfc313-3922-4870-b568-ff0818da38b3',
|
||||
layerType: 'annotations',
|
||||
annotations: [
|
||||
{
|
||||
type: 'manual',
|
||||
id: 'ffe44253-a8c7-4755-821f-47be5bfac288',
|
||||
label: 'Alert Line',
|
||||
key: {
|
||||
type: 'point_in_time',
|
||||
timestamp: moment(alertRange.from).toISOString(),
|
||||
},
|
||||
lineWidth: 3,
|
||||
color: euiTheme.colors.danger,
|
||||
icon: 'alert',
|
||||
},
|
||||
{
|
||||
type: 'manual',
|
||||
label: 'Alert',
|
||||
key: {
|
||||
type: 'range',
|
||||
timestamp: moment(alertRange.from).toISOString(),
|
||||
endTimestamp: moment(alertRange.to).toISOString(),
|
||||
},
|
||||
id: '07d15b13-4b6c-4d82-b45d-9d58ced1c2a8',
|
||||
color: transparentize(euiTheme.colors.danger, 0.2),
|
||||
},
|
||||
],
|
||||
ignoreGlobalFilters: true,
|
||||
persistanceType: 'byValue',
|
||||
},
|
||||
{
|
||||
layerId: '90f87c46-9685-49af-b4ed-066eb65e2b39',
|
||||
layerType: 'referenceLine',
|
||||
accessors: ['7fb02af1-0823-4787-a316-3b05a4539d2c'],
|
||||
yConfig: [
|
||||
{
|
||||
forAccessor: '7fb02af1-0823-4787-a316-3b05a4539d2c',
|
||||
axisMode: 'left',
|
||||
color: euiTheme.colors.danger,
|
||||
lineWidth: 2,
|
||||
fill: threshold.fill,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
query: {
|
||||
query: filter || '',
|
||||
language: 'kuery',
|
||||
},
|
||||
filters: [],
|
||||
datasourceStates: {
|
||||
formBased: {
|
||||
layers: {
|
||||
'e6f553a0-9e36-4eea-8ecf-8261523c6f44': layerDef,
|
||||
'90f87c46-9685-49af-b4ed-066eb65e2b39': {
|
||||
linkToLayers: [],
|
||||
columns: {
|
||||
'7fb02af1-0823-4787-a316-3b05a4539d2c': {
|
||||
label: 'Threshold',
|
||||
dataType: 'number',
|
||||
operationType: 'static_value',
|
||||
isStaticValue: true,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
value: threshold.value,
|
||||
},
|
||||
references: [],
|
||||
customLabel: true,
|
||||
},
|
||||
},
|
||||
columnOrder: ['7fb02af1-0823-4787-a316-3b05a4539d2c'],
|
||||
sampling: 1,
|
||||
ignoreGlobalFilters: false,
|
||||
incompleteColumns: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
indexpattern: {
|
||||
layers: {},
|
||||
},
|
||||
textBased: {
|
||||
layers: {},
|
||||
},
|
||||
},
|
||||
internalReferences: [
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: 'd09436e6-20c0-4982-aaf6-b67ec371b27d',
|
||||
name: 'indexpattern-datasource-layer-e6f553a0-9e36-4eea-8ecf-8261523c6f44',
|
||||
},
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: 'd09436e6-20c0-4982-aaf6-b67ec371b27d',
|
||||
name: 'indexpattern-datasource-layer-90f87c46-9685-49af-b4ed-066eb65e2b39',
|
||||
},
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: 'd09436e6-20c0-4982-aaf6-b67ec371b27d',
|
||||
name: 'xy-visualization-layer-62dfc313-3922-4870-b568-ff0818da38b3',
|
||||
},
|
||||
],
|
||||
adHocDataViews: {
|
||||
'd09436e6-20c0-4982-aaf6-b67ec371b27d': {
|
||||
id: 'd09436e6-20c0-4982-aaf6-b67ec371b27d',
|
||||
title: index.pattern,
|
||||
timeFieldName: index.timestampField,
|
||||
sourceFilters: [],
|
||||
fieldFormats: {},
|
||||
runtimeFieldMap: {},
|
||||
fieldAttrs: {},
|
||||
allowNoIndex: false,
|
||||
name: 'adhoc',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createLensDefinitionForRatioChart(
|
||||
index: IndexPattern,
|
||||
euiTheme: EuiThemeComputed,
|
||||
numeratorKql: string,
|
||||
denominatorKql: string,
|
||||
threshold: Threshold,
|
||||
alertRange: Timerange,
|
||||
interval: string,
|
||||
filter?: string
|
||||
) {
|
||||
const layerDef = {
|
||||
columns: {
|
||||
'8ed7d473-ff48-4c90-be2c-ae46f3a11030': {
|
||||
label: index.timestampField,
|
||||
dataType: 'date',
|
||||
operationType: 'date_histogram',
|
||||
sourceField: index.timestampField,
|
||||
isBucketed: true,
|
||||
scale: 'interval',
|
||||
params: {
|
||||
interval,
|
||||
includeEmptyRows: true,
|
||||
dropPartials: false,
|
||||
},
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0': {
|
||||
label: 'Part of ratio',
|
||||
dataType: 'number',
|
||||
operationType: 'count',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
sourceField: '___records___',
|
||||
filter: {
|
||||
query: numeratorKql,
|
||||
language: 'kuery',
|
||||
},
|
||||
params: {
|
||||
emptyAsNull: false,
|
||||
},
|
||||
customLabel: true,
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X1': {
|
||||
label: 'Part of ratio',
|
||||
dataType: 'number',
|
||||
operationType: 'count',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
sourceField: '___records___',
|
||||
filter: {
|
||||
query: denominatorKql,
|
||||
language: 'kuery',
|
||||
},
|
||||
params: {
|
||||
emptyAsNull: false,
|
||||
},
|
||||
customLabel: true,
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X2': {
|
||||
label: 'Part of ratio',
|
||||
dataType: 'number',
|
||||
operationType: 'math',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
tinymathAst: {
|
||||
type: 'function',
|
||||
name: 'divide',
|
||||
args: [
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X1',
|
||||
],
|
||||
location: {
|
||||
min: 0,
|
||||
max: 94,
|
||||
},
|
||||
text: `count(kql=\'${numeratorKql}\') / count(kql=\'${denominatorKql}\')`,
|
||||
},
|
||||
},
|
||||
references: [
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X1',
|
||||
],
|
||||
customLabel: true,
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4': {
|
||||
label: 'ratio',
|
||||
dataType: 'number',
|
||||
operationType: 'formula',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
formula: `count(kql=\'${numeratorKql}\') / count(kql=\'${denominatorKql}\')`,
|
||||
isFormulaBroken: false,
|
||||
},
|
||||
references: ['607b2253-ed20-4f0a-bf62-07a1f846cca4X2'],
|
||||
customLabel: true,
|
||||
},
|
||||
},
|
||||
columnOrder: [
|
||||
'8ed7d473-ff48-4c90-be2c-ae46f3a11030',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X1',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X2',
|
||||
],
|
||||
incompleteColumns: {},
|
||||
sampling: 1,
|
||||
};
|
||||
return createBaseLensDefinition(
|
||||
index,
|
||||
euiTheme,
|
||||
threshold,
|
||||
alertRange,
|
||||
layerDef,
|
||||
filter
|
||||
) as unknown as TypedLensByValueInput['attributes'];
|
||||
}
|
||||
|
||||
export function createLensDefinitionForCountChart(
|
||||
index: IndexPattern,
|
||||
euiTheme: EuiThemeComputed,
|
||||
kql: string,
|
||||
threshold: Threshold,
|
||||
alertRange: Timerange,
|
||||
interval: string,
|
||||
filter?: string
|
||||
) {
|
||||
const layerDef = {
|
||||
columns: {
|
||||
'8ed7d473-ff48-4c90-be2c-ae46f3a11030': {
|
||||
label: index.timestampField,
|
||||
dataType: 'date',
|
||||
operationType: 'date_histogram',
|
||||
sourceField: index.timestampField,
|
||||
isBucketed: true,
|
||||
scale: 'interval',
|
||||
params: {
|
||||
interval,
|
||||
includeEmptyRows: true,
|
||||
dropPartials: false,
|
||||
},
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0': {
|
||||
label: `Part of count(kql=\'${kql}\')`,
|
||||
dataType: 'number',
|
||||
operationType: 'count',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
sourceField: '___records___',
|
||||
filter: {
|
||||
query: kql,
|
||||
language: 'kuery',
|
||||
},
|
||||
params: {
|
||||
emptyAsNull: false,
|
||||
},
|
||||
customLabel: true,
|
||||
},
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4': {
|
||||
label: 'document count',
|
||||
dataType: 'number',
|
||||
operationType: 'formula',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
params: {
|
||||
formula: `count(kql=\'${kql}\')`,
|
||||
isFormulaBroken: false,
|
||||
},
|
||||
references: ['607b2253-ed20-4f0a-bf62-07a1f846cca4X0'],
|
||||
customLabel: true,
|
||||
},
|
||||
},
|
||||
columnOrder: [
|
||||
'8ed7d473-ff48-4c90-be2c-ae46f3a11030',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4',
|
||||
'607b2253-ed20-4f0a-bf62-07a1f846cca4X0',
|
||||
],
|
||||
incompleteColumns: {},
|
||||
sampling: 1,
|
||||
};
|
||||
|
||||
return createBaseLensDefinition(
|
||||
index,
|
||||
euiTheme,
|
||||
threshold,
|
||||
alertRange,
|
||||
layerDef,
|
||||
filter
|
||||
) as unknown as TypedLensByValueInput['attributes'];
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { LogThresholdCountChart } from './log_threshold_count_chart';
|
||||
export { LogThresholdRatioChart } from './log_threshold_ratio_chart';
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana';
|
||||
import {
|
||||
createLensDefinitionForCountChart,
|
||||
IndexPattern,
|
||||
Threshold,
|
||||
Timerange,
|
||||
} from './create_lens_definition';
|
||||
|
||||
interface LogThresholdCountChartProps {
|
||||
index: IndexPattern;
|
||||
threshold: Threshold;
|
||||
timeRange: { from: string; to: string };
|
||||
alertRange: Timerange;
|
||||
kql: string;
|
||||
height: number;
|
||||
interval?: string;
|
||||
filter?: string;
|
||||
}
|
||||
|
||||
export function LogThresholdCountChart({
|
||||
kql,
|
||||
index,
|
||||
threshold,
|
||||
timeRange,
|
||||
alertRange,
|
||||
height,
|
||||
interval = 'auto',
|
||||
filter = '',
|
||||
}: LogThresholdCountChartProps) {
|
||||
const {
|
||||
lens: { EmbeddableComponent },
|
||||
} = useKibanaContextForPlugin().services;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const lensDef = createLensDefinitionForCountChart(
|
||||
index,
|
||||
euiTheme,
|
||||
kql,
|
||||
threshold,
|
||||
alertRange,
|
||||
interval,
|
||||
filter
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<EmbeddableComponent
|
||||
id="logThresholdCountChart"
|
||||
style={{ height }}
|
||||
timeRange={timeRange}
|
||||
attributes={lensDef}
|
||||
viewMode={ViewMode.VIEW}
|
||||
noPadding
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana';
|
||||
import {
|
||||
createLensDefinitionForRatioChart,
|
||||
IndexPattern,
|
||||
Threshold,
|
||||
Timerange,
|
||||
} from './create_lens_definition';
|
||||
|
||||
interface LogThresholdRatioChartProps {
|
||||
index: IndexPattern;
|
||||
threshold: Threshold;
|
||||
timeRange: { from: string; to: string };
|
||||
alertRange: Timerange;
|
||||
numeratorKql: string;
|
||||
denominatorKql: string;
|
||||
height: number;
|
||||
interval?: string;
|
||||
filter?: string;
|
||||
}
|
||||
|
||||
export function LogThresholdRatioChart({
|
||||
numeratorKql,
|
||||
denominatorKql,
|
||||
index,
|
||||
threshold,
|
||||
timeRange,
|
||||
alertRange,
|
||||
height,
|
||||
interval = 'auto',
|
||||
filter = '',
|
||||
}: LogThresholdRatioChartProps) {
|
||||
const {
|
||||
lens: { EmbeddableComponent },
|
||||
} = useKibanaContextForPlugin().services;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const lensDef = createLensDefinitionForRatioChart(
|
||||
index,
|
||||
euiTheme,
|
||||
numeratorKql,
|
||||
denominatorKql,
|
||||
threshold,
|
||||
alertRange,
|
||||
interval,
|
||||
filter
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<EmbeddableComponent
|
||||
id="logThresholdRatioChart"
|
||||
style={{ height }}
|
||||
timeRange={timeRange}
|
||||
attributes={lensDef}
|
||||
viewMode={ViewMode.VIEW}
|
||||
noPadding
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { LIGHT_THEME } from '@elastic/charts';
|
||||
import { EuiPanel } from '@elastic/eui';
|
||||
|
@ -18,28 +18,22 @@ import moment from 'moment';
|
|||
import { useTheme } from '@emotion/react';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
AlertAnnotation,
|
||||
getPaddedAlertTimeRange,
|
||||
AlertActiveTimeRangeAnnotation,
|
||||
} from '@kbn/observability-alert-details';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/public';
|
||||
import { get } from 'lodash';
|
||||
import { getPaddedAlertTimeRange } from '@kbn/observability-alert-details';
|
||||
import { get, identity } from 'lodash';
|
||||
import { CoPilotContextProvider } from '@kbn/observability-plugin/public';
|
||||
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
|
||||
import { getChartGroupNames } from '../../../../../common/utils/get_chart_group_names';
|
||||
import {
|
||||
Comparator,
|
||||
ComparatorToi18nMap,
|
||||
ComparatorToi18nSymbolsMap,
|
||||
isRatioRule,
|
||||
type PartialCriterion,
|
||||
} from '../../../../../common/alerting/logs/log_threshold';
|
||||
import { CriterionPreview } from '../expression_editor/criterion_preview_chart';
|
||||
import { AlertDetailsAppSectionProps } from './types';
|
||||
import { Threshold } from '../../../common/components/threshold';
|
||||
import LogsRatioChart from './components/logs_ratio_chart';
|
||||
import { ExplainLogRateSpikes } from './components/explain_log_rate_spike';
|
||||
import { LogThresholdCountChart, LogThresholdRatioChart } from './components/threhsold_chart';
|
||||
import { useLogView } from '../../../../hooks/use_log_view';
|
||||
import { useLicense } from '../../../../hooks/use_license';
|
||||
|
||||
const LogsHistoryChart = React.lazy(() => import('./components/logs_history_chart'));
|
||||
|
@ -50,12 +44,30 @@ const AlertDetailsAppSection = ({
|
|||
alert,
|
||||
setAlertSummaryFields,
|
||||
}: AlertDetailsAppSectionProps) => {
|
||||
const [selectedSeries, setSelectedSeries] = useState<string>('');
|
||||
const { uiSettings, observability } = useKibanaContextForPlugin().services;
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const { observability, logViews } = useKibanaContextForPlugin().services;
|
||||
const theme = useTheme();
|
||||
const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]);
|
||||
const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined;
|
||||
const interval = `${rule.params.timeSize}${rule.params.timeUnit}`;
|
||||
const thresholdFill = convertComparatorToFill(rule.params.count.comparator);
|
||||
const filter = rule.params.groupBy
|
||||
? rule.params.groupBy
|
||||
.map((field) => {
|
||||
const value = get(
|
||||
alert.fields[ALERT_CONTEXT],
|
||||
['groupByKeys', ...field.split('.')],
|
||||
null
|
||||
);
|
||||
return value ? `${field} : "${value}"` : null;
|
||||
})
|
||||
.filter(identity)
|
||||
.join(' AND ')
|
||||
: '';
|
||||
|
||||
const { derivedDataView } = useLogView({
|
||||
initialLogViewReference: rule.params.logView,
|
||||
logViews: logViews.client,
|
||||
});
|
||||
|
||||
const { hasAtLeast } = useLicense();
|
||||
const hasLicenseForExplainLogSpike = hasAtLeast('platinum');
|
||||
|
@ -80,7 +92,6 @@ const AlertDetailsAppSection = ({
|
|||
{}
|
||||
) || {};
|
||||
|
||||
setSelectedSeries(getChartGroupNames(Object.values(alertFieldsFromGroupBy)));
|
||||
const alertSummaryFields = Object.entries(alertFieldsFromGroupBy).map(([label, value]) => ({
|
||||
label,
|
||||
value,
|
||||
|
@ -90,6 +101,13 @@ const AlertDetailsAppSection = ({
|
|||
|
||||
const getLogRatioChart = () => {
|
||||
if (isRatioRule(rule.params.criteria)) {
|
||||
const numeratorKql = rule.params.criteria[0]
|
||||
.map((criteria) => convertCriteriaToKQL(criteria))
|
||||
.join(' AND ');
|
||||
const denominatorKql = rule.params.criteria[1]
|
||||
.map((criteria) => convertCriteriaToKQL(criteria))
|
||||
.join(' AND ');
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true} data-test-subj="logsRatioChartAlertDetails">
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
|
@ -106,7 +124,6 @@ const AlertDetailsAppSection = ({
|
|||
<EuiFlexGroup>
|
||||
<EuiFlexItem style={{ maxHeight: 120 }} grow={1}>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<Threshold
|
||||
title={`Threshold breached`}
|
||||
chartProps={{ theme, baseTheme: LIGHT_THEME }}
|
||||
|
@ -119,38 +136,22 @@ const AlertDetailsAppSection = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5}>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<LogsRatioChart
|
||||
buckets={1}
|
||||
logViewReference={{
|
||||
type: 'log-view-reference',
|
||||
logViewId: rule.params.logView.logViewId,
|
||||
}}
|
||||
ruleParams={rule.params}
|
||||
filterSeriesByGroupName={selectedSeries}
|
||||
showThreshold={true}
|
||||
threshold={rule.params.count}
|
||||
executionTimeRange={{
|
||||
gte: Number(moment(timeRange.from).format('x')),
|
||||
lte: Number(moment(timeRange.to).format('x')),
|
||||
}}
|
||||
annotations={[
|
||||
<AlertAnnotation
|
||||
key={`${alert.start}-start-alert-annotation`}
|
||||
id={`${alert.start}-start-alert-annotation`}
|
||||
alertStart={alert.start}
|
||||
color={euiTheme.colors.danger}
|
||||
dateFormat={uiSettings.get(UI_SETTINGS.DATE_FORMAT)}
|
||||
/>,
|
||||
<AlertActiveTimeRangeAnnotation
|
||||
key={`${alert.start}-active-alert-annotation`}
|
||||
id={`${alert.start}-active-alert-annotation`}
|
||||
alertStart={alert.start}
|
||||
alertEnd={alertEnd}
|
||||
color={euiTheme.colors.danger}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
{derivedDataView && (
|
||||
<LogThresholdRatioChart
|
||||
filter={filter}
|
||||
numeratorKql={numeratorKql}
|
||||
denominatorKql={denominatorKql}
|
||||
threshold={{ value: rule.params.count.value, fill: thresholdFill }}
|
||||
timeRange={timeRange}
|
||||
alertRange={{ from: alert.start, to: alertEnd }}
|
||||
index={{
|
||||
pattern: derivedDataView.getIndexPattern(),
|
||||
timestampField: derivedDataView.timeFieldName || '@timestamp',
|
||||
}}
|
||||
height={150}
|
||||
interval={interval}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
@ -160,84 +161,66 @@ const AlertDetailsAppSection = ({
|
|||
|
||||
const getLogCountChart = () => {
|
||||
if (!!rule.params.criteria && !isRatioRule(rule.params.criteria)) {
|
||||
return rule.params.criteria.map((criteria, idx) => {
|
||||
const chartCriterion = criteria as PartialCriterion;
|
||||
return (
|
||||
<EuiPanel
|
||||
key={`${chartCriterion.field}${idx}`}
|
||||
hasBorder={true}
|
||||
data-test-subj={`logsCountChartAlertDetails-${chartCriterion.field}${idx}`}
|
||||
>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
{chartCriterion.comparator && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chart.chartTitle', {
|
||||
defaultMessage: 'Logs for {field} {comparator} {value}',
|
||||
values: {
|
||||
field: chartCriterion.field,
|
||||
comparator: ComparatorToi18nMap[chartCriterion.comparator],
|
||||
value: chartCriterion.value,
|
||||
},
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem style={{ maxHeight: 120 }} grow={1}>
|
||||
<EuiSpacer size="s" />
|
||||
{chartCriterion.comparator && (
|
||||
<Threshold
|
||||
title={`Threshold breached`}
|
||||
chartProps={{ theme, baseTheme: LIGHT_THEME }}
|
||||
comparator={ComparatorToi18nSymbolsMap[rule.params.count.comparator]}
|
||||
id={`${chartCriterion.field}-${chartCriterion.value}`}
|
||||
threshold={rule.params.count.value}
|
||||
value={Number(alert.fields[ALERT_EVALUATION_VALUE])}
|
||||
valueFormatter={formatThreshold}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5}>
|
||||
<CriterionPreview
|
||||
ruleParams={rule.params}
|
||||
logViewReference={{
|
||||
type: 'log-view-reference',
|
||||
logViewId: rule.params.logView.logViewId,
|
||||
const kql = rule.params.criteria
|
||||
.map((criteria) => convertCriteriaToKQL(criteria))
|
||||
.join(' AND ');
|
||||
const criteriaAsText = rule.params.criteria
|
||||
.map((criteria) => {
|
||||
if (!criteria.field || !criteria.comparator || !criteria.value) {
|
||||
return '';
|
||||
}
|
||||
return `${criteria.field} ${ComparatorToi18nMap[criteria.comparator]} ${criteria.value}`;
|
||||
})
|
||||
.filter((text) => text)
|
||||
.join(' AND ');
|
||||
return (
|
||||
<EuiPanel hasBorder={true} data-test-subj={`logsCountChartAlertDetails`}>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{i18n.translate('xpack.infra.logs.alertDetails.chart.chartTitle', {
|
||||
defaultMessage: 'Logs for {criteria}',
|
||||
values: { criteria: criteriaAsText },
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem style={{ maxHeight: 120 }} grow={1}>
|
||||
<EuiSpacer size="s" />
|
||||
<Threshold
|
||||
title={`Threshold breached`}
|
||||
chartProps={{ theme, baseTheme: LIGHT_THEME }}
|
||||
comparator={ComparatorToi18nSymbolsMap[rule.params.count.comparator]}
|
||||
id="logCountThreshold"
|
||||
threshold={rule.params.count.value}
|
||||
value={Number(alert.fields[ALERT_EVALUATION_VALUE])}
|
||||
valueFormatter={formatThreshold}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={5}>
|
||||
{derivedDataView && (
|
||||
<LogThresholdCountChart
|
||||
filter={filter}
|
||||
kql={kql}
|
||||
threshold={{ value: rule.params.count.value, fill: thresholdFill }}
|
||||
timeRange={timeRange}
|
||||
alertRange={{ from: alert.start, to: alertEnd }}
|
||||
index={{
|
||||
pattern: derivedDataView.getIndexPattern(),
|
||||
timestampField: derivedDataView.timeFieldName || '@timestamp',
|
||||
}}
|
||||
chartCriterion={chartCriterion}
|
||||
showThreshold={true}
|
||||
executionTimeRange={{
|
||||
gte: Number(moment(timeRange.from).format('x')),
|
||||
lte: Number(moment(timeRange.to).format('x')),
|
||||
}}
|
||||
annotations={[
|
||||
<AlertAnnotation
|
||||
key={`${alert.start}${chartCriterion.field}${idx}-start-alert-annotation`}
|
||||
id={`${alert.start}${chartCriterion.field}${idx}-start-alert-annotation`}
|
||||
alertStart={alert.start}
|
||||
color={euiTheme.colors.danger}
|
||||
dateFormat={uiSettings.get(UI_SETTINGS.DATE_FORMAT)}
|
||||
/>,
|
||||
<AlertActiveTimeRangeAnnotation
|
||||
key={`${alert.start}${chartCriterion.field}${idx}-active-alert-annotation`}
|
||||
id={`${alert.start}${chartCriterion.field}${idx}-active-alert-annotation`}
|
||||
alertStart={alert.start}
|
||||
alertEnd={alertEnd}
|
||||
color={euiTheme.colors.danger}
|
||||
/>,
|
||||
]}
|
||||
filterSeriesByGroupName={[selectedSeries]}
|
||||
height={150}
|
||||
interval={interval}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
});
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
);
|
||||
} else return null;
|
||||
};
|
||||
|
||||
|
@ -269,5 +252,45 @@ const AlertDetailsAppSection = ({
|
|||
</CoPilotContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
function convertComparatorToFill(comparator: Comparator) {
|
||||
switch (comparator) {
|
||||
case Comparator.GT:
|
||||
case Comparator.GT_OR_EQ:
|
||||
return 'above';
|
||||
default:
|
||||
return 'below';
|
||||
}
|
||||
}
|
||||
|
||||
function convertCriteriaToKQL(criteria: PartialCriterion) {
|
||||
if (!criteria.value || !criteria.comparator || !criteria.field) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch (criteria.comparator) {
|
||||
case Comparator.MATCH:
|
||||
case Comparator.EQ:
|
||||
return `${criteria.field} : "${criteria.value}"`;
|
||||
case Comparator.NOT_MATCH:
|
||||
case Comparator.NOT_EQ:
|
||||
return `NOT ${criteria.field} : "${criteria.value}"`;
|
||||
case Comparator.MATCH_PHRASE:
|
||||
return `${criteria.field} : ${criteria.value}`;
|
||||
case Comparator.NOT_MATCH_PHRASE:
|
||||
return `NOT ${criteria.field} : ${criteria.value}`;
|
||||
case Comparator.GT:
|
||||
return `${criteria.field} > ${criteria.value}`;
|
||||
case Comparator.GT_OR_EQ:
|
||||
return `${criteria.field} >= ${criteria.value}`;
|
||||
case Comparator.LT:
|
||||
return `${criteria.field} < ${criteria.value}`;
|
||||
case Comparator.LT_OR_EQ:
|
||||
return `${criteria.field} <= ${criteria.value}`;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default AlertDetailsAppSection;
|
||||
|
|
|
@ -18160,7 +18160,7 @@
|
|||
"xpack.infra.linkTo.hostWithIp.loading": "Chargement de l'hôte avec l'adresse IP \"{hostIp}\" en cours.",
|
||||
"xpack.infra.logFlyout.flyoutSubTitle": "À partir de l'index {indexName}",
|
||||
"xpack.infra.logFlyout.flyoutTitle": "Détails de l'entrée de log {logEntryId}",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "Logs pour {field} {comparator} {value}",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "Logs pour {criteria}",
|
||||
"xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "Lors de la définition d'une valeur \"regrouper par\", nous recommandons fortement d'utiliser le comparateur \"{comparator}\" pour votre seuil. Cela peut permettre d'améliorer considérablement les performances.",
|
||||
"xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "{actualCount, plural, one {la {actualCount} dernière entrée de log} many {les {actualCount} dernières entrées de log} other {les {actualCount} dernières entrées de log}} dans {duration} pour {groupName}. Alerte lorsque {comparator} {expectedCount}.",
|
||||
"xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription": "Le rapport des logs sélectionnés est de {actualRatio} dans les dernières {duration} pour {groupName}. Alerte lorsque {comparator} {expectedRatio}.",
|
||||
|
|
|
@ -18159,7 +18159,7 @@
|
|||
"xpack.infra.linkTo.hostWithIp.loading": "IPアドレス「{hostIp}」のホストを読み込み中です。",
|
||||
"xpack.infra.logFlyout.flyoutSubTitle": "インデックス{indexName}から",
|
||||
"xpack.infra.logFlyout.flyoutTitle": "ログエントリ{logEntryId}の詳細",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "{field} {comparator} {value}のログ",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "{criteria} のログ",
|
||||
"xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "「group by」を設定するときには、しきい値で\"{comparator}\"比較演算子を使用することを強くお勧めします。これにより、パフォーマンスを大きく改善できます。",
|
||||
"xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "{groupName}の過去{duration}の{actualCount, plural, other {{actualCount}件のログエントリ}}。{comparator} {expectedCount}のときにアラートを通知します。",
|
||||
"xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription": "{groupName}の過去{duration}における選択されたログの比率は{actualRatio}です。{comparator} {expectedRatio}のときにアラートを通知します。",
|
||||
|
|
|
@ -18159,7 +18159,7 @@
|
|||
"xpack.infra.linkTo.hostWithIp.loading": "正在加载 IP 地址为“{hostIp}”的主机。",
|
||||
"xpack.infra.logFlyout.flyoutSubTitle": "从索引 {indexName}",
|
||||
"xpack.infra.logFlyout.flyoutTitle": "日志条目 {logEntryId} 的详细信息",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "{field} {comparator} {value} 的日志",
|
||||
"xpack.infra.logs.alertDetails.chart.chartTitle": "{criteria} 的日志",
|
||||
"xpack.infra.logs.alertFlyout.groupByOptimizationWarning": "设置“分组依据”时,强烈建议将“{comparator}”比较符用于阈值。这会使性能有较大提升。",
|
||||
"xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription": "对于 {groupName},过去 {duration}中有 {actualCount, plural, other {{actualCount} 个日志条目}}。{comparator} {expectedCount} 时告警。",
|
||||
"xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription": "对于 {groupName},过去 {duration}选定日志的比率为 {actualRatio}。{comparator} {expectedRatio} 时告警。",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue