in x-pack/solutions/observability/plugins/apm/public/components/app/correlations/latency_correlations.tsx [67:392]
export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) {
const {
core: { notifications },
} = useApmPluginContext();
const { euiTheme } = useEuiTheme();
const { progress, response, startFetch, cancelFetch } = useLatencyCorrelations();
const { overallHistogram, hasData, status } = getOverallHistogram(response, progress.isRunning);
useEffect(() => {
if (progress.error) {
notifications.toasts.addDanger({
title: i18n.translate('xpack.apm.correlations.latencyCorrelations.errorTitle', {
defaultMessage: 'An error occurred fetching correlations',
}),
text: progress.error,
});
}
}, [progress.error, notifications.toasts]);
const [pinnedSignificantTerm, setPinnedSignificantTerm] = useState<LatencyCorrelation | null>(
null
);
const [selectedSignificantTerm, setSelectedSignificantTerm] = useState<LatencyCorrelation | null>(
null
);
const history = useHistory();
const trackApmEvent = useUiTracker({ app: 'apm' });
const onAddFilter = useCallback<OnAddFilter>(
({ fieldName, fieldValue, include }) => {
if (include) {
push(history, {
query: {
kuery: `${fieldName}:"${fieldValue}"`,
},
});
trackApmEvent({ metric: 'correlations_term_include_filter' });
} else {
push(history, {
query: {
kuery: `not ${fieldName}:"${fieldValue}"`,
},
});
trackApmEvent({ metric: 'correlations_term_exclude_filter' });
}
onFilter();
},
[onFilter, history, trackApmEvent]
);
const mlCorrelationColumns: Array<EuiBasicTableColumn<LatencyCorrelation>> = useMemo(
() => [
{
width: '116px',
field: 'correlation',
name: (
<>
{i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationLabel',
{
defaultMessage: 'Correlation',
}
)}
<EuiIconTip
content={i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription',
{
defaultMessage:
'The correlation score [0-1] of an attribute; the greater the score, the more an attribute increases latency.',
}
)}
size="s"
color="subdued"
type="questionInCircle"
className="eui-alignTop"
/>
</>
),
render: (_, { correlation }) => {
return <div>{asPreciseDecimal(correlation, 2)}</div>;
},
sortable: true,
},
{
width: '116px',
field: 'pValue',
name: (
<>
{i18n.translate(
'xpack.apm.correlations.failedTransactions.correlationsTable.impactLabel',
{
defaultMessage: 'Impact',
}
)}
</>
),
render: (_, { correlation, isFallbackResult }) => {
const label = getLatencyCorrelationImpactLabel(correlation, isFallbackResult);
return label ? <EuiBadge color={label.color}>{label.impact}</EuiBadge> : null;
},
sortable: true,
},
{
field: 'fieldName',
name: i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.fieldNameLabel',
{ defaultMessage: 'Field name' }
),
render: (_, { fieldName, fieldValue }) => (
<>
{fieldName}
<FieldStatsPopover
fieldName={fieldName}
fieldValue={fieldValue}
onAddFilter={onAddFilter}
/>
</>
),
sortable: true,
},
{
field: 'fieldValue',
name: i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.fieldValueLabel',
{ defaultMessage: 'Field value' }
),
render: (_, { fieldValue }) => String(fieldValue).slice(0, 50),
sortable: true,
},
{
width: '100px',
actions: [
{
name: i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.filterLabel',
{ defaultMessage: 'Filter' }
),
description: ({ fieldName }) =>
i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.filterDescription',
{ defaultMessage: 'Filter by {fieldName}', values: { fieldName } }
),
icon: 'plusInCircle',
type: 'icon',
onClick: ({ fieldName, fieldValue }: LatencyCorrelation) =>
onAddFilter({
fieldName,
fieldValue,
include: true,
}),
},
{
name: i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.excludeLabel',
{ defaultMessage: 'Exclude' }
),
description: ({ fieldName }) =>
i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.excludeDescription',
{ defaultMessage: 'Filter out {fieldName}', values: { fieldName } }
),
icon: 'minusInCircle',
type: 'icon',
onClick: ({ fieldName, fieldValue }: LatencyCorrelation) =>
onAddFilter({
fieldName,
fieldValue,
include: false,
}),
},
],
name: i18n.translate(
'xpack.apm.correlations.latencyCorrelations.correlationsTable.actionsLabel',
{ defaultMessage: 'Filter' }
),
},
],
[onAddFilter]
);
const [sortField, setSortField] = useState<keyof LatencyCorrelation>('correlation');
const [sortDirection, setSortDirection] = useState<Direction>('desc');
const onTableChange = useCallback(({ sort }: any) => {
const { field: currentSortField, direction: currentSortDirection } = sort;
setSortField(currentSortField);
setSortDirection(currentSortDirection);
}, []);
const sorting: EuiTableSortingType<LatencyCorrelation> = {
sort: { field: sortField, direction: sortDirection },
};
const histogramTerms = useMemo(
() => orderBy(response.latencyCorrelations ?? [], sortField, sortDirection),
[response.latencyCorrelations, sortField, sortDirection]
);
const selectedHistogram = useMemo(() => {
if (!histogramTerms) {
return;
} else if (selectedSignificantTerm) {
return histogramTerms?.find(
(h) =>
h.fieldName === selectedSignificantTerm.fieldName &&
h.fieldValue === selectedSignificantTerm.fieldValue
);
} else if (pinnedSignificantTerm) {
return histogramTerms.find(
(h) =>
h.fieldName === pinnedSignificantTerm.fieldName &&
h.fieldValue === pinnedSignificantTerm.fieldValue
);
}
return histogramTerms[0];
}, [histogramTerms, pinnedSignificantTerm, selectedSignificantTerm]);
const showCorrelationsTable = progress.isRunning || histogramTerms.length > 0;
const showCorrelationsEmptyStatePrompt =
histogramTerms.length < 1 && (progress.loaded === 1 || !progress.isRunning);
const transactionDistributionChartData = getTransactionDistributionChartData({
euiTheme,
allTransactionsHistogram: overallHistogram,
selectedTerm: selectedHistogram,
});
return (
<div data-test-subj="apmLatencyCorrelationsTabContent">
<EuiFlexGroup
css={css`
min-height: ${MIN_TAB_TITLE_HEIGHT};
`}
alignItems="center"
gutterSize="s"
>
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<h5 data-test-subj="apmCorrelationsLatencyCorrelationsChartTitle">
{i18n.translate('xpack.apm.correlations.latencyCorrelations.panelTitle', {
defaultMessage: 'Latency distribution',
})}
</h5>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ChartTitleToolTip />
</EuiFlexItem>
<EuiFlexItem>
<TotalDocCountLabel
eventType={ProcessorEvent.transaction}
totalDocCount={response.totalDocCount}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<LatencyCorrelationsHelpPopover />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<DurationDistributionChart
markerValue={response.percentileThresholdValue ?? 0}
data={transactionDistributionChartData}
hasData={hasData}
status={status}
eventType={ProcessorEvent.transaction}
/>
<EuiSpacer size="s" />
<EuiTitle size="xs">
<h5 data-test-subj="apmCorrelationsLatencyCorrelationsTablePanelTitle">
{i18n.translate('xpack.apm.correlations.latencyCorrelations.tableTitle', {
defaultMessage: 'Correlations',
})}
</h5>
</EuiTitle>
<EuiSpacer size="s" />
<CorrelationsProgressControls
progress={progress.loaded}
isRunning={progress.isRunning}
onRefresh={startFetch}
onCancel={cancelFetch}
/>
{response.ccsWarning && (
<>
<EuiSpacer size="m" />
{/* Latency correlations uses ES aggs that are available since 7.14 */}
<CrossClusterSearchCompatibilityWarning version="7.14" />
</>
)}
<EuiSpacer size="m" />
<div data-test-subj="apmCorrelationsTable">
{showCorrelationsTable && (
<CorrelationsTable<LatencyCorrelation>
columns={mlCorrelationColumns}
rowHeader="correlation"
significantTerms={histogramTerms}
status={progress.isRunning ? FETCH_STATUS.LOADING : FETCH_STATUS.SUCCESS}
setPinnedSignificantTerm={setPinnedSignificantTerm}
setSelectedSignificantTerm={setSelectedSignificantTerm}
selectedTerm={selectedHistogram}
onTableChange={onTableChange}
sorting={sorting}
/>
)}
{showCorrelationsEmptyStatePrompt && <CorrelationsEmptyStatePrompt />}
</div>
</div>
);
}