123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import {Fragment, useCallback, useMemo} from 'react';
- import {browserHistory} from 'react-router';
- import styled from '@emotion/styled';
- import type {Location} from 'history';
- import {CompactSelect} from 'sentry/components/compactSelect';
- import * as Layout from 'sentry/components/layouts/thirds';
- import Pagination from 'sentry/components/pagination';
- import {AggregateFlamegraphPanel} from 'sentry/components/profiling/aggregateFlamegraphPanel';
- import {ProfileEventsTable} from 'sentry/components/profiling/profileEventsTable';
- import {SuspectFunctionsTable} from 'sentry/components/profiling/suspectFunctions/suspectFunctionsTable';
- import {mobile} from 'sentry/data/platformCategories';
- import {t} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import type {PageFilters, Project} from 'sentry/types';
- import {useProfileEvents} from 'sentry/utils/profiling/hooks/useProfileEvents';
- import {formatSort} from 'sentry/utils/profiling/hooks/utils';
- import {decodeScalar} from 'sentry/utils/queryString';
- import {ProfilesChart} from 'sentry/views/profiling/landing/profileCharts';
- interface ProfileSummaryContentProps {
- location: Location;
- project: Project;
- query: string;
- selection: PageFilters;
- transaction: string;
- }
- function ProfileSummaryContent(props: ProfileSummaryContentProps) {
- const fields = useMemo(
- () => getProfilesTableFields(props.project.platform),
- [props.project]
- );
- const profilesCursor = useMemo(
- () => decodeScalar(props.location.query.cursor),
- [props.location.query.cursor]
- );
- const sort = formatSort<ProfilingFieldType>(
- decodeScalar(props.location.query.sort),
- fields,
- {
- key: 'timestamp',
- order: 'desc',
- }
- );
- const profiles = useProfileEvents<ProfilingFieldType>({
- cursor: profilesCursor,
- fields,
- query: props.query,
- sort,
- limit: 5,
- referrer: 'api.profiling.profile-summary-table',
- });
- const handleFilterChange = useCallback(
- value => {
- browserHistory.push({
- ...props.location,
- query: {...props.location.query, cursor: undefined, sort: value},
- });
- },
- [props.location]
- );
- return (
- <Fragment>
- <Layout.Main fullWidth>
- <ProfilesChart
- referrer="api.profiling.profile-summary-chart"
- query={props.query}
- hideCount
- compact
- />
- <AggregateFlamegraphPanel transaction={props.transaction} />
- <SuspectFunctionsTable
- project={props.project}
- transaction={props.transaction}
- analyticsPageSource="profiling_transaction"
- />
- <TableHeader>
- <CompactSelect
- triggerProps={{prefix: t('Filter'), size: 'xs'}}
- value={sort.order === 'asc' ? sort.key : `-${sort.key}`}
- options={FILTER_OPTIONS}
- onChange={opt => handleFilterChange(opt.value)}
- />
- <StyledPagination
- pageLinks={
- profiles.status === 'success'
- ? profiles.getResponseHeader?.('Link') ?? null
- : null
- }
- size="xs"
- />
- </TableHeader>
- <ProfileEventsTable
- columns={fields}
- data={profiles.status === 'success' ? profiles.data : null}
- error={profiles.status === 'error' ? t('Unable to load profiles') : null}
- isLoading={profiles.status === 'loading'}
- sort={sort}
- />
- </Layout.Main>
- </Fragment>
- );
- }
- const ALL_FIELDS = [
- 'profile.id',
- 'timestamp',
- 'release',
- 'device.model',
- 'device.classification',
- 'device.arch',
- 'transaction.duration',
- 'p75()',
- 'p95()',
- 'p99()',
- 'count()',
- 'last_seen()',
- ] as const;
- export type ProfilingFieldType = (typeof ALL_FIELDS)[number];
- export function getProfilesTableFields(platform: Project['platform']) {
- if (mobile.includes(platform as any)) {
- return MOBILE_FIELDS;
- }
- return DEFAULT_FIELDS;
- }
- const MOBILE_FIELDS: ProfilingFieldType[] = [
- 'profile.id',
- 'timestamp',
- 'release',
- 'device.model',
- 'device.classification',
- 'device.arch',
- 'transaction.duration',
- ];
- const DEFAULT_FIELDS: ProfilingFieldType[] = [
- 'profile.id',
- 'timestamp',
- 'release',
- 'transaction.duration',
- ];
- const FILTER_OPTIONS = [
- {
- label: t('Recent Profiles'),
- value: '-timestamp',
- },
- {
- label: t('Slowest Profiles'),
- value: '-transaction.duration',
- },
- {
- label: t('Fastest Profiles'),
- value: 'transaction.duration',
- },
- ];
- const TableHeader = styled('div')`
- display: flex;
- justify-content: space-between;
- margin-bottom: ${space(1)};
- `;
- const StyledPagination = styled(Pagination)`
- margin: 0 0 0 ${space(1)};
- `;
- export {ProfileSummaryContent};
|