index.tsx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. return (
  142. <Fragment>
  143. <GeneralInfo
  144. node={node}
  145. organization={organization}
  146. location={location}
  147. onParentClick={onParentClick}
  148. />
  149. <InterimSection title={t('Span Specific')} type="span_specifc" initialCollapse>
  150. <TraceDrawerComponents.SectionCardGroup>
  151. {hasSpanHTTPInfo(node.value) ? <SpanHTTPInfo span={node.value} /> : null}
  152. {hasSpanKeys(node) ? <SpanKeys node={node} /> : null}
  153. {hasSpanTags(node.value) ? <Tags span={node.value} /> : null}
  154. {eventHasCustomMetrics(organization, node.value._metrics_summary) ? (
  155. <CustomMetricsEventData
  156. projectId={project?.id || ''}
  157. metricsSummary={node.value._metrics_summary}
  158. startTimestamp={node.value.start_timestamp}
  159. />
  160. ) : null}
  161. </TraceDrawerComponents.SectionCardGroup>
  162. </InterimSection>
  163. </Fragment>
  164. );
  165. }
  166. function LegacySpanSections({
  167. node,
  168. project,
  169. organization,
  170. location,
  171. onParentClick,
  172. }: {
  173. location: Location;
  174. node: TraceTreeNode<TraceTree.Span>;
  175. onParentClick: (node: TraceTreeNode<TraceTree.NodeValue>) => void;
  176. organization: Organization;
  177. project: Project | undefined;
  178. }) {
  179. return (
  180. <TraceDrawerComponents.SectionCardGroup>
  181. {hasFormattedSpanDescription(node) ? (
  182. <SpanDescription
  183. node={node}
  184. project={project}
  185. organization={organization}
  186. location={location}
  187. />
  188. ) : null}
  189. <GeneralInfo
  190. node={node}
  191. organization={organization}
  192. location={location}
  193. onParentClick={onParentClick}
  194. />
  195. {hasSpanHTTPInfo(node.value) ? <SpanHTTPInfo span={node.value} /> : null}
  196. {hasSpanTags(node.value) ? <Tags span={node.value} /> : null}
  197. {hasSpanKeys(node) ? <SpanKeys node={node} /> : null}
  198. {eventHasCustomMetrics(organization, node.value._metrics_summary) ? (
  199. <CustomMetricsEventData
  200. projectId={project?.id || ''}
  201. metricsSummary={node.value._metrics_summary}
  202. startTimestamp={node.value.start_timestamp}
  203. />
  204. ) : null}
  205. </TraceDrawerComponents.SectionCardGroup>
  206. );
  207. }
  208. function ProfileDetails({
  209. event,
  210. span,
  211. }: {
  212. event: Readonly<EventTransaction>;
  213. span: Readonly<SpanType>;
  214. }) {
  215. const hasNewTraceUi = useHasTraceNewUi();
  216. const {profile, frames} = useSpanProfileDetails(event, span);
  217. if (!hasNewTraceUi) {
  218. return <SpanProfileDetails span={span} event={event} />;
  219. }
  220. if (!defined(profile) || frames.length === 0) {
  221. return null;
  222. }
  223. return (
  224. <InterimSection title={t('Profile')} type="span_profile_details" initialCollapse>
  225. <EmbededContentWrapper>
  226. <SpanProfileDetails span={span} event={event} />
  227. </EmbededContentWrapper>
  228. </InterimSection>
  229. );
  230. }
  231. const EmbededContentWrapper = styled('div')`
  232. margin-top: ${space(0.5)};
  233. `;
  234. export function SpanNodeDetails({
  235. node,
  236. organization,
  237. onTabScrollToNode,
  238. onParentClick,
  239. }: TraceTreeNodeDetailsProps<TraceTreeNode<TraceTree.Span>>) {
  240. const location = useLocation();
  241. const hasNewTraceUi = useHasTraceNewUi();
  242. const {projects} = useProjects();
  243. const issues = useMemo(() => {
  244. return [...node.errors, ...node.performance_issues];
  245. }, [node.errors, node.performance_issues]);
  246. const project = projects.find(proj => proj.slug === node.event?.projectSlug);
  247. const profileId = node.event?.contexts?.profile?.profile_id ?? null;
  248. return (
  249. <TraceDrawerComponents.DetailContainer hasNewTraceUi={hasNewTraceUi}>
  250. <SpanNodeDetailHeader
  251. node={node}
  252. organization={organization}
  253. project={project}
  254. onTabScrollToNode={onTabScrollToNode}
  255. />
  256. {node.event?.projectSlug ? (
  257. <ProfilesProvider
  258. orgSlug={organization.slug}
  259. projectSlug={node.event?.projectSlug}
  260. profileId={profileId || ''}
  261. >
  262. <ProfileContext.Consumer>
  263. {profiles => (
  264. <ProfileGroupProvider
  265. type="flamechart"
  266. input={profiles?.type === 'resolved' ? profiles.data : null}
  267. traceID={profileId || ''}
  268. >
  269. <Alerts node={node} />
  270. {issues.length > 0 ? (
  271. <IssueList organization={organization} issues={issues} node={node} />
  272. ) : null}
  273. <SpanDescription
  274. node={node}
  275. project={project}
  276. organization={organization}
  277. location={location}
  278. />
  279. <SpanSections
  280. node={node}
  281. project={project}
  282. organization={organization}
  283. location={location}
  284. onParentClick={onParentClick}
  285. />
  286. {organization.features.includes('profiling') ? (
  287. <ProfileDetails event={node.event!} span={node.value} />
  288. ) : null}
  289. </ProfileGroupProvider>
  290. )}
  291. </ProfileContext.Consumer>
  292. </ProfilesProvider>
  293. ) : null}
  294. </TraceDrawerComponents.DetailContainer>
  295. );
  296. }