mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Profiling] Click on series in stacked chart opens traces view (#153325)
In the Threads view when the user clicks on one of the stacked bar it navigates to the Traces view filtering the result based on the category. https://user-images.githubusercontent.com/55978943/226430014-0fbeaad8-6ecb-4e34-a841-926ee2c8ddb9.mov
This commit is contained in:
parent
1c8d63775d
commit
ba755f95e0
5 changed files with 130 additions and 25 deletions
|
@ -20,6 +20,7 @@ import { useProfilingDependencies } from '../contexts/profiling_dependencies/use
|
|||
import { ProfilingAppPageTemplate } from '../profiling_app_page_template';
|
||||
import { StackedBarChart } from '../stacked_bar_chart';
|
||||
import { getStackTracesTabs } from './get_stack_traces_tabs';
|
||||
import { getTracesViewRouteParams } from './utils';
|
||||
|
||||
export function StackTracesView() {
|
||||
const routePath = useProfilingRoutePath();
|
||||
|
@ -80,6 +81,13 @@ export function StackTracesView() {
|
|||
|
||||
const { data } = state;
|
||||
|
||||
function onStackedBarClick(category: string) {
|
||||
profilingRouter.push(
|
||||
'/stacktraces/{topNType}',
|
||||
getTracesViewRouteParams({ query, topNType: path.topNType, category })
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ProfilingAppPageTemplate tabs={tabs}>
|
||||
<EuiFlexGroup direction="column">
|
||||
|
@ -140,6 +148,7 @@ export function StackTracesView() {
|
|||
});
|
||||
}}
|
||||
showFrames={topNType === TopNType.Traces}
|
||||
onClick={topNType === TopNType.Threads ? onStackedBarClick : undefined}
|
||||
/>
|
||||
</AsyncComponent>
|
||||
</EuiFlexItem>
|
||||
|
@ -155,7 +164,7 @@ export function StackTracesView() {
|
|||
/>
|
||||
</AsyncComponent>
|
||||
</EuiFlexItem>
|
||||
{(data?.charts.length ?? 0) > limit ? (
|
||||
{(data?.charts.length ?? 0) > limit && (
|
||||
<EuiFlexItem>
|
||||
<EuiButton
|
||||
onClick={() => {
|
||||
|
@ -173,7 +182,7 @@ export function StackTracesView() {
|
|||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
</ProfilingAppPageTemplate>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 { StackTracesDisplayOption, TopNType } from '../../../common/stack_traces';
|
||||
import { getTracesViewRouteParams } from './utils';
|
||||
|
||||
describe('stack traces view utils', () => {
|
||||
describe('getTracesViewRouteParams', () => {
|
||||
it('filters by category only', () => {
|
||||
expect(
|
||||
getTracesViewRouteParams({
|
||||
query: {
|
||||
rangeFrom: 'now-15m',
|
||||
rangeTo: 'now',
|
||||
displayAs: StackTracesDisplayOption.StackTraces,
|
||||
kuery: '',
|
||||
limit: 10,
|
||||
},
|
||||
topNType: TopNType.Traces,
|
||||
category: 'Foo',
|
||||
})
|
||||
).toEqual({
|
||||
path: { topNType: 'traces' },
|
||||
query: {
|
||||
rangeFrom: 'now-15m',
|
||||
rangeTo: 'now',
|
||||
displayAs: 'stackTraces',
|
||||
kuery: 'Stacktrace.id:"Foo"',
|
||||
limit: 10,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps current filter and adds category', () => {
|
||||
expect(
|
||||
getTracesViewRouteParams({
|
||||
query: {
|
||||
rangeFrom: 'now-15m',
|
||||
rangeTo: 'now',
|
||||
displayAs: StackTracesDisplayOption.StackTraces,
|
||||
kuery: 'container.name:"bar"',
|
||||
limit: 10,
|
||||
},
|
||||
topNType: TopNType.Traces,
|
||||
category: 'Foo',
|
||||
})
|
||||
).toEqual({
|
||||
path: { topNType: 'traces' },
|
||||
query: {
|
||||
rangeFrom: 'now-15m',
|
||||
rangeTo: 'now',
|
||||
displayAs: 'stackTraces',
|
||||
kuery: '(container.name:"bar") AND Stacktrace.id:"Foo"',
|
||||
limit: 10,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { TypeOf } from '@kbn/typed-react-router-config';
|
||||
import { getFieldNameForTopNType, TopNType } from '../../../common/stack_traces';
|
||||
import { ProfilingRoutes } from '../../routing';
|
||||
|
||||
export function getTracesViewRouteParams({
|
||||
query,
|
||||
topNType,
|
||||
category,
|
||||
}: {
|
||||
query: TypeOf<ProfilingRoutes, '/stacktraces/{topNType}'>['query'];
|
||||
topNType: TopNType;
|
||||
category: string;
|
||||
}) {
|
||||
return {
|
||||
path: { topNType: TopNType.Traces },
|
||||
query: {
|
||||
...query,
|
||||
kuery: `${query.kuery ? `(${query.kuery}) AND ` : ''}${getFieldNameForTopNType(
|
||||
topNType
|
||||
)}:"${category}"`,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -22,11 +22,11 @@ import {
|
|||
import { EuiPanel } from '@elastic/eui';
|
||||
import { keyBy } from 'lodash';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { TopNSample, TopNSubchart } from '../../common/topn';
|
||||
import { useKibanaTimeZoneSetting } from '../hooks/use_kibana_timezone_setting';
|
||||
import { useProfilingChartsTheme } from '../hooks/use_profiling_charts_theme';
|
||||
import { asPercentage } from '../utils/formatters/as_percentage';
|
||||
import { SubChart } from './subchart';
|
||||
import { TopNSample, TopNSubchart } from '../../../common/topn';
|
||||
import { useKibanaTimeZoneSetting } from '../../hooks/use_kibana_timezone_setting';
|
||||
import { useProfilingChartsTheme } from '../../hooks/use_profiling_charts_theme';
|
||||
import { asPercentage } from '../../utils/formatters/as_percentage';
|
||||
import { SubChart } from '../subchart';
|
||||
|
||||
// 2 * padding (16px)
|
||||
const MAX_TOOLTIP_WIDTH = 224;
|
||||
|
@ -37,15 +37,17 @@ export interface StackedBarChartProps {
|
|||
onBrushEnd: (range: { rangeFrom: string; rangeTo: string }) => void;
|
||||
charts: TopNSubchart[];
|
||||
showFrames: boolean;
|
||||
onClick?: (category: string) => void;
|
||||
}
|
||||
|
||||
export const StackedBarChart: React.FC<StackedBarChartProps> = ({
|
||||
export function StackedBarChart({
|
||||
height,
|
||||
asPercentages,
|
||||
onBrushEnd,
|
||||
charts,
|
||||
showFrames,
|
||||
}) => {
|
||||
onClick,
|
||||
}: StackedBarChartProps) {
|
||||
const chartsbyCategoryMap = useMemo(() => {
|
||||
return keyBy(charts, 'Category');
|
||||
}, [charts]);
|
||||
|
@ -107,6 +109,15 @@ export const StackedBarChart: React.FC<StackedBarChartProps> = ({
|
|||
const [value] = events[0] as XYChartElementEvent;
|
||||
setHighlightedSample(value.datum as TopNSample);
|
||||
}}
|
||||
onElementClick={
|
||||
onClick
|
||||
? (elements) => {
|
||||
const [value] = elements[0] as XYChartElementEvent;
|
||||
const sample = value.datum as TopNSample;
|
||||
onClick(sample.Category);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onElementOut={() => {
|
||||
setHighlightedSample(undefined);
|
||||
}}
|
||||
|
@ -135,4 +146,4 @@ export const StackedBarChart: React.FC<StackedBarChartProps> = ({
|
|||
/>
|
||||
</Chart>
|
||||
);
|
||||
};
|
||||
}
|
|
@ -32,7 +32,6 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { StackFrameMetadata } from '../../common/profiling';
|
||||
import { getFieldNameForTopNType, TopNType } from '../../common/stack_traces';
|
||||
import { CountPerTime, OTHER_BUCKET_LABEL, TopNSample } from '../../common/topn';
|
||||
import { useKibanaTimeZoneSetting } from '../hooks/use_kibana_timezone_setting';
|
||||
import { useProfilingChartsTheme } from '../hooks/use_profiling_charts_theme';
|
||||
|
@ -41,6 +40,7 @@ import { useProfilingRouter } from '../hooks/use_profiling_router';
|
|||
import { asNumber } from '../utils/formatters/as_number';
|
||||
import { asPercentage } from '../utils/formatters/as_percentage';
|
||||
import { StackFrameSummary } from './stack_frame_summary';
|
||||
import { getTracesViewRouteParams } from './stack_traces_view/utils';
|
||||
|
||||
export interface SubChartProps {
|
||||
index: number;
|
||||
|
@ -62,7 +62,7 @@ export interface SubChartProps {
|
|||
|
||||
const NUM_DISPLAYED_FRAMES = 5;
|
||||
|
||||
export const SubChart: React.FC<SubChartProps> = ({
|
||||
export function SubChart({
|
||||
index,
|
||||
color,
|
||||
category,
|
||||
|
@ -78,24 +78,17 @@ export const SubChart: React.FC<SubChartProps> = ({
|
|||
showFrames,
|
||||
padTitle,
|
||||
sample,
|
||||
}) => {
|
||||
}: SubChartProps) {
|
||||
const theme = useEuiTheme();
|
||||
|
||||
const profilingRouter = useProfilingRouter();
|
||||
|
||||
const { path, query } = useProfilingParams('/stacktraces/{topNType}');
|
||||
|
||||
const href = profilingRouter.link('/stacktraces/{topNType}', {
|
||||
path: {
|
||||
topNType: TopNType.Traces,
|
||||
},
|
||||
query: {
|
||||
...query,
|
||||
kuery: `${query.kuery ? `(${query.kuery}) AND ` : ''}${getFieldNameForTopNType(
|
||||
path.topNType
|
||||
)}:"${category}"`,
|
||||
},
|
||||
});
|
||||
const href = profilingRouter.link(
|
||||
'/stacktraces/{topNType}',
|
||||
getTracesViewRouteParams({ query, topNType: path.topNType, category })
|
||||
);
|
||||
|
||||
const timeZone = useKibanaTimeZoneSetting();
|
||||
|
||||
|
@ -304,4 +297,4 @@ export const SubChart: React.FC<SubChartProps> = ({
|
|||
{bottomElement}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue