Echo: Add config option to prevent duplicate page views for GA4 (#57619)

This commit is contained in:
Timur Olzhabayev 2022-11-09 15:09:19 +01:00 committed by GitHub
parent 463f993186
commit 008c554d7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 77 additions and 56 deletions

View File

@ -230,6 +230,9 @@ google_analytics_ua_id =
# Google Analytics 4 tracking code, only enabled if you specify an id here
google_analytics_4_id =
# When Google Analytics 4 Enhanced event measurement is enabled, we will try to avoid sending duplicate events and let Google Analytics 4 detect navigation changes, etc.
google_analytics_4_send_manual_page_views = false
# Google Tag Manager ID, only enabled if you specify an id here
google_tag_manager_id =

View File

@ -237,6 +237,9 @@
# Google Analytics 4 tracking code, only enabled if you specify an id here
;google_analytics_4_id =
# When Google Analytics 4 Enhanced event measurement is enabled, we will try to avoid sending duplicate events and let Google Analytics 4 detect navigation changes, etc.
;google_analytics_4_send_manual_page_views = false
# Google Tag Manager ID, only enabled if you specify an id here
;google_tag_manager_id =

View File

@ -211,6 +211,7 @@ export interface GrafanaConfig {
secretsManagerPluginEnabled: boolean;
googleAnalyticsId: string | undefined;
googleAnalytics4Id: string | undefined;
googleAnalytics4SendManualPageViews: boolean;
rudderstackWriteKey: string | undefined;
rudderstackDataPlaneUrl: string | undefined;
rudderstackSdkUrl: string | undefined;

View File

@ -136,6 +136,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
};
googleAnalyticsId: undefined;
googleAnalytics4Id: undefined;
googleAnalytics4SendManualPageViews = false;
rudderstackWriteKey: undefined;
rudderstackDataPlaneUrl: undefined;
rudderstackSdkUrl: undefined;

View File

@ -14,6 +14,7 @@ type IndexViewData struct {
AppSubUrl string
GoogleAnalyticsId string
GoogleAnalytics4Id string
GoogleAnalytics4SendManualPageViews bool
GoogleTagManagerId string
NavTree *navtree.NavTreeRoot
BuildVersion string

View File

@ -124,6 +124,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled,
"googleAnalyticsId": setting.GoogleAnalyticsId,
"googleAnalytics4Id": setting.GoogleAnalytics4Id,
"GoogleAnalytics4SendManualPageViews": setting.GoogleAnalytics4SendManualPageViews,
"rudderstackWriteKey": setting.RudderstackWriteKey,
"rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl,
"rudderstackSdkUrl": setting.RudderstackSdkUrl,

View File

@ -104,6 +104,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
AppSubUrl: appSubURL,
GoogleAnalyticsId: setting.GoogleAnalyticsId,
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews,
GoogleTagManagerId: setting.GoogleTagManagerId,
BuildVersion: setting.BuildVersion,
BuildCommit: setting.BuildCommit,

View File

@ -139,6 +139,7 @@ var (
// analytics
GoogleAnalyticsId string
GoogleAnalytics4Id string
GoogleAnalytics4SendManualPageViews bool
GoogleTagManagerId string
RudderstackDataPlaneUrl string
RudderstackWriteKey string
@ -995,6 +996,8 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
cfg.CheckForPluginUpdates = analytics.Key("check_for_plugin_updates").MustBool(true)
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
GoogleAnalytics4Id = analytics.Key("google_analytics_4_id").String()
GoogleAnalytics4SendManualPageViews = analytics.Key("google_analytics_4_send_manual_page_views").MustBool(false)
GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
RudderstackWriteKey = analytics.Key("rudderstack_write_key").String()
RudderstackDataPlaneUrl = analytics.Key("rudderstack_data_plane_url").String()

View File

@ -268,6 +268,7 @@ function initEchoSrv() {
registerEchoBackend(
new GA4EchoBackend({
googleAnalyticsId: config.googleAnalytics4Id,
googleAnalytics4SendManualPageViews: config.googleAnalytics4SendManualPageViews,
})
);
}

View File

@ -11,16 +11,17 @@ declare global {
export interface GA4EchoBackendOptions {
googleAnalyticsId: string;
googleAnalytics4SendManualPageViews: boolean;
user?: CurrentUserDTO;
}
export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBackendOptions> {
supportedEvents = [EchoEventType.Pageview];
trackedUserId: number | null = null;
googleAnalytics4SendManualPageViews = false;
constructor(public options: GA4EchoBackendOptions) {
const url = `https://www.googletagmanager.com/gtag/js?id=${options.googleAnalyticsId}`;
loadScript(url);
loadScript(url, true);
window.dataLayer = window.dataLayer || [];
window.gtag = function gtag() {
@ -28,12 +29,15 @@ export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBac
};
window.gtag('js', new Date());
const configOptions: Gtag.CustomParams = {};
const configOptions: Gtag.CustomParams = {
page_path: window.location.pathname,
};
if (options.user) {
configOptions.user_id = getUserIdentifier(options.user);
}
this.googleAnalytics4SendManualPageViews = options.googleAnalytics4SendManualPageViews;
window.gtag('config', options.googleAnalyticsId, configOptions);
}
@ -41,9 +45,10 @@ export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBac
if (!window.gtag) {
return;
}
window.gtag('set', 'page_path', e.payload.page);
window.gtag('event', 'page_view');
// this should prevent duplicate events in case enhanced tracking is enabled
if (this.googleAnalytics4SendManualPageViews) {
window.gtag('event', 'page_view', { page_path: e.payload.page });
}
};
// Not using Echo buffering, addEvent above sends events to GA as soon as they appear

View File

@ -14,11 +14,12 @@ export function getUserIdentifier(user: CurrentUserDTO) {
return user.email;
}
export function loadScript(url: string) {
export function loadScript(url: string, async = false) {
return new Promise((resolve) => {
const script = document.createElement('script');
script.onload = resolve;
script.src = url;
script.async = async;
document.head.appendChild(script);
});
}