From 8b45a157810eec24e846c1aed073ed836f11f4e7 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 13 Dec 2023 14:52:06 -0500 Subject: [PATCH 01/20] Add new session metrics to core glean metrics --- glean/src/metrics.yaml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/glean/src/metrics.yaml b/glean/src/metrics.yaml index fbe3a703d..1464ba7d5 100644 --- a/glean/src/metrics.yaml +++ b/glean/src/metrics.yaml @@ -122,6 +122,40 @@ glean.internal.metrics: - glean-team@mozilla.com expires: never + session_id: + type: uuid + description: | + A UUID uniquely identifying the client's current session. + send_in_pings: + - glean_client_info + lifetime: user + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955 + data_reviews: + - TODO + data_sensitivity: + - technical + notification_emails: + - glean-team@mozilla.com + expires: never + + session_count: + type: counter + description: | + A running counter of the number of sessions for this client. + send_in_pings: + - glean_client_info + lifetime: user + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955 + data_reviews: + - TODO + data_sensitivity: + - technical + notification_emails: + - glean-team@mozilla.com + expires: never + app_build: type: string lifetime: application From e2d06139fe4aee539b7a11b277728f2fa93175a0 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 13 Dec 2023 15:46:29 -0500 Subject: [PATCH 02/20] Record session info metrics Add checks in `internal_metrics` to determine when a session has expired and we should create a new one. We use a timestamp in LocalStorage for keeping track of the last active date. --- glean/src/core/internal_metrics.ts | 62 ++++++++++++++++++++++++++++++ glean/src/core/sessions.ts | 24 ++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 glean/src/core/sessions.ts diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index 08dc4f341..9ec97b414 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -6,12 +6,14 @@ import { KNOWN_CLIENT_ID, CLIENT_INFO_STORAGE } from "./constants.js"; import { InternalUUIDMetricType as UUIDMetricType } from "./metrics/types/uuid.js"; import { InternalDatetimeMetricType as DatetimeMetricType } from "./metrics/types/datetime.js"; import { InternalStringMetricType as StringMetricType } from "./metrics/types/string.js"; +import { InternalCounterMetricType as CounterMetricType } from "./metrics/types/counter.js"; import { createMetric } from "./metrics/utils.js"; import TimeUnit from "./metrics/time_unit.js"; import { generateUUIDv4, isWindowObjectUnavailable } from "./utils.js"; import { Lifetime } from "./metrics/lifetime.js"; import log, { LoggingLevel } from "./log.js"; import { Context } from "./context.js"; +import { hasSessionBeenInactiveForOverThirtyMinutes } from "./sessions.js"; const LOG_TAG = "core.InternalMetrics"; @@ -28,6 +30,8 @@ export class CoreMetrics { readonly osVersion: StringMetricType; readonly architecture: StringMetricType; readonly locale: StringMetricType; + readonly sessionId: UUIDMetricType; + readonly sessionCount: CounterMetricType; // Provided by the user readonly appChannel: StringMetricType; readonly appBuild: StringMetricType; @@ -120,6 +124,22 @@ export class CoreMetrics { }, "second" ); + + this.sessionId = new UUIDMetricType({ + name: "session_id", + category: "", + sendInPings: ["glean_client_info"], + lifetime: Lifetime.User, + disabled: false + }); + + this.sessionCount = new CounterMetricType({ + name: "session_count", + category: "", + sendInPings: ["glean_client_info"], + lifetime: Lifetime.User, + disabled: false + }); } initialize(migrateFromLegacyStorage?: boolean): void { @@ -159,6 +179,42 @@ export class CoreMetrics { } } + /** + * If this is the first session, then we set a new session ID and a + * lastActive timestamp. + * + * If the lastActive time is under 30 minutes, then we only update + * the lastActive time. + * + * If the lastActive time is over 30 minutes, then we update the + * session ID, the session sequence number, and the lastActive time. + */ + updateSessionInfo(): void { + const existingSessionId = Context.metricsDatabase.getMetric( + CLIENT_INFO_STORAGE, + this.sessionId + ); + + if (existingSessionId) { + try { + // If over 30 minutes has passed since last session interaction, + // then we create a new session. + if (hasSessionBeenInactiveForOverThirtyMinutes()) { + this.generateNewSession(); + } + } catch (e) { + // Error parsing the last active timestamp, create a new session. + this.generateNewSession(); + } + } else { + // There is no previous session information, create a new session. + this.generateNewSession(); + } + + // Set the session new session last active time in LocalStorage. + localStorage.setItem("glean_session_last_active", Date.now().toString()); + } + /** * Generates and sets the client_id if it is not set, * or if the current value is corrupted. @@ -202,12 +258,18 @@ export class CoreMetrics { } } + private generateNewSession(): void { + this.sessionId.set(generateUUIDv4()); + this.sessionCount.add(); + } + /** * Initializes the Glean internal user-lifetime metrics. */ private initializeUserLifetimeMetrics(): void { this.initializeClientId(); this.initializeFirstRunDate(); + this.updateSessionInfo(); } /** diff --git a/glean/src/core/sessions.ts b/glean/src/core/sessions.ts new file mode 100644 index 000000000..22af6e804 --- /dev/null +++ b/glean/src/core/sessions.ts @@ -0,0 +1,24 @@ +/* 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/. */ + +/** + * Check if the current session has been inactive for over thirty minutes. If + * it has, then we create a new session. + * + * @returns {boolean} If the session has been inactive for over thirty minutes. + */ +export function hasSessionBeenInactiveForOverThirtyMinutes(): boolean { + const lastActive = localStorage.getItem("glean_session_last_active"); + const lastActiveDate = new Date(Number(lastActive)); + + // Create a date 30 minutes ago to compare to our lastActiveDate. + // + // 60000 - number of milliseconds in a minute + // 30 - the number of minutes that can pass before a session is inactive + const thirtyMinutesAgo = new Date(Date.now() - (60000 * 30)); + + // If the date we created from 30 minutes ago is more recent than the last + // active date, then the current session has expired. + return thirtyMinutesAgo > lastActiveDate; +} From 0291c2bf584983d528599e52c09dec332b816942 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 15 Dec 2023 13:57:07 -0500 Subject: [PATCH 03/20] Add data review bugs --- glean/src/metrics.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glean/src/metrics.yaml b/glean/src/metrics.yaml index 1464ba7d5..3b1411d21 100644 --- a/glean/src/metrics.yaml +++ b/glean/src/metrics.yaml @@ -132,7 +132,7 @@ glean.internal.metrics: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955 data_reviews: - - TODO + - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955#c2 data_sensitivity: - technical notification_emails: @@ -149,7 +149,7 @@ glean.internal.metrics: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955 data_reviews: - - TODO + - https://bugzilla.mozilla.org/show_bug.cgi?id=1862955#c2 data_sensitivity: - technical notification_emails: From 4aea218ac84e5cb5ca38b750e0fcd3b2ca8c9615 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 15 Dec 2023 14:40:13 -0500 Subject: [PATCH 04/20] Move updateSessionInfo call during init changelog - move updateSessionInfo call during init so it is always triggered - add isWindowObjectUnavailable check to updateSessionInfo --- glean/src/core/internal_metrics.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index 9ec97b414..55b1248ee 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -165,6 +165,8 @@ export class CoreMetrics { this.initializeUserLifetimeMetrics(); } + this.updateSessionInfo(); + this.os.set(Context.platform.info.os()); this.osVersion.set(Context.platform.info.osVersion()); this.architecture.set(Context.platform.info.arch()); @@ -180,16 +182,28 @@ export class CoreMetrics { } /** - * If this is the first session, then we set a new session ID and a + * Update local stored session information for Glean. This is called whenever + * the app is initialized and on every read/write to storage. + * + * There are a few scenarios to handle depending on what we already have + * stored about the session and how long it has been since the last action. + * + * SCENARIOS: + * + * 1. If this is the first session, then we set a new session ID and a * lastActive timestamp. * - * If the lastActive time is under 30 minutes, then we only update + * 2. If the lastActive time is under 30 minutes, then we only update * the lastActive time. * - * If the lastActive time is over 30 minutes, then we update the + * 3. If the lastActive time is over 30 minutes, then we update the * session ID, the session sequence number, and the lastActive time. */ updateSessionInfo(): void { + if (isWindowObjectUnavailable()) { + return; + } + const existingSessionId = Context.metricsDatabase.getMetric( CLIENT_INFO_STORAGE, this.sessionId @@ -269,7 +283,6 @@ export class CoreMetrics { private initializeUserLifetimeMetrics(): void { this.initializeClientId(); this.initializeFirstRunDate(); - this.updateSessionInfo(); } /** From 04951e0dd6fe9a2530a991ab70167e94aacc17d6 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 15 Dec 2023 19:23:23 -0500 Subject: [PATCH 05/20] Call updateSessionInfo when writing data to storage --- glean/src/platform/browser/web/storage.ts | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/glean/src/platform/browser/web/storage.ts b/glean/src/platform/browser/web/storage.ts index 2fcf4d592..9dabbd911 100644 --- a/glean/src/platform/browser/web/storage.ts +++ b/glean/src/platform/browser/web/storage.ts @@ -6,6 +6,7 @@ import type Store from "../../../core/storage.js"; import type { StorageIndex } from "../../../core/storage.js"; import type { JSONObject, JSONValue } from "../../../core/utils.js"; +import { Context } from "../../../core/context.js"; import log, { LoggingLevel } from "../../../core/log.js"; import { deleteKeyFromNestedObject, @@ -44,6 +45,10 @@ class WebStore implements Store { log(LOG_TAG, ["Unable to fetch value from local storage.", err], LoggingLevel.Error); } + if (this.shouldUpdateSession(index)) { + Context.coreMetrics.updateSessionInfo(); + } + return result; } @@ -61,6 +66,10 @@ class WebStore implements Store { } catch (err) { log(LOG_TAG, ["Unable to update value from local storage.", err], LoggingLevel.Error); } + + if (this.shouldUpdateSession(index)) { + Context.coreMetrics.updateSessionInfo(); + } } delete(index: StorageIndex): void { @@ -89,6 +98,23 @@ class WebStore implements Store { } catch (err) { log(LOG_TAG, ["Unable to delete value from storage.", err], LoggingLevel.Error); } + + if (this.shouldUpdateSession(index)) { + Context.coreMetrics.updateSessionInfo(); + } + } + + /** + * Check to see if the session information should be updated whenever + * interacting with storage. If we are updating the existing session metrics + * then running the `updateSessionInfo` function again would result in an + * infinite loop of updating the metrics and re-running this function. + * + * @param {StorageIndex} index Index to update in storage. + * @returns {boolean} Whether we should update session metrics. + */ + private shouldUpdateSession(index: StorageIndex): boolean { + return !index.includes("session_id") && !index.includes("session_count"); } } From 73ee7fd1ec34ab1c8c5cfe7505d4d2e345f1d8d4 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Mon, 18 Dec 2023 15:35:09 -0500 Subject: [PATCH 06/20] #1850 changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f69c96677..53d3b7989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ * [#1848](https://github.com/mozilla/glean.js/pull/1848): Support for automatically collecting element click events (first version) * [#1849](https://github.com/mozilla/glean.js/pull/1849): Truncate event extra strings to 500 bytes. This also updates other string-based metrics to truncate based on max bytes rather than a set number of characters. +* [#1850](https://github.com/mozilla/glean.js/pull/1850): Automatically record basic session information (`session_id` & `session_count`) for web properties. + # v4.0.0-pre.2 (2023-12-06) [Full changelog](https://github.com/mozilla/glean.js/compare/v4.0.0-pre.1...v4.0.0-pre.2) From 7ff5f62d400bf055d96582a4eab59c489e87d8cf Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 1 Feb 2024 16:58:06 -0500 Subject: [PATCH 07/20] Session scenarios clarifying comment --- glean/src/core/internal_metrics.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index 55b1248ee..f1df2ee27 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -190,8 +190,8 @@ export class CoreMetrics { * * SCENARIOS: * - * 1. If this is the first session, then we set a new session ID and a - * lastActive timestamp. + * 1. If this is the first session (there is no existing session ID), + * then we set a new session ID and a lastActive timestamp. * * 2. If the lastActive time is under 30 minutes, then we only update * the lastActive time. From 4ce696e99d62cb2ffe52f613532a32ee338fcfaf Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 1 Feb 2024 23:43:06 -0500 Subject: [PATCH 08/20] Use uuid `.generateAndSet` function --- glean/src/core/internal_metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index f1df2ee27..ec555d05c 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -273,7 +273,7 @@ export class CoreMetrics { } private generateNewSession(): void { - this.sessionId.set(generateUUIDv4()); + this.sessionId.generateAndSet(); this.sessionCount.add(); } From 891a49bc71f4b72bcac1bcd96d22066543daf4c0 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 2 Feb 2024 02:07:54 -0500 Subject: [PATCH 09/20] Make session expiry checking function more generic, allow custom timeout --- glean/src/core/internal_metrics.ts | 14 ++++++-------- glean/src/core/sessions.ts | 21 +++++++++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index ec555d05c..8f99e9213 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -13,7 +13,7 @@ import { generateUUIDv4, isWindowObjectUnavailable } from "./utils.js"; import { Lifetime } from "./metrics/lifetime.js"; import log, { LoggingLevel } from "./log.js"; import { Context } from "./context.js"; -import { hasSessionBeenInactiveForOverThirtyMinutes } from "./sessions.js"; +import { isSessionInactive } from "./sessions.js"; const LOG_TAG = "core.InternalMetrics"; @@ -193,11 +193,10 @@ export class CoreMetrics { * 1. If this is the first session (there is no existing session ID), * then we set a new session ID and a lastActive timestamp. * - * 2. If the lastActive time is under 30 minutes, then we only update - * the lastActive time. + * 2. If the session is not expired, then we only update the lastActive time. * - * 3. If the lastActive time is over 30 minutes, then we update the - * session ID, the session sequence number, and the lastActive time. + * 3. If the session is expired (inactive threshold is more recent than lastActive) + * then we update the session ID, the session sequence number, and the lastActive time. */ updateSessionInfo(): void { if (isWindowObjectUnavailable()) { @@ -211,9 +210,8 @@ export class CoreMetrics { if (existingSessionId) { try { - // If over 30 minutes has passed since last session interaction, - // then we create a new session. - if (hasSessionBeenInactiveForOverThirtyMinutes()) { + // If the session has timed out, then we create a new session. + if (isSessionInactive()) { this.generateNewSession(); } } catch (e) { diff --git a/glean/src/core/sessions.ts b/glean/src/core/sessions.ts index 22af6e804..e6fd1b482 100644 --- a/glean/src/core/sessions.ts +++ b/glean/src/core/sessions.ts @@ -3,22 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Check if the current session has been inactive for over thirty minutes. If - * it has, then we create a new session. + * Check if the current session is inactive - the default timeout is 30 minutes. * - * @returns {boolean} If the session has been inactive for over thirty minutes. + * @param sessionLengthInMinutes Length of the session in minutes - defaults to 30. + * @returns {boolean} If the current session is inactive. */ -export function hasSessionBeenInactiveForOverThirtyMinutes(): boolean { +export function isSessionInactive(sessionLengthInMinutes = 30): boolean { const lastActive = localStorage.getItem("glean_session_last_active"); const lastActiveDate = new Date(Number(lastActive)); - // Create a date 30 minutes ago to compare to our lastActiveDate. - // - // 60000 - number of milliseconds in a minute - // 30 - the number of minutes that can pass before a session is inactive - const thirtyMinutesAgo = new Date(Date.now() - (60000 * 30)); + // Subtract the session length from the current date + const inactiveThreshold = new Date(Date.now() - (60000 * sessionLengthInMinutes)); - // If the date we created from 30 minutes ago is more recent than the last - // active date, then the current session has expired. - return thirtyMinutesAgo > lastActiveDate; + // If the inactiveThreshold is more recent than the lastActiveDate, then the + // current session is expired. + return inactiveThreshold > lastActiveDate; } From 28bb74ce2d539ce505b8ec76d410a7d79b05c142 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 2 Feb 2024 02:18:37 -0500 Subject: [PATCH 10/20] Simplify inactiveThreshold date math --- glean/src/core/sessions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/glean/src/core/sessions.ts b/glean/src/core/sessions.ts index e6fd1b482..f0d127b1c 100644 --- a/glean/src/core/sessions.ts +++ b/glean/src/core/sessions.ts @@ -12,8 +12,9 @@ export function isSessionInactive(sessionLengthInMinutes = 30): boolean { const lastActive = localStorage.getItem("glean_session_last_active"); const lastActiveDate = new Date(Number(lastActive)); - // Subtract the session length from the current date - const inactiveThreshold = new Date(Date.now() - (60000 * sessionLengthInMinutes)); + // Subtract the session length from the current date. + const inactiveThreshold = new Date(); + inactiveThreshold.setMinutes(inactiveThreshold.getMinutes() - sessionLengthInMinutes); // If the inactiveThreshold is more recent than the lastActiveDate, then the // current session is expired. From e2615cecbbc024ca406b56961cf43d7e51bdd05b Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Fri, 2 Feb 2024 16:12:19 -0500 Subject: [PATCH 11/20] Add session length to Glean config object --- glean/src/core/config.ts | 4 ++++ glean/src/core/internal_metrics.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/glean/src/core/config.ts b/glean/src/core/config.ts index daafe308e..2f28a033a 100644 --- a/glean/src/core/config.ts +++ b/glean/src/core/config.ts @@ -55,6 +55,8 @@ export interface ConfigurationInterface { readonly enableAutoElementClickEvents?: boolean, // Experimentation identifier to be set in all pings experimentationId?: string, + // Allow the client to explicitly define the length of a session in MINUTES. + readonly sessionLengthInMinutesOverride?: number, } // Important: the `Configuration` should only be used internally by the Glean singleton. @@ -69,6 +71,7 @@ export class Configuration implements ConfigurationInterface { readonly enableAutoPageLoadEvents?: boolean; readonly enableAutoElementClickEvents?: boolean; experimentationId?: string; + readonly sessionLengthInMinutesOverride?: number; // Debug configuration. debug: DebugOptions; @@ -85,6 +88,7 @@ export class Configuration implements ConfigurationInterface { this.enableAutoPageLoadEvents = config?.enableAutoPageLoadEvents; this.enableAutoElementClickEvents = config?.enableAutoElementClickEvents; this.experimentationId = config?.experimentationId; + this.sessionLengthInMinutesOverride = config?.sessionLengthInMinutesOverride; this.debug = {}; diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index 8f99e9213..eec69dfd7 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -211,7 +211,7 @@ export class CoreMetrics { if (existingSessionId) { try { // If the session has timed out, then we create a new session. - if (isSessionInactive()) { + if (isSessionInactive(Context.config.sessionLengthInMinutesOverride)) { this.generateNewSession(); } } catch (e) { From 25933892e5d199d0a16ef8bddef4673d3756313f Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Tue, 6 Feb 2024 01:06:49 -0500 Subject: [PATCH 12/20] Browserstack tests for session ID --- automation/compat/index.html | 3 ++- automation/compat/index.js | 44 +++++++++++++++++++++++++------- automation/compat/tests/utils.js | 11 ++++++-- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/automation/compat/index.html b/automation/compat/index.html index 3008fcc53..7d0ba3125 100644 --- a/automation/compat/index.html +++ b/automation/compat/index.html @@ -7,7 +7,8 @@ Glean Benchmarks Sample -

