index.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import Alert from 'sentry/components/alert';
  4. import {EventContexts} from 'sentry/components/events/contexts';
  5. import {EventAttachments} from 'sentry/components/events/eventAttachments';
  6. import {EventEvidence} from 'sentry/components/events/eventEvidence';
  7. import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy';
  8. import {EventRRWebIntegration} from 'sentry/components/events/rrwebIntegration';
  9. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  10. import type {LazyRenderProps} from 'sentry/components/lazyRender';
  11. import ExternalLink from 'sentry/components/links/externalLink';
  12. import LoadingError from 'sentry/components/loadingError';
  13. import LoadingIndicator from 'sentry/components/loadingIndicator';
  14. import {
  15. CustomMetricsEventData,
  16. eventHasCustomMetrics,
  17. } from 'sentry/components/metrics/customMetricsEventData';
  18. import {Tooltip} from 'sentry/components/tooltip';
  19. import {t, tct} from 'sentry/locale';
  20. import type {EventTransaction} from 'sentry/types/event';
  21. import type {Organization} from 'sentry/types/organization';
  22. import type {Project} from 'sentry/types/project';
  23. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  24. import {useLocation} from 'sentry/utils/useLocation';
  25. import useProjects from 'sentry/utils/useProjects';
  26. import {useSpanMetrics} from 'sentry/views/insights/common/queries/useDiscover';
  27. import type {SpanMetricsQueryFilters} from 'sentry/views/insights/types';
  28. import {Referrer} from '../../../referrers';
  29. import {traceAnalytics} from '../../../traceAnalytics';
  30. import {useTransaction} from '../../../traceApi/useTransaction';
  31. import {getCustomInstrumentationLink} from '../../../traceConfigurations';
  32. import {CacheMetrics} from '../../../traceDrawer/details/transaction/sections/cacheMetrics';
  33. import type {TraceTreeNodeDetailsProps} from '../../../traceDrawer/tabs/traceTreeNodeDetails';
  34. import type {TraceTree} from '../../../traceModels/traceTree';
  35. import type {TraceTreeNode} from '../../../traceModels/traceTreeNode';
  36. import {IssueList} from '../issues/issues';
  37. import {TraceDrawerComponents} from '../styles';
  38. import {AdditionalData, hasAdditionalData} from './sections/additionalData';
  39. import {BreadCrumbs} from './sections/breadCrumbs';
  40. import {Entries} from './sections/entries';
  41. import GeneralInfo from './sections/generalInfo';
  42. import {hasMeasurements, Measurements} from './sections/measurements';
  43. import ReplayPreview from './sections/replayPreview';
  44. import {Request} from './sections/request';
  45. import {hasSDKContext, Sdk} from './sections/sdk';
  46. export const LAZY_RENDER_PROPS: Partial<LazyRenderProps> = {
  47. observerOptions: {rootMargin: '50px'},
  48. };
  49. type TransactionNodeDetailHeaderProps = {
  50. event: EventTransaction;
  51. node: TraceTreeNode<TraceTree.Transaction>;
  52. onTabScrollToNode: (node: TraceTreeNode<any>) => void;
  53. organization: Organization;
  54. project: Project | undefined;
  55. };
  56. function TransactionNodeDetailHeader({
  57. node,
  58. organization,
  59. project,
  60. onTabScrollToNode,
  61. event,
  62. }: TransactionNodeDetailHeaderProps) {
  63. return (
  64. <TraceDrawerComponents.HeaderContainer>
  65. <TraceDrawerComponents.Title>
  66. <Tooltip title={node.value.project_slug}>
  67. <ProjectBadge
  68. project={project ? project : {slug: node.value.project_slug}}
  69. avatarSize={30}
  70. hideName
  71. />
  72. </Tooltip>
  73. <TraceDrawerComponents.TitleText>
  74. <div>{t('transaction')}</div>
  75. <TraceDrawerComponents.TitleOp
  76. text={node.value['transaction.op'] + ' - ' + node.value.transaction}
  77. />
  78. </TraceDrawerComponents.TitleText>
  79. </TraceDrawerComponents.Title>
  80. <TraceDrawerComponents.NodeActions
  81. node={node}
  82. organization={organization}
  83. onTabScrollToNode={onTabScrollToNode}
  84. eventSize={event?.size}
  85. />
  86. </TraceDrawerComponents.HeaderContainer>
  87. );
  88. }
  89. export function TransactionNodeDetails({
  90. node,
  91. organization,
  92. onTabScrollToNode,
  93. onParentClick,
  94. replay,
  95. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.Transaction>>) {
  96. const location = useLocation();
  97. const {projects} = useProjects();
  98. const issues = useMemo(() => {
  99. return [...node.errors, ...node.performance_issues];
  100. }, [node.errors, node.performance_issues]);
  101. const {
  102. data: event,
  103. isError,
  104. isPending,
  105. } = useTransaction({
  106. node,
  107. organization,
  108. });
  109. const {data: cacheMetrics} = useSpanMetrics(
  110. {
  111. search: MutableSearch.fromQueryObject({
  112. transaction: node.value.transaction,
  113. } satisfies SpanMetricsQueryFilters),
  114. fields: ['avg(cache.item_size)', 'cache_miss_rate()'],
  115. },
  116. Referrer.TRACE_DRAWER_TRANSACTION_CACHE_METRICS
  117. );
  118. if (isPending) {
  119. return <LoadingIndicator />;
  120. }
  121. if (isError) {
  122. return <LoadingError message={t('Failed to fetch transaction details')} />;
  123. }
  124. const project = projects.find(proj => proj.slug === event?.projectSlug);
  125. return (
  126. <TraceDrawerComponents.DetailContainer>
  127. {!node.canFetch ? (
  128. <StyledAlert type="info" showIcon>
  129. {tct(
  130. 'This transaction does not have any child spans. You can add more child spans via [customInstrumentationLink:custom instrumentation].',
  131. {
  132. customInstrumentationLink: (
  133. <ExternalLink
  134. onClick={() => {
  135. traceAnalytics.trackMissingSpansDocLinkClicked(organization);
  136. }}
  137. href={getCustomInstrumentationLink(project)}
  138. />
  139. ),
  140. }
  141. )}
  142. </StyledAlert>
  143. ) : null}
  144. <TransactionNodeDetailHeader
  145. node={node}
  146. organization={organization}
  147. project={project}
  148. event={event}
  149. onTabScrollToNode={onTabScrollToNode}
  150. />
  151. <IssueList node={node} organization={organization} issues={issues} />
  152. <TraceDrawerComponents.SectionCardGroup>
  153. <GeneralInfo
  154. node={node}
  155. onParentClick={onParentClick}
  156. organization={organization}
  157. event={event}
  158. location={location}
  159. />
  160. {hasAdditionalData(event) ? <AdditionalData event={event} /> : null}
  161. {hasMeasurements(event) ? (
  162. <Measurements event={event} location={location} organization={organization} />
  163. ) : null}
  164. {cacheMetrics.length > 0 ? <CacheMetrics cacheMetrics={cacheMetrics} /> : null}
  165. {hasSDKContext(event) ? <Sdk event={event} /> : null}
  166. {eventHasCustomMetrics(organization, event._metrics_summary) ? (
  167. <CustomMetricsEventData
  168. metricsSummary={event._metrics_summary}
  169. startTimestamp={event.startTimestamp}
  170. projectId={event.projectID}
  171. />
  172. ) : null}
  173. {event.contexts.trace?.data ? (
  174. <TraceDrawerComponents.TraceDataSection event={event} />
  175. ) : null}
  176. </TraceDrawerComponents.SectionCardGroup>
  177. <Request event={event} />
  178. {event.projectSlug ? (
  179. <Entries
  180. definedEvent={event}
  181. projectSlug={event.projectSlug}
  182. group={undefined}
  183. organization={organization}
  184. />
  185. ) : null}
  186. <TraceDrawerComponents.EventTags
  187. projectSlug={node.value.project_slug}
  188. event={event}
  189. />
  190. <EventContexts event={event} />
  191. {project ? <EventEvidence event={event} project={project} /> : null}
  192. {replay ? null : <ReplayPreview event={event} organization={organization} />}
  193. <BreadCrumbs event={event} organization={organization} />
  194. {project ? (
  195. <EventAttachments event={event} project={project} group={undefined} />
  196. ) : null}
  197. {project ? <EventViewHierarchy event={event} project={project} /> : null}
  198. {event.projectSlug ? (
  199. <EventRRWebIntegration
  200. event={event}
  201. orgId={organization.slug}
  202. projectSlug={event.projectSlug}
  203. />
  204. ) : null}
  205. </TraceDrawerComponents.DetailContainer>
  206. );
  207. }
  208. const StyledAlert = styled(Alert)`
  209. margin: 0;
  210. `;