Browse Source

feat(releases): Add session duration graph to release details (#28232)

Scott Cooper 3 years ago
parent
commit
159675c8b8

+ 2 - 0
static/app/types/index.tsx

@@ -2131,6 +2131,7 @@ export type SessionApiResponse = SeriesApi & {
 export enum SessionField {
   SESSIONS = 'sum(session)',
   USERS = 'count_unique(user)',
+  DURATION = 'p50(session.duration)',
 }
 
 export enum SessionStatus {
@@ -2156,6 +2157,7 @@ export enum ReleaseComparisonChartType {
   ERROR_COUNT = 'errorCount',
   TRANSACTION_COUNT = 'transactionCount',
   FAILURE_RATE = 'failureRate',
+  SESSION_DURATION = 'sessionDuration',
 }
 
 export enum HealthStatsPeriodOption {

+ 18 - 0
static/app/views/releases/detail/overview/releaseComparisonChart/index.tsx

@@ -9,6 +9,7 @@ import {Client} from 'app/api';
 import ErrorPanel from 'app/components/charts/errorPanel';
 import {ChartContainer} from 'app/components/charts/styles';
 import Count from 'app/components/count';
+import Duration from 'app/components/duration';
 import GlobalSelectionLink from 'app/components/globalSelectionLink';
 import NotAvailable from 'app/components/notAvailable';
 import {Panel, PanelTable} from 'app/components/panels';
@@ -386,6 +387,9 @@ function ReleaseComparisonChart({
   const releaseUsersCount = getCount(releaseSessions?.groups, SessionField.USERS);
   const allUsersCount = getCount(allSessions?.groups, SessionField.USERS);
 
+  const sessionDurationTotal = getCount(releaseSessions?.groups, SessionField.DURATION);
+  const allSessionDurationTotal = getCount(allSessions?.groups, SessionField.DURATION);
+
   const diffFailure =
     eventsTotals?.releaseFailureRate && eventsTotals?.allFailureRate
       ? eventsTotals.releaseFailureRate - eventsTotals.allFailureRate
@@ -699,6 +703,20 @@ function ReleaseComparisonChart({
         diffDirection: null,
         diffColor: null,
       },
+      {
+        type: ReleaseComparisonChartType.SESSION_DURATION,
+        role: 'default',
+        drilldown: null,
+        thisRelease: defined(sessionDurationTotal) ? (
+          <Duration seconds={sessionDurationTotal} abbreviation />
+        ) : null,
+        allReleases: defined(allSessionDurationTotal) ? (
+          <Duration seconds={allSessionDurationTotal} abbreviation />
+        ) : null,
+        diff: null,
+        diffDirection: null,
+        diffColor: null,
+      },
       {
         type: ReleaseComparisonChartType.USER_COUNT,
         role: 'default',

+ 16 - 0
static/app/views/releases/detail/overview/releaseComparisonChart/releaseSessionsChart.tsx

@@ -78,6 +78,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
       case ReleaseComparisonChartType.CRASHED_USERS:
         return defined(value) ? `${value}%` : '\u2015';
       case ReleaseComparisonChartType.SESSION_COUNT:
+      case ReleaseComparisonChartType.SESSION_DURATION:
       case ReleaseComparisonChartType.USER_COUNT:
       default:
         return typeof value === 'number' ? value.toLocaleString() : value;
@@ -113,6 +114,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
           },
         };
       case ReleaseComparisonChartType.SESSION_COUNT:
+      case ReleaseComparisonChartType.SESSION_DURATION:
       case ReleaseComparisonChartType.USER_COUNT:
       default:
         return undefined;
@@ -137,6 +139,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
       default:
         return AreaChart;
       case ReleaseComparisonChartType.SESSION_COUNT:
+      case ReleaseComparisonChartType.SESSION_DURATION:
       case ReleaseComparisonChartType.USER_COUNT:
         return StackedAreaChart;
     }
@@ -167,6 +170,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
       case ReleaseComparisonChartType.CRASHED_USERS:
         return [theme.red300];
       case ReleaseComparisonChartType.SESSION_COUNT:
+      case ReleaseComparisonChartType.SESSION_DURATION:
       case ReleaseComparisonChartType.USER_COUNT:
       default:
         return undefined;
@@ -461,6 +465,18 @@ class ReleaseSessionsChart extends React.Component<Props> {
           ),
           markLines,
         };
+      case ReleaseComparisonChartType.SESSION_DURATION:
+        return {
+          series: Object.values(
+            fillChartDataFromSessionsResponse({
+              response: releaseSessions,
+              field: SessionField.DURATION,
+              groupBy: 'session.status',
+              chartData: initSessionsBreakdownChartData(theme),
+            })
+          ),
+          markLines,
+        };
       case ReleaseComparisonChartType.USER_COUNT:
         return {
           series: Object.values(

+ 1 - 1
static/app/views/releases/detail/overview/releaseDetailsRequest.tsx

@@ -75,7 +75,7 @@ class ReleaseDetailsRequest extends React.Component<Props, State> {
     });
 
     return {
-      field: ['count_unique(user)', 'sum(session)'],
+      field: ['count_unique(user)', 'sum(session)', 'p50(session.duration)'],
       groupBy: ['session.status'],
       interval: getSessionsInterval(
         {

+ 2 - 0
static/app/views/releases/detail/utils.tsx

@@ -154,6 +154,7 @@ export const releaseComparisonChartLabels = {
   [ReleaseComparisonChartType.ERRORED_USERS]: t('Errored'),
   [ReleaseComparisonChartType.CRASHED_USERS]: t('Crashed User Rate'),
   [ReleaseComparisonChartType.SESSION_COUNT]: t('Session Count'),
+  [ReleaseComparisonChartType.SESSION_DURATION]: t('Session Duration p50'),
   [ReleaseComparisonChartType.USER_COUNT]: t('User Count'),
   [ReleaseComparisonChartType.ERROR_COUNT]: t('Error Count'),
   [ReleaseComparisonChartType.TRANSACTION_COUNT]: t('Transaction Count'),
@@ -172,6 +173,7 @@ export const releaseComparisonChartTitles = {
   [ReleaseComparisonChartType.ERRORED_USERS]: t('Errored User Rate'),
   [ReleaseComparisonChartType.CRASHED_USERS]: t('Crashed User Rate'),
   [ReleaseComparisonChartType.SESSION_COUNT]: t('Session Count'),
+  [ReleaseComparisonChartType.SESSION_DURATION]: t('Session Duration'),
   [ReleaseComparisonChartType.USER_COUNT]: t('User Count'),
   [ReleaseComparisonChartType.ERROR_COUNT]: t('Error Count'),
   [ReleaseComparisonChartType.TRANSACTION_COUNT]: t('Transaction Count'),

+ 48 - 8
tests/fixtures/js-stubs/sessions.js

@@ -22,40 +22,60 @@ export function SessionUserCountByStatus() {
         by: {
           'session.status': 'crashed',
         },
-        totals: {'sum(session)': 492, 'count_unique(user)': 92},
+        totals: {
+          'sum(session)': 492,
+          'count_unique(user)': 92,
+          'p50(session.duration)': 195,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 490],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 90],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 193],
         },
       },
       {
         by: {
           'session.status': 'healthy',
         },
-        totals: {'sum(session)': 9260, 'count_unique(user)': 260},
+        totals: {
+          'sum(session)': 9260,
+          'count_unique(user)': 260,
+          'p50(session.duration)': 195,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9167, 93],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 258],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 193],
         },
       },
       {
         by: {
           'session.status': 'abnormal',
         },
-        totals: {'sum(session)': 0, 'count_unique(user)': 0},
+        totals: {
+          'sum(session)': 0,
+          'count_unique(user)': 0,
+          'p50(session.duration)': 0,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         },
       },
       {
         by: {
           'session.status': 'errored',
         },
-        totals: {'sum(session)': 99, 'count_unique(user)': 9},
+        totals: {
+          'sum(session)': 99,
+          'count_unique(user)': 9,
+          'p50(session.duration)': 195,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 8],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 194],
         },
       },
     ],
@@ -88,40 +108,60 @@ export function SessionUserCountByStatus2() {
         by: {
           'session.status': 'crashed',
         },
-        totals: {'sum(session)': 992, 'count_unique(user)': 92},
+        totals: {
+          'sum(session)': 992,
+          'count_unique(user)': 92,
+          'p50(session.duration)': 802,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 990],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 90],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 800],
         },
       },
       {
         by: {
           'session.status': 'abnormal',
         },
-        totals: {'sum(session)': 0, 'count_unique(user)': 0},
+        totals: {
+          'sum(session)': 0,
+          'count_unique(user)': 0,
+          'p50(session.duration)': 0,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
         },
       },
       {
         by: {
           'session.status': 'healthy',
         },
-        totals: {'sum(session)': 202136, 'count_unique(user)': 99136},
+        totals: {
+          'sum(session)': 202136,
+          'count_unique(user)': 99136,
+          'p50(session.duration)': 108404,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3404, 198732],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 404, 98732],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 404, 108000],
         },
       },
       {
         by: {
           'session.status': 'errored',
         },
-        totals: {'sum(session)': 1954, 'count_unique(user)': 915},
+        totals: {
+          'sum(session)': 1954,
+          'count_unique(user)': 915,
+          'p50(session.duration)': 800,
+        },
         series: {
           'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 1914],
           'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 914],
+          'p50(session.duration)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 799],
         },
       },
     ],

