123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import {Fragment} from 'react';
- import {useTheme} from '@emotion/react';
- import ChartZoom from 'sentry/components/charts/chartZoom';
- import ErrorPanel from 'sentry/components/charts/errorPanel';
- import EventsRequest from 'sentry/components/charts/eventsRequest';
- import {LineChart} from 'sentry/components/charts/lineChart';
- import {HeaderTitleLegend} from 'sentry/components/charts/styles';
- import TransitionChart from 'sentry/components/charts/transitionChart';
- import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
- import {getInterval, getSeriesSelection} from 'sentry/components/charts/utils';
- import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
- import Placeholder from 'sentry/components/placeholder';
- import QuestionTooltip from 'sentry/components/questionTooltip';
- import {IconWarning} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import type {Organization} from 'sentry/types/organization';
- import {browserHistory} from 'sentry/utils/browserHistory';
- import {getUtcToLocalDateObject} from 'sentry/utils/dates';
- import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
- import type EventView from 'sentry/utils/discover/eventView';
- import getDynamicText from 'sentry/utils/getDynamicText';
- import type {SpanSlug} from 'sentry/utils/performance/suspectSpans/types';
- import useApi from 'sentry/utils/useApi';
- import {useLocation} from 'sentry/utils/useLocation';
- import useRouter from 'sentry/utils/useRouter';
- import {getExclusiveTimeDisplayedValue} from '../utils';
- type Props = {
- eventView: EventView;
- organization: Organization;
- spanSlug: SpanSlug;
- withoutZerofill: boolean;
- };
- export default function ExclusiveTimeTimeSeries(props: Props) {
- const location = useLocation();
- const router = useRouter();
- const {organization, eventView, spanSlug, withoutZerofill} = props;
- const api = useApi();
- const theme = useTheme();
- const period = eventView.statsPeriod;
- const start = eventView.start ? getUtcToLocalDateObject(eventView.start) : null;
- const end = eventView.end ? getUtcToLocalDateObject(eventView.end) : null;
- const {utc} = normalizeDateTimeParams(location.query);
- const datetimeSelection = {
- start,
- end,
- period,
- };
- const yAxis = [
- 'percentileArray(spans_exclusive_time, 0.50)',
- 'percentileArray(spans_exclusive_time, 0.75)',
- 'percentileArray(spans_exclusive_time, 0.95)',
- 'percentileArray(spans_exclusive_time, 0.99)',
- ];
- const handleLegendSelectChanged = legendChange => {
- const {selected} = legendChange;
- const unselected = Object.keys(selected).filter(key => !selected[key]);
- const to = {
- ...location,
- query: {
- ...location.query,
- unselectedSeries: unselected,
- },
- };
- browserHistory.push(to);
- };
- return (
- <Fragment>
- <HeaderTitleLegend>
- {t('Self Time Breakdown')}
- <QuestionTooltip
- size="sm"
- position="top"
- title={t(
- 'Self Time Breakdown reflects the span self time by percentile over time.'
- )}
- />
- </HeaderTitleLegend>
- <ChartZoom
- router={router}
- period={period}
- start={start}
- end={end}
- utc={utc === 'true'}
- >
- {zoomRenderProps => (
- <EventsRequest
- api={api}
- organization={organization}
- project={eventView.project}
- environment={eventView.environment}
- start={start}
- end={end}
- period={period}
- interval={getInterval(datetimeSelection, 'high')}
- showLoading={false}
- query={eventView.query}
- includePrevious={false}
- yAxis={yAxis}
- partial
- withoutZerofill={withoutZerofill}
- queryExtras={{span: `${spanSlug.op}:${spanSlug.group}`}}
- generatePathname={org => `/organizations/${org.slug}/events-spans-stats/`}
- >
- {({results, errored, loading, reloading, timeframe}) => {
- if (errored) {
- return (
- <ErrorPanel>
- <IconWarning color="gray300" size="lg" />
- </ErrorPanel>
- );
- }
- const chartOptions = {
- grid: {
- left: '10px',
- right: '10px',
- top: '40px',
- bottom: '0px',
- },
- colors: theme.charts.getColorPalette(yAxis.length - 2),
- seriesOptions: {
- showSymbol: false,
- },
- tooltip: {
- trigger: 'axis' as const,
- // p50() coerces the axis to be time based
- valueFormatter: (value, _seriesName) =>
- tooltipFormatter(value, 'duration'),
- },
- xAxis: timeframe
- ? {
- min: timeframe.start,
- max: timeframe.end,
- }
- : undefined,
- yAxis: {
- axisLabel: {
- color: theme.chartLabel,
- formatter: (value: number) => axisLabelFormatter(value, 'duration'),
- },
- },
- };
- const legend = {
- right: 10,
- top: 5,
- selected: getSeriesSelection(location),
- };
- const formattedResults = results?.map(result => ({
- ...result,
- seriesName: getExclusiveTimeDisplayedValue(result.seriesName),
- }));
- return (
- <TransitionChart loading={loading} reloading={reloading}>
- <TransparentLoadingMask visible={reloading} />
- {getDynamicText({
- value: (
- <LineChart
- {...zoomRenderProps}
- {...chartOptions}
- legend={legend}
- onLegendSelectChanged={handleLegendSelectChanged}
- series={formattedResults ?? []}
- />
- ),
- fixed: <Placeholder height="200px" />,
- })}
- </TransitionChart>
- );
- }}
- </EventsRequest>
- )}
- </ChartZoom>
- </Fragment>
- );
- }
|