groupEventDetailsContent.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import {Fragment, lazy, useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {CommitRow} from 'sentry/components/commitRow';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import {EventContexts} from 'sentry/components/events/contexts';
  6. import {EventDevice} from 'sentry/components/events/device';
  7. import {EventAttachments} from 'sentry/components/events/eventAttachments';
  8. import {EventDataSection} from 'sentry/components/events/eventDataSection';
  9. import {EventEntry} from 'sentry/components/events/eventEntry';
  10. import {EventEvidence} from 'sentry/components/events/eventEvidence';
  11. import {EventExtraData} from 'sentry/components/events/eventExtraData';
  12. import EventReplay from 'sentry/components/events/eventReplay';
  13. import {EventSdk} from 'sentry/components/events/eventSdk';
  14. import AggregateSpanDiff from 'sentry/components/events/eventStatisticalDetector/aggregateSpanDiff';
  15. import EventBreakpointChart from 'sentry/components/events/eventStatisticalDetector/breakpointChart';
  16. import {EventAffectedTransactions} from 'sentry/components/events/eventStatisticalDetector/eventAffectedTransactions';
  17. import EventComparison from 'sentry/components/events/eventStatisticalDetector/eventComparison';
  18. import {EventDifferentialFlamegraph} from 'sentry/components/events/eventStatisticalDetector/eventDifferentialFlamegraph';
  19. import {EventFunctionComparisonList} from 'sentry/components/events/eventStatisticalDetector/eventFunctionComparisonList';
  20. import {EventRegressionSummary} from 'sentry/components/events/eventStatisticalDetector/eventRegressionSummary';
  21. import {EventFunctionBreakpointChart} from 'sentry/components/events/eventStatisticalDetector/functionBreakpointChart';
  22. import {TransactionsDeltaProvider} from 'sentry/components/events/eventStatisticalDetector/transactionsDeltaProvider';
  23. import {EventTagsAndScreenshot} from 'sentry/components/events/eventTagsAndScreenshot';
  24. import {EventViewHierarchy} from 'sentry/components/events/eventViewHierarchy';
  25. import {EventGroupingInfo} from 'sentry/components/events/groupingInfo';
  26. import HighlightsDataSection from 'sentry/components/events/highlights/highlightsDataSection';
  27. import {ActionableItems} from 'sentry/components/events/interfaces/crashContent/exception/actionableItems';
  28. import {actionableItemsEnabled} from 'sentry/components/events/interfaces/crashContent/exception/useActionableItems';
  29. import {CronTimelineSection} from 'sentry/components/events/interfaces/crons/cronTimelineSection';
  30. import {AnrRootCause} from 'sentry/components/events/interfaces/performance/anrRootCause';
  31. import {SpanEvidenceSection} from 'sentry/components/events/interfaces/performance/spanEvidence';
  32. import {EventPackageData} from 'sentry/components/events/packageData';
  33. import {EventRRWebIntegration} from 'sentry/components/events/rrwebIntegration';
  34. import {DataSection} from 'sentry/components/events/styles';
  35. import {SuspectCommits} from 'sentry/components/events/suspectCommits';
  36. import {EventUserFeedback} from 'sentry/components/events/userFeedback';
  37. import LazyLoad from 'sentry/components/lazyLoad';
  38. import {t} from 'sentry/locale';
  39. import {space} from 'sentry/styles/space';
  40. import {
  41. type EntryException,
  42. type Event,
  43. EventOrGroupType,
  44. type Group,
  45. IssueCategory,
  46. IssueType,
  47. type Project,
  48. } from 'sentry/types';
  49. import type {EventTransaction} from 'sentry/types/event';
  50. import {EntryType} from 'sentry/types/event';
  51. import {shouldShowCustomErrorResourceConfig} from 'sentry/utils/issueTypeConfig';
  52. import {QuickTraceContext} from 'sentry/utils/performance/quickTrace/quickTraceContext';
  53. import QuickTraceQuery from 'sentry/utils/performance/quickTrace/quickTraceQuery';
  54. import {getReplayIdFromEvent} from 'sentry/utils/replays/getReplayIdFromEvent';
  55. import {useLocation} from 'sentry/utils/useLocation';
  56. import useOrganization from 'sentry/utils/useOrganization';
  57. import {ResourcesAndMaybeSolutions} from 'sentry/views/issueDetails/resourcesAndMaybeSolutions';
  58. const LLMMonitoringSection = lazy(
  59. () => import('sentry/components/events/interfaces/llm-monitoring/llmMonitoringSection')
  60. );
  61. type GroupEventDetailsContentProps = {
  62. group: Group;
  63. project: Project;
  64. event?: Event;
  65. };
  66. type GroupEventEntryProps = {
  67. entryType: EntryType;
  68. event: Event;
  69. group: Group;
  70. project: Project;
  71. };
  72. function GroupEventEntry({event, entryType, group, project}: GroupEventEntryProps) {
  73. const organization = useOrganization();
  74. const matchingEntry = event.entries.find(entry => entry.type === entryType);
  75. if (!matchingEntry) {
  76. return null;
  77. }
  78. return (
  79. <EventEntry
  80. projectSlug={project.slug}
  81. group={group}
  82. entry={matchingEntry}
  83. {...{organization, event}}
  84. />
  85. );
  86. }
  87. function DefaultGroupEventDetailsContent({
  88. group,
  89. event,
  90. project,
  91. }: Required<GroupEventDetailsContentProps>) {
  92. const organization = useOrganization();
  93. const location = useLocation();
  94. const tagsRef = useRef<HTMLDivElement>(null);
  95. const projectSlug = project.slug;
  96. const hasReplay = Boolean(getReplayIdFromEvent(event));
  97. const mechanism = event.tags?.find(({key}) => key === 'mechanism')?.value;
  98. const isANR = mechanism === 'ANR' || mechanism === 'AppExitInfo';
  99. const hasAnrImprovementsFeature = organization.features.includes('anr-improvements');
  100. const showMaybeSolutionsHigher = shouldShowCustomErrorResourceConfig(group, project);
  101. const eventEntryProps = {group, event, project};
  102. const hasActionableItems = actionableItemsEnabled({
  103. eventId: event.id,
  104. organization,
  105. projectSlug,
  106. });
  107. return (
  108. <Fragment>
  109. {hasActionableItems && (
  110. <ActionableItems event={event} project={project} isShare={false} />
  111. )}
  112. <StyledDataSection>
  113. <SuspectCommits
  114. project={project}
  115. eventId={event.id}
  116. group={group}
  117. commitRow={CommitRow}
  118. />
  119. </StyledDataSection>
  120. {event.userReport && (
  121. <EventDataSection title={t('User Feedback')} type="user-feedback">
  122. <EventUserFeedback
  123. report={event.userReport}
  124. orgSlug={organization.slug}
  125. issueId={group.id}
  126. showEventLink={false}
  127. />
  128. </EventDataSection>
  129. )}
  130. {event.type === EventOrGroupType.ERROR &&
  131. organization.features.includes('ai-analytics') &&
  132. event?.entries
  133. ?.filter((x): x is EntryException => x.type === EntryType.EXCEPTION)
  134. .flatMap(x => x.data.values ?? [])
  135. .some(({value}) => {
  136. const lowerText = value?.toLowerCase();
  137. return (
  138. lowerText &&
  139. (lowerText.includes('api key') || lowerText.includes('429')) &&
  140. (lowerText.includes('openai') ||
  141. lowerText.includes('anthropic') ||
  142. lowerText.includes('cohere') ||
  143. lowerText.includes('langchain'))
  144. );
  145. }) ? (
  146. <LazyLoad
  147. LazyComponent={LLMMonitoringSection}
  148. event={event}
  149. organization={organization}
  150. />
  151. ) : null}
  152. {group.issueCategory === IssueCategory.CRON && (
  153. <CronTimelineSection
  154. event={event}
  155. organization={organization}
  156. project={project}
  157. />
  158. )}
  159. <HighlightsDataSection
  160. event={event}
  161. group={group}
  162. project={project}
  163. viewAllRef={tagsRef}
  164. />
  165. {showMaybeSolutionsHigher && (
  166. <ResourcesAndMaybeSolutionsIssueDetailsContent
  167. event={event}
  168. project={project}
  169. group={group}
  170. />
  171. )}
  172. <EventEvidence event={event} group={group} project={project} />
  173. <GroupEventEntry entryType={EntryType.MESSAGE} {...eventEntryProps} />
  174. <GroupEventEntry entryType={EntryType.EXCEPTION} {...eventEntryProps} />
  175. <GroupEventEntry entryType={EntryType.STACKTRACE} {...eventEntryProps} />
  176. <GroupEventEntry entryType={EntryType.THREADS} {...eventEntryProps} />
  177. {hasAnrImprovementsFeature && isANR && (
  178. <QuickTraceQuery
  179. event={event}
  180. location={location}
  181. orgSlug={organization.slug}
  182. type={'spans'}
  183. skipLight
  184. >
  185. {results => {
  186. return (
  187. <QuickTraceContext.Provider value={results}>
  188. <AnrRootCause event={event} organization={organization} />
  189. </QuickTraceContext.Provider>
  190. );
  191. }}
  192. </QuickTraceQuery>
  193. )}
  194. {group.issueCategory === IssueCategory.PERFORMANCE && (
  195. <SpanEvidenceSection
  196. event={event as EventTransaction}
  197. organization={organization}
  198. projectSlug={project.slug}
  199. />
  200. )}
  201. <EventReplay event={event} group={group} projectSlug={project.slug} />
  202. <GroupEventEntry entryType={EntryType.HPKP} {...eventEntryProps} />
  203. <GroupEventEntry entryType={EntryType.CSP} {...eventEntryProps} />
  204. <GroupEventEntry entryType={EntryType.EXPECTCT} {...eventEntryProps} />
  205. <GroupEventEntry entryType={EntryType.EXPECTSTAPLE} {...eventEntryProps} />
  206. <GroupEventEntry entryType={EntryType.TEMPLATE} {...eventEntryProps} />
  207. <GroupEventEntry entryType={EntryType.BREADCRUMBS} {...eventEntryProps} />
  208. {!showMaybeSolutionsHigher && (
  209. <ResourcesAndMaybeSolutionsIssueDetailsContent
  210. event={event}
  211. project={project}
  212. group={group}
  213. />
  214. )}
  215. <GroupEventEntry entryType={EntryType.DEBUGMETA} {...eventEntryProps} />
  216. <GroupEventEntry entryType={EntryType.REQUEST} {...eventEntryProps} />
  217. <div ref={tagsRef}>
  218. <EventTagsAndScreenshot event={event} projectSlug={project.slug} />
  219. </div>
  220. <EventContexts group={group} event={event} />
  221. <EventExtraData event={event} />
  222. <EventPackageData event={event} />
  223. <EventDevice event={event} />
  224. <EventViewHierarchy event={event} project={project} />
  225. <EventAttachments event={event} projectSlug={project.slug} />
  226. <EventSdk sdk={event.sdk} meta={event._meta?.sdk} />
  227. {event.groupID && (
  228. <EventGroupingInfo
  229. projectSlug={project.slug}
  230. event={event}
  231. showGroupingConfig={
  232. organization.features.includes('set-grouping-config') &&
  233. 'groupingConfig' in event
  234. }
  235. group={group}
  236. />
  237. )}
  238. {!hasReplay && (
  239. <EventRRWebIntegration
  240. event={event}
  241. orgId={organization.slug}
  242. projectSlug={project.slug}
  243. />
  244. )}
  245. </Fragment>
  246. );
  247. }
  248. function ResourcesAndMaybeSolutionsIssueDetailsContent({
  249. event,
  250. project,
  251. group,
  252. }: Required<GroupEventDetailsContentProps>) {
  253. return (
  254. <ErrorBoundary mini>
  255. <ResourcesAndMaybeSolutions event={event} project={project} group={group} />
  256. </ErrorBoundary>
  257. );
  258. }
  259. function PerformanceDurationRegressionIssueDetailsContent({
  260. group,
  261. event,
  262. project,
  263. }: Required<GroupEventDetailsContentProps>) {
  264. return (
  265. <Fragment>
  266. <ErrorBoundary mini>
  267. <EventRegressionSummary event={event} group={group} />
  268. </ErrorBoundary>
  269. <ErrorBoundary mini>
  270. <EventBreakpointChart event={event} />
  271. </ErrorBoundary>
  272. <ErrorBoundary mini>
  273. <AggregateSpanDiff event={event} project={project} />
  274. </ErrorBoundary>
  275. <ErrorBoundary mini>
  276. <EventComparison event={event} project={project} />
  277. </ErrorBoundary>
  278. </Fragment>
  279. );
  280. }
  281. function ProfilingDurationRegressionIssueDetailsContent({
  282. group,
  283. event,
  284. project,
  285. }: Required<GroupEventDetailsContentProps>) {
  286. return (
  287. <TransactionsDeltaProvider event={event} project={project}>
  288. <Fragment>
  289. <ErrorBoundary mini>
  290. <EventRegressionSummary event={event} group={group} />
  291. </ErrorBoundary>
  292. <ErrorBoundary mini>
  293. <EventFunctionBreakpointChart event={event} />
  294. </ErrorBoundary>
  295. <ErrorBoundary mini>
  296. <EventAffectedTransactions event={event} group={group} project={project} />
  297. </ErrorBoundary>
  298. <ErrorBoundary mini>
  299. <DataSection>
  300. <b>{t('Largest Changes in Call Stack Frequency')}</b>
  301. <p>
  302. {t(`See which functions changed the most before and after the regression. The
  303. frame with the largest increase in call stack population likely
  304. contributed to the cause for the duration regression.`)}
  305. </p>
  306. <EventDifferentialFlamegraph event={event} />
  307. </DataSection>
  308. </ErrorBoundary>
  309. <ErrorBoundary mini>
  310. <EventFunctionComparisonList event={event} group={group} project={project} />
  311. </ErrorBoundary>
  312. </Fragment>
  313. </TransactionsDeltaProvider>
  314. );
  315. }
  316. function GroupEventDetailsContent({
  317. group,
  318. event,
  319. project,
  320. }: GroupEventDetailsContentProps) {
  321. if (!event) {
  322. return (
  323. <NotFoundMessage>
  324. <h3>{t('Latest event not available')}</h3>
  325. </NotFoundMessage>
  326. );
  327. }
  328. switch (group.issueType) {
  329. case IssueType.PERFORMANCE_DURATION_REGRESSION:
  330. case IssueType.PERFORMANCE_ENDPOINT_REGRESSION: {
  331. return (
  332. <PerformanceDurationRegressionIssueDetailsContent
  333. group={group}
  334. event={event}
  335. project={project}
  336. />
  337. );
  338. }
  339. case IssueType.PROFILE_FUNCTION_REGRESSION_EXPERIMENTAL:
  340. case IssueType.PROFILE_FUNCTION_REGRESSION: {
  341. return (
  342. <ProfilingDurationRegressionIssueDetailsContent
  343. group={group}
  344. event={event}
  345. project={project}
  346. />
  347. );
  348. }
  349. default: {
  350. return (
  351. <DefaultGroupEventDetailsContent group={group} event={event} project={project} />
  352. );
  353. }
  354. }
  355. }
  356. const NotFoundMessage = styled('div')`
  357. padding: ${space(2)} ${space(4)};
  358. `;
  359. const StyledDataSection = styled(DataSection)`
  360. padding: ${space(0.5)} ${space(2)};
  361. @media (min-width: ${p => p.theme.breakpoints.medium}) {
  362. padding: ${space(1)} ${space(4)};
  363. }
  364. &:empty {
  365. display: none;
  366. }
  367. `;
  368. export default GroupEventDetailsContent;