+ 2 - 2
tests/js/spec/views/releases/detail/overview/releaseComparisonChart/index.spec.tsx

@@ -40,14 +40,14 @@ describe('Releases > Detail > Overview > ReleaseComparison', () => {
     );
     expect(screen.getByLabelText('Chart Value').textContent).toContain('95.006% 4.51%');
 
-    expect(screen.getAllByRole('radio').length).toBe(12);
+    expect(screen.getAllByRole('radio').length).toBe(13);
 
     // lazy way to make sure that all percentages are calculated correctly
     expect(
       screen.getByTestId('release-comparison-table').textContent
     ).toMatchInlineSnapshot(
       // eslint-disable-next-line no-irregular-whitespace
-      `"DescriptionAll ReleasesThis ReleaseChangeCrash Free Session Rate 99.516%95.006%4.51% Healthy 98.564%94.001%4.563% Abnormal 0%0%0% —Errored 0.953%1.005%0.052% Crashed Session Rate 0.484%4.994%4.511% Crash Free User Rate 99.908%75%24.908% Healthy 98.994%72.022%26.972% Abnormal 0%0%0% —Errored 0.914%2.493%1.579% Crashed User Rate 0.092%25.485%25.393% Session Count 205k9.8k—User Count 100k361—"`
+      `"DescriptionAll ReleasesThis ReleaseChangeCrash Free Session Rate 99.516%95.006%4.51% Healthy 98.564%94.001%4.563% Abnormal 0%0%0% —Errored 0.953%1.005%0.052% Crashed Session Rate 0.484%4.994%4.511% Crash Free User Rate 99.908%75%24.908% Healthy 98.994%72.022%26.972% Abnormal 0%0%0% —Errored 0.914%2.493%1.579% Crashed User Rate 0.092%25.485%25.393% Session Count 205k9.8k—Session Duration p50 31hr10min—User Count 100k361—"`
     );
   });