Browse Source

feat(trace): Add performance issues to txn details (#47030)

- This updates the transaction details page to also mention performance
issues when they exist on the transaction
- Closes https://github.com/getsentry/sentry/issues/45776
William Mak 1 year ago
parent
commit
f5c4d48f4c

+ 22 - 8
static/app/components/events/interfaces/spans/index.tsx

@@ -14,7 +14,10 @@ import {EventTransaction} from 'sentry/types/event';
 import {objectIsEmpty} from 'sentry/utils';
 import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
 import {QuickTraceContext} from 'sentry/utils/performance/quickTrace/quickTraceContext';
-import {TraceError} from 'sentry/utils/performance/quickTrace/types';
+import {
+  TraceError,
+  TracePerformanceIssue,
+} from 'sentry/utils/performance/quickTrace/types';
 import withOrganization from 'sentry/utils/withOrganization';
 
 import Filter from './filter';
@@ -34,27 +37,36 @@ function TraceErrorAlerts({
   isLoading,
   errors,
   parsedTrace,
+  performanceIssues,
 }: {
   errors: TraceError[] | undefined;
   isLoading: boolean;
   parsedTrace: ParsedTraceType;
+  performanceIssues: TracePerformanceIssue[] | undefined;
 }) {
   if (isLoading) {
     return null;
   }
 
-  if (!errors || errors.length <= 0) {
+  const traceErrors: (TraceError | TracePerformanceIssue)[] = [];
+  if (errors && errors.length > 0) {
+    traceErrors.push(...errors);
+  }
+  if (performanceIssues && performanceIssues.length > 0) {
+    traceErrors.push(...performanceIssues);
+  }
+  if (traceErrors.length === 0) {
     return null;
   }
 
   // This is intentional as unbalanced string formatters in `tn()` are problematic
   const label =
-    errors.length === 1
-      ? t('There is an error event associated with this transaction event.')
+    traceErrors.length === 1
+      ? t('There is an issue associated with this transaction event.')
       : tn(
-          `There are %s error events associated with this transaction event.`,
-          `There are %s error events associated with this transaction event.`,
-          errors.length
+          `There are %s issues associated with this transaction event.`,
+          `There are %s issues associated with this transaction event.`,
+          traceErrors.length
         );
 
   return (
@@ -62,7 +74,7 @@ function TraceErrorAlerts({
       <Alert type={getCumulativeAlertLevelFromErrors(errors)}>
         <ErrorLabel>{label}</ErrorLabel>
 
-        <TraceErrorList trace={parsedTrace} errors={errors} onClickSpan={() => {}} />
+        <TraceErrorList trace={parsedTrace} errors={traceErrors} onClickSpan={() => {}} />
       </Alert>
     </AlertContainer>
   );
@@ -92,6 +104,7 @@ function SpansInterface({event, affectedSpanIds, organization}: Props) {
             <TraceErrorAlerts
               isLoading={quickTrace?.isLoading ?? false}
               errors={quickTrace?.currentEvent?.errors}
+              performanceIssues={quickTrace?.currentEvent?.performance_issues}
               parsedTrace={parsedTrace}
             />
             <Observer>
@@ -118,6 +131,7 @@ function SpansInterface({event, affectedSpanIds, organization}: Props) {
                 {() => {
                   return (
                     <TraceView
+                      performanceIssues={quickTrace?.currentEvent?.performance_issues}
                       waterfallModel={waterfallModel}
                       organization={organization}
                     />

+ 9 - 15
static/app/components/events/interfaces/spans/spanTree.tsx

@@ -469,12 +469,10 @@ class SpanTree extends Component<PropType> {
         const {span, treeDepth, continuingTreeDepths} = payload;
 
         if (payload.type === 'span_group_chain') {
-          const groupingContainsAffectedSpan =
-            isEmbeddedSpanTree &&
-            payload.spanNestedGrouping?.find(
-              ({span: s}) =>
-                !isGapSpan(s) && waterfallModel.affectedSpanIds?.includes(s.span_id)
-            );
+          const groupingContainsAffectedSpan = payload.spanNestedGrouping?.find(
+            ({span: s}) =>
+              !isGapSpan(s) && waterfallModel.affectedSpanIds?.includes(s.span_id)
+          );
 
           acc.spanTree.push({
             type: SpanTreeNodeType.DESCENDANT_GROUP,
@@ -505,12 +503,10 @@ class SpanTree extends Component<PropType> {
         }
 
         if (payload.type === 'span_group_siblings') {
-          const groupingContainsAffectedSpan =
-            isEmbeddedSpanTree &&
-            payload.spanSiblingGrouping?.find(
-              ({span: s}) =>
-                !isGapSpan(s) && waterfallModel.affectedSpanIds?.includes(s.span_id)
-            );
+          const groupingContainsAffectedSpan = payload.spanSiblingGrouping?.find(
+            ({span: s}) =>
+              !isGapSpan(s) && waterfallModel.affectedSpanIds?.includes(s.span_id)
+          );
 
           acc.spanTree.push({
             type: SpanTreeNodeType.SIBLING_GROUP,
@@ -572,9 +568,7 @@ class SpanTree extends Component<PropType> {
         }
 
         const isAffectedSpan =
-          !('type' in span) &&
-          isEmbeddedSpanTree &&
-          waterfallModel.affectedSpanIds?.includes(span.span_id);
+          !('type' in span) && waterfallModel.affectedSpanIds?.includes(span.span_id);
 
         let spanBarType: SpanBarType | undefined = undefined;
 

+ 5 - 2
static/app/components/events/interfaces/spans/traceErrorList.tsx

@@ -6,12 +6,15 @@ import groupBy from 'lodash/groupBy';
 import List from 'sentry/components/list';
 import ListItem from 'sentry/components/list/listItem';
 import {tct, tn} from 'sentry/locale';
-import {TraceError} from 'sentry/utils/performance/quickTrace/types';
+import {
+  TraceError,
+  TracePerformanceIssue,
+} from 'sentry/utils/performance/quickTrace/types';
 
 import {ParsedTraceType, SpanType} from './types';
 
 interface TraceErrorListProps {
-  errors: TraceError[];
+  errors: (TraceError | TracePerformanceIssue)[];
   onClickSpan: (event: React.MouseEvent, spanId: SpanType['span_id']) => void;
   trace: ParsedTraceType;
 }

+ 8 - 1
static/app/components/events/interfaces/spans/traceView.tsx

@@ -4,6 +4,7 @@ import {Observer} from 'mobx-react';
 import EmptyStateWarning from 'sentry/components/emptyStateWarning';
 import {t} from 'sentry/locale';
 import {Organization} from 'sentry/types';
+import {TracePerformanceIssue} from 'sentry/utils/performance/quickTrace/types';
 
 import * as CursorGuideHandler from './cursorGuideHandler';
 import * as DividerHandlerManager from './dividerHandlerManager';
@@ -19,6 +20,7 @@ type Props = {
   organization: Organization;
   waterfallModel: WaterfallModel;
   isEmbedded?: boolean;
+  performanceIssues?: TracePerformanceIssue[];
 };
 
 function TraceView(props: Props) {
@@ -69,7 +71,7 @@ function TraceView(props: Props) {
     </Observer>
   );
 
-  const {organization, waterfallModel, isEmbedded} = props;
+  const {organization, waterfallModel, isEmbedded, performanceIssues} = props;
 
   if (!getTraceContext(waterfallModel.event)) {
     return (
@@ -78,6 +80,11 @@ function TraceView(props: Props) {
       </EmptyStateWarning>
     );
   }
+  if (!waterfallModel.affectedSpanIds && performanceIssues) {
+    waterfallModel.affectedSpanIds = performanceIssues
+      .map(issue => issue.suspect_spans)
+      .flat();
+  }
 
   return (
     <SpanContext.Provider>