mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
190 lines
6.3 KiB
TypeScript
190 lines
6.3 KiB
TypeScript
import {Fragment} from 'react';
|
|
import {browserHistory} from 'react-router';
|
|
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';
|
|
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>
|
|
);
|
|
}
|