Skip to content

Commit

Permalink
[APM] Services inventory: add time comparisons to match service overv…
Browse files Browse the repository at this point in the history
…iew design (#107094) (#107607)

* adding comparison to inventory page

* new api to get detailed statistics

* show comparison data

* adding api test

* fixing unit test

* fixing ts issue

* adding loading to table

* refactoring

* fixing TS issue

* addressing PR comments

* fixing merge

* addressing PR comments

* fixing api test

* adding comment

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Cauê Marcondes <[email protected]>
  • Loading branch information
kibanamachine and cauemarcondes authored Aug 4, 2021
1 parent 9234cba commit a57b716
Show file tree
Hide file tree
Showing 16 changed files with 694 additions and 219 deletions.
103 changes: 89 additions & 14 deletions x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect } from 'react';
import uuid from 'uuid';
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';
import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
Expand All @@ -16,26 +17,44 @@ import { useLocalStorage } from '../../../hooks/useLocalStorage';
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
import { useUpgradeAssistantHref } from '../../shared/Links/kibana';
import { SearchBar } from '../../shared/search_bar';
import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison';
import { NoServicesMessage } from './no_services_message';
import { ServiceList } from './service_list';
import { MLCallout } from './service_list/MLCallout';

const initialData = {
items: [],
hasHistoricalData: true,
hasLegacyData: false,
requestId: '',
mainStatisticsData: {
items: [],
hasHistoricalData: true,
hasLegacyData: false,
},
};

let hasDisplayedToast = false;

function useServicesFetcher() {
const {
urlParams: { environment, kuery, start, end },
urlParams: {
environment,
kuery,
start,
end,
comparisonEnabled,
comparisonType,
},
} = useUrlParams();
const { core } = useApmPluginContext();
const upgradeAssistantHref = useUpgradeAssistantHref();

const { data = initialData, status } = useFetcher(
const { offset } = getTimeRangeComparison({
start,
end,
comparisonEnabled,
comparisonType,
});

const { data = initialData, status: mainStatisticsStatus } = useFetcher(
(callApmApi) => {
if (start && end) {
return callApmApi({
Expand All @@ -48,14 +67,50 @@ function useServicesFetcher() {
end,
},
},
}).then((mainStatisticsData) => {
return {
requestId: uuid(),
mainStatisticsData,
};
});
}
},
[environment, kuery, start, end]
);

const { mainStatisticsData, requestId } = data;

const { data: comparisonData, status: comparisonStatus } = useFetcher(
(callApmApi) => {
if (start && end && mainStatisticsData.items.length) {
return callApmApi({
endpoint: 'GET /api/apm/services/detailed_statistics',
params: {
query: {
environment,
kuery,
start,
end,
serviceNames: JSON.stringify(
mainStatisticsData.items
.map(({ serviceName }) => serviceName)
// Service name is sorted to guarantee the same order every time this API is called so the result can be cached.
.sort()
),
offset,
},
},
});
}
},
// only fetches detailed statistics when requestId is invalidated by main statistics api call or offset is changed
// eslint-disable-next-line react-hooks/exhaustive-deps
[requestId, offset],
{ preservePreviousData: false }
);

useEffect(() => {
if (data.hasLegacyData && !hasDisplayedToast) {
if (mainStatisticsData.hasLegacyData && !hasDisplayedToast) {
hasDisplayedToast = true;

core.notifications.toasts.addWarning({
Expand All @@ -82,14 +137,30 @@ function useServicesFetcher() {
),
});
}
}, [data.hasLegacyData, upgradeAssistantHref, core.notifications.toasts]);
}, [
mainStatisticsData.hasLegacyData,
upgradeAssistantHref,
core.notifications.toasts,
]);

return { servicesData: data, servicesStatus: status };
return {
servicesData: mainStatisticsData,
servicesStatus: mainStatisticsStatus,
comparisonData,
isLoading:
mainStatisticsStatus === FETCH_STATUS.LOADING ||
comparisonStatus === FETCH_STATUS.LOADING,
};
}

export function ServiceInventory() {
const { core } = useApmPluginContext();
const { servicesData, servicesStatus } = useServicesFetcher();
const {
servicesData,
servicesStatus,
comparisonData,
isLoading,
} = useServicesFetcher();

const {
anomalyDetectionJobsData,
Expand All @@ -111,7 +182,7 @@ export function ServiceInventory() {

return (
<>
<SearchBar />
<SearchBar showTimeComparison />
<EuiFlexGroup direction="column" gutterSize="s">
{displayMlCallout && (
<EuiFlexItem>
Expand All @@ -120,12 +191,16 @@ export function ServiceInventory() {
)}
<EuiFlexItem>
<ServiceList
isLoading={isLoading}
items={servicesData.items}
comparisonData={comparisonData}
noItemsMessage={
<NoServicesMessage
historicalDataFound={servicesData.hasHistoricalData}
status={servicesStatus}
/>
!isLoading && (
<NoServicesMessage
historicalDataFound={servicesData.hasHistoricalData}
status={servicesStatus}
/>
)
}
/>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { SetupInstructionsLink } from '../../shared/Links/SetupInstructionsLink';
import { LoadingStatePrompt } from '../../shared/LoadingStatePrompt';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
import { ErrorStatePrompt } from '../../shared/ErrorStatePrompt';
import { useUpgradeAssistantHref } from '../../shared/Links/kibana';
import { SetupInstructionsLink } from '../../shared/Links/SetupInstructionsLink';

interface Props {
// any data submitted from APM agents found (not just in the given time range)
Expand All @@ -23,10 +22,6 @@ interface Props {
export function NoServicesMessage({ historicalDataFound, status }: Props) {
const upgradeAssistantHref = useUpgradeAssistantHref();

if (status === 'loading') {
return <LoadingStatePrompt />;
}

if (status === 'failure') {
return <ErrorStatePrompt />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@
*/

import React from 'react';
import { Coordinate } from '../../../../../typings/timeseries';
import { SparkPlot } from '../../../shared/charts/spark_plot';

export function ServiceListMetric({
color,
series,
valueLabel,
comparisonSeries,
}: {
color: 'euiColorVis1' | 'euiColorVis0' | 'euiColorVis7';
series?: Array<{ x: number; y: number | null }>;
series?: Coordinate[];
comparisonSeries?: Coordinate[];
valueLabel: React.ReactNode;
}) {
return <SparkPlot valueLabel={valueLabel} series={series} color={color} />;
return (
<SparkPlot
valueLabel={valueLabel}
series={series}
color={color}
comparisonSeries={comparisonSeries}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ export const items: ServiceListAPIResponse['items'] = [
serviceName: 'opbeans-node',
transactionType: 'request',
agentName: 'nodejs',
transactionsPerMinute: { value: 0, timeseries: [] },
transactionErrorRate: { value: 46.06666666666667, timeseries: [] },
avgResponseTime: { value: null, timeseries: [] },
throughput: 0,
transactionErrorRate: 46.06666666666667,
latency: null,
environments: ['test'],
},
{
serviceName: 'opbeans-python',
transactionType: 'page-load',
agentName: 'python',
transactionsPerMinute: { value: 86.93333333333334, timeseries: [] },
transactionErrorRate: { value: 12.6, timeseries: [] },
avgResponseTime: { value: 91535.42944785276, timeseries: [] },
throughput: 86.93333333333334,
transactionErrorRate: 12.6,
latency: 91535.42944785276,
environments: [],
},
];
Loading

0 comments on commit a57b716

Please sign in to comment.