index.tsx 9.2 KB

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