mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
178 lines
4.6 KiB
TypeScript
178 lines
4.6 KiB
TypeScript
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};
|