123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- import {Fragment} from 'react';
- import {browserHistory} from 'react-router';
- import {Location} from 'history';
- import GridEditable, {
- COL_WIDTH_UNDEFINED,
- GridColumnHeader,
- } from 'sentry/components/gridEditable';
- import Pagination, {CursorHandler} from 'sentry/components/pagination';
- import {Organization} from 'sentry/types';
- import {EventsMetaType} from 'sentry/utils/discover/eventView';
- import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
- import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
- import {decodeScalar} from 'sentry/utils/queryString';
- import {useLocation} from 'sentry/utils/useLocation';
- import useOrganization from 'sentry/utils/useOrganization';
- import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
- import {SpanDescriptionCell} from 'sentry/views/starfish/components/tableCells/spanDescriptionCell';
- import {useSpanList} from 'sentry/views/starfish/queries/useSpanList';
- import {ModuleName, SpanMetricsField} from 'sentry/views/starfish/types';
- import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
- import {DataTitles, getThroughputTitle} from 'sentry/views/starfish/views/spans/types';
- import type {ValidSort} from 'sentry/views/starfish/views/spans/useModuleSort';
- type Row = {
- 'avg(span.self_time)': number;
- 'http_error_count()': number;
- 'span.description': string;
- 'span.domain': Array<string>;
- 'span.group': string;
- 'span.op': string;
- 'spm()': number;
- 'time_spent_percentage()': number;
- };
- type Column = GridColumnHeader<keyof Row>;
- type Props = {
- moduleName: ModuleName;
- sort: ValidSort;
- columnOrder?: Column[];
- endpoint?: string;
- limit?: number;
- method?: string;
- spanCategory?: string;
- };
- const {SPAN_SELF_TIME, SPAN_DESCRIPTION, SPAN_GROUP, SPAN_OP, PROJECT_ID, SPAN_DOMAIN} =
- SpanMetricsField;
- export default function SpansTable({
- moduleName,
- sort,
- columnOrder,
- spanCategory,
- endpoint,
- method,
- limit = 25,
- }: Props) {
- const location = useLocation();
- const organization = useOrganization();
- const cursor = decodeScalar(location.query?.[QueryParameterNames.SPANS_CURSOR]);
- const {isLoading, data, meta, pageLinks} = useSpanList(
- moduleName ?? ModuleName.ALL,
- endpoint,
- method,
- spanCategory,
- [sort],
- limit,
- 'api.starfish.use-span-list',
- cursor
- );
- const handleCursor: CursorHandler = (newCursor, pathname, query) => {
- browserHistory.push({
- pathname,
- query: {...query, [QueryParameterNames.SPANS_CURSOR]: newCursor},
- });
- };
- const shouldTrackVCD = Boolean(endpoint);
- return (
- <Fragment>
- <VisuallyCompleteWithData
- id="SpansTable"
- hasData={(data?.length ?? 0) > 0}
- isLoading={isLoading}
- disabled={shouldTrackVCD}
- >
- <GridEditable
- isLoading={isLoading}
- data={data as Row[]}
- columnOrder={columnOrder ?? getColumns(moduleName, spanCategory)}
- columnSortBy={[
- {
- key: sort.field,
- order: sort.kind,
- },
- ]}
- grid={{
- renderHeadCell: column =>
- renderHeadCell({
- column,
- sort,
- location,
- sortParameterName: QueryParameterNames.SPANS_SORT,
- }),
- renderBodyCell: (column, row) =>
- renderBodyCell(
- column,
- row,
- moduleName,
- meta,
- location,
- organization,
- endpoint,
- method
- ),
- }}
- location={location}
- />
- <Pagination pageLinks={pageLinks} onCursor={handleCursor} />
- </VisuallyCompleteWithData>
- </Fragment>
- );
- }
- function renderBodyCell(
- column: Column,
- row: Row,
- moduleName: ModuleName,
- meta: EventsMetaType | undefined,
- location: Location,
- organization: Organization,
- endpoint?: string,
- endpointMethod?: string
- ) {
- if (column.key === SPAN_DESCRIPTION) {
- return (
- <SpanDescriptionCell
- moduleName={moduleName}
- description={row[SPAN_DESCRIPTION]}
- group={row[SPAN_GROUP]}
- projectId={row[PROJECT_ID]}
- endpoint={endpoint}
- endpointMethod={endpointMethod}
- />
- );
- }
- if (!meta || !meta?.fields) {
- return row[column.key];
- }
- const renderer = getFieldRenderer(column.key, meta.fields, false);
- const rendered = renderer(row, {
- location,
- organization,
- unit: meta.units?.[column.key],
- });
- return rendered;
- }
- function getDomainHeader(moduleName: ModuleName) {
- if (moduleName === ModuleName.HTTP) {
- return 'Host';
- }
- if (moduleName === ModuleName.DB) {
- return 'Table';
- }
- return 'Domain';
- }
- function getDescriptionHeader(moduleName: ModuleName, spanCategory?: string) {
- if (moduleName === ModuleName.HTTP) {
- return 'URL Request';
- }
- if (moduleName === ModuleName.DB) {
- return 'Query Description';
- }
- if (spanCategory === 'cache') {
- return 'Cache Query';
- }
- if (spanCategory === 'serialize') {
- return 'Serializer';
- }
- if (spanCategory === 'middleware') {
- return 'Middleware';
- }
- if (spanCategory === 'app') {
- return 'Application Task';
- }
- if (moduleName === 'other') {
- return 'Requests';
- }
- return 'Description';
- }
- function getColumns(moduleName: ModuleName, spanCategory?: string): Column[] {
- const description = getDescriptionHeader(moduleName, spanCategory);
- const domain = getDomainHeader(moduleName);
- const order = [
- // We don't show the operation selector in specific modules, so there's no
- // point having that column
- [ModuleName.ALL, ModuleName.OTHER].includes(moduleName)
- ? {
- key: SPAN_OP,
- name: 'Operation',
- width: 120,
- }
- : undefined,
- {
- key: SPAN_DESCRIPTION,
- name: description,
- width: COL_WIDTH_UNDEFINED,
- },
- ...(moduleName !== ModuleName.ALL && moduleName !== ModuleName.DB
- ? [
- {
- key: SPAN_DOMAIN,
- name: domain,
- width: COL_WIDTH_UNDEFINED,
- } as Column,
- ]
- : []),
- {
- key: 'spm()',
- name: getThroughputTitle(moduleName),
- width: COL_WIDTH_UNDEFINED,
- },
- {
- key: `avg(${SPAN_SELF_TIME})`,
- name: DataTitles.avg,
- width: COL_WIDTH_UNDEFINED,
- },
- ...(moduleName === ModuleName.HTTP
- ? [
- {
- key: 'http_error_count()',
- name: DataTitles.errorCount,
- width: COL_WIDTH_UNDEFINED,
- } as Column,
- ]
- : []),
- {
- key: 'time_spent_percentage()',
- name: DataTitles.timeSpent,
- width: COL_WIDTH_UNDEFINED,
- },
- ];
- return order.filter((item): item is NonNullable<Column> => Boolean(item));
- }
|