Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Synthetics] Fix overview page vizs for large number of monitors !! #199512

Merged
merged 13 commits into from
Nov 21, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { i18n } from '@kbn/i18n';
import { capitalize } from 'lodash';
import { ExistsFilter, isExistsFilter } from '@kbn/es-query';
import { ExistsFilter, Filter, isExistsFilter } from '@kbn/es-query';
import {
AvgIndexPatternColumn,
CardinalityIndexPatternColumn,
Expand Down Expand Up @@ -41,6 +41,7 @@ import type { DataView } from '@kbn/data-views-plugin/common';
import { PersistableFilter } from '@kbn/lens-plugin/common';
import { DataViewSpec } from '@kbn/data-views-plugin/common';
import { LegendSize } from '@kbn/visualizations-plugin/common/constants';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { urlFiltersToKueryString } from '../utils/stringify_kueries';
import {
FILTER_RECORDS,
Expand Down Expand Up @@ -169,17 +170,20 @@ export class LensAttributes {
globalFilter?: { query: string; language: string };
reportType: string;
lensFormulaHelper?: FormulaPublicApi;
dslFilters?: QueryDslQueryContainer[];

constructor(
layerConfigs: LayerConfig[],
reportType: string,
lensFormulaHelper?: FormulaPublicApi
lensFormulaHelper?: FormulaPublicApi,
dslFilters?: QueryDslQueryContainer[]
) {
this.layers = {};
this.seriesReferenceLines = {};
this.reportType = reportType;
this.lensFormulaHelper = lensFormulaHelper;
this.isMultiSeries = layerConfigs.length > 1;
this.dslFilters = dslFilters;

layerConfigs.forEach(({ seriesConfig, operationType }) => {
if (operationType && reportType !== ReportTypes.SINGLE_METRIC) {
Expand Down Expand Up @@ -1267,6 +1271,31 @@ export class LensAttributes {
return { internalReferences, adHocDataViews };
}

getFilters(): Filter[] {
const { internalReferences } = this.getReferences();

const dslFilters = this.dslFilters;
if (!dslFilters) {
return [];
}
return dslFilters.map((filter) => {
return {
meta: {
index: internalReferences?.[0].id,
type: 'query_string',
disabled: false,
negate: false,
alias: null,
key: 'query',
},
$state: {
store: 'appState',
},
query: filter,
} as Filter;
});
}

getJSON(
visualizationType: 'lnsXY' | 'lnsLegacyMetric' | 'lnsHeatmap' = 'lnsXY',
lastRefresh?: number
Expand All @@ -1290,7 +1319,7 @@ export class LensAttributes {
},
visualization: this.visualization,
query: query || { query: '', language: 'kuery' },
filters: [],
filters: this.getFilters(),
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ViewMode } from '@kbn/embeddable-plugin/common';
import { observabilityFeatureId } from '@kbn/observability-shared-plugin/public';
import styled from 'styled-components';
import { AnalyticsServiceSetup } from '@kbn/core-analytics-browser';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
import { useEBTTelemetry } from '../hooks/use_ebt_telemetry';
import { AllSeries } from '../../../..';
import { AppDataType, ReportViewType } from '../types';
Expand Down Expand Up @@ -57,6 +58,7 @@ export interface ExploratoryEmbeddableProps {
lineHeight?: number;
dataTestSubj?: string;
searchSessionId?: string;
dslFilters?: QueryDslQueryContainer[];
}

export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const useEmbeddableAttributes = ({
reportType,
reportConfigMap = {},
lensFormulaHelper,
dslFilters,
}: ExploratoryEmbeddableComponentProps) => {
const spaceId = useKibanaSpace();
const theme = useTheme();
Expand Down Expand Up @@ -51,7 +52,12 @@ export const useEmbeddableAttributes = ({
);
return lensAttributes?.getJSON('lnsHeatmap');
} else {
const lensAttributes = new LensAttributes(layerConfigs, reportType, lensFormulaHelper);
const lensAttributes = new LensAttributes(
layerConfigs,
reportType,
lensFormulaHelper,
dslFilters
);
return lensAttributes?.getJSON();
}
} catch (error) {
Expand All @@ -60,6 +66,7 @@ export const useEmbeddableAttributes = ({
}, [
attributes,
dataViewState,
dslFilters,
lensFormulaHelper,
reportConfigMap,
reportType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,18 @@ export const getTimeSpanFilter = () => ({
},
},
});

export const getQueryFilters = (query: string) => ({
query_string: {
query: `${query}*`,
fields: [
'monitor.name.text',
'tags',
'observer.geo.name',
'observer.name',
'urls',
'hosts',
'monitor.project.id',
],
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { UrlFilter } from '@kbn/exploratory-view-plugin/public';
import { useGetUrlParams } from '../../../hooks';
import { useKibanaSpace } from '../../../../../hooks/use_kibana_space';

export const useMonitorFilters = ({ forAlerts }: { forAlerts?: boolean }): UrlFilter[] => {
const { space } = useKibanaSpace();
const { locations, monitorTypes, tags, projects } = useGetUrlParams();

return [
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a lot of code branching in here, ideally we'd have some unit tests to make sure we avoid inadvertent modifications to the hook's output.

...(projects?.length ? [{ field: 'monitor.project.id', values: getValues(projects) }] : []),
...(monitorTypes?.length ? [{ field: 'monitor.type', values: getValues(monitorTypes) }] : []),
...(tags?.length ? [{ field: 'tags', values: getValues(tags) }] : []),
...(locations?.length ? [{ field: 'observer.geo.name', values: getValues(locations) }] : []),
...(space
? [{ field: forAlerts ? 'kibana.space_ids' : 'meta.space_id', values: [space.id] }]
: []),
];
};

const getValues = (values: string | string[]): string[] => {
return Array.isArray(values) ? values : [values];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 { useMemo } from 'react';
import { useGetUrlParams } from '../../../hooks';
import { getQueryFilters } from '../../../../../../common/constants/client_defaults';

export const useMonitorQueryFilters = () => {
const { query } = useGetUrlParams();

return useMemo(() => {
return query ? [getQueryFilters(query)] : undefined;
}, [query]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const MonitorAsyncError = () => {
defaultMessage="There was a problem running your monitors for one or more locations:"
/>
</p>
<ul>
<ul style={{ maxHeight: 100, overflow: 'auto' }}>
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
{Object.values(syncErrors ?? {}).map((e) => {
return (
<li key={e.locationId}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ export const MonitorStats = ({
<EuiFlexItem
css={{ display: 'flex', flexDirection: 'row', gap: euiTheme.size.l, height: '200px' }}
>
<MonitorTestRunsCount monitorIds={overviewStatus?.allIds ?? []} />
<MonitorTestRunsCount />
<EuiFlexItem grow={true}>
<MonitorTestRunsSparkline monitorIds={overviewStatus?.allIds ?? []} />
<MonitorTestRunsSparkline />
</EuiFlexItem>
</EuiFlexItem>
</EuiPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,36 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useTheme } from '@kbn/observability-shared-plugin/public';
import { ReportTypes } from '@kbn/exploratory-view-plugin/public';

import { useMonitorFilters } from '../../hooks/use_monitor_filters';
import { useRefreshedRange } from '../../../../hooks';
import { ClientPluginsStart } from '../../../../../../plugin';
import * as labels from '../labels';
import { useMonitorQueryFilters } from '../../hooks/use_monitor_query_filters';

export const MonitorTestRunsCount = ({ monitorIds }: { monitorIds: string[] }) => {
export const MonitorTestRunsCount = () => {
const {
exploratoryView: { ExploratoryViewEmbeddable },
} = useKibana<ClientPluginsStart>().services;
const theme = useTheme();

const { from, to } = useRefreshedRange(30, 'days');
const filters = useMonitorFilters({});
const queryFilter = useMonitorQueryFilters();

return (
<ExploratoryViewEmbeddable
dslFilters={queryFilter}
align="left"
reportType={ReportTypes.SINGLE_METRIC}
attributes={[
{
filters,
time: { from, to },
reportDefinitions: {
'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], // Show no data when monitorIds is empty
'monitor.type': ['http', 'tcp', 'browser', 'icmp'],
},
dataType: 'synthetics',
selectedMetricField: 'monitor_total_runs',
filters: [],
name: labels.TEST_RUNS_LABEL,
color: theme.eui.euiColorVis1,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@ import React, { useMemo } from 'react';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useTheme } from '@kbn/observability-shared-plugin/public';

import { useMonitorQueryFilters } from '../../hooks/use_monitor_query_filters';
import { useMonitorFilters } from '../../hooks/use_monitor_filters';
import { useRefreshedRange } from '../../../../hooks';
import { ClientPluginsStart } from '../../../../../../plugin';
import * as labels from '../labels';

export const MonitorTestRunsSparkline = ({ monitorIds }: { monitorIds: string[] }) => {
export const MonitorTestRunsSparkline = () => {
const {
exploratoryView: { ExploratoryViewEmbeddable },
} = useKibana<ClientPluginsStart>().services;

const theme = useTheme();

const { from, to } = useRefreshedRange(30, 'days');
const filters = useMonitorFilters({});
const queryFilter = useMonitorQueryFilters();

const attributes = useMemo(() => {
return [
{
seriesType: 'area' as const,
time: { from, to },
reportDefinitions: {
'monitor.id': monitorIds.length > 0 ? monitorIds : ['false-monitor-id'], // Show no data when monitorIds is empty
'monitor.type': ['http', 'tcp', 'browser', 'icmp'],
},
dataType: 'synthetics' as const,
selectedMetricField: 'total_test_runs',
filters: [],
filters,
name: labels.TEST_RUNS_LABEL,
color: theme.eui.euiColorVis1,
operationType: 'count',
},
];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [from, JSON.stringify({ ids: [...monitorIds].sort() }), theme.eui.euiColorVis1, to]);
}, [from, theme.eui.euiColorVis1, to]);

return (
<ExploratoryViewEmbeddable
Expand All @@ -51,6 +55,7 @@ export const MonitorTestRunsSparkline = ({ monitorIds }: { monitorIds: string[]
hideTicks={true}
attributes={attributes}
customHeight={'68px'}
dslFilters={queryFilter}
/>
);
};
Loading