mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Echo: Add config option to prevent duplicate page views for GA4 (#57619)
This commit is contained in:
parent
463f993186
commit
008c554d7f
@ -230,6 +230,9 @@ google_analytics_ua_id =
|
|||||||
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
||||||
google_analytics_4_id =
|
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, only enabled if you specify an id here
|
||||||
google_tag_manager_id =
|
google_tag_manager_id =
|
||||||
|
|
||||||
|
@ -237,6 +237,9 @@
|
|||||||
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
# Google Analytics 4 tracking code, only enabled if you specify an id here
|
||||||
;google_analytics_4_id =
|
;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, only enabled if you specify an id here
|
||||||
;google_tag_manager_id =
|
;google_tag_manager_id =
|
||||||
|
|
||||||
|
@ -211,6 +211,7 @@ export interface GrafanaConfig {
|
|||||||
secretsManagerPluginEnabled: boolean;
|
secretsManagerPluginEnabled: boolean;
|
||||||
googleAnalyticsId: string | undefined;
|
googleAnalyticsId: string | undefined;
|
||||||
googleAnalytics4Id: string | undefined;
|
googleAnalytics4Id: string | undefined;
|
||||||
|
googleAnalytics4SendManualPageViews: boolean;
|
||||||
rudderstackWriteKey: string | undefined;
|
rudderstackWriteKey: string | undefined;
|
||||||
rudderstackDataPlaneUrl: string | undefined;
|
rudderstackDataPlaneUrl: string | undefined;
|
||||||
rudderstackSdkUrl: string | undefined;
|
rudderstackSdkUrl: string | undefined;
|
||||||
|
@ -136,6 +136,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
|||||||
};
|
};
|
||||||
googleAnalyticsId: undefined;
|
googleAnalyticsId: undefined;
|
||||||
googleAnalytics4Id: undefined;
|
googleAnalytics4Id: undefined;
|
||||||
|
googleAnalytics4SendManualPageViews = false;
|
||||||
rudderstackWriteKey: undefined;
|
rudderstackWriteKey: undefined;
|
||||||
rudderstackDataPlaneUrl: undefined;
|
rudderstackDataPlaneUrl: undefined;
|
||||||
rudderstackSdkUrl: undefined;
|
rudderstackSdkUrl: undefined;
|
||||||
|
@ -8,27 +8,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type IndexViewData struct {
|
type IndexViewData struct {
|
||||||
User *CurrentUser
|
User *CurrentUser
|
||||||
Settings map[string]interface{}
|
Settings map[string]interface{}
|
||||||
AppUrl string
|
AppUrl string
|
||||||
AppSubUrl string
|
AppSubUrl string
|
||||||
GoogleAnalyticsId string
|
GoogleAnalyticsId string
|
||||||
GoogleAnalytics4Id string
|
GoogleAnalytics4Id string
|
||||||
GoogleTagManagerId string
|
GoogleAnalytics4SendManualPageViews bool
|
||||||
NavTree *navtree.NavTreeRoot
|
GoogleTagManagerId string
|
||||||
BuildVersion string
|
NavTree *navtree.NavTreeRoot
|
||||||
BuildCommit string
|
BuildVersion string
|
||||||
Theme string
|
BuildCommit string
|
||||||
NewGrafanaVersionExists bool
|
Theme string
|
||||||
NewGrafanaVersion string
|
NewGrafanaVersionExists bool
|
||||||
AppName string
|
NewGrafanaVersion string
|
||||||
AppNameBodyClass string
|
AppName string
|
||||||
FavIcon template.URL
|
AppNameBodyClass string
|
||||||
AppleTouchIcon template.URL
|
FavIcon template.URL
|
||||||
AppTitle string
|
AppleTouchIcon template.URL
|
||||||
Sentry *setting.Sentry
|
AppTitle string
|
||||||
ContentDeliveryURL string
|
Sentry *setting.Sentry
|
||||||
LoadingLogo template.URL
|
ContentDeliveryURL string
|
||||||
|
LoadingLogo template.URL
|
||||||
// Nonce is a cryptographic identifier for use with Content Security Policy.
|
// Nonce is a cryptographic identifier for use with Content Security Policy.
|
||||||
Nonce string
|
Nonce string
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
|||||||
"queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled,
|
"queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled,
|
||||||
"googleAnalyticsId": setting.GoogleAnalyticsId,
|
"googleAnalyticsId": setting.GoogleAnalyticsId,
|
||||||
"googleAnalytics4Id": setting.GoogleAnalytics4Id,
|
"googleAnalytics4Id": setting.GoogleAnalytics4Id,
|
||||||
|
"GoogleAnalytics4SendManualPageViews": setting.GoogleAnalytics4SendManualPageViews,
|
||||||
"rudderstackWriteKey": setting.RudderstackWriteKey,
|
"rudderstackWriteKey": setting.RudderstackWriteKey,
|
||||||
"rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl,
|
"rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl,
|
||||||
"rudderstackSdkUrl": setting.RudderstackSdkUrl,
|
"rudderstackSdkUrl": setting.RudderstackSdkUrl,
|
||||||
|
@ -98,27 +98,28 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
|||||||
HelpFlags1: c.HelpFlags1,
|
HelpFlags1: c.HelpFlags1,
|
||||||
HasEditPermissionInFolders: hasEditPerm,
|
HasEditPermissionInFolders: hasEditPerm,
|
||||||
},
|
},
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
Theme: prefs.Theme,
|
Theme: prefs.Theme,
|
||||||
AppUrl: appURL,
|
AppUrl: appURL,
|
||||||
AppSubUrl: appSubURL,
|
AppSubUrl: appSubURL,
|
||||||
GoogleAnalyticsId: setting.GoogleAnalyticsId,
|
GoogleAnalyticsId: setting.GoogleAnalyticsId,
|
||||||
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
|
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
|
||||||
GoogleTagManagerId: setting.GoogleTagManagerId,
|
GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews,
|
||||||
BuildVersion: setting.BuildVersion,
|
GoogleTagManagerId: setting.GoogleTagManagerId,
|
||||||
BuildCommit: setting.BuildCommit,
|
BuildVersion: setting.BuildVersion,
|
||||||
NewGrafanaVersion: hs.grafanaUpdateChecker.LatestVersion(),
|
BuildCommit: setting.BuildCommit,
|
||||||
NewGrafanaVersionExists: hs.grafanaUpdateChecker.UpdateAvailable(),
|
NewGrafanaVersion: hs.grafanaUpdateChecker.LatestVersion(),
|
||||||
AppName: setting.ApplicationName,
|
NewGrafanaVersionExists: hs.grafanaUpdateChecker.UpdateAvailable(),
|
||||||
AppNameBodyClass: "app-grafana",
|
AppName: setting.ApplicationName,
|
||||||
FavIcon: "public/img/fav32.png",
|
AppNameBodyClass: "app-grafana",
|
||||||
AppleTouchIcon: "public/img/apple-touch-icon.png",
|
FavIcon: "public/img/fav32.png",
|
||||||
AppTitle: "Grafana",
|
AppleTouchIcon: "public/img/apple-touch-icon.png",
|
||||||
NavTree: navTree,
|
AppTitle: "Grafana",
|
||||||
Sentry: &hs.Cfg.Sentry,
|
NavTree: navTree,
|
||||||
Nonce: c.RequestNonce,
|
Sentry: &hs.Cfg.Sentry,
|
||||||
ContentDeliveryURL: hs.Cfg.GetContentDeliveryURL(hs.License.ContentDeliveryPrefix()),
|
Nonce: c.RequestNonce,
|
||||||
LoadingLogo: "public/img/grafana_icon.svg",
|
ContentDeliveryURL: hs.Cfg.GetContentDeliveryURL(hs.License.ContentDeliveryPrefix()),
|
||||||
|
LoadingLogo: "public/img/grafana_icon.svg",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hs.AccessControl.IsDisabled() {
|
if !hs.AccessControl.IsDisabled() {
|
||||||
|
@ -137,13 +137,14 @@ var (
|
|||||||
appliedEnvOverrides []string
|
appliedEnvOverrides []string
|
||||||
|
|
||||||
// analytics
|
// analytics
|
||||||
GoogleAnalyticsId string
|
GoogleAnalyticsId string
|
||||||
GoogleAnalytics4Id string
|
GoogleAnalytics4Id string
|
||||||
GoogleTagManagerId string
|
GoogleAnalytics4SendManualPageViews bool
|
||||||
RudderstackDataPlaneUrl string
|
GoogleTagManagerId string
|
||||||
RudderstackWriteKey string
|
RudderstackDataPlaneUrl string
|
||||||
RudderstackSdkUrl string
|
RudderstackWriteKey string
|
||||||
RudderstackConfigUrl string
|
RudderstackSdkUrl string
|
||||||
|
RudderstackConfigUrl string
|
||||||
|
|
||||||
// LDAP
|
// LDAP
|
||||||
LDAPEnabled bool
|
LDAPEnabled bool
|
||||||
@ -995,6 +996,8 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
|
|||||||
cfg.CheckForPluginUpdates = analytics.Key("check_for_plugin_updates").MustBool(true)
|
cfg.CheckForPluginUpdates = analytics.Key("check_for_plugin_updates").MustBool(true)
|
||||||
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
|
GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
|
||||||
GoogleAnalytics4Id = analytics.Key("google_analytics_4_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()
|
GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
|
||||||
RudderstackWriteKey = analytics.Key("rudderstack_write_key").String()
|
RudderstackWriteKey = analytics.Key("rudderstack_write_key").String()
|
||||||
RudderstackDataPlaneUrl = analytics.Key("rudderstack_data_plane_url").String()
|
RudderstackDataPlaneUrl = analytics.Key("rudderstack_data_plane_url").String()
|
||||||
|
@ -268,6 +268,7 @@ function initEchoSrv() {
|
|||||||
registerEchoBackend(
|
registerEchoBackend(
|
||||||
new GA4EchoBackend({
|
new GA4EchoBackend({
|
||||||
googleAnalyticsId: config.googleAnalytics4Id,
|
googleAnalyticsId: config.googleAnalytics4Id,
|
||||||
|
googleAnalytics4SendManualPageViews: config.googleAnalytics4SendManualPageViews,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,17 @@ declare global {
|
|||||||
|
|
||||||
export interface GA4EchoBackendOptions {
|
export interface GA4EchoBackendOptions {
|
||||||
googleAnalyticsId: string;
|
googleAnalyticsId: string;
|
||||||
|
googleAnalytics4SendManualPageViews: boolean;
|
||||||
user?: CurrentUserDTO;
|
user?: CurrentUserDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBackendOptions> {
|
export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBackendOptions> {
|
||||||
supportedEvents = [EchoEventType.Pageview];
|
supportedEvents = [EchoEventType.Pageview];
|
||||||
trackedUserId: number | null = null;
|
googleAnalytics4SendManualPageViews = false;
|
||||||
|
|
||||||
constructor(public options: GA4EchoBackendOptions) {
|
constructor(public options: GA4EchoBackendOptions) {
|
||||||
const url = `https://www.googletagmanager.com/gtag/js?id=${options.googleAnalyticsId}`;
|
const url = `https://www.googletagmanager.com/gtag/js?id=${options.googleAnalyticsId}`;
|
||||||
loadScript(url);
|
loadScript(url, true);
|
||||||
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
window.gtag = function gtag() {
|
window.gtag = function gtag() {
|
||||||
@ -28,12 +29,15 @@ export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBac
|
|||||||
};
|
};
|
||||||
window.gtag('js', new Date());
|
window.gtag('js', new Date());
|
||||||
|
|
||||||
const configOptions: Gtag.CustomParams = {};
|
const configOptions: Gtag.CustomParams = {
|
||||||
|
page_path: window.location.pathname,
|
||||||
|
};
|
||||||
|
|
||||||
if (options.user) {
|
if (options.user) {
|
||||||
configOptions.user_id = getUserIdentifier(options.user);
|
configOptions.user_id = getUserIdentifier(options.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.googleAnalytics4SendManualPageViews = options.googleAnalytics4SendManualPageViews;
|
||||||
window.gtag('config', options.googleAnalyticsId, configOptions);
|
window.gtag('config', options.googleAnalyticsId, configOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,9 +45,10 @@ export class GA4EchoBackend implements EchoBackend<PageviewEchoEvent, GA4EchoBac
|
|||||||
if (!window.gtag) {
|
if (!window.gtag) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// this should prevent duplicate events in case enhanced tracking is enabled
|
||||||
window.gtag('set', 'page_path', e.payload.page);
|
if (this.googleAnalytics4SendManualPageViews) {
|
||||||
window.gtag('event', 'page_view');
|
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
|
// Not using Echo buffering, addEvent above sends events to GA as soon as they appear
|
||||||
|
@ -14,11 +14,12 @@ export function getUserIdentifier(user: CurrentUserDTO) {
|
|||||||
return user.email;
|
return user.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadScript(url: string) {
|
export function loadScript(url: string, async = false) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.onload = resolve;
|
script.onload = resolve;
|
||||||
script.src = url;
|
script.src = url;
|
||||||
|
script.async = async;
|
||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user