Browse Source

feat(related_issues): Add analytics (#71780)

We want to know if a related issue was clicked after rendering it.
Armen Zambrano G 9 months ago
parent
commit
a2f5ba2814

+ 5 - 0
static/app/utils/analytics/issueAnalyticsEvents.tsx

@@ -107,6 +107,9 @@ export type IssueEventParameters = {
   'issue_details.issue_status_docs_clicked': {};
   'issue_details.performance.autogrouped_siblings_toggle': {};
   'issue_details.performance.hidden_spans_expanded': {};
+  'issue_details.related_trace_issue.trace_issue_clicked': {
+    group_id: number;
+  };
   'issue_details.set_priority': SetPriorityParams;
   'issue_details.similar_issues.diff_clicked': {
     error_message?: string;
@@ -321,6 +324,8 @@ export const issueEventMap: Record<IssueEventKey, string | null> = {
   'issue_details.view_hierarchy.select_from_wireframe':
     'View Hierarchy: Selection from wireframe',
   'issue_details.issue_status_docs_clicked': 'Issue Details: Issue Status Docs Clicked',
+  'issue_details.related_trace_issue.trace_issue_clicked':
+    'Related Issue: Trace Issue Clicked',
   'issue_error_banner.viewed': 'Issue Error Banner Viewed',
   'issue_error_banner.proguard_misconfigured.displayed':
     'Proguard Potentially Misconfigured Issue Error Banner Displayed',

+ 10 - 4
static/app/views/issueDetails/traceTimeline/traceIssue.tsx

@@ -5,6 +5,7 @@ import ProjectBadge from 'sentry/components/idBadge/projectBadge';
 import Link from 'sentry/components/links/link';
 import Placeholder from 'sentry/components/placeholder';
 import {space} from 'sentry/styles/space';
+import {trackAnalytics} from 'sentry/utils/analytics';
 import useOrganization from 'sentry/utils/useOrganization';
 import useProjectFromSlug from 'sentry/utils/useProjectFromSlug';
 
@@ -21,18 +22,23 @@ export function TraceIssueEvent({event}: TraceIssueEventProps) {
   const {title, subtitle} = getTitleSubtitle(event);
   const avatarSize = parseInt(space(4), 10);
 
-  // If any of data fails to load, we don't want to render the component
-  // Only "One other issue appears in the same trace. View Full Trace (X issues)" would show up
+  const referrer = 'issue_details.related_trace_issue';
+
   return (
     <Fragment>
-      {/* // XXX: Determine plan for analytics */}
       <TraceIssueLinkContainer
         to={{
           pathname: `/organizations/${organization.slug}/issues/${issueId}/events/${event.id}/`,
           query: {
-            referrer: 'issues_trace_issue',
+            referrer: referrer,
           },
         }}
+        onClick={() => {
+          trackAnalytics(`${referrer}.trace_issue_clicked`, {
+            organization: organization.slug,
+            group_id: issueId,
+          });
+        }}
       >
         <TraceIssueProjectBadge>
           {project ? (

+ 17 - 3
static/app/views/issueDetails/traceTimeline/traceTimeline.spec.tsx

@@ -5,12 +5,14 @@ import {ProjectFixture} from 'sentry-fixture/project';
 import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
 
 import ProjectsStore from 'sentry/stores/projectsStore';
+import {trackAnalytics} from 'sentry/utils/analytics';
 import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
 
 import {TraceTimeline} from './traceTimeline';
 import type {TraceEventResponse} from './useTraceTimelineEvents';
 
 jest.mock('sentry/utils/routeAnalytics/useRouteAnalyticsParams');
+jest.mock('sentry/utils/analytics');
 
 describe('TraceTimeline', () => {
   const organization = OrganizationFixture();
@@ -83,6 +85,7 @@ describe('TraceTimeline', () => {
     await userEvent.hover(screen.getByTestId('trace-timeline-tooltip-1'));
     expect(await screen.findByText('You are here')).toBeInTheDocument();
     expect(useRouteAnalyticsParams).toHaveBeenCalledWith({
+      has_related_trace_issue: false,
       trace_timeline_status: 'shown',
     });
   });
@@ -101,6 +104,7 @@ describe('TraceTimeline', () => {
     const {container} = render(<TraceTimeline event={event} />, {organization});
     await waitFor(() =>
       expect(useRouteAnalyticsParams).toHaveBeenCalledWith({
+        has_related_trace_issue: false,
         trace_timeline_status: 'empty',
       })
     );
@@ -121,6 +125,7 @@ describe('TraceTimeline', () => {
     const {container} = render(<TraceTimeline event={event} />, {organization});
     await waitFor(() =>
       expect(useRouteAnalyticsParams).toHaveBeenCalledWith({
+        has_related_trace_issue: false,
         trace_timeline_status: 'empty',
       })
     );
@@ -187,9 +192,17 @@ describe('TraceTimeline', () => {
       await screen.findByText('SELECT "sentry_monitorcheckin"."monitor_id"')
     ).toBeInTheDocument();
     expect(screen.queryByLabelText('Current Event')).not.toBeInTheDocument();
-    expect(useRouteAnalyticsParams).toHaveBeenCalledWith({
-      trace_timeline_status: 'empty',
-    });
+
+    // Test analytics
+    await userEvent.click(await screen.findByText('Slow DB Query'));
+    expect(trackAnalytics).toHaveBeenCalledTimes(1);
+    expect(trackAnalytics).toHaveBeenCalledWith(
+      'issue_details.related_trace_issue.trace_issue_clicked',
+      {
+        group_id: issuePlatformBody.data[0]['issue.id'],
+        organization: organization.slug,
+      }
+    );
   });
 
   it('skips the timeline and shows NO related issues (only 1 issue)', async () => {
@@ -225,6 +238,7 @@ describe('TraceTimeline', () => {
     // We do not display the timeline because we only have 1 event
     expect(await screen.queryByLabelText('Current Event')).not.toBeInTheDocument();
     expect(useRouteAnalyticsParams).toHaveBeenCalledWith({
+      has_related_trace_issue: false,
       trace_timeline_status: 'empty',
     });
   });

+ 11 - 7
static/app/views/issueDetails/traceTimeline/traceTimeline.tsx

@@ -1,7 +1,6 @@
 import {Fragment, useRef} from 'react';
 import styled from '@emotion/styled';
 
-import Feature from 'sentry/components/acl/feature';
 import ErrorBoundary from 'sentry/components/errorBoundary';
 import QuestionTooltip from 'sentry/components/questionTooltip';
 import {t} from 'sentry/locale';
@@ -41,7 +40,15 @@ export function TraceTimeline({event}: TraceTimelineProps) {
     timelineStatus = 'no_trace_id';
   }
 
-  useRouteAnalyticsParams(timelineStatus ? {trace_timeline_status: timelineStatus} : {});
+  const showTraceRelatedIssue =
+    timelineStatus !== 'shown' &&
+    organization.features.includes('related-issues-issue-details-page') &&
+    oneOtherIssueEvent !== undefined;
+
+  useRouteAnalyticsParams({
+    trace_timeline_status: timelineStatus,
+    has_related_trace_issue: showTraceRelatedIssue,
+  });
 
   if (!hasTraceId) {
     return null;
@@ -60,7 +67,7 @@ export function TraceTimeline({event}: TraceTimelineProps) {
 
   return (
     <ErrorBoundary mini>
-      {timelineStatus === 'shown' ? (
+      {timelineStatus === 'shown' && (
         <Fragment>
           <TimelineWrapper>
             <div ref={timelineRef}>
@@ -81,11 +88,8 @@ export function TraceTimeline({event}: TraceTimelineProps) {
             </QuestionTooltipWrapper>
           </TimelineWrapper>
         </Fragment>
-      ) : (
-        <Feature features="related-issues-issue-details-page">
-          {oneOtherIssueEvent && <TraceIssueEvent event={oneOtherIssueEvent} />}
-        </Feature>
       )}
+      {showTraceRelatedIssue && <TraceIssueEvent event={oneOtherIssueEvent} />}
     </ErrorBoundary>
   );
 }