mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
250 lines
6.4 KiB
TypeScript
250 lines
6.4 KiB
TypeScript
import type {ReactNode} from 'react';
|
|
import type {Location} from 'history';
|
|
|
|
import type {GridColumnOrder} from 'sentry/components/gridEditable';
|
|
import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
|
|
import SortLink from 'sentry/components/gridEditable/sortLink';
|
|
import Link from 'sentry/components/links/link';
|
|
import {t} from 'sentry/locale';
|
|
import type {Organization, Project} from 'sentry/types';
|
|
import {defined} from 'sentry/utils';
|
|
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
|
|
import type {ColumnType} from 'sentry/utils/discover/fields';
|
|
import {fieldAlignment} from 'sentry/utils/discover/fields';
|
|
import {Container as TableCellContainer} from 'sentry/utils/discover/styles';
|
|
import type {SuspectSpans} from 'sentry/utils/performance/suspectSpans/types';
|
|
|
|
import {spanDetailsRouteWithQuery} from './spanDetails/utils';
|
|
import type {SpanSort, SpansTotalValues} from './types';
|
|
import {SpanSortOthers, SpanSortPercentiles} from './types';
|
|
|
|
type Props = {
|
|
isLoading: boolean;
|
|
location: Location;
|
|
organization: Organization;
|
|
sort: SpanSort;
|
|
suspectSpans: SuspectSpans;
|
|
totals: SpansTotalValues | null;
|
|
transactionName: string;
|
|
project?: Project;
|
|
};
|
|
|
|
export default function SuspectSpansTable(props: Props) {
|
|
const {
|
|
location,
|
|
organization,
|
|
transactionName,
|
|
isLoading,
|
|
suspectSpans,
|
|
totals,
|
|
sort,
|
|
project,
|
|
} = props;
|
|
|
|
const data: TableDataRowWithExtras[] = suspectSpans.map(suspectSpan => ({
|
|
operation: suspectSpan.op,
|
|
group: suspectSpan.group,
|
|
description: suspectSpan.description,
|
|
frequency:
|
|
// Frequency is computed using the `uniq` function in ClickHouse.
|
|
// Because it is an approximation, it can occasionally exceed the number of events.
|
|
defined(suspectSpan.frequency) && defined(totals?.['count()'])
|
|
? Math.min(1, suspectSpan.frequency / totals!['count()'])
|
|
: null,
|
|
avgOccurrences: suspectSpan.avgOccurrences,
|
|
p50ExclusiveTime: suspectSpan.p50ExclusiveTime,
|
|
p75ExclusiveTime: suspectSpan.p75ExclusiveTime,
|
|
p95ExclusiveTime: suspectSpan.p95ExclusiveTime,
|
|
p99ExclusiveTime: suspectSpan.p99ExclusiveTime,
|
|
sumExclusiveTime: suspectSpan.sumExclusiveTime,
|
|
}));
|
|
|
|
return (
|
|
<GridEditable
|
|
isLoading={isLoading}
|
|
data={data}
|
|
columnOrder={COLUMN_ORDER[sort].map(column => COLUMNS[column])}
|
|
columnSortBy={[]}
|
|
grid={{
|
|
renderHeadCell,
|
|
renderBodyCell: renderBodyCellWithMeta(
|
|
location,
|
|
organization,
|
|
transactionName,
|
|
project
|
|
),
|
|
}}
|
|
location={location}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function renderHeadCell(column: TableColumn, _index: number): ReactNode {
|
|
const align = fieldAlignment(column.key, COLUMN_TYPE[column.key]);
|
|
return (
|
|
<SortLink
|
|
title={column.name}
|
|
align={align}
|
|
direction={undefined}
|
|
canSort={false}
|
|
generateSortLink={() => undefined}
|
|
/>
|
|
);
|
|
}
|
|
|
|
function renderBodyCellWithMeta(
|
|
location: Location,
|
|
organization: Organization,
|
|
transactionName: string,
|
|
project?: Project
|
|
) {
|
|
return function (
|
|
column: TableColumn,
|
|
dataRow: TableDataRowWithExtras
|
|
): React.ReactNode {
|
|
const fieldRenderer = getFieldRenderer(column.key, COLUMN_TYPE);
|
|
|
|
if (column.key === 'description') {
|
|
const target = spanDetailsRouteWithQuery({
|
|
orgSlug: organization.slug,
|
|
transaction: transactionName,
|
|
query: location.query,
|
|
spanSlug: {op: dataRow.operation, group: dataRow.group},
|
|
projectID: project?.id,
|
|
});
|
|
return (
|
|
<TableCellContainer>
|
|
<Link to={target}>{dataRow[column.key] ?? t('(unnamed span)')}</Link>
|
|
</TableCellContainer>
|
|
);
|
|
}
|
|
|
|
return fieldRenderer(dataRow, {location, organization});
|
|
};
|
|
}
|
|
|
|
type TableColumnKey =
|
|
| 'operation'
|
|
| 'description'
|
|
| 'frequency'
|
|
| 'avgOccurrences'
|
|
| 'p50ExclusiveTime'
|
|
| 'p75ExclusiveTime'
|
|
| 'p95ExclusiveTime'
|
|
| 'p99ExclusiveTime'
|
|
| 'sumExclusiveTime';
|
|
|
|
type TableColumn = GridColumnOrder<TableColumnKey>;
|
|
|
|
type TableDataRow = Record<TableColumnKey, any>;
|
|
|
|
type TableDataRowWithExtras = TableDataRow & {
|
|
group: string;
|
|
};
|
|
|
|
const COLUMN_ORDER: Record<SpanSort, TableColumnKey[]> = {
|
|
[SpanSortOthers.AVG_OCCURRENCE]: [
|
|
'operation',
|
|
'description',
|
|
'avgOccurrences',
|
|
'frequency',
|
|
'p75ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
[SpanSortOthers.SUM_EXCLUSIVE_TIME]: [
|
|
'operation',
|
|
'description',
|
|
'frequency',
|
|
'p75ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
[SpanSortPercentiles.P50_EXCLUSIVE_TIME]: [
|
|
'operation',
|
|
'description',
|
|
'frequency',
|
|
'p50ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
[SpanSortPercentiles.P75_EXCLUSIVE_TIME]: [
|
|
'operation',
|
|
'description',
|
|
'frequency',
|
|
'p75ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
[SpanSortPercentiles.P95_EXCLUSIVE_TIME]: [
|
|
'operation',
|
|
'description',
|
|
'frequency',
|
|
'p95ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
[SpanSortPercentiles.P99_EXCLUSIVE_TIME]: [
|
|
'operation',
|
|
'description',
|
|
'frequency',
|
|
'p99ExclusiveTime',
|
|
'sumExclusiveTime',
|
|
],
|
|
};
|
|
|
|
const COLUMNS: Record<TableColumnKey, TableColumn> = {
|
|
operation: {
|
|
key: 'operation',
|
|
name: t('Span Operation'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
description: {
|
|
key: 'description',
|
|
name: t('Span Name'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
frequency: {
|
|
key: 'frequency',
|
|
name: t('Frequency'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
avgOccurrences: {
|
|
key: 'avgOccurrences',
|
|
name: t('Average Occurrences'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
p50ExclusiveTime: {
|
|
key: 'p50ExclusiveTime',
|
|
name: t('P50 Self Time'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
p75ExclusiveTime: {
|
|
key: 'p75ExclusiveTime',
|
|
name: t('P75 Self Time'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
p95ExclusiveTime: {
|
|
key: 'p95ExclusiveTime',
|
|
name: t('P95 Self Time'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
p99ExclusiveTime: {
|
|
key: 'p99ExclusiveTime',
|
|
name: t('P99 Self Time'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
sumExclusiveTime: {
|
|
key: 'sumExclusiveTime',
|
|
name: t('Total Self Time'),
|
|
width: COL_WIDTH_UNDEFINED,
|
|
},
|
|
};
|
|
|
|
const COLUMN_TYPE: Record<TableColumnKey, ColumnType> = {
|
|
operation: 'string',
|
|
description: 'string',
|
|
frequency: 'percentage',
|
|
avgOccurrences: 'number',
|
|
p50ExclusiveTime: 'duration',
|
|
p75ExclusiveTime: 'duration',
|
|
p95ExclusiveTime: 'duration',
|
|
p99ExclusiveTime: 'duration',
|
|
sumExclusiveTime: 'duration',
|
|
};
|