+

+

diff --git a/automation/compat/index.js b/automation/compat/index.js index f7e4f2894..0a606d716 100644 --- a/automation/compat/index.js +++ b/automation/compat/index.js @@ -8,19 +8,12 @@ import Glean from "@mozilla/glean/web"; import { benchmark } from "./generated/pings.js"; import * as metrics from "./generated/sample.js"; -Glean.setSourceTags(["automation"]); -Glean.initialize("glean-compat-benchmark", true, { - enableAutoPageLoadEvents: true -}); - -metrics.pageLoaded.set(); -benchmark.submit(); - // !BIG HACK! // // Overwrite the console.info function in order to know when (and if) the benchmark ping was sent. // If a success ping message is logged we show that in the document. let pingSubmissionCount = 0; +let sessionId = ""; console.info = function () { var message = ""; for (var i = 0; i < arguments.length; i++) { @@ -30,6 +23,7 @@ console.info = function () { message += " "; } } + console.log(message); if (/successfully sent 200.$/.test(message)) { pingSubmissionCount++; @@ -38,8 +32,40 @@ console.info = function () { // 1. The built-in page_load event, which submits an events ping. // 2. The benchmark ping. if (pingSubmissionCount == 2) { - var elem = document.getElementById("msg"); + var elem = document.getElementById("ping_msg"); elem.innerHTML = "Pings submitted successfully."; } } + + const sessionRegex = /"session_id": .+"/; + const sessionInfo = sessionRegex.exec(message); + if (!!sessionInfo) { + const currSessionId = sessionInfo?.[0].split(`"`)?.[3]; + if (!!sessionId) { + if (currSessionId !== sessionId) { + var elem = document.getElementById("session_msg"); + elem.innerHTML = "Session IDs updated successfully."; + } else { + console.log("Something went wrong..."); + } + } + + sessionId = currSessionId; + } } + +Glean.setSourceTags(["automation"]); + +// This needs to be set so we can pull the session ID from the log messages. +Glean.setLogPings(true); + +Glean.initialize("glean-compat-benchmark", true, { + enableAutoPageLoadEvents: true, + // Setting the override to 0 means every action will trigger + // a new session. We use this to check that the session ID + // changes whenever a session has expired. + sessionLengthInMinutesOverride: 0 +}); + +metrics.pageLoaded.set(); +benchmark.submit(); diff --git a/automation/compat/tests/utils.js b/automation/compat/tests/utils.js index 2af9146da..929d31994 100644 --- a/automation/compat/tests/utils.js +++ b/automation/compat/tests/utils.js @@ -73,7 +73,7 @@ export async function runWebTest(driver) { // will receive the text "Ping submitted successfully." await driver.get(`http://localhost:${PORT}/`); // Give it time to send the ping request. - const successTextContainer = await driver.findElement(By.id("msg")); + const pingTextContainer = await driver.findElement(By.id("ping_msg")); const areGleanWindowVarsSet = await driver.executeScript(() => { // Verify that all Glean `window` vars are properly set. @@ -100,10 +100,17 @@ export async function runWebTest(driver) { await driver.wait( until.elementTextIs( - successTextContainer, + pingTextContainer, "Pings submitted successfully." ), 11_000); // 1s more than the default upload timeout in Glean. + const sessionTextContainer = await driver.findElement(By.id("session_msg")); + await driver.wait( + until.elementTextIs( + sessionTextContainer, + "Session IDs updated successfully." + ), 1000); + console.log("Test passed."); } catch(e) { console.log("Test failed.", e); From 7ce3caa0c7e459937835737cd26374fc7c50ce23 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 7 Feb 2024 12:24:20 -0500 Subject: [PATCH 13/20] Add session description in metrics.yaml --- glean/src/metrics.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/glean/src/metrics.yaml b/glean/src/metrics.yaml index 3b1411d21..91fa17aa4 100644 --- a/glean/src/metrics.yaml +++ b/glean/src/metrics.yaml @@ -125,7 +125,11 @@ glean.internal.metrics: session_id: type: uuid description: | - A UUID uniquely identifying the client's current session. + A UUID uniquely identifying the client's current session. A session is + the period of time in which a user interacts with the application. After + a period of inactivity (default being 30 minutes) a new session will be + created the next time the user interacts with the application. On each + new session, the session_id will be updated. send_in_pings: - glean_client_info lifetime: user @@ -142,7 +146,11 @@ glean.internal.metrics: session_count: type: counter description: | - A running counter of the number of sessions for this client. + A running counter of the number of sessions for this client. A session is + the period of time in which a user interacts with the application. After + a period of inactivity (default being 30 minutes) a new session will be + created the next time the user interacts with the application. On each + new session, the session_count will be incremented. send_in_pings: - glean_client_info lifetime: user From 5056d0ddf4e466a653817570aa19b75cf4719062 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 7 Feb 2024 16:21:29 -0500 Subject: [PATCH 14/20] Update innerHTML to innerText --- automation/compat/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automation/compat/index.js b/automation/compat/index.js index 0a606d716..0ff4bb4d6 100644 --- a/automation/compat/index.js +++ b/automation/compat/index.js @@ -33,7 +33,7 @@ console.info = function () { // 2. The benchmark ping. if (pingSubmissionCount == 2) { var elem = document.getElementById("ping_msg"); - elem.innerHTML = "Pings submitted successfully."; + elem.innerText = "Pings submitted successfully."; } } @@ -44,7 +44,7 @@ console.info = function () { if (!!sessionId) { if (currSessionId !== sessionId) { var elem = document.getElementById("session_msg"); - elem.innerHTML = "Session IDs updated successfully."; + elem.innerText = "Session IDs updated successfully."; } else { console.log("Something went wrong..."); } From 9f89f0acf6e11a76be0571df25a3eb92fecaccc1 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 7 Feb 2024 16:43:57 -0500 Subject: [PATCH 15/20] Fix clarifying comment --- glean/src/core/internal_metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index eec69dfd7..e5164f323 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -223,7 +223,7 @@ export class CoreMetrics { this.generateNewSession(); } - // Set the session new session last active time in LocalStorage. + // Update the last-active timestamp in LocalStorage to the current time. localStorage.setItem("glean_session_last_active", Date.now().toString()); } From 7f977aad5c08022a5bb454e4141e7547a2da7c90 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 8 Feb 2024 00:35:27 -0500 Subject: [PATCH 16/20] Update session override clarifying comment --- glean/src/core/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glean/src/core/config.ts b/glean/src/core/config.ts index 2f28a033a..98e41587d 100644 --- a/glean/src/core/config.ts +++ b/glean/src/core/config.ts @@ -55,7 +55,7 @@ export interface ConfigurationInterface { readonly enableAutoElementClickEvents?: boolean, // Experimentation identifier to be set in all pings experimentationId?: string, - // Allow the client to explicitly define the length of a session in MINUTES. + // Allows custom session length, in minutes. The default value is 30 minutes. readonly sessionLengthInMinutesOverride?: number, } From 87514257d94e6c68d470acc04e3585be9428f4a9 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 8 Feb 2024 00:38:00 -0500 Subject: [PATCH 17/20] updateSessionInfo jsdoc correction --- glean/src/core/internal_metrics.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/glean/src/core/internal_metrics.ts b/glean/src/core/internal_metrics.ts index e5164f323..62791777d 100644 --- a/glean/src/core/internal_metrics.ts +++ b/glean/src/core/internal_metrics.ts @@ -183,7 +183,8 @@ export class CoreMetrics { /** * Update local stored session information for Glean. This is called whenever - * the app is initialized and on every read/write to storage. + * the app on initialization and just after every read/write/delete to/from + * storage. * * There are a few scenarios to handle depending on what we already have * stored about the session and how long it has been since the last action. From 3289a2e63ce22fd811f9a21e2ea48bddb8ee1950 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 8 Feb 2024 00:42:27 -0500 Subject: [PATCH 18/20] Update session_count description --- glean/src/metrics.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/glean/src/metrics.yaml b/glean/src/metrics.yaml index 91fa17aa4..0df9230e8 100644 --- a/glean/src/metrics.yaml +++ b/glean/src/metrics.yaml @@ -151,6 +151,7 @@ glean.internal.metrics: a period of inactivity (default being 30 minutes) a new session will be created the next time the user interacts with the application. On each new session, the session_count will be incremented. + This count will ONLY be reset on opt-out or whenever storage is deleted. send_in_pings: - glean_client_info lifetime: user From ed6be6704b133dbf2b031f445066611efa998d70 Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Wed, 21 Feb 2024 11:40:06 -0500 Subject: [PATCH 19/20] Exclude session_id if ping has no client_id --- glean/src/core/pings/maker.ts | 3 +++ glean/src/core/pings/ping_payload.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/glean/src/core/pings/maker.ts b/glean/src/core/pings/maker.ts index 506c5ac47..ae22d194b 100644 --- a/glean/src/core/pings/maker.ts +++ b/glean/src/core/pings/maker.ts @@ -216,6 +216,9 @@ export function buildClientInfoSection(ping: CommonPingData): ClientInfo { if (!ping.includeClientId) { delete finalClientInfo["client_id"]; + + // If the ping doesn't include the client_id, we also should exclude session_id. + delete finalClientInfo["session_id"]; } return finalClientInfo; diff --git a/glean/src/core/pings/ping_payload.ts b/glean/src/core/pings/ping_payload.ts index 54a8f3637..20bd2f392 100644 --- a/glean/src/core/pings/ping_payload.ts +++ b/glean/src/core/pings/ping_payload.ts @@ -27,7 +27,9 @@ export interface ClientInfo extends JSONObject { first_run_date?: string, os?: string, os_version?: string, - telemetry_sdk_build: string + telemetry_sdk_build: string, + session_id?: string, + session_count?: number } /** From 04ccc1f5194c842a0b7a210e5ba03652213d397e Mon Sep 17 00:00:00 2001 From: Bruno Rosa Date: Thu, 22 Feb 2024 16:50:26 -0500 Subject: [PATCH 20/20] Update session_id description for include_client_id --- glean/src/metrics.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glean/src/metrics.yaml b/glean/src/metrics.yaml index 0df9230e8..f8df19e60 100644 --- a/glean/src/metrics.yaml +++ b/glean/src/metrics.yaml @@ -130,6 +130,8 @@ glean.internal.metrics: a period of inactivity (default being 30 minutes) a new session will be created the next time the user interacts with the application. On each new session, the session_id will be updated. + + This metric WILL NOT be included for pings where `include_client_id` is `false`. send_in_pings: - glean_client_info lifetime: user