diff options
Diffstat (limited to 'toolkit/components/translations/TranslationsTelemetry.sys.mjs')
-rw-r--r-- | toolkit/components/translations/TranslationsTelemetry.sys.mjs | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/toolkit/components/translations/TranslationsTelemetry.sys.mjs b/toolkit/components/translations/TranslationsTelemetry.sys.mjs new file mode 100644 index 0000000000..f01ee1d144 --- /dev/null +++ b/toolkit/components/translations/TranslationsTelemetry.sys.mjs @@ -0,0 +1,379 @@ +/* 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/. */ + +// Switch this to true to help with local debugging. +// Not intended for use in production. +const LOG_TELEMETRY_EVENTS_FOR_DEBUGGING = false; + +const lazy = {}; + +ChromeUtils.defineLazyGetter(lazy, "console", () => { + return console.createInstance({ + maxLogLevelPref: "browser.translations.logLevel", + prefix: "TranslationsTelemetry", + }); +}); + +/** + * Telemetry functions for Translations actors + */ +export class TranslationsTelemetry { + /** + * A cached value to hold the current flowId. + */ + static #flowId = null; + + /** + * Logs the telemetry event to the console if enabled by + * the LOG_TELEMETRY_EVENTS constant. + */ + static logEventToConsole(eventInfo) { + if (!LOG_TELEMETRY_EVENTS_FOR_DEBUGGING) { + return; + } + lazy.console.debug( + `${ + Panel.isFirstUserInteraction() ? "[FIRST OPEN] " : "" + }flowId(${TranslationsTelemetry.getOrCreateFlowId()}): ${eventInfo}` + ); + } + + /** + * Telemetry functions for the Translations panel. + * @returns {Panel} + */ + static panel() { + return Panel; + } + + /** + * Forces the creation of a new Translations telemetry flowId and returns it. + * @returns {string} + */ + static createFlowId() { + const flowId = crypto.randomUUID(); + TranslationsTelemetry.#flowId = flowId; + return flowId; + } + + /** + * Returns a Translations telemetry flowId by retrieving the cached value + * if available, or creating a new one otherwise. + * @returns {string} + */ + static getOrCreateFlowId() { + // If we have the flowId cached, return it. + if (TranslationsTelemetry.#flowId) { + return TranslationsTelemetry.#flowId; + } + + // If no flowId exists, create one. + return TranslationsTelemetry.createFlowId(); + } + + /** + * Records a telemetry event when full page translation fails. + * + * @param {string} errorMessage + */ + static onError(errorMessage) { + Glean.translations.errorRate.addToNumerator(1); + Glean.translations.error.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + reason: errorMessage, + }); + TranslationsTelemetry.logEventToConsole("onError"); + } + + /** + * Records a telemetry event when a translation request is sent. + * + * @param {object} data + * @param {string} data.docLangTag + * @param {string} data.fromLanguage + * @param {string} data.toLanguage + * @param {string} data.topPreferredLanguage + * @param {boolean} data.autoTranslate + */ + static onTranslate(data) { + const { + docLangTag, + fromLanguage, + toLanguage, + autoTranslate, + topPreferredLanguage, + } = data; + Glean.translations.requestsCount.add(1); + Glean.translations.translationRequest.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + from_language: fromLanguage, + to_language: toLanguage, + auto_translate: autoTranslate, + document_language: docLangTag, + top_preferred_language: topPreferredLanguage, + }); + TranslationsTelemetry.logEventToConsole( + `onTranslate[page(${docLangTag}), preferred(${topPreferredLanguage})](${ + autoTranslate ? "auto" : "manual" + }, ${fromLanguage}-${toLanguage})` + ); + } + + static onRestorePage() { + Glean.translations.restorePage.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onRestorePage"); + } +} + +/** + * Telemetry functions for the Translations panel + */ +class Panel { + /** + * A value to retain whether this is the user's first time + * interacting with the translations panel. It is propagated + * to all events. + * + * This value is set only through the onOpen() function. + */ + static #isFirstUserInteraction = false; + + /** + * True if this is the user's first time interacting with the + * Translations panel, otherwise false. + * + * @returns {boolean} + */ + static isFirstUserInteraction() { + return Panel.#isFirstUserInteraction; + } + + /** + * Records a telemetry event when the translations panel is opened. + * + * @param {object} data + * @param {string} data.viewName + * @param {string} data.docLangTag + * @param {boolean} data.autoShow + * @param {boolean} data.maintainFlow + * @param {boolean} data.openedFromAppMenu + * @param {boolean} data.isFirstUserInteraction + */ + static onOpen({ + viewName = null, + autoShow = null, + docLangTag = null, + maintainFlow = false, + openedFromAppMenu = false, + isFirstUserInteraction = null, + }) { + if (isFirstUserInteraction !== null || !maintainFlow) { + Panel.#isFirstUserInteraction = isFirstUserInteraction ?? false; + } + Glean.translationsPanel.open.record({ + flow_id: maintainFlow + ? TranslationsTelemetry.getOrCreateFlowId() + : TranslationsTelemetry.createFlowId(), + auto_show: autoShow, + view_name: viewName, + document_language: docLangTag, + first_interaction: Panel.isFirstUserInteraction(), + opened_from: openedFromAppMenu ? "appMenu" : "translationsButton", + }); + TranslationsTelemetry.logEventToConsole( + `onOpen[${autoShow ? "auto" : "manual"}, ${ + openedFromAppMenu ? "appMenu" : "translationsButton" + }, ${viewName ? viewName : "NULL"}](${docLangTag})` + ); + } + + static onClose() { + Glean.translationsPanel.close.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onClose"); + } + + static onOpenFromLanguageMenu() { + Glean.translationsPanel.openFromLanguageMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onOpenFromLanguageMenu"); + } + + static onChangeFromLanguage(langTag) { + Glean.translationsPanel.changeFromLanguage.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + language: langTag, + }); + TranslationsTelemetry.logEventToConsole(`onChangeFromLanguage(${langTag})`); + } + + static onCloseFromLanguageMenu() { + Glean.translationsPanel.closeFromLanguageMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onCloseFromLanguageMenu"); + } + + static onOpenToLanguageMenu() { + Glean.translationsPanel.openToLanguageMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onOpenToLanguageMenu"); + } + + static onChangeToLanguage(langTag) { + Glean.translationsPanel.changeToLanguage.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + language: langTag, + }); + TranslationsTelemetry.logEventToConsole(`onChangeToLanguage(${langTag})`); + } + + static onCloseToLanguageMenu() { + Glean.translationsPanel.closeToLanguageMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onCloseToLanguageMenu"); + } + + static onOpenSettingsMenu() { + Glean.translationsPanel.openSettingsMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onOpenSettingsMenu"); + } + + static onCloseSettingsMenu() { + Glean.translationsPanel.closeSettingsMenu.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onCloseSettingsMenu"); + } + + static onCancelButton() { + Glean.translationsPanel.cancelButton.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onCancelButton"); + } + + static onChangeSourceLanguageButton() { + Glean.translationsPanel.changeSourceLanguageButton.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onChangeSourceLanguageButton"); + } + + static onDismissErrorButton() { + Glean.translationsPanel.dismissErrorButton.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onDismissErrorButton"); + } + + static onRestorePageButton() { + Glean.translationsPanel.restorePageButton.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onRestorePageButton"); + } + + static onTranslateButton() { + Glean.translationsPanel.translateButton.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onTranslateButton"); + } + + static onAlwaysOfferTranslations(toggledOn) { + Glean.translationsPanel.alwaysOfferTranslations.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + toggled_on: toggledOn, + }); + TranslationsTelemetry.logEventToConsole( + `[${toggledOn ? "✔" : "x"}] onAlwaysOfferTranslations` + ); + } + + static onAlwaysTranslateLanguage(langTag, toggledOn) { + Glean.translationsPanel.alwaysTranslateLanguage.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + language: langTag, + toggled_on: toggledOn, + }); + TranslationsTelemetry.logEventToConsole( + `[${toggledOn ? "✔" : "x"}] onAlwaysTranslateLanguage(${langTag})` + ); + } + + static onNeverTranslateLanguage(langTag, toggledOn) { + Glean.translationsPanel.neverTranslateLanguage.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + language: langTag, + toggled_on: toggledOn, + }); + TranslationsTelemetry.logEventToConsole( + `[${toggledOn ? "✔" : "x"}] onNeverTranslateLanguage(${langTag})` + ); + } + + static onNeverTranslateSite(toggledOn) { + Glean.translationsPanel.neverTranslateSite.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + toggled_on: toggledOn, + }); + TranslationsTelemetry.logEventToConsole( + `[${toggledOn ? "✔" : "x"}] onNeverTranslateSite` + ); + } + + static onManageLanguages() { + Glean.translationsPanel.manageLanguages.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onManageLanguages"); + } + + static onAboutTranslations() { + Glean.translationsPanel.aboutTranslations.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onAboutTranslations"); + } + + static onLearnMoreLink() { + Glean.translationsPanel.learnMore.record({ + flow_id: TranslationsTelemetry.getOrCreateFlowId(), + first_interaction: Panel.isFirstUserInteraction(), + }); + TranslationsTelemetry.logEventToConsole("onLearnMoreLink"); + } +} |