import {Fragment, useMemo} from 'react'; import styled from '@emotion/styled'; import Alert from 'sentry/components/alert'; import {EventContexts} from 'sentry/components/events/contexts'; import {EventAttachments} from 'sentry/components/events/eventAttachments'; import {EventEvidence} from 'sentry/components/events/eventEvidence'; import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy'; import {EventRRWebIntegration} from 'sentry/components/events/rrwebIntegration'; import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import type {LazyRenderProps} from 'sentry/components/lazyRender'; import ExternalLink from 'sentry/components/links/externalLink'; import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import { CustomMetricsEventData, eventHasCustomMetrics, } from 'sentry/components/metrics/customMetricsEventData'; import {Tooltip} from 'sentry/components/tooltip'; import {t, tct} from 'sentry/locale'; import type {EventTransaction} from 'sentry/types/event'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; import {useLocation} from 'sentry/utils/useLocation'; import useProjects from 'sentry/utils/useProjects'; import {useSpanMetrics} from 'sentry/views/insights/common/queries/useDiscover'; import type { SpanMetricsQueryFilters, SpanMetricsResponse, } from 'sentry/views/insights/types'; import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; import {Referrer} from '../../../referrers'; import {traceAnalytics} from '../../../traceAnalytics'; import {useTransaction} from '../../../traceApi/useTransaction'; import {getCustomInstrumentationLink} from '../../../traceConfigurations'; import {CacheMetrics} from '../../../traceDrawer/details/transaction/sections/cacheMetrics'; import type {TraceTreeNodeDetailsProps} from '../../../traceDrawer/tabs/traceTreeNodeDetails'; import type {TraceTree} from '../../../traceModels/traceTree'; import type {TraceTreeNode} from '../../../traceModels/traceTreeNode'; import {useHasTraceNewUi} from '../../../useHasTraceNewUi'; import {IssueList} from '../issues/issues'; import {TraceDrawerComponents} from '../styles'; import {AdditionalData, hasAdditionalData} from './sections/additionalData'; import {BreadCrumbs} from './sections/breadCrumbs'; import {BuiltIn} from './sections/builtIn'; import {Entries} from './sections/entries'; import GeneralInfo from './sections/generalInfo'; import {TransactionHighlights} from './sections/highlights'; import {hasMeasurements, Measurements} from './sections/measurements'; import ReplayPreview from './sections/replayPreview'; import {Request} from './sections/request'; import {hasSDKContext, Sdk} from './sections/sdk'; export const LAZY_RENDER_PROPS: Partial = { observerOptions: {rootMargin: '50px'}, }; type TransactionNodeDetailHeaderProps = { event: EventTransaction; node: TraceTreeNode; onTabScrollToNode: (node: TraceTreeNode) => void; organization: Organization; project: Project | undefined; }; function TransactionNodeDetailHeader({ node, organization, project, onTabScrollToNode, event, }: TransactionNodeDetailHeaderProps) { const hasNewTraceUi = useHasTraceNewUi(); if (!hasNewTraceUi) { return ( ); } return ( {t('Transaction')} ); } function LegacyTransactionNodeDetailHeader({ node, organization, project, onTabScrollToNode, event, }: TransactionNodeDetailHeaderProps) { return (
{t('transaction')}
); } export function TransactionNodeDetails({ node, organization, onTabScrollToNode, onParentClick, replay, }: TraceTreeNodeDetailsProps>) { const {projects} = useProjects(); const issues = useMemo(() => { return [...node.errors, ...node.performance_issues]; }, [node.errors, node.performance_issues]); const { data: event, isError, isPending, } = useTransaction({ node, organization, }); const hasNewTraceUi = useHasTraceNewUi(); const {data: cacheMetrics} = useSpanMetrics( { search: MutableSearch.fromQueryObject({ transaction: node.value.transaction, } satisfies SpanMetricsQueryFilters), fields: ['avg(cache.item_size)', 'cache_miss_rate()'], }, Referrer.TRACE_DRAWER_TRANSACTION_CACHE_METRICS ); if (isPending) { return ; } if (isError) { return ; } const project = projects.find(proj => proj.slug === event?.projectSlug); return ( {!node.canFetch ? ( {tct( 'This transaction does not have any child spans. You can add more child spans via [customInstrumentationLink:custom instrumentation].', { customInstrumentationLink: ( { traceAnalytics.trackMissingSpansDocLinkClicked(organization); }} href={getCustomInstrumentationLink(project)} /> ), } )} ) : null} {hasNewTraceUi ? ( ) : null} {event.projectSlug ? ( ) : null} {project ? : null} {replay ? null : } {project ? ( ) : null} {project ? : null} {event.projectSlug ? ( ) : null} ); } type TransactionSpecificSectionsProps = { cacheMetrics: Pick[]; event: EventTransaction; node: TraceTreeNode; onParentClick: (node: TraceTreeNode) => void; organization: Organization; }; function TransactionSpecificSections(props: TransactionSpecificSectionsProps) { const location = useLocation(); const hasNewTraceUi = useHasTraceNewUi(); const {event, node, onParentClick, organization, cacheMetrics} = props; if (!hasNewTraceUi) { return ; } return ( {hasSDKContext(event) || cacheMetrics.length > 0 ? ( ) : null} {hasAdditionalData(event) ? : null} {hasMeasurements(event) ? ( ) : null} {eventHasCustomMetrics(organization, event._metrics_summary) ? ( ) : null} {event.contexts.trace?.data ? ( ) : null} ); } function LegacyTransactionSpecificSections({ event, node, onParentClick, organization, cacheMetrics, }: TransactionSpecificSectionsProps) { const location = useLocation(); return ( {hasAdditionalData(event) ? : null} {hasMeasurements(event) ? ( ) : null} {cacheMetrics.length > 0 ? : null} {hasSDKContext(event) ? : null} {eventHasCustomMetrics(organization, event._metrics_summary) ? ( ) : null} {event.contexts.trace?.data ? ( ) : null} ); } const StyledAlert = styled(Alert)` margin: 0; `;