1
0
Fork 0
firefox/toolkit/components/nimbus/lib/Telemetry.sys.mjs
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

328 lines
9.1 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.sys.mjs",
});
const EXPERIMENT_ACTIVE_PREFIX = "nimbus-";
const EnrollmentStatus = Object.freeze({
ENROLLED: "Enrolled",
NOT_ENROLLED: "NotEnrolled",
DISQUALIFIED: "Disqualified",
WAS_ENROLLED: "WasEnrolled",
ERROR: "Error",
});
const EnrollmentStatusReason = Object.freeze({
CHANGED_PREF: "ChangedPref",
QUALIFIED: "Qualified",
OPT_IN: "OptIn",
OPT_OUT: "OptOut",
NOT_SELECTED: "NotSelected",
NOT_TARGETED: "NotTargeted",
ENROLLMENTS_PAUSED: "EnrollmentsPaused",
FEATURE_CONFLICT: "FeatureConflict",
FORCE_ENROLLMENT: "ForceEnrollment",
NAME_CONFLICT: "NameConflict",
PREF_FLIPS_CONFLICT: "PrefFlipsConflict",
ERROR: "Error",
});
const EnrollmentFailureReason = Object.freeze({
FEATURE_CONFLICT: "feature-conflict",
NAME_CONFLICT: "name-conflict",
});
const EnrollmentSource = Object.freeze({
RS_LOADER: "rs-loader",
FORCE_ENROLLMENT: "force-enrollment",
});
const UnenrollmentFailureReason = Object.freeze({
ALREADY_UNENROLLED: "already-unenrolled",
DOES_NOT_EXIST: "does-not-exist",
});
const ValidationFailureReason = Object.freeze({
INVALID_BRANCH: "invalid-branch",
INVALID_FEATURE: "invalid-feature",
INVALID_RECIPE: "invalid-recipe",
L10N_MISSING_ENTRY: "l10n-missing-entry",
L10N_MISSING_LOCALE: "l10n-missing-locale",
UNSUPPORTED_FEATURES: "unsupported-feature",
});
const UnenrollReason = Object.freeze({
BRANCH_REMOVED: "branch-removed",
BUCKETING: "bucketing",
CHANGED_PREF: "changed-pref",
FORCE_ENROLLMENT: "force-enrollment",
INDIVIDUAL_OPT_OUT: "individual-opt-out",
LABS_OPT_OUT: "labs-opt-out",
PREF_FLIPS_CONFLICT: "prefFlips-conflict",
PREF_FLIPS_FAILED: "prefFlips-failed",
PREF_VARIABLE_CHANGED: "pref-variable-changed",
PREF_VARIABLE_MISSING: "pref-variable-missing",
PREF_VARIABLE_NO_LONGER: "pref-variable-no-longer",
RECIPE_NOT_SEEN: "recipe-not-seen",
STUDIES_OPT_OUT: "studies-opt-out",
TARGETING_MISMATCH: "targeting-mismatch",
UNKNOWN: "unknown",
// Validation failure can cause unenrollment.
...ValidationFailureReason,
});
const EXPERIMENT_TYPE_ROLLOUT = "rollout";
const EXPERIMENT_TYPE_NIMBUS = "nimbus";
export const NimbusTelemetry = {
EnrollmentFailureReason,
EnrollmentSource,
EnrollmentStatus,
EnrollmentStatusReason,
UnenrollReason,
UnenrollmentFailureReason,
ValidationFailureReason,
recordEnrollment(enrollment) {
this.setExperimentActive(enrollment);
const experimentType = enrollment.isRollout
? EXPERIMENT_TYPE_ROLLOUT
: EXPERIMENT_TYPE_NIMBUS;
Glean.normandy.enrollNimbusExperiment.record({
value: enrollment.slug,
branch: enrollment.branch.slug,
experimentType,
});
Glean.nimbusEvents.enrollment.record({
experiment: enrollment.slug,
branch: enrollment.branch.slug,
experiment_type: experimentType,
});
this.recordEnrollmentStatus({
slug: enrollment.slug,
branch: enrollment.branch.slug,
status: EnrollmentStatus.ENROLLED,
reason:
enrollment.isFirefoxLabsOptIn ||
enrollment.source === EnrollmentSource.FORCE_ENROLLMENT
? EnrollmentStatusReason.OPT_IN
: EnrollmentStatusReason.QUALIFIED,
});
},
recordEnrollmentFailure(slug, reason) {
Glean.normandy.enrollFailedNimbusExperiment.record({
value: slug,
reason,
});
Glean.nimbusEvents.enrollFailed.record({
experiment: slug,
reason,
});
},
recordEnrollmentStatus({
slug,
status,
reason,
branch,
error_string,
conflict_slug,
}) {
Glean.nimbusEvents.enrollmentStatus.record({
slug,
status,
reason,
branch,
error_string,
conflict_slug,
});
},
recordExposure(slug, branchSlug, featureId) {
Glean.normandy.exposeNimbusExperiment.record({
value: slug,
branchSlug,
featureId,
});
Glean.nimbusEvents.exposure.record({
experiment: slug,
branch: branchSlug,
feature_id: featureId,
});
},
recordMigration(migration, error) {
Glean.nimbusEvents.migration.record(
Object.assign(
{
migration_id: migration,
success: typeof error === "undefined",
},
typeof error !== "undefined" ? { error_reason: error } : {}
)
);
},
recordUnenrollment(enrollment, cause) {
lazy.TelemetryEnvironment.setExperimentInactive(enrollment.slug);
Services.fog.setExperimentInactive(enrollment.slug);
const legacyEvent = {
value: enrollment.slug,
branch: enrollment.branch.slug,
reason: cause.reason,
};
const gleanEvent = {
experiment: enrollment.slug,
branch: enrollment.branch.slug,
reason: cause.reason,
};
switch (cause.reason) {
case UnenrollReason.CHANGED_PREF:
legacyEvent.changedPref = cause.changedPref.name;
gleanEvent.changed_pref = cause.changedPref.name;
break;
case UnenrollReason.PREF_FLIPS_CONFLICT:
legacyEvent.conflictingSlug = cause.conflictingSlug;
gleanEvent.conflicting_slug = cause.conflictingSlug;
break;
case UnenrollReason.PREF_FLIPS_FAILED:
legacyEvent.prefType = cause.prefType;
gleanEvent.pref_type = cause.prefType;
legacyEvent.prefName = cause.prefName;
gleanEvent.pref_name = cause.prefName;
break;
}
Glean.normandy.unenrollNimbusExperiment.record(legacyEvent);
Glean.nimbusEvents.unenrollment.record(gleanEvent);
const enrollmentStatus = {
slug: enrollment.slug,
branch: enrollment.branch.slug,
};
switch (cause.reason) {
case UnenrollReason.BUCKETING:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.NOT_SELECTED;
break;
case UnenrollReason.RECIPE_NOT_SEEN:
enrollmentStatus.status = EnrollmentStatus.WAS_ENROLLED;
break;
case UnenrollReason.TARGETING_MISMATCH:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.NOT_TARGETED;
break;
case UnenrollReason.INDIVIDUAL_OPT_OUT:
case UnenrollReason.LABS_OPT_OUT:
case UnenrollReason.STUDIES_OPT_OUT:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.OPT_OUT;
break;
case UnenrollReason.CHANGED_PREF:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.CHANGED_PREF;
break;
case UnenrollReason.FORCE_ENROLLMENT:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.FORCE_ENROLLMENT;
break;
case UnenrollReason.PREF_FLIPS_CONFLICT:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.PREF_FLIPS_CONFLICT;
enrollmentStatus.conflict_slug = cause.conflictingSlug;
break;
default:
enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED;
enrollmentStatus.reason = EnrollmentStatusReason.ERROR;
enrollmentStatus.error_string = cause.reason;
}
this.recordEnrollmentStatus(enrollmentStatus);
},
recordUnenrollmentFailure(slug, reason) {
Glean.normandy.unenrollFailedNimbusExperiment.record({
value: slug,
reason,
});
Glean.nimbusEvents.unenrollFailed.record({
experiment: slug,
reason,
});
},
setExperimentActive(enrollment) {
const experimentType = enrollment.isRollout
? EXPERIMENT_TYPE_ROLLOUT
: EXPERIMENT_TYPE_NIMBUS;
const type = `${EXPERIMENT_ACTIVE_PREFIX}${experimentType}`;
lazy.TelemetryEnvironment.setExperimentActive(
enrollment.slug,
enrollment.branch.slug,
{ type }
);
Services.fog.setExperimentActive(enrollment.slug, enrollment.branch.slug, {
type,
});
},
recordValidationFailure(
slug,
reason,
{ branch, locale, l10nIds: l10n_ids } = {}
) {
// Do not record invalid feature telemetry.
if (reason === ValidationFailureReason.INVALID_FEATURE) {
return;
}
// Do not record unsupported feature telemetry.
if (reason === ValidationFailureReason.UNSUPPORTED_FEATURES) {
return;
}
const extra = Object.assign(
{ reason },
reason === ValidationFailureReason.INVALID_BRANCH ? { branch } : {},
reason === ValidationFailureReason.L10N_MISSING_ENTRY
? { l10n_ids, locale }
: {},
reason === ValidationFailureReason.L10N_MISSING_LOCALE ? { locale } : {}
);
Glean.normandy.validationFailedNimbusExperiment.record({
value: slug,
...extra,
});
Glean.nimbusEvents.validationFailed.record({
experiment: slug,
...extra,
});
},
};