import {Fragment, useMemo} from 'react'; import {useTheme} from '@emotion/react'; import moment from 'moment-timezone'; import Count from 'sentry/components/count'; import {EmptyStreamWrapper} from 'sentry/components/emptyStateWarning'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import PerformanceDuration from 'sentry/components/performanceDuration'; import {IconWarning} from 'sentry/icons/iconWarning'; import {t, tct} from 'sentry/locale'; import type {Organization} from 'sentry/types/organization'; import {trackAnalytics} from 'sentry/utils/analytics'; import {getUtcDateString} from 'sentry/utils/dates'; import useOrganization from 'sentry/utils/useOrganization'; import {useDataset} from 'sentry/views/explore/hooks/useDataset'; import type {TraceResult} from 'sentry/views/explore/hooks/useTraces'; import {type SpanResult, useTraceSpans} from 'sentry/views/explore/hooks/useTraceSpans'; import {useUserQuery} from 'sentry/views/explore/hooks/useUserQuery'; import {type Field, FIELDS, SORTS} from 'sentry/views/explore/tables/tracesTable/data'; import { SpanBreakdownSliceRenderer, SpanDescriptionRenderer, SpanIdRenderer, SpanTimeRenderer, TraceBreakdownContainer, } from 'sentry/views/explore/tables/tracesTable/fieldRenderers'; import { MoreMatchingSpans, SpanPanelContent, SpanTablePanelItem, StyledPanel, StyledPanelHeader, StyledPanelItem, StyledSpanPanelItem, } from 'sentry/views/explore/tables/tracesTable/styles'; import { getSecondaryNameFromSpan, getStylingSliceName, } from 'sentry/views/explore/tables/tracesTable/utils'; const ONE_MINUTE = 60 * 1000; // in milliseconds export function SpanTable({ trace, setHighlightedSliceName, }: { setHighlightedSliceName: (sliceName: string) => void; trace: TraceResult; }) { const organization = useOrganization(); const [dataset] = useDataset(); const [query] = useUserQuery(); const {data, isPending, isError} = useTraceSpans({ dataset, trace, fields: [ ...FIELDS, ...SORTS.map(field => field.startsWith('-') ? (field.substring(1) as Field) : (field as Field) ), ], datetime: { // give a 1 minute buffer on each side so that start != end start: getUtcDateString(moment(trace.start - ONE_MINUTE)), end: getUtcDateString(moment(trace.end + ONE_MINUTE)), period: null, utc: true, }, limit: 10, query, sort: SORTS, }); const spans = useMemo(() => data?.data ?? [], [data]); const showErrorState = useMemo(() => { return !isPending && isError; }, [isPending, isError]); const hasData = useMemo(() => { return !isPending && !showErrorState && spans.length > 0; }, [spans, isPending, showErrorState]); return ( {t('Span ID')} {t('Span Description')} {t('Span Duration')} {t('Timestamp')} {isPending && ( )} {isError && ( // TODO: need an error state )} {data?.data.map(span => ( ))} {hasData && spans.length < trace.matchingSpans && ( {tct('[more][space]more [matching]spans can be found in the trace.', { more: , space:  , matching: query ? 'matching ' : '', })} )} ); } function SpanRow({ organization, span, trace, setHighlightedSliceName, }: { organization: Organization; setHighlightedSliceName: (sliceName: string) => void; span: SpanResult; trace: TraceResult; }) { const theme = useTheme(); return ( trackAnalytics('trace_explorer.open_trace_span', { organization, }) } /> setHighlightedSliceName('')}> setHighlightedSliceName( getStylingSliceName(span.project, getSecondaryNameFromSpan(span)) ?? '' ) } /> ); }