Skip to content

Commit 8f21261

Browse files
authored
chore(aci): add alert creation + success analytics (#104403)
Adding analytics for creating old alert rules, cron monitors, and uptime monitors in the old UI so that we can compare these numbers to the new UI. Also added a `success: boolean` field to the monitor/automation creation analytics so we can see if the new UI is intuitive or if users are running into lots of errors.
1 parent cbf0e7a commit 8f21261

File tree

11 files changed

+120
-21
lines changed

11 files changed

+120
-21
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
1+
import type {MetricRule} from 'sentry/views/alerts/rules/metric/types';
2+
import type {UptimeMonitorMode} from 'sentry/views/alerts/rules/uptime/types';
3+
import type {MonitorConfig} from 'sentry/views/insights/crons/types';
4+
15
export type AlertsEventParameters = {
26
'anomaly-detection.feedback-submitted': {
37
choice_selected: boolean;
48
incident_id: string;
59
};
10+
'cron_monitor.created': {
11+
cron_schedule_type: MonitorConfig['schedule_type'];
12+
};
13+
'issue_alert_rule.created': Record<string, unknown>;
14+
'metric_alert_rule.created': {
15+
aggregate: MetricRule['aggregate'];
16+
dataset: MetricRule['dataset'];
17+
};
18+
'uptime_monitor.created': {
19+
uptime_mode: UptimeMonitorMode;
20+
};
621
};
722

823
type AlertsEventKey = keyof AlertsEventParameters;
924

1025
export const alertsEventMap: Record<AlertsEventKey, string | null> = {
1126
'anomaly-detection.feedback-submitted': 'Anomaly Detection Feedback Submitted',
27+
'issue_alert_rule.created': 'Issue Alert Rule Created',
28+
'metric_alert_rule.created': 'Metric Alert Rule Created',
29+
'cron_monitor.created': 'Cron Monitor Created',
30+
'uptime_monitor.created': 'Uptime Monitor Created',
1231
};

static/app/utils/analytics/monitorsAnalyticsEvents.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@ type DetectorAnalyticsEventPayload = ReturnType<typeof getDetectorAnalyticsPaylo
66

77
type AutomationAnalyticsEventPayload = ReturnType<typeof getAutomationAnalyticsPayload>;
88

9+
type DetectorCreateAnalyticsEventPayload =
10+
| (DetectorAnalyticsEventPayload & {
11+
success: true;
12+
})
13+
| {detector_type: string; success: false};
14+
915
export type MonitorsEventParameters = {
1016
'automation.created': AutomationAnalyticsEventPayload & {
1117
organization: Organization;
18+
success: boolean;
1219
};
1320
'automation.updated': AutomationAnalyticsEventPayload & {
1421
organization: Organization;
@@ -17,7 +24,7 @@ export type MonitorsEventParameters = {
1724
guide: string;
1825
platform: string;
1926
};
20-
'monitor.created': DetectorAnalyticsEventPayload;
27+
'monitor.created': DetectorCreateAnalyticsEventPayload;
2128
'monitor.updated': DetectorAnalyticsEventPayload;
2229
};
2330

static/app/views/alerts/create.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {t} from 'sentry/locale';
77
import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
88
import type {Member, Organization} from 'sentry/types/organization';
99
import type {Project} from 'sentry/types/project';
10+
import {trackAnalytics} from 'sentry/utils/analytics';
1011
import EventView from 'sentry/utils/discover/eventView';
1112
import {uniqueId} from 'sentry/utils/guid';
1213
import {decodeScalar} from 'sentry/utils/queryString';
@@ -166,14 +167,18 @@ function Create(props: Props) {
166167
<MonitorForm
167168
apiMethod="POST"
168169
apiEndpoint={`/organizations/${organization.slug}/monitors/`}
169-
onSubmitSuccess={(data: Monitor) =>
170+
onSubmitSuccess={(data: Monitor) => {
171+
trackAnalytics('cron_monitor.created', {
172+
organization,
173+
cron_schedule_type: data.config.schedule_type,
174+
});
170175
navigate(
171176
makeAlertsPathname({
172177
path: `/rules/crons/${data.project.slug}/${data.slug}/details/`,
173178
organization,
174179
})
175-
)
176-
}
180+
);
181+
}}
177182
submitLabel={t('Create')}
178183
/>
179184
) : !hasMetricAlerts || alertType === AlertRuleType.ISSUE ? (

static/app/views/alerts/rules/issue/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,12 @@ class IssueRuleEditor extends DeprecatedAsyncComponent<Props, State> {
457457

458458
metric.endSpan({name: 'saveAlertRule'});
459459

460+
if (isNew) {
461+
trackAnalytics('issue_alert_rule.created', {
462+
organization,
463+
});
464+
}
465+
460466
router.push(
461467
makeAlertsPathname({
462468
path: `/rules/${project.slug}/${rule.id}/details/`,

static/app/views/alerts/rules/metric/ruleForm.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,15 @@ class RuleFormContainer extends DeprecatedAsyncComponent<Props, State> {
370370
}
371371
if (alertRule) {
372372
addSuccessMessage(ruleId ? t('Updated alert rule') : t('Created alert rule'));
373+
374+
if (!ruleId) {
375+
trackAnalytics('metric_alert_rule.created', {
376+
organization,
377+
aggregate: alertRule.aggregate,
378+
dataset: alertRule.dataset,
379+
});
380+
}
381+
373382
if (onSubmitSuccess) {
374383
onSubmitSuccess(alertRule, model);
375384
}
@@ -849,6 +858,15 @@ class RuleFormContainer extends DeprecatedAsyncComponent<Props, State> {
849858
IndicatorStore.remove(loadingIndicator);
850859
this.setState({loading: false});
851860
addSuccessMessage(ruleId ? t('Updated alert rule') : t('Created alert rule'));
861+
862+
if (!ruleId) {
863+
trackAnalytics('metric_alert_rule.created', {
864+
organization,
865+
aggregate: data.aggregate,
866+
dataset: data.dataset,
867+
});
868+
}
869+
852870
if (onSubmitSuccess) {
853871
onSubmitSuccess(data, model);
854872
}

static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import ListItem from 'sentry/components/list/listItem';
2626
import Panel from 'sentry/components/panels/panel';
2727
import {t, tct} from 'sentry/locale';
2828
import {space} from 'sentry/styles/space';
29+
import {trackAnalytics} from 'sentry/utils/analytics';
2930
import getDuration from 'sentry/utils/duration/getDuration';
3031
import {useQueryClient} from 'sentry/utils/queryClient';
3132
import {useNavigate} from 'sentry/utils/useNavigate';
@@ -129,6 +130,14 @@ export function UptimeAlertForm({handleDelete, rule}: Props) {
129130
exact: true,
130131
});
131132
}
133+
134+
if (!rule) {
135+
trackAnalytics('uptime_monitor.created', {
136+
organization,
137+
uptime_mode: response.mode,
138+
});
139+
}
140+
132141
navigate(
133142
makeAlertsPathname({
134143
path: `/rules/uptime/${projectSlug}/${response.id}/details/`,

static/app/views/automations/components/forms/common/getAutomationAnalyticsPayload.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type {Automation} from 'sentry/types/workflowEngine/automations';
1+
import type {NewAutomation} from 'sentry/types/workflowEngine/automations';
22

3-
export function getAutomationAnalyticsPayload(automation: Automation): {
3+
export function getAutomationAnalyticsPayload(automation: NewAutomation): {
44
actions_count: number;
55
detectors_count: number;
66
environment: string | null;

static/app/views/automations/new.spec.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('AutomationNewSettings', () => {
2828
});
2929

3030
beforeEach(() => {
31+
jest.clearAllMocks();
3132
MockApiClient.clearMockResponses();
3233

3334
// Available actions (include Slack with a default integration)
@@ -205,15 +206,14 @@ describe('AutomationNewSettings', () => {
205206
);
206207

207208
// Verify analytics was called with correct event and payload structure
208-
await waitFor(() => {
209-
expect(trackAnalytics).toHaveBeenCalledWith('automation.created', {
210-
organization,
211-
frequency_minutes: expect.any(Number),
212-
environment: expect.anything(),
213-
detectors_count: expect.any(Number),
214-
trigger_conditions_count: expect.any(Number),
215-
actions_count: expect.any(Number),
216-
});
209+
expect(trackAnalytics).toHaveBeenCalledWith('automation.created', {
210+
organization,
211+
frequency_minutes: expect.any(Number),
212+
environment: null,
213+
detectors_count: expect.any(Number),
214+
trigger_conditions_count: expect.any(Number),
215+
success: true,
216+
actions_count: expect.any(Number),
217217
});
218218
});
219219
});

static/app/views/automations/new.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,30 @@ export default function AutomationNewSettings() {
108108
async (data, _, __, ___, ____) => {
109109
const errors = validateAutomationBuilderState(state);
110110
setAutomationBuilderErrors(errors);
111+
const newAutomationData = getNewAutomationData(data as AutomationFormData, state);
111112

112113
if (Object.keys(errors).length === 0) {
113-
const automation = await createAutomation(
114-
getNewAutomationData(data as AutomationFormData, state)
115-
);
114+
try {
115+
const automation = await createAutomation(newAutomationData);
116+
trackAnalytics('automation.created', {
117+
organization,
118+
...getAutomationAnalyticsPayload(newAutomationData),
119+
success: true,
120+
});
121+
navigate(makeAutomationDetailsPathname(organization.slug, automation.id));
122+
} catch {
123+
trackAnalytics('automation.created', {
124+
organization,
125+
...getAutomationAnalyticsPayload(newAutomationData),
126+
success: false,
127+
});
128+
}
129+
} else {
116130
trackAnalytics('automation.created', {
117131
organization,
118-
...getAutomationAnalyticsPayload(automation),
132+
...getAutomationAnalyticsPayload(newAutomationData),
133+
success: false,
119134
});
120-
navigate(makeAutomationDetailsPathname(organization.slug, automation.id));
121135
}
122136
},
123137
[createAutomation, state, navigate, organization]

static/app/views/detectors/components/forms/newDetectorLayout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function NewDetectorLayout<
4646
const maxWidth = theme.breakpoints.xl;
4747

4848
const formSubmitHandler = useCreateDetectorFormSubmit({
49+
detectorType,
4950
formDataToEndpointPayload,
5051
});
5152

0 commit comments

Comments
 (0)