mirror of
https://github.com/GoogleChrome/chrome-extensions-samples.git
synced 2026-03-27 13:29:34 +07:00
* Use <number>.<number> format for Google Analytics client ID With the `ENFORCE_RECOMMENDATIONS` [validation behavior](https://developers.google.com/analytics/devguides/collection/protocol/ga4/validating-events?client_type=firebase#send_events_for_validation), Google Analytics warns about client IDs not in the <number>.<number> format. This is not an issue in practice - that recommendation is for compatibility with existing client IDs and events are still processed with a client ID in any format. Additionally, the validation is not enabled by default. However, this PR updates our sample code to use a consistent ID regardless to reduce noise if the validation is enabled. We use a random ID concatenated with a UNIX timestamp to match other GA client libraries. * Run eslint
133 lines
4.5 KiB
JavaScript
133 lines
4.5 KiB
JavaScript
const GA_ENDPOINT = 'https://www.google-analytics.com/mp/collect';
|
|
const GA_DEBUG_ENDPOINT = 'https://www.google-analytics.com/debug/mp/collect';
|
|
|
|
// Get via https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#recommended_parameters_for_reports
|
|
const MEASUREMENT_ID = '<measurement_id>';
|
|
const API_SECRET = '<api_secret>';
|
|
const DEFAULT_ENGAGEMENT_TIME_MSEC = 100;
|
|
|
|
// Duration of inactivity after which a new session is created
|
|
const SESSION_EXPIRATION_IN_MIN = 30;
|
|
|
|
class Analytics {
|
|
constructor(debug = false) {
|
|
this.debug = debug;
|
|
}
|
|
|
|
getRandomId() {
|
|
const digits = '123456789'.split('');
|
|
let result = '';
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
result += digits[Math.floor(Math.random() * 9)];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Returns the client id, or creates a new one if one doesn't exist.
|
|
// Stores client id in local storage to keep the same client id as long as
|
|
// the extension is installed.
|
|
async getOrCreateClientId() {
|
|
let { clientId } = await chrome.storage.local.get('clientId');
|
|
if (!clientId) {
|
|
// Generate a unique client ID, the actual value is not relevant. We use
|
|
// the <number>.<number> format since this is typical for GA client IDs.
|
|
const unixTimestampSeconds = Math.floor(new Date().getTime() / 1000);
|
|
clientId = `${this.getRandomId()}.${unixTimestampSeconds}`;
|
|
await chrome.storage.local.set({ clientId });
|
|
}
|
|
return clientId;
|
|
}
|
|
|
|
// Returns the current session id, or creates a new one if one doesn't exist or
|
|
// the previous one has expired.
|
|
async getOrCreateSessionId() {
|
|
// Use storage.session because it is only in memory
|
|
let { sessionData } = await chrome.storage.session.get('sessionData');
|
|
const currentTimeInMs = Date.now();
|
|
// Check if session exists and is still valid
|
|
if (sessionData && sessionData.timestamp) {
|
|
// Calculate how long ago the session was last updated
|
|
const durationInMin = (currentTimeInMs - sessionData.timestamp) / 60000;
|
|
// Check if last update lays past the session expiration threshold
|
|
if (durationInMin > SESSION_EXPIRATION_IN_MIN) {
|
|
// Clear old session id to start a new session
|
|
sessionData = null;
|
|
} else {
|
|
// Update timestamp to keep session alive
|
|
sessionData.timestamp = currentTimeInMs;
|
|
await chrome.storage.session.set({ sessionData });
|
|
}
|
|
}
|
|
if (!sessionData) {
|
|
// Create and store a new session
|
|
sessionData = {
|
|
session_id: currentTimeInMs.toString(),
|
|
timestamp: currentTimeInMs.toString()
|
|
};
|
|
await chrome.storage.session.set({ sessionData });
|
|
}
|
|
return sessionData.session_id;
|
|
}
|
|
|
|
// Fires an event with optional params. Event names must only include letters and underscores.
|
|
async fireEvent(name, params = {}) {
|
|
// Configure session id and engagement time if not present, for more details see:
|
|
// https://developers.google.com/analytics/devguides/collection/protocol/ga4/sending-events?client_type=gtag#recommended_parameters_for_reports
|
|
if (!params.session_id) {
|
|
params.session_id = await this.getOrCreateSessionId();
|
|
}
|
|
if (!params.engagement_time_msec) {
|
|
params.engagement_time_msec = DEFAULT_ENGAGEMENT_TIME_MSEC;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(
|
|
`${
|
|
this.debug ? GA_DEBUG_ENDPOINT : GA_ENDPOINT
|
|
}?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`,
|
|
{
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
client_id: await this.getOrCreateClientId(),
|
|
events: [
|
|
{
|
|
name,
|
|
params
|
|
}
|
|
]
|
|
})
|
|
}
|
|
);
|
|
if (!this.debug) {
|
|
return;
|
|
}
|
|
console.log(await response.text());
|
|
} catch (e) {
|
|
console.error('Google Analytics request failed with an exception', e);
|
|
}
|
|
}
|
|
|
|
// Fire a page view event.
|
|
async firePageViewEvent(pageTitle, pageLocation, additionalParams = {}) {
|
|
return this.fireEvent('page_view', {
|
|
page_title: pageTitle,
|
|
page_location: pageLocation,
|
|
...additionalParams
|
|
});
|
|
}
|
|
|
|
// Fire an error event.
|
|
async fireErrorEvent(error, additionalParams = {}) {
|
|
// Note: 'error' is a reserved event name and cannot be used
|
|
// see https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference?client_type=gtag#reserved_names
|
|
return this.fireEvent('extension_error', {
|
|
...error,
|
|
...additionalParams
|
|
});
|
|
}
|
|
}
|
|
|
|
export default new Analytics();
|