groupEventDetailsContent.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. import {Fragment, lazy, useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {usePrompt} from 'sentry/actionCreators/prompts';
  4. import {Button} from 'sentry/components/button';
  5. import {CommitRow} from 'sentry/components/commitRow';
  6. import ErrorBoundary from 'sentry/components/errorBoundary';
  7. import BreadcrumbsDataSection from 'sentry/components/events/breadcrumbs/breadcrumbsDataSection';
  8. import {EventContexts} from 'sentry/components/events/contexts';
  9. import {EventDevice} from 'sentry/components/events/device';
  10. import {EventAttachments} from 'sentry/components/events/eventAttachments';
  11. import {EventEntry} from 'sentry/components/events/eventEntry';
  12. import {EventEvidence} from 'sentry/components/events/eventEvidence';
  13. import {EventExtraData} from 'sentry/components/events/eventExtraData';
  14. import EventHydrationDiff from 'sentry/components/events/eventHydrationDiff';
  15. import EventReplay from 'sentry/components/events/eventReplay';
  16. import {EventSdk} from 'sentry/components/events/eventSdk';
  17. import AggregateSpanDiff from 'sentry/components/events/eventStatisticalDetector/aggregateSpanDiff';
  18. import EventBreakpointChart from 'sentry/components/events/eventStatisticalDetector/breakpointChart';
  19. import {EventAffectedTransactions} from 'sentry/components/events/eventStatisticalDetector/eventAffectedTransactions';
  20. import EventComparison from 'sentry/components/events/eventStatisticalDetector/eventComparison';
  21. import {EventDifferentialFlamegraph} from 'sentry/components/events/eventStatisticalDetector/eventDifferentialFlamegraph';
  22. import {EventFunctionComparisonList} from 'sentry/components/events/eventStatisticalDetector/eventFunctionComparisonList';
  23. import {EventRegressionSummary} from 'sentry/components/events/eventStatisticalDetector/eventRegressionSummary';
  24. import {EventFunctionBreakpointChart} from 'sentry/components/events/eventStatisticalDetector/functionBreakpointChart';
  25. import {TransactionsDeltaProvider} from 'sentry/components/events/eventStatisticalDetector/transactionsDeltaProvider';
  26. import {EventTagsAndScreenshot} from 'sentry/components/events/eventTagsAndScreenshot';
  27. import {ScreenshotDataSection} from 'sentry/components/events/eventTagsAndScreenshot/screenshot/screenshotDataSection';
  28. import EventTagsDataSection from 'sentry/components/events/eventTagsAndScreenshot/tags';
  29. import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy';
  30. import {EventGroupingInfo} from 'sentry/components/events/groupingInfo';
  31. import HighlightsDataSection from 'sentry/components/events/highlights/highlightsDataSection';
  32. import {HighlightsIconSummary} from 'sentry/components/events/highlights/highlightsIconSummary';
  33. import {ActionableItems} from 'sentry/components/events/interfaces/crashContent/exception/actionableItems';
  34. import {actionableItemsEnabled} from 'sentry/components/events/interfaces/crashContent/exception/useActionableItems';
  35. import {CronTimelineSection} from 'sentry/components/events/interfaces/crons/cronTimelineSection';
  36. import {AnrRootCause} from 'sentry/components/events/interfaces/performance/anrRootCause';
  37. import {SpanEvidenceSection} from 'sentry/components/events/interfaces/performance/spanEvidence';
  38. import {UptimeDataSection} from 'sentry/components/events/interfaces/uptime/uptimeDataSection';
  39. import {EventPackageData} from 'sentry/components/events/packageData';
  40. import {EventRRWebIntegration} from 'sentry/components/events/rrwebIntegration';
  41. import {DataSection} from 'sentry/components/events/styles';
  42. import {SuspectCommits} from 'sentry/components/events/suspectCommits';
  43. import {EventUserFeedback} from 'sentry/components/events/userFeedback';
  44. import LazyLoad from 'sentry/components/lazyLoad';
  45. import Placeholder from 'sentry/components/placeholder';
  46. import {useHasNewTimelineUI} from 'sentry/components/timeline/utils';
  47. import {IconChevron} from 'sentry/icons';
  48. import {t} from 'sentry/locale';
  49. import {space} from 'sentry/styles/space';
  50. import type {EntryException, Event, EventTransaction} from 'sentry/types/event';
  51. import {EntryType, EventOrGroupType} from 'sentry/types/event';
  52. import type {Group} from 'sentry/types/group';
  53. import {IssueCategory, IssueType} from 'sentry/types/group';
  54. import type {Project} from 'sentry/types/project';
  55. import {shouldShowCustomErrorResourceConfig} from 'sentry/utils/issueTypeConfig';
  56. import {QuickTraceContext} from 'sentry/utils/performance/quickTrace/quickTraceContext';
  57. import QuickTraceQuery from 'sentry/utils/performance/quickTrace/quickTraceQuery';
  58. import {getReplayIdFromEvent} from 'sentry/utils/replays/getReplayIdFromEvent';
  59. import {useLocation} from 'sentry/utils/useLocation';
  60. import useOrganization from 'sentry/utils/useOrganization';
  61. import {ResourcesAndPossibleSolutions} from 'sentry/views/issueDetails/resourcesAndPossibleSolutions';
  62. import {EventDetails} from 'sentry/views/issueDetails/streamline/eventDetails';
  63. import {FoldSectionKey} from 'sentry/views/issueDetails/streamline/foldSection';
  64. import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
  65. import {TraceDataSection} from 'sentry/views/issueDetails/traceDataSection';
  66. import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils';
  67. const LLMMonitoringSection = lazy(
  68. () => import('sentry/components/events/interfaces/llm-monitoring/llmMonitoringSection')
  69. );
  70. export type GroupEventDetailsContentProps = {
  71. group: Group;
  72. project: Project;
  73. event?: Event;
  74. };
  75. type GroupEventEntryProps = {
  76. entryType: EntryType;
  77. event: Event;
  78. group: Group;
  79. project: Project;
  80. sectionKey: FoldSectionKey;
  81. };
  82. function GroupEventEntry({
  83. event,
  84. entryType,
  85. group,
  86. project,
  87. ...props
  88. }: GroupEventEntryProps) {
  89. const organization = useOrganization();
  90. const matchingEntry = event.entries.find(entry => entry.type === entryType);
  91. if (!matchingEntry) {
  92. return null;
  93. }
  94. return (
  95. <EventEntry
  96. projectSlug={project.slug}
  97. group={group}
  98. entry={matchingEntry}
  99. {...{organization, event}}
  100. {...props}
  101. />
  102. );
  103. }
  104. export function DefaultGroupEventDetailsContent({
  105. group,
  106. event,
  107. project,
  108. }: Required<GroupEventDetailsContentProps>) {
  109. const organization = useOrganization();
  110. const location = useLocation();
  111. const hasNewTimelineUI = useHasNewTimelineUI();
  112. const hasStreamlinedUI = useHasStreamlinedUI();
  113. const tagsRef = useRef<HTMLDivElement>(null);
  114. const projectSlug = project.slug;
  115. const hasReplay = Boolean(getReplayIdFromEvent(event));
  116. const mechanism = event.tags?.find(({key}) => key === 'mechanism')?.value;
  117. const isANR = mechanism === 'ANR' || mechanism === 'AppExitInfo';
  118. const showPossibleSolutionsHigher = shouldShowCustomErrorResourceConfig(group, project);
  119. const eventEntryProps = {group, event, project};
  120. const hasActionableItems = actionableItemsEnabled({
  121. eventId: event.id,
  122. organization,
  123. projectSlug,
  124. });
  125. const {
  126. isLoading: promptLoading,
  127. isError: promptError,
  128. isPromptDismissed,
  129. dismissPrompt,
  130. showPrompt,
  131. } = usePrompt({
  132. feature: 'issue_feedback_hidden',
  133. organization,
  134. projectId: project.id,
  135. });
  136. // default to show on error or isPromptDismissed === undefined
  137. const showFeedback = !isPromptDismissed || promptError || hasStreamlinedUI;
  138. return (
  139. <Fragment>
  140. {hasStreamlinedUI && <HighlightsIconSummary event={event} />}
  141. {hasActionableItems && (
  142. <ActionableItems event={event} project={project} isShare={false} />
  143. )}
  144. {hasStreamlinedUI && <TraceDataSection event={event} />}
  145. <StyledDataSection>
  146. {!hasStreamlinedUI && <TraceDataSection event={event} />}
  147. <SuspectCommits
  148. project={project}
  149. eventId={event.id}
  150. group={group}
  151. commitRow={CommitRow}
  152. />
  153. </StyledDataSection>
  154. {event.userReport && (
  155. <InterimSection
  156. title={t('User Feedback')}
  157. type={FoldSectionKey.USER_FEEDBACK}
  158. actions={
  159. hasStreamlinedUI ? null : (
  160. <ErrorBoundary mini>
  161. <Button
  162. size="xs"
  163. icon={<IconChevron direction={showFeedback ? 'up' : 'down'} />}
  164. onClick={showFeedback ? dismissPrompt : showPrompt}
  165. title={
  166. showFeedback
  167. ? t('Hide feedback on all issue details')
  168. : t('Unhide feedback on all issue details')
  169. }
  170. disabled={promptError}
  171. busy={promptLoading}
  172. >
  173. {showFeedback ? t('Hide') : t('Show')}
  174. </Button>
  175. </ErrorBoundary>
  176. )
  177. }
  178. >
  179. {promptLoading ? (
  180. <Placeholder />
  181. ) : showFeedback ? (
  182. <EventUserFeedback
  183. report={event.userReport}
  184. orgSlug={organization.slug}
  185. issueId={group.id}
  186. showEventLink={false}
  187. />
  188. ) : null}
  189. </InterimSection>
  190. )}
  191. {event.type === EventOrGroupType.ERROR &&
  192. organization.features.includes('insights-addon-modules') &&
  193. event?.entries
  194. ?.filter((x): x is EntryException => x.type === EntryType.EXCEPTION)
  195. .flatMap(x => x.data.values ?? [])
  196. .some(({value}) => {
  197. const lowerText = value?.toLowerCase();
  198. return (
  199. lowerText &&
  200. (lowerText.includes('api key') || lowerText.includes('429')) &&
  201. (lowerText.includes('openai') ||
  202. lowerText.includes('anthropic') ||
  203. lowerText.includes('cohere') ||
  204. lowerText.includes('langchain'))
  205. );
  206. }) ? (
  207. <LazyLoad
  208. LazyComponent={LLMMonitoringSection}
  209. event={event}
  210. organization={organization}
  211. />
  212. ) : null}
  213. {group.issueCategory === IssueCategory.UPTIME && (
  214. <UptimeDataSection group={group} />
  215. )}
  216. {group.issueCategory === IssueCategory.CRON && (
  217. <CronTimelineSection
  218. event={event}
  219. organization={organization}
  220. project={project}
  221. />
  222. )}
  223. <HighlightsDataSection event={event} project={project} viewAllRef={tagsRef} />
  224. {showPossibleSolutionsHigher && (
  225. <ResourcesAndPossibleSolutionsIssueDetailsContent
  226. event={event}
  227. project={project}
  228. group={group}
  229. />
  230. )}
  231. <EventEvidence event={event} group={group} project={project} />
  232. <GroupEventEntry
  233. sectionKey={FoldSectionKey.MESSAGE}
  234. entryType={EntryType.MESSAGE}
  235. {...eventEntryProps}
  236. />
  237. <GroupEventEntry
  238. sectionKey={FoldSectionKey.STACKTRACE}
  239. entryType={EntryType.EXCEPTION}
  240. {...eventEntryProps}
  241. />
  242. <GroupEventEntry
  243. sectionKey={FoldSectionKey.STACKTRACE}
  244. entryType={EntryType.STACKTRACE}
  245. {...eventEntryProps}
  246. />
  247. <GroupEventEntry
  248. sectionKey={FoldSectionKey.STACKTRACE}
  249. entryType={EntryType.THREADS}
  250. {...eventEntryProps}
  251. />
  252. {isANR && (
  253. <QuickTraceQuery
  254. event={event}
  255. location={location}
  256. orgSlug={organization.slug}
  257. type={'spans'}
  258. skipLight
  259. >
  260. {results => {
  261. return (
  262. <QuickTraceContext.Provider value={results}>
  263. <AnrRootCause event={event} organization={organization} />
  264. </QuickTraceContext.Provider>
  265. );
  266. }}
  267. </QuickTraceQuery>
  268. )}
  269. {group.issueCategory === IssueCategory.PERFORMANCE && (
  270. <SpanEvidenceSection
  271. event={event as EventTransaction}
  272. organization={organization}
  273. projectSlug={project.slug}
  274. />
  275. )}
  276. <EventHydrationDiff event={event} group={group} />
  277. <EventReplay event={event} group={group} projectSlug={project.slug} />
  278. <GroupEventEntry
  279. sectionKey={FoldSectionKey.HPKP}
  280. entryType={EntryType.HPKP}
  281. {...eventEntryProps}
  282. />
  283. <GroupEventEntry
  284. sectionKey={FoldSectionKey.CSP}
  285. entryType={EntryType.CSP}
  286. {...eventEntryProps}
  287. />
  288. <GroupEventEntry
  289. sectionKey={FoldSectionKey.EXPECTCT}
  290. entryType={EntryType.EXPECTCT}
  291. {...eventEntryProps}
  292. />
  293. <GroupEventEntry
  294. sectionKey={FoldSectionKey.EXPECTCT}
  295. entryType={EntryType.EXPECTSTAPLE}
  296. {...eventEntryProps}
  297. />
  298. <GroupEventEntry
  299. sectionKey={FoldSectionKey.TEMPLATE}
  300. entryType={EntryType.TEMPLATE}
  301. {...eventEntryProps}
  302. />
  303. {hasNewTimelineUI ? (
  304. <BreadcrumbsDataSection event={event} group={group} project={project} />
  305. ) : (
  306. <GroupEventEntry
  307. sectionKey={FoldSectionKey.BREADCRUMBS}
  308. entryType={EntryType.BREADCRUMBS}
  309. {...eventEntryProps}
  310. />
  311. )}
  312. {!showPossibleSolutionsHigher && (
  313. <ResourcesAndPossibleSolutionsIssueDetailsContent
  314. event={event}
  315. project={project}
  316. group={group}
  317. />
  318. )}
  319. <GroupEventEntry
  320. sectionKey={FoldSectionKey.DEBUGMETA}
  321. entryType={EntryType.DEBUGMETA}
  322. {...eventEntryProps}
  323. />
  324. <GroupEventEntry
  325. sectionKey={FoldSectionKey.REQUEST}
  326. entryType={EntryType.REQUEST}
  327. {...eventEntryProps}
  328. />
  329. {hasStreamlinedUI ? (
  330. <EventTagsDataSection event={event} projectSlug={project.slug} ref={tagsRef} />
  331. ) : (
  332. <div ref={tagsRef}>
  333. <EventTagsAndScreenshot event={event} projectSlug={project.slug} />
  334. </div>
  335. )}
  336. <EventContexts group={group} event={event} />
  337. <EventExtraData event={event} />
  338. <EventPackageData event={event} />
  339. <EventDevice event={event} />
  340. <EventViewHierarchy event={event} project={project} />
  341. {hasStreamlinedUI && (
  342. <ScreenshotDataSection event={event} projectSlug={project.slug} />
  343. )}
  344. <EventAttachments event={event} projectSlug={project.slug} />
  345. <EventSdk sdk={event.sdk} meta={event._meta?.sdk} />
  346. {event.groupID && (
  347. <EventGroupingInfo
  348. projectSlug={project.slug}
  349. event={event}
  350. showGroupingConfig={
  351. organization.features.includes('set-grouping-config') &&
  352. 'groupingConfig' in event
  353. }
  354. group={group}
  355. />
  356. )}
  357. {!hasReplay && (
  358. <EventRRWebIntegration
  359. event={event}
  360. orgId={organization.slug}
  361. projectSlug={project.slug}
  362. />
  363. )}
  364. </Fragment>
  365. );
  366. }
  367. function ResourcesAndPossibleSolutionsIssueDetailsContent({
  368. event,
  369. project,
  370. group,
  371. }: Required<GroupEventDetailsContentProps>) {
  372. return (
  373. <ErrorBoundary mini>
  374. <ResourcesAndPossibleSolutions event={event} project={project} group={group} />
  375. </ErrorBoundary>
  376. );
  377. }
  378. function PerformanceDurationRegressionIssueDetailsContent({
  379. group,
  380. event,
  381. project,
  382. }: Required<GroupEventDetailsContentProps>) {
  383. return (
  384. <Fragment>
  385. <ErrorBoundary mini>
  386. <EventRegressionSummary event={event} group={group} />
  387. </ErrorBoundary>
  388. <ErrorBoundary mini>
  389. <EventBreakpointChart event={event} />
  390. </ErrorBoundary>
  391. <ErrorBoundary mini>
  392. <AggregateSpanDiff event={event} project={project} />
  393. </ErrorBoundary>
  394. <ErrorBoundary mini>
  395. <EventComparison event={event} project={project} />
  396. </ErrorBoundary>
  397. </Fragment>
  398. );
  399. }
  400. function ProfilingDurationRegressionIssueDetailsContent({
  401. group,
  402. event,
  403. project,
  404. }: Required<GroupEventDetailsContentProps>) {
  405. const organization = useOrganization();
  406. return (
  407. <TransactionsDeltaProvider event={event} project={project}>
  408. <Fragment>
  409. <ErrorBoundary mini>
  410. <EventRegressionSummary event={event} group={group} />
  411. </ErrorBoundary>
  412. <ErrorBoundary mini>
  413. <EventFunctionBreakpointChart event={event} />
  414. </ErrorBoundary>
  415. {!organization.features.includes('continuous-profiling-compat') && (
  416. <ErrorBoundary mini>
  417. <EventAffectedTransactions event={event} group={group} project={project} />
  418. </ErrorBoundary>
  419. )}
  420. <ErrorBoundary mini>
  421. <DataSection>
  422. <b>{t('Largest Changes in Call Stack Frequency')}</b>
  423. <p>
  424. {t(`See which functions changed the most before and after the regression. The
  425. frame with the largest increase in call stack population likely
  426. contributed to the cause for the duration regression.`)}
  427. </p>
  428. <EventDifferentialFlamegraph event={event} />
  429. </DataSection>
  430. </ErrorBoundary>
  431. <ErrorBoundary mini>
  432. <EventFunctionComparisonList event={event} group={group} project={project} />
  433. </ErrorBoundary>
  434. </Fragment>
  435. </TransactionsDeltaProvider>
  436. );
  437. }
  438. export default function GroupEventDetailsContent({
  439. group,
  440. event,
  441. project,
  442. }: GroupEventDetailsContentProps) {
  443. const hasStreamlinedUI = useHasStreamlinedUI();
  444. if (!event) {
  445. return (
  446. <NotFoundMessage>
  447. <h3>{t('Latest event not available')}</h3>
  448. </NotFoundMessage>
  449. );
  450. }
  451. switch (group.issueType) {
  452. case IssueType.PERFORMANCE_DURATION_REGRESSION:
  453. case IssueType.PERFORMANCE_ENDPOINT_REGRESSION: {
  454. return (
  455. <PerformanceDurationRegressionIssueDetailsContent
  456. group={group}
  457. event={event}
  458. project={project}
  459. />
  460. );
  461. }
  462. case IssueType.PROFILE_FUNCTION_REGRESSION_EXPERIMENTAL:
  463. case IssueType.PROFILE_FUNCTION_REGRESSION: {
  464. return (
  465. <ProfilingDurationRegressionIssueDetailsContent
  466. group={group}
  467. event={event}
  468. project={project}
  469. />
  470. );
  471. }
  472. default: {
  473. return hasStreamlinedUI ? (
  474. <EventDetails event={event} group={group} project={project} />
  475. ) : (
  476. <DefaultGroupEventDetailsContent group={group} event={event} project={project} />
  477. );
  478. }
  479. }
  480. }
  481. const NotFoundMessage = styled('div')`
  482. padding: ${space(2)} ${space(4)};
  483. `;
  484. const StyledDataSection = styled(DataSection)`
  485. padding: ${space(0.5)} ${space(2)};
  486. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  487. padding: ${space(1)} ${space(4)};
  488. }
  489. &:empty {
  490. display: none;
  491. }
  492. `;