index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import {Fragment, useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import type {Location} from 'history';
  4. import {
  5. SpanProfileDetails,
  6. useSpanProfileDetails,
  7. } from 'sentry/components/events/interfaces/spans/spanProfileDetails';
  8. import type {SpanType} from 'sentry/components/events/interfaces/spans/types';
  9. import {getSpanOperation} from 'sentry/components/events/interfaces/spans/utils';
  10. import ProjectBadge from 'sentry/components/idBadge/projectBadge';
  11. import {
  12. CustomMetricsEventData,
  13. eventHasCustomMetrics,
  14. } from 'sentry/components/metrics/customMetricsEventData';
  15. import {Tooltip} from 'sentry/components/tooltip';
  16. import {t} from 'sentry/locale';
  17. import {space} from 'sentry/styles/space';
  18. import type {EventTransaction} from 'sentry/types/event';
  19. import type {Organization} from 'sentry/types/organization';
  20. import type {Project} from 'sentry/types/project';
  21. import {defined} from 'sentry/utils';
  22. import {useLocation} from 'sentry/utils/useLocation';
  23. import useProjects from 'sentry/utils/useProjects';
  24. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  25. import {ProfileGroupProvider} from 'sentry/views/profiling/profileGroupProvider';
  26. import {ProfileContext, ProfilesProvider} from 'sentry/views/profiling/profilesProvider';
  27. import type {TraceTreeNodeDetailsProps} from '../../../traceDrawer/tabs/traceTreeNodeDetails';
  28. import type {TraceTree} from '../../../traceModels/traceTree';
  29. import type {TraceTreeNode} from '../../../traceModels/traceTreeNode';
  30. import {useHasTraceNewUi} from '../../../useHasTraceNewUi';
  31. import {TraceDrawerComponents} from '.././styles';
  32. import {IssueList} from '../issues/issues';
  33. import Alerts from './sections/alerts';
  34. import {hasFormattedSpanDescription, SpanDescription} from './sections/description';
  35. import {GeneralInfo} from './sections/generalInfo';
  36. import {hasSpanHTTPInfo, SpanHTTPInfo} from './sections/http';
  37. import {hasSpanKeys, SpanKeys} from './sections/keys';
  38. import {hasSpanTags, Tags} from './sections/tags';
  39. function SpanNodeDetailHeader({
  40. node,
  41. organization,
  42. onTabScrollToNode,
  43. project,
  44. }: {
  45. node: TraceTreeNode<TraceTree.Span>;
  46. onTabScrollToNode: (node: TraceTreeNode<any>) => void;
  47. organization: Organization;
  48. project: Project | undefined;
  49. }) {
  50. const hasNewTraceUi = useHasTraceNewUi();
  51. if (!hasNewTraceUi) {
  52. return (
  53. <LegacySpanNodeDetailHeader
  54. node={node}
  55. organization={organization}
  56. onTabScrollToNode={onTabScrollToNode}
  57. project={project}
  58. />
  59. );
  60. }
  61. return (
  62. <TraceDrawerComponents.HeaderContainer>
  63. <TraceDrawerComponents.Title>
  64. <TraceDrawerComponents.LegacyTitleText>
  65. <TraceDrawerComponents.TitleText>{t('Span')}</TraceDrawerComponents.TitleText>
  66. <TraceDrawerComponents.SubtitleWithCopyButton
  67. text={`ID: ${node.value.span_id}`}
  68. />
  69. </TraceDrawerComponents.LegacyTitleText>
  70. </TraceDrawerComponents.Title>
  71. <TraceDrawerComponents.NodeActions
  72. node={node}
  73. organization={organization}
  74. onTabScrollToNode={onTabScrollToNode}
  75. />
  76. </TraceDrawerComponents.HeaderContainer>
  77. );
  78. }
  79. function LegacySpanNodeDetailHeader({
  80. node,
  81. organization,
  82. onTabScrollToNode,
  83. project,
  84. }: {
  85. node: TraceTreeNode<TraceTree.Span>;
  86. onTabScrollToNode: (node: TraceTreeNode<any>) => void;
  87. organization: Organization;
  88. project: Project | undefined;
  89. }) {
  90. const span = node.value;
  91. return (
  92. <TraceDrawerComponents.LegacyHeaderContainer>
  93. <TraceDrawerComponents.Title>
  94. <Tooltip title={node.event?.projectSlug}>
  95. <ProjectBadge
  96. project={project ? project : {slug: node.event?.projectSlug ?? ''}}
  97. avatarSize={30}
  98. hideName
  99. />
  100. </Tooltip>
  101. <TraceDrawerComponents.LegacyTitleText>
  102. <div>{t('span')}</div>
  103. <TraceDrawerComponents.TitleOp
  104. text={getSpanOperation(span) + ' - ' + (span.description ?? span.span_id)}
  105. />
  106. </TraceDrawerComponents.LegacyTitleText>
  107. </TraceDrawerComponents.Title>
  108. <TraceDrawerComponents.NodeActions
  109. node={node}
  110. organization={organization}
  111. onTabScrollToNode={onTabScrollToNode}
  112. />
  113. </TraceDrawerComponents.LegacyHeaderContainer>
  114. );
  115. }
  116. function SpanSections({
  117. node,
  118. project,
  119. organization,
  120. location,
  121. onParentClick,
  122. }: {
  123. location: Location;
  124. node: TraceTreeNode<TraceTree.Span>;
  125. onParentClick: (node: TraceTreeNode<TraceTree.NodeValue>) => void;
  126. organization: Organization;
  127. project: Project | undefined;
  128. }) {
  129. const hasTraceNewUi = useHasTraceNewUi();
  130. if (!hasTraceNewUi) {
  131. return (
  132. <LegacySpanSections
  133. node={node}
  134. project={project}
  135. organization={organization}
  136. location={location}
  137. onParentClick={onParentClick}
  138. />
  139. );
  140. }
  141. const hasSpanSpecificData =
  142. hasSpanHTTPInfo(node.value) ||
  143. hasSpanKeys(node) ||
  144. hasSpanTags(node.value) ||
  145. eventHasCustomMetrics(organization, node.value._metrics_summary);
  146. return (
  147. <Fragment>
  148. <GeneralInfo
  149. node={node}
  150. organization={organization}
  151. location={location}
  152. onParentClick={onParentClick}
  153. />
  154. {hasSpanSpecificData ? (
  155. <InterimSection title={t('Span Specific')} type="span_specifc" initialCollapse>
  156. <TraceDrawerComponents.SectionCardGroup>
  157. {hasSpanKeys(node) ? <SpanKeys node={node} /> : null}
  158. {hasSpanHTTPInfo(node.value) ? <SpanHTTPInfo span={node.value} /> : null}
  159. {hasSpanTags(node.value) ? <Tags span={node.value} /> : null}
  160. {eventHasCustomMetrics(organization, node.value._metrics_summary) ? (
  161. <CustomMetricsEventData
  162. projectId={project?.id || ''}
  163. metricsSummary={node.value._metrics_summary}
  164. startTimestamp={node.value.start_timestamp}
  165. />
  166. ) : null}
  167. </TraceDrawerComponents.SectionCardGroup>
  168. </InterimSection>
  169. ) : null}
  170. </Fragment>
  171. );
  172. }
  173. function LegacySpanSections({
  174. node,
  175. project,
  176. organization,
  177. location,
  178. onParentClick,
  179. }: {
  180. location: Location;
  181. node: TraceTreeNode<TraceTree.Span>;
  182. onParentClick: (node: TraceTreeNode<TraceTree.NodeValue>) => void;
  183. organization: Organization;
  184. project: Project | undefined;
  185. }) {
  186. return (
  187. <TraceDrawerComponents.SectionCardGroup>
  188. {hasFormattedSpanDescription(node) ? (
  189. <SpanDescription
  190. node={node}
  191. project={project}
  192. organization={organization}
  193. location={location}
  194. />
  195. ) : null}
  196. <GeneralInfo
  197. node={node}
  198. organization={organization}
  199. location={location}
  200. onParentClick={onParentClick}
  201. />
  202. {hasSpanHTTPInfo(node.value) ? <SpanHTTPInfo span={node.value} /> : null}
  203. {hasSpanTags(node.value) ? <Tags span={node.value} /> : null}
  204. {hasSpanKeys(node) ? <SpanKeys node={node} /> : null}
  205. {eventHasCustomMetrics(organization, node.value._metrics_summary) ? (
  206. <CustomMetricsEventData
  207. projectId={project?.id || ''}
  208. metricsSummary={node.value._metrics_summary}
  209. startTimestamp={node.value.start_timestamp}
  210. />
  211. ) : null}
  212. </TraceDrawerComponents.SectionCardGroup>
  213. );
  214. }
  215. function ProfileDetails({
  216. event,
  217. span,
  218. }: {
  219. event: Readonly<EventTransaction>;
  220. span: Readonly<SpanType>;
  221. }) {
  222. const hasNewTraceUi = useHasTraceNewUi();
  223. const {profile, frames} = useSpanProfileDetails(event, span);
  224. if (!hasNewTraceUi) {
  225. return <SpanProfileDetails span={span} event={event} />;
  226. }
  227. if (!defined(profile) || frames.length === 0) {
  228. return null;
  229. }
  230. return (
  231. <InterimSection title={t('Profile')} type="span_profile_details" initialCollapse>
  232. <EmbededContentWrapper>
  233. <SpanProfileDetails span={span} event={event} />
  234. </EmbededContentWrapper>
  235. </InterimSection>
  236. );
  237. }
  238. const EmbededContentWrapper = styled('div')`
  239. margin-top: ${space(0.5)};
  240. `;
  241. export function SpanNodeDetails({
  242. node,
  243. organization,
  244. onTabScrollToNode,
  245. onParentClick,
  246. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.Span>>) {
  247. const location = useLocation();
  248. const hasNewTraceUi = useHasTraceNewUi();
  249. const {projects} = useProjects();
  250. const issues = useMemo(() => {
  251. return [...node.errors, ...node.performance_issues];
  252. }, [node.errors, node.performance_issues]);
  253. const project = projects.find(proj => proj.slug === node.event?.projectSlug);
  254. const profileId = node.event?.contexts?.profile?.profile_id ?? null;
  255. return (
  256. <TraceDrawerComponents.DetailContainer hasNewTraceUi={hasNewTraceUi}>
  257. <SpanNodeDetailHeader
  258. node={node}
  259. organization={organization}
  260. project={project}
  261. onTabScrollToNode={onTabScrollToNode}
  262. />
  263. {node.event?.projectSlug ? (
  264. <ProfilesProvider
  265. orgSlug={organization.slug}
  266. projectSlug={node.event?.projectSlug}
  267. profileId={profileId || ''}
  268. >
  269. <ProfileContext.Consumer>
  270. {profiles => (
  271. <ProfileGroupProvider
  272. type="flamechart"
  273. input={profiles?.type === 'resolved' ? profiles.data : null}
  274. traceID={profileId || ''}
  275. >
  276. <Alerts node={node} />
  277. {issues.length > 0 ? (
  278. <IssueList organization={organization} issues={issues} node={node} />
  279. ) : null}
  280. <SpanDescription
  281. node={node}
  282. project={project}
  283. organization={organization}
  284. location={location}
  285. />
  286. <SpanSections
  287. node={node}
  288. project={project}
  289. organization={organization}
  290. location={location}
  291. onParentClick={onParentClick}
  292. />
  293. {organization.features.includes('profiling') ? (
  294. <ProfileDetails event={node.event!} span={node.value} />
  295. ) : null}
  296. </ProfileGroupProvider>
  297. )}
  298. </ProfileContext.Consumer>
  299. </ProfilesProvider>
  300. ) : null}
  301. </TraceDrawerComponents.DetailContainer>
  302. );
  303. }