missingInstrumentation.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import {useTheme} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import {IconSpan} from 'sentry/icons';
  5. import {t, tct} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import getDuration from 'sentry/utils/duration/getDuration';
  8. import {generateProfileFlamechartRouteWithQuery} from 'sentry/utils/profiling/routes';
  9. import useProjects from 'sentry/utils/useProjects';
  10. import {ProfileGroupProvider} from 'sentry/views/profiling/profileGroupProvider';
  11. import {ProfileContext, ProfilesProvider} from 'sentry/views/profiling/profilesProvider';
  12. import {getCustomInstrumentationLink} from '../../traceConfigurations';
  13. import {ProfilePreview} from '../../traceDrawer/details/profiling/profilePreview';
  14. import type {TraceTreeNodeDetailsProps} from '../../traceDrawer/tabs/traceTreeNodeDetails';
  15. import type {MissingInstrumentationNode} from '../../traceModels/missingInstrumentationNode';
  16. import {TraceTree} from '../../traceModels/traceTree';
  17. import {makeTraceNodeBarColor} from '../../traceRow/traceBar';
  18. import {getTraceTabTitle} from '../../traceState/traceTabs';
  19. import {useHasTraceNewUi} from '../../useHasTraceNewUi';
  20. import {type SectionCardKeyValueList, TraceDrawerComponents} from './styles';
  21. import {getProfileMeta} from './utils';
  22. export function MissingInstrumentationNodeDetails(
  23. props: TraceTreeNodeDetailsProps<MissingInstrumentationNode>
  24. ) {
  25. const {projects} = useProjects();
  26. const hasTraceNewUi = useHasTraceNewUi();
  27. if (!hasTraceNewUi) {
  28. return <LegacyMissingInstrumentationNodeDetails {...props} />;
  29. }
  30. const {node, organization, onTabScrollToNode} = props;
  31. const event = node.previous.event ?? node.next.event ?? null;
  32. const project = projects.find(proj => proj.slug === event?.projectSlug);
  33. const profileMeta = getProfileMeta(event) || '';
  34. const profileId =
  35. typeof profileMeta === 'string' ? profileMeta : profileMeta.profiler_id;
  36. return (
  37. <TraceDrawerComponents.DetailContainer>
  38. <TraceDrawerComponents.HeaderContainer>
  39. <TraceDrawerComponents.Title>
  40. <TraceDrawerComponents.LegacyTitleText>
  41. <TraceDrawerComponents.TitleText>
  42. {t('No Instrumentation')}
  43. </TraceDrawerComponents.TitleText>
  44. <TraceDrawerComponents.SubtitleWithCopyButton
  45. hideCopyButton
  46. text={t('How Awkward')}
  47. />
  48. </TraceDrawerComponents.LegacyTitleText>
  49. </TraceDrawerComponents.Title>
  50. <TraceDrawerComponents.NodeActions
  51. node={node}
  52. organization={organization}
  53. onTabScrollToNode={onTabScrollToNode}
  54. />
  55. </TraceDrawerComponents.HeaderContainer>
  56. <TraceDrawerComponents.BodyContainer hasNewTraceUi={hasTraceNewUi}>
  57. <TextBlock>
  58. {tct(
  59. 'It looks like there’s more than 100ms unaccounted for. This might be a missing service or just idle time. If you know there’s something going on, you can [customInstrumentationLink: add more spans using custom instrumentation].',
  60. {
  61. customInstrumentationLink: (
  62. <ExternalLink href={getCustomInstrumentationLink(project)} />
  63. ),
  64. }
  65. )}
  66. </TextBlock>
  67. {event?.projectSlug ? (
  68. <ProfilesProvider
  69. orgSlug={organization.slug}
  70. projectSlug={event?.projectSlug ?? ''}
  71. profileMeta={profileMeta}
  72. >
  73. <ProfileContext.Consumer>
  74. {profiles => (
  75. <ProfileGroupProvider
  76. type="flamechart"
  77. input={profiles?.type === 'resolved' ? profiles.data : null}
  78. traceID={profileId || ''}
  79. >
  80. <ProfilePreview event={event!} node={node} />
  81. </ProfileGroupProvider>
  82. )}
  83. </ProfileContext.Consumer>
  84. </ProfilesProvider>
  85. ) : null}
  86. <TextBlock>
  87. {t(
  88. "You can turn off the 'No Instrumentation' feature using the settings dropdown above."
  89. )}
  90. </TextBlock>
  91. </TraceDrawerComponents.BodyContainer>
  92. </TraceDrawerComponents.DetailContainer>
  93. );
  94. }
  95. const TextBlock = styled('div')`
  96. font-size: ${p => p.theme.fontSizeLarge};
  97. line-height: 1.5;
  98. margin-bottom: ${space(2)};
  99. `;
  100. function LegacyMissingInstrumentationNodeDetails({
  101. node,
  102. onParentClick,
  103. onTabScrollToNode,
  104. organization,
  105. }: TraceTreeNodeDetailsProps<MissingInstrumentationNode>) {
  106. const theme = useTheme();
  107. const {projects} = useProjects();
  108. const parentTransaction = TraceTree.ParentTransaction(node);
  109. const event = node.previous.event ?? node.next.event ?? null;
  110. const project = projects.find(proj => proj.slug === event?.projectSlug);
  111. const profileId = event?.contexts?.profile?.profile_id ?? null;
  112. const items: SectionCardKeyValueList = [
  113. {
  114. key: 'duration',
  115. subject: t('Duration'),
  116. value: getDuration(node.value.timestamp - node.value.start_timestamp, 2, true),
  117. },
  118. {
  119. key: 'previous_span',
  120. subject: t('Previous Span'),
  121. value: `${node.previous.value.op} - ${node.previous.value.description}`,
  122. },
  123. {
  124. key: 'next_span',
  125. subject: t('Next Span'),
  126. value: `${node.next.value.op} - ${node.next.value.description}`,
  127. },
  128. ];
  129. if (profileId && project?.slug) {
  130. items.push({
  131. key: 'profile_id',
  132. subject: 'Profile ID',
  133. value: (
  134. <TraceDrawerComponents.CopyableCardValueWithLink
  135. value={profileId}
  136. linkTarget={generateProfileFlamechartRouteWithQuery({
  137. orgSlug: organization.slug,
  138. projectSlug: project.slug,
  139. profileId,
  140. })}
  141. linkText={t('View Profile')}
  142. />
  143. ),
  144. });
  145. }
  146. if (parentTransaction) {
  147. items.push({
  148. key: 'parent_transaction',
  149. subject: t('Parent Transaction'),
  150. value: (
  151. <a onClick={() => onParentClick(parentTransaction)}>
  152. {getTraceTabTitle(parentTransaction)}
  153. </a>
  154. ),
  155. });
  156. }
  157. return (
  158. <TraceDrawerComponents.DetailContainer>
  159. <TraceDrawerComponents.LegacyHeaderContainer>
  160. <TraceDrawerComponents.Title>
  161. <TraceDrawerComponents.IconTitleWrapper>
  162. <TraceDrawerComponents.IconBorder
  163. backgroundColor={makeTraceNodeBarColor(theme, node)}
  164. >
  165. <IconSpan size="md" />
  166. </TraceDrawerComponents.IconBorder>
  167. <div style={{fontWeight: 'bold'}}>{t('Missing Instrumentation')}</div>
  168. </TraceDrawerComponents.IconTitleWrapper>
  169. </TraceDrawerComponents.Title>
  170. <TraceDrawerComponents.NodeActions
  171. organization={organization}
  172. node={node}
  173. onTabScrollToNode={onTabScrollToNode}
  174. />
  175. </TraceDrawerComponents.LegacyHeaderContainer>
  176. <TraceDrawerComponents.BodyContainer>
  177. {node.event?.projectSlug ? (
  178. <ProfilesProvider
  179. orgSlug={organization.slug}
  180. projectSlug={node.event?.projectSlug ?? ''}
  181. profileMeta={profileId || ''}
  182. >
  183. <ProfileContext.Consumer>
  184. {profiles => (
  185. <ProfileGroupProvider
  186. type="flamechart"
  187. input={profiles?.type === 'resolved' ? profiles.data : null}
  188. traceID={profileId || ''}
  189. >
  190. <ProfilePreview event={node.event!} node={node} />
  191. </ProfileGroupProvider>
  192. )}
  193. </ProfileContext.Consumer>
  194. </ProfilesProvider>
  195. ) : null}
  196. <TraceDrawerComponents.SectionCard items={items} title={t('General')} />
  197. </TraceDrawerComponents.BodyContainer>
  198. </TraceDrawerComponents.DetailContainer>
  199. );
  200. }