Healthcheck failures analytics

Summary:
Send per-healthcheck success/failure event to be able to analyze most common problems.
Send event when doctor warning bar shown.
Send event when doctor report is opened by user.
Send event when user set flag "Do not show warning again" in the doctor report.

Reviewed By: passy

Differential Revision: D19312127

fbshipit-source-id: 01b648d1154a3aeadc85980190cb9e5e221b572e
This commit is contained in:
Anton Nikolaev
2020-01-10 06:16:48 -08:00
committed by Facebook Github Bot
parent 451db57fa5
commit 2599dffe48
4 changed files with 47 additions and 2 deletions

View File

@@ -29,6 +29,8 @@ import {
finishHealthchecks, finishHealthchecks,
} from '../reducers/healthchecks'; } from '../reducers/healthchecks';
import {reportUsage} from '../utils/metrics';
type StateFromProps = { type StateFromProps = {
healthcheckResult: HealthcheckResult; healthcheckResult: HealthcheckResult;
} & HealthcheckSettings; } & HealthcheckSettings;
@@ -58,6 +60,7 @@ class DoctorBar extends Component<Props, State> {
!this.props.healthcheckResult.isAcknowledged !this.props.healthcheckResult.isAcknowledged
) { ) {
this.setVisible(true); this.setVisible(true);
reportUsage('doctor:warning:shown');
} }
} }
render() { render() {

View File

@@ -38,6 +38,7 @@ import runHealthchecks, {
HealthcheckEventsHandler, HealthcheckEventsHandler,
} from '../utils/runHealthchecks'; } from '../utils/runHealthchecks';
import {shell} from 'electron'; import {shell} from 'electron';
import {reportUsage} from '../utils/metrics';
type StateFromProps = { type StateFromProps = {
healthcheckReport: HealthcheckReport; healthcheckReport: HealthcheckReport;
@@ -234,6 +235,10 @@ class DoctorSheet extends Component<Props, State> {
}; };
} }
componentDidMount() {
reportUsage('doctor:report:opened');
}
static getDerivedStateFromProps(props: Props, state: State): State | null { static getDerivedStateFromProps(props: Props, state: State): State | null {
if ( if (
!state.acknowledgeCheckboxVisible && !state.acknowledgeCheckboxVisible &&
@@ -264,8 +269,16 @@ class DoctorSheet extends Component<Props, State> {
componentWillUnmount(): void { componentWillUnmount(): void {
if (this.state.acknowledgeOnClose) { if (this.state.acknowledgeOnClose) {
if (hasNewProblems(this.props.healthcheckReport.result)) {
reportUsage('doctor:report:closed:newProblems:acknowledged');
}
reportUsage('doctor:report:closed:acknowleged');
this.props.acknowledgeProblems(); this.props.acknowledgeProblems();
} else { } else {
if (hasNewProblems(this.props.healthcheckReport.result)) {
reportUsage('doctor:report:closed:newProblems:notAcknowledged');
}
reportUsage('doctor:report:closed:notAcknowledged');
this.props.resetAcknowledgedProblems(); this.props.resetAcknowledgedProblems();
} }
} }

View File

@@ -123,7 +123,7 @@ export function reportUsage(
getInstance().track('usage', action, data, plugin); getInstance().track('usage', action, data, plugin);
} }
function logPlatformSuccessRate(name: string, result: Result) { export function logPlatformSuccessRate(name: string, result: Result) {
if (result.kind === 'success') { if (result.kind === 'success') {
getInstance().track('success-rate', name, {value: 1}); getInstance().track('success-rate', name, {value: 1});
} else if (result.kind === 'cancelled') { } else if (result.kind === 'cancelled') {

View File

@@ -9,6 +9,7 @@
import {HealthcheckResult} from '../reducers/healthchecks'; import {HealthcheckResult} from '../reducers/healthchecks';
import {getHealthchecks, getEnvInfo, Healthchecks} from 'flipper-doctor'; import {getHealthchecks, getEnvInfo, Healthchecks} from 'flipper-doctor';
import {logPlatformSuccessRate, reportPlatformFailures} from '../utils/metrics';
let healthcheckIsRunning: boolean; let healthcheckIsRunning: boolean;
let runningHealthcheck: Promise<void>; let runningHealthcheck: Promise<void>;
@@ -41,12 +42,26 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
} }
options.startHealthchecks(healthchecks); options.startHealthchecks(healthchecks);
const environmentInfo = await getEnvInfo(); const environmentInfo = await getEnvInfo();
let hasProblems = false;
for (const [categoryKey, category] of Object.entries(healthchecks)) { for (const [categoryKey, category] of Object.entries(healthchecks)) {
if (category.isSkipped) { if (category.isSkipped) {
continue; continue;
} }
for (const h of category.healthchecks) { for (const h of category.healthchecks) {
const checkResult = await h.run(environmentInfo); const checkResult = await h.run(environmentInfo);
const metricName = `doctor:${h.key.replace('.', ':')}.healthcheck`; // e.g. "doctor:ios:xcode-select.healthcheck"
if (checkResult.hasProblem) {
hasProblems = true;
logPlatformSuccessRate(metricName, {
kind: 'failure',
supportedOperation: true,
error: null,
});
} else {
logPlatformSuccessRate(metricName, {
kind: 'success',
});
}
const result: HealthcheckResult = const result: HealthcheckResult =
checkResult.hasProblem && h.isRequired checkResult.hasProblem && h.isRequired
? { ? {
@@ -63,6 +78,17 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> {
} }
} }
options.finishHealthchecks(); options.finishHealthchecks();
if (hasProblems) {
logPlatformSuccessRate('doctor.healthcheck', {
kind: 'failure',
supportedOperation: true,
error: null,
});
} else {
logPlatformSuccessRate('doctor.healthcheck', {
kind: 'success',
});
}
} }
export default async function runHealthchecks( export default async function runHealthchecks(
@@ -71,6 +97,9 @@ export default async function runHealthchecks(
if (healthcheckIsRunning) { if (healthcheckIsRunning) {
return runningHealthcheck; return runningHealthcheck;
} }
runningHealthcheck = launchHealthchecks(options); runningHealthcheck = reportPlatformFailures(
launchHealthchecks(options),
'doctor:runHealthchecks',
);
return runningHealthcheck; return runningHealthcheck;
} }