mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[APM] Fix loading state in trace samples (#142831)
* Fix loading state in trace samples * Add space before the loading to avoid jumping * refactor results from fetch
This commit is contained in:
parent
4b88273767
commit
9782417578
14 changed files with 177 additions and 144 deletions
|
@ -5,14 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
TraceSearchQuery,
|
||||
TraceSearchType,
|
||||
} from '../../../../common/trace_explorer';
|
||||
import { useApmParams } from '../../../hooks/use_apm_params';
|
||||
import { useFetcher, FETCH_STATUS } from '../../../hooks/use_fetcher';
|
||||
import { useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { useTimeRange } from '../../../hooks/use_time_range';
|
||||
import { ApmDatePicker } from '../../shared/date_picker/apm_date_picker';
|
||||
import { fromQuery, toQuery, push } from '../../shared/links/url_helpers';
|
||||
|
@ -20,6 +20,10 @@ import { useWaterfallFetcher } from '../transaction_details/use_waterfall_fetche
|
|||
import { WaterfallWithSummary } from '../transaction_details/waterfall_with_summary';
|
||||
import { TraceSearchBox } from './trace_search_box';
|
||||
|
||||
const INITIAL_DATA = {
|
||||
traceSamples: [],
|
||||
};
|
||||
|
||||
export function TraceExplorer() {
|
||||
const [query, setQuery] = useState<TraceSearchQuery>({
|
||||
query: '',
|
||||
|
@ -54,7 +58,11 @@ export function TraceExplorer() {
|
|||
rangeTo,
|
||||
});
|
||||
|
||||
const { data: traceSamplesData, status: traceSamplesStatus } = useFetcher(
|
||||
const {
|
||||
data = INITIAL_DATA,
|
||||
status,
|
||||
error,
|
||||
} = useFetcher(
|
||||
(callApmApi) => {
|
||||
return callApmApi('GET /internal/apm/traces/find', {
|
||||
params: {
|
||||
|
@ -72,7 +80,7 @@ export function TraceExplorer() {
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
const nextSample = traceSamplesData?.samples[0];
|
||||
const nextSample = data.traceSamples[0];
|
||||
const nextWaterfallItemId = '';
|
||||
history.replace({
|
||||
...history.location,
|
||||
|
@ -83,18 +91,23 @@ export function TraceExplorer() {
|
|||
waterfallItemId: nextWaterfallItemId,
|
||||
}),
|
||||
});
|
||||
}, [traceSamplesData, history]);
|
||||
}, [data, history]);
|
||||
|
||||
const { waterfall, status: waterfallStatus } = useWaterfallFetcher({
|
||||
const waterfallFetchResult = useWaterfallFetcher({
|
||||
traceId,
|
||||
transactionId,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
const isLoading =
|
||||
traceSamplesStatus === FETCH_STATUS.LOADING ||
|
||||
waterfallStatus === FETCH_STATUS.LOADING;
|
||||
const traceSamplesFetchResult = useMemo(
|
||||
() => ({
|
||||
data,
|
||||
status,
|
||||
error,
|
||||
}),
|
||||
[data, status, error]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
|
@ -127,8 +140,9 @@ export function TraceExplorer() {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<WaterfallWithSummary
|
||||
waterfallFetchResult={waterfallFetchResult}
|
||||
traceSamplesFetchResult={traceSamplesFetchResult}
|
||||
environment={environment}
|
||||
isLoading={isLoading}
|
||||
onSampleClick={(sample) => {
|
||||
push(history, {
|
||||
query: {
|
||||
|
@ -145,11 +159,12 @@ export function TraceExplorer() {
|
|||
},
|
||||
});
|
||||
}}
|
||||
traceSamples={traceSamplesData?.samples ?? []}
|
||||
waterfall={waterfall}
|
||||
detailTab={detailTab}
|
||||
waterfallItemId={waterfallItemId}
|
||||
serviceName={waterfall.entryWaterfallTransaction?.doc.service.name}
|
||||
serviceName={
|
||||
waterfallFetchResult.waterfall.entryWaterfallTransaction?.doc
|
||||
.service.name
|
||||
}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -87,8 +87,11 @@ describe('transaction_details/distribution', () => {
|
|||
<TransactionDistribution
|
||||
onChartSelection={jest.fn()}
|
||||
onClearSelection={jest.fn()}
|
||||
traceSamples={[]}
|
||||
traceSamplesStatus={useFetcherModule.FETCH_STATUS.LOADING}
|
||||
traceSamplesFetchResult={{
|
||||
data: { traceSamples: [] },
|
||||
status: useFetcherModule.FETCH_STATUS.LOADING,
|
||||
error: undefined,
|
||||
}}
|
||||
/>,
|
||||
|
||||
{ wrapper: Wrapper }
|
||||
|
@ -112,8 +115,11 @@ describe('transaction_details/distribution', () => {
|
|||
<TransactionDistribution
|
||||
onChartSelection={jest.fn()}
|
||||
onClearSelection={jest.fn()}
|
||||
traceSamples={[]}
|
||||
traceSamplesStatus={useFetcherModule.FETCH_STATUS.SUCCESS}
|
||||
traceSamplesFetchResult={{
|
||||
data: { traceSamples: [] },
|
||||
status: useFetcherModule.FETCH_STATUS.LOADING,
|
||||
error: undefined,
|
||||
}}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
@ -12,9 +12,7 @@ import { useHistory } from 'react-router-dom';
|
|||
|
||||
import { ProcessorEvent } from '@kbn/observability-plugin/common';
|
||||
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||
|
||||
import type { TabContentProps } from '../types';
|
||||
import { useWaterfallFetcher } from '../use_waterfall_fetcher';
|
||||
import { WaterfallWithSummary } from '../waterfall_with_summary';
|
||||
|
||||
|
@ -26,21 +24,20 @@ import { HeightRetainer } from '../../../shared/height_retainer';
|
|||
import { fromQuery, toQuery } from '../../../shared/links/url_helpers';
|
||||
import { TransactionTab } from '../waterfall_with_summary/transaction_tabs';
|
||||
import { useTransactionDistributionChartData } from './use_transaction_distribution_chart_data';
|
||||
import { TraceSamplesFetchResult } from '../../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
|
||||
interface TransactionDistributionProps {
|
||||
onChartSelection: (event: XYBrushEvent) => void;
|
||||
onClearSelection: () => void;
|
||||
selection?: [number, number];
|
||||
traceSamples: TabContentProps['traceSamples'];
|
||||
traceSamplesStatus: FETCH_STATUS;
|
||||
traceSamplesFetchResult: TraceSamplesFetchResult;
|
||||
}
|
||||
|
||||
export function TransactionDistribution({
|
||||
onChartSelection,
|
||||
onClearSelection,
|
||||
selection,
|
||||
traceSamples,
|
||||
traceSamplesStatus,
|
||||
traceSamplesFetchResult,
|
||||
}: TransactionDistributionProps) {
|
||||
const { urlParams } = useLegacyUrlParams();
|
||||
const { traceId, transactionId } = urlParams;
|
||||
|
@ -52,7 +49,7 @@ export function TransactionDistribution({
|
|||
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
|
||||
|
||||
const history = useHistory();
|
||||
const { waterfall, status: waterfallStatus } = useWaterfallFetcher({
|
||||
const waterfallFetchResult = useWaterfallFetcher({
|
||||
traceId,
|
||||
transactionId,
|
||||
start,
|
||||
|
@ -65,12 +62,10 @@ export function TransactionDistribution({
|
|||
} = useApmParams('/services/{serviceName}/transactions/view');
|
||||
|
||||
const { serviceName } = useApmServiceContext();
|
||||
const isLoading =
|
||||
waterfallStatus === FETCH_STATUS.LOADING ||
|
||||
traceSamplesStatus === FETCH_STATUS.LOADING;
|
||||
|
||||
const markerCurrentEvent =
|
||||
waterfall.entryWaterfallTransaction?.doc.transaction.duration.us;
|
||||
waterfallFetchResult.waterfall.entryWaterfallTransaction?.doc.transaction
|
||||
.duration.us;
|
||||
|
||||
const {
|
||||
chartData,
|
||||
|
@ -121,9 +116,8 @@ export function TransactionDistribution({
|
|||
serviceName={serviceName}
|
||||
waterfallItemId={waterfallItemId}
|
||||
detailTab={detailTab as TransactionTab | undefined}
|
||||
waterfall={waterfall}
|
||||
isLoading={isLoading}
|
||||
traceSamples={traceSamples}
|
||||
waterfallFetchResult={waterfallFetchResult}
|
||||
traceSamplesFetchResult={traceSamplesFetchResult}
|
||||
/>
|
||||
</div>
|
||||
</HeightRetainer>
|
||||
|
|
|
@ -18,8 +18,7 @@ import { useLicenseContext } from '../../../context/license/use_license_context'
|
|||
import { LicensePrompt } from '../../shared/license_prompt';
|
||||
|
||||
import { FailedTransactionsCorrelations } from '../correlations/failed_transactions_correlations';
|
||||
|
||||
import type { TabContentProps } from './types';
|
||||
import { TabContentProps } from './transaction_details_tabs';
|
||||
|
||||
function FailedTransactionsCorrelationsTab({ onFilter }: TabContentProps) {
|
||||
const license = useLicenseContext();
|
||||
|
|
|
@ -18,8 +18,7 @@ import { useLicenseContext } from '../../../context/license/use_license_context'
|
|||
import { LicensePrompt } from '../../shared/license_prompt';
|
||||
|
||||
import { LatencyCorrelations } from '../correlations/latency_correlations';
|
||||
|
||||
import type { TabContentProps } from './types';
|
||||
import { TabContentProps } from './transaction_details_tabs';
|
||||
|
||||
function LatencyCorrelationsTab({ onFilter }: TabContentProps) {
|
||||
const license = useLicenseContext();
|
||||
|
|
|
@ -10,15 +10,14 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TransactionDistribution } from './distribution';
|
||||
import type { TabContentProps } from './types';
|
||||
import { TabContentProps } from './transaction_details_tabs';
|
||||
|
||||
function TraceSamplesTab({
|
||||
selectSampleFromChartSelection,
|
||||
clearChartSelection,
|
||||
sampleRangeFrom,
|
||||
sampleRangeTo,
|
||||
traceSamples,
|
||||
traceSamplesStatus,
|
||||
traceSamplesFetchResult,
|
||||
}: TabContentProps) {
|
||||
return (
|
||||
<TransactionDistribution
|
||||
|
@ -29,8 +28,7 @@ function TraceSamplesTab({
|
|||
? [sampleRangeFrom, sampleRangeTo]
|
||||
: undefined
|
||||
}
|
||||
traceSamplesStatus={traceSamplesStatus}
|
||||
traceSamples={traceSamples}
|
||||
traceSamplesFetchResult={traceSamplesFetchResult}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,13 @@ import { omit } from 'lodash';
|
|||
import { useHistory } from 'react-router-dom';
|
||||
import { EuiPanel, EuiSpacer, EuiTabs, EuiTab } from '@elastic/eui';
|
||||
|
||||
import { XYBrushEvent } from '@elastic/charts';
|
||||
import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { useApmParams } from '../../../hooks/use_apm_params';
|
||||
import { useTransactionTraceSamplesFetcher } from '../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
import {
|
||||
TraceSamplesFetchResult,
|
||||
useTransactionTraceSamplesFetcher,
|
||||
} from '../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
|
||||
import { maybe } from '../../../../common/utils/maybe';
|
||||
import { fromQuery, toQuery } from '../../shared/links/url_helpers';
|
||||
|
@ -24,6 +28,15 @@ import { traceSamplesTab } from './trace_samples_tab';
|
|||
import { useSampleChartSelection } from '../../../hooks/use_sample_chart_selection';
|
||||
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
|
||||
|
||||
export interface TabContentProps {
|
||||
clearChartSelection: () => void;
|
||||
onFilter: () => void;
|
||||
sampleRangeFrom?: number;
|
||||
sampleRangeTo?: number;
|
||||
selectSampleFromChartSelection: (selection: XYBrushEvent) => void;
|
||||
traceSamplesFetchResult: TraceSamplesFetchResult;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
traceSamplesTab,
|
||||
latencyCorrelationsTab,
|
||||
|
@ -41,15 +54,14 @@ export function TransactionDetailsTabs() {
|
|||
tabs.find((tab) => tab.key === currentTab) ?? traceSamplesTab;
|
||||
|
||||
const { environment, kuery, transactionName } = query;
|
||||
const { traceSamplesData, traceSamplesStatus } =
|
||||
useTransactionTraceSamplesFetcher({
|
||||
transactionName,
|
||||
kuery,
|
||||
environment,
|
||||
});
|
||||
|
||||
const traceSamplesFetchResult = useTransactionTraceSamplesFetcher({
|
||||
transactionName,
|
||||
kuery,
|
||||
environment,
|
||||
});
|
||||
|
||||
const { sampleRangeFrom, sampleRangeTo, transactionId, traceId } = urlParams;
|
||||
const { traceSamples } = traceSamplesData;
|
||||
|
||||
const { clearChartSelection, selectSampleFromChartSelection } =
|
||||
useSampleChartSelection();
|
||||
|
@ -65,14 +77,19 @@ export function TransactionDetailsTabs() {
|
|||
}, [traceSamplesTabKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const selectedSample = traceSamples.find(
|
||||
const selectedSample = traceSamplesFetchResult.data?.traceSamples.find(
|
||||
(sample) =>
|
||||
sample.transactionId === transactionId && sample.traceId === traceId
|
||||
);
|
||||
|
||||
if (traceSamplesStatus === FETCH_STATUS.SUCCESS && !selectedSample) {
|
||||
if (
|
||||
traceSamplesFetchResult.status === FETCH_STATUS.SUCCESS &&
|
||||
!selectedSample
|
||||
) {
|
||||
// selected sample was not found. select a new one:
|
||||
const preferredSample = maybe(traceSamples[0]);
|
||||
const preferredSample = maybe(
|
||||
traceSamplesFetchResult.data?.traceSamples[0]
|
||||
);
|
||||
|
||||
history.replace({
|
||||
...history.location,
|
||||
|
@ -85,7 +102,7 @@ export function TransactionDetailsTabs() {
|
|||
}),
|
||||
});
|
||||
}
|
||||
}, [history, traceSamples, transactionId, traceId, traceSamplesStatus]);
|
||||
}, [history, transactionId, traceId, traceSamplesFetchResult]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -112,8 +129,7 @@ export function TransactionDetailsTabs() {
|
|||
sampleRangeFrom,
|
||||
sampleRangeTo,
|
||||
selectSampleFromChartSelection,
|
||||
traceSamples,
|
||||
traceSamplesStatus,
|
||||
traceSamplesFetchResult,
|
||||
}}
|
||||
/>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -1,21 +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 { XYBrushEvent } from '@elastic/charts';
|
||||
|
||||
import type { TraceSample } from '../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
|
||||
|
||||
export interface TabContentProps {
|
||||
clearChartSelection: () => void;
|
||||
onFilter: () => void;
|
||||
sampleRangeFrom?: number;
|
||||
sampleRangeTo?: number;
|
||||
selectSampleFromChartSelection: (selection: XYBrushEvent) => void;
|
||||
traceSamples: TraceSample[];
|
||||
traceSamplesStatus: FETCH_STATUS;
|
||||
}
|
|
@ -16,6 +16,7 @@ const INITIAL_DATA: APIReturnType<'GET /internal/apm/traces/{traceId}'> = {
|
|||
exceedsMax: false,
|
||||
linkedChildrenOfSpanCountBySpanId: {},
|
||||
};
|
||||
export type WaterfallFetchResult = ReturnType<typeof useWaterfallFetcher>;
|
||||
|
||||
export function useWaterfallFetcher({
|
||||
traceId,
|
||||
|
|
|
@ -12,22 +12,22 @@ import {
|
|||
EuiPagination,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiLoadingContent,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { LoadingStatePrompt } from '../../../shared/loading_state_prompt';
|
||||
import { TransactionSummary } from '../../../shared/summary/transaction_summary';
|
||||
import { TransactionActionMenu } from '../../../shared/transaction_action_menu/transaction_action_menu';
|
||||
import type { TraceSample } from '../../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
import { MaybeViewTraceLink } from './maybe_view_trace_link';
|
||||
import { TransactionTab, TransactionTabs } from './transaction_tabs';
|
||||
import { IWaterfall } from './waterfall_container/waterfall/waterfall_helpers/waterfall_helpers';
|
||||
import { Environment } from '../../../../../common/environment_rt';
|
||||
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
||||
import { TraceSamplesFetchResult } from '../../../../hooks/use_transaction_trace_samples_fetcher';
|
||||
import { WaterfallFetchResult } from '../use_waterfall_fetcher';
|
||||
|
||||
interface Props {
|
||||
waterfall: IWaterfall;
|
||||
isLoading: boolean;
|
||||
traceSamples: TraceSample[];
|
||||
waterfallFetchResult: WaterfallFetchResult;
|
||||
traceSamplesFetchResult: TraceSamplesFetchResult;
|
||||
environment: Environment;
|
||||
onSampleClick: (sample: { transactionId: string; traceId: string }) => void;
|
||||
onTabClick: (tab: string) => void;
|
||||
|
@ -37,9 +37,8 @@ interface Props {
|
|||
}
|
||||
|
||||
export function WaterfallWithSummary({
|
||||
waterfall,
|
||||
isLoading,
|
||||
traceSamples,
|
||||
waterfallFetchResult,
|
||||
traceSamplesFetchResult,
|
||||
environment,
|
||||
onSampleClick,
|
||||
onTabClick,
|
||||
|
@ -51,17 +50,27 @@ export function WaterfallWithSummary({
|
|||
|
||||
useEffect(() => {
|
||||
setSampleActivePage(0);
|
||||
}, [traceSamples]);
|
||||
}, [traceSamplesFetchResult.data.traceSamples]);
|
||||
|
||||
const goToSample = (index: number) => {
|
||||
setSampleActivePage(index);
|
||||
const sample = traceSamples[index];
|
||||
const sample = traceSamplesFetchResult.data.traceSamples[index];
|
||||
onSampleClick(sample);
|
||||
};
|
||||
|
||||
const { entryWaterfallTransaction } = waterfall;
|
||||
const { entryWaterfallTransaction } = waterfallFetchResult.waterfall;
|
||||
const isLoading =
|
||||
waterfallFetchResult.status === FETCH_STATUS.LOADING ||
|
||||
traceSamplesFetchResult.status === FETCH_STATUS.LOADING;
|
||||
const isSucceded =
|
||||
waterfallFetchResult.status === FETCH_STATUS.SUCCESS &&
|
||||
traceSamplesFetchResult.status === FETCH_STATUS.SUCCESS;
|
||||
|
||||
if ((!entryWaterfallTransaction || traceSamples.length === 0) && !isLoading) {
|
||||
if (
|
||||
!entryWaterfallTransaction &&
|
||||
traceSamplesFetchResult.data.traceSamples.length === 0 &&
|
||||
isSucceded
|
||||
) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
title={
|
||||
|
@ -91,9 +100,9 @@ export function WaterfallWithSummary({
|
|||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{traceSamples.length > 0 && (
|
||||
{traceSamplesFetchResult.data.traceSamples.length > 0 && (
|
||||
<EuiPagination
|
||||
pageCount={traceSamples.length}
|
||||
pageCount={traceSamplesFetchResult.data.traceSamples.length}
|
||||
activePage={sampleActivePage}
|
||||
onPageClick={goToSample}
|
||||
compressed
|
||||
|
@ -112,7 +121,7 @@ export function WaterfallWithSummary({
|
|||
<MaybeViewTraceLink
|
||||
isLoading={isLoading}
|
||||
transaction={entryTransaction}
|
||||
waterfall={waterfall}
|
||||
waterfall={waterfallFetchResult.waterfall}
|
||||
environment={environment}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
@ -123,25 +132,34 @@ export function WaterfallWithSummary({
|
|||
<EuiSpacer size="s" />
|
||||
|
||||
{isLoading || !entryTransaction ? (
|
||||
<LoadingStatePrompt />
|
||||
) : (
|
||||
<>
|
||||
<TransactionSummary
|
||||
errorCount={waterfall.apiResponse.errorDocs.length}
|
||||
totalDuration={waterfall.rootTransaction?.transaction.duration.us}
|
||||
transaction={entryTransaction}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
<TransactionTabs
|
||||
transaction={entryTransaction}
|
||||
detailTab={detailTab}
|
||||
serviceName={serviceName}
|
||||
waterfallItemId={waterfallItemId}
|
||||
onTabClick={onTabClick}
|
||||
waterfall={waterfall}
|
||||
/>
|
||||
<EuiLoadingContent lines={1} data-test-sub="loading-content" />
|
||||
</>
|
||||
) : (
|
||||
<TransactionSummary
|
||||
errorCount={
|
||||
waterfallFetchResult.waterfall.apiResponse.errorDocs.length
|
||||
}
|
||||
totalDuration={
|
||||
waterfallFetchResult.waterfall.rootTransaction?.transaction.duration
|
||||
.us
|
||||
}
|
||||
transaction={entryTransaction}
|
||||
/>
|
||||
)}
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<TransactionTabs
|
||||
transaction={entryTransaction}
|
||||
detailTab={detailTab}
|
||||
serviceName={serviceName}
|
||||
waterfallItemId={waterfallItemId}
|
||||
onTabClick={onTabClick}
|
||||
waterfall={waterfallFetchResult.waterfall}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
|
||||
import { EuiSpacer, EuiTab, EuiTabs, EuiLoadingContent } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { LogStream } from '@kbn/infra-plugin/public';
|
||||
import React from 'react';
|
||||
|
@ -15,7 +15,8 @@ import { WaterfallContainer } from './waterfall_container';
|
|||
import { IWaterfall } from './waterfall_container/waterfall/waterfall_helpers/waterfall_helpers';
|
||||
|
||||
interface Props {
|
||||
transaction: Transaction;
|
||||
transaction?: Transaction;
|
||||
isLoading: boolean;
|
||||
waterfall: IWaterfall;
|
||||
detailTab?: TransactionTab;
|
||||
serviceName?: string;
|
||||
|
@ -26,6 +27,7 @@ interface Props {
|
|||
export function TransactionTabs({
|
||||
transaction,
|
||||
waterfall,
|
||||
isLoading,
|
||||
detailTab,
|
||||
waterfallItemId,
|
||||
serviceName,
|
||||
|
@ -36,7 +38,7 @@ export function TransactionTabs({
|
|||
const TabContent = currentTab.component;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<>
|
||||
<EuiTabs>
|
||||
{tabs.map(({ key, label }) => {
|
||||
return (
|
||||
|
@ -54,14 +56,17 @@ export function TransactionTabs({
|
|||
</EuiTabs>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<TabContent
|
||||
waterfallItemId={waterfallItemId}
|
||||
serviceName={serviceName}
|
||||
waterfall={waterfall}
|
||||
transaction={transaction}
|
||||
/>
|
||||
</React.Fragment>
|
||||
{isLoading || !transaction ? (
|
||||
<EuiLoadingContent lines={3} data-test-sub="loading-content" />
|
||||
) : (
|
||||
<TabContent
|
||||
waterfallItemId={waterfallItemId}
|
||||
serviceName={serviceName}
|
||||
waterfall={waterfall}
|
||||
transaction={transaction}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useFetcher } from './use_fetcher';
|
||||
import { useLegacyUrlParams } from '../context/url_params_context/use_url_params';
|
||||
import { useApmServiceContext } from '../context/apm_service/use_apm_service_context';
|
||||
import { useApmParams } from './use_apm_params';
|
||||
import { useTimeRange } from './use_time_range';
|
||||
|
||||
export interface TraceSample {
|
||||
traceId: string;
|
||||
transactionId: string;
|
||||
}
|
||||
|
||||
const INITIAL_DATA = {
|
||||
traceSamples: [] as TraceSample[],
|
||||
traceSamples: [],
|
||||
};
|
||||
|
||||
export type TraceSamplesFetchResult = ReturnType<
|
||||
typeof useTransactionTraceSamplesFetcher
|
||||
>;
|
||||
|
||||
export function useTransactionTraceSamplesFetcher({
|
||||
transactionName,
|
||||
kuery,
|
||||
|
@ -87,9 +87,12 @@ export function useTransactionTraceSamplesFetcher({
|
|||
]
|
||||
);
|
||||
|
||||
return {
|
||||
traceSamplesData: data,
|
||||
traceSamplesStatus: status,
|
||||
traceSamplesError: error,
|
||||
};
|
||||
return useMemo(
|
||||
() => ({
|
||||
data,
|
||||
status,
|
||||
error,
|
||||
}),
|
||||
[data, status, error]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -169,14 +169,14 @@ const findTracesRoute = createApmServerRoute({
|
|||
handler: async (
|
||||
resources
|
||||
): Promise<{
|
||||
samples: Array<{ traceId: string; transactionId: string }>;
|
||||
traceSamples: Array<{ traceId: string; transactionId: string }>;
|
||||
}> => {
|
||||
const { start, end, environment, query, type } = resources.params.query;
|
||||
|
||||
const setup = await setupRequest(resources);
|
||||
|
||||
return {
|
||||
samples: await getTraceSamplesByQuery({
|
||||
traceSamples: await getTraceSamplesByQuery({
|
||||
setup,
|
||||
start,
|
||||
end,
|
||||
|
|
|
@ -58,13 +58,13 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
}
|
||||
|
||||
function fetchTraces(samples: Array<{ traceId: string; transactionId: string }>) {
|
||||
if (!samples.length) {
|
||||
function fetchTraces(traceSamples: Array<{ traceId: string; transactionId: string }>) {
|
||||
if (!traceSamples.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
samples.map(async ({ traceId }) => {
|
||||
traceSamples.map(async ({ traceId }) => {
|
||||
const response = await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/traces/{traceId}`,
|
||||
params: {
|
||||
|
@ -90,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body).to.eql({
|
||||
samples: [],
|
||||
traceSamples: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -168,28 +168,28 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
describe('and the query is empty', () => {
|
||||
it('returns all trace samples', async () => {
|
||||
const {
|
||||
body: { samples },
|
||||
body: { traceSamples },
|
||||
} = await fetchTraceSamples({
|
||||
query: '',
|
||||
type: TraceSearchType.kql,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
});
|
||||
|
||||
expect(samples.length).to.eql(5);
|
||||
expect(traceSamples.length).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and query is set', () => {
|
||||
it('returns the relevant traces', async () => {
|
||||
const {
|
||||
body: { samples },
|
||||
body: { traceSamples },
|
||||
} = await fetchTraceSamples({
|
||||
query: 'span.destination.service.resource:elasticsearch',
|
||||
type: TraceSearchType.kql,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
});
|
||||
|
||||
expect(samples.length).to.eql(1);
|
||||
expect(traceSamples.length).to.eql(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -214,7 +214,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
describe('and the query is set', () => {
|
||||
it('returns the correct trace samples for transaction sequences', async () => {
|
||||
const {
|
||||
body: { samples },
|
||||
body: { traceSamples },
|
||||
} = await fetchTraceSamples({
|
||||
query: `sequence by trace.id
|
||||
[ transaction where service.name == "java" ]
|
||||
|
@ -223,7 +223,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
environment: 'ENVIRONMENT_ALL',
|
||||
});
|
||||
|
||||
const traces = await fetchTraces(samples);
|
||||
const traces = await fetchTraces(traceSamples);
|
||||
|
||||
expect(traces.length).to.eql(2);
|
||||
|
||||
|
@ -242,7 +242,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns the correct trace samples for join sequences', async () => {
|
||||
const {
|
||||
body: { samples },
|
||||
body: { traceSamples },
|
||||
} = await fetchTraceSamples({
|
||||
query: `sequence by trace.id
|
||||
[ span where service.name == "java" ] by span.id
|
||||
|
@ -251,7 +251,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
environment: 'ENVIRONMENT_ALL',
|
||||
});
|
||||
|
||||
const traces = await fetchTraces(samples);
|
||||
const traces = await fetchTraces(traceSamples);
|
||||
|
||||
expect(traces.length).to.eql(1);
|
||||
|
||||
|
@ -266,7 +266,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns the correct trace samples for exit spans', async () => {
|
||||
const {
|
||||
body: { samples },
|
||||
body: { traceSamples },
|
||||
} = await fetchTraceSamples({
|
||||
query: `sequence by trace.id
|
||||
[ transaction where service.name == "python" ]
|
||||
|
@ -275,7 +275,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
environment: 'ENVIRONMENT_ALL',
|
||||
});
|
||||
|
||||
const traces = await fetchTraces(samples);
|
||||
const traces = await fetchTraces(traceSamples);
|
||||
|
||||
expect(traces.length).to.eql(1);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue