mirror of https://github.com/grafana/grafana
User analytics: Add Rudderstack integration (#36567)
* Replace analytics service with Echo backend * Add Rudderstack integration and general pageview and interaction Echo events * Update conf/defaults.ini Co-authored-by: Dan Cech <dcech@grafana.com> * Update packages/grafana-runtime/src/types/analytics.ts Co-authored-by: Dan Cech <dcech@grafana.com> * Update conf/defaults.ini Co-authored-by: Dan Cech <dcech@grafana.com> * Update tests * Force cla check Co-authored-by: Dan Cech <dcech@grafana.com>pull/36556/head
parent
cd95e28c30
commit
663a8935f7
@ -1,52 +0,0 @@ |
||||
import $ from 'jquery'; |
||||
import config from 'app/core/config'; |
||||
import { locationService } from '@grafana/runtime'; |
||||
|
||||
export class Analytics { |
||||
private gaId?: string; |
||||
private ga?: any; |
||||
|
||||
constructor() { |
||||
this.track = this.track.bind(this); |
||||
this.gaId = (config as any).googleAnalyticsId; |
||||
this.init(); |
||||
} |
||||
|
||||
init() { |
||||
if (!this.gaId) { |
||||
return; |
||||
} |
||||
|
||||
$.ajax({ |
||||
url: 'https://www.google-analytics.com/analytics.js', |
||||
dataType: 'script', |
||||
cache: true, |
||||
}); |
||||
|
||||
const ga = ((window as any).ga = |
||||
(window as any).ga || |
||||
// this had the equivalent of `eslint-disable-next-line prefer-arrow/prefer-arrow-functions`
|
||||
function () { |
||||
(ga.q = ga.q || []).push(arguments); |
||||
}); |
||||
ga.l = +new Date(); |
||||
ga('create', (config as any).googleAnalyticsId, 'auto'); |
||||
ga('set', 'anonymizeIp', true); |
||||
this.ga = ga; |
||||
return ga; |
||||
} |
||||
|
||||
track() { |
||||
if (!this.ga) { |
||||
return; |
||||
} |
||||
|
||||
const location = locationService.getLocation(); |
||||
const track = { page: `${config.appSubUrl ?? ''}${location.pathname}${location.search}${location.hash}` }; |
||||
|
||||
this.ga('set', track); |
||||
this.ga('send', 'pageview'); |
||||
} |
||||
} |
||||
|
||||
export const analyticsService = new Analytics(); |
||||
@ -0,0 +1,43 @@ |
||||
import $ from 'jquery'; |
||||
import { EchoBackend, EchoEventType, PageviewEchoEvent } from '@grafana/runtime'; |
||||
|
||||
export interface GAEchoBackendOptions { |
||||
googleAnalyticsId: string; |
||||
debug?: boolean; |
||||
} |
||||
|
||||
export class GAEchoBackend implements EchoBackend<PageviewEchoEvent, GAEchoBackendOptions> { |
||||
supportedEvents = [EchoEventType.Pageview]; |
||||
|
||||
constructor(public options: GAEchoBackendOptions) { |
||||
const url = `https://www.google-analytics.com/analytics${options.debug ? '_debug' : ''}.js`; |
||||
|
||||
$.ajax({ |
||||
url, |
||||
dataType: 'script', |
||||
cache: true, |
||||
}); |
||||
|
||||
const ga = ((window as any).ga = |
||||
(window as any).ga || |
||||
// this had the equivalent of `eslint-disable-next-line prefer-arrow/prefer-arrow-functions`
|
||||
function () { |
||||
(ga.q = ga.q || []).push(arguments); |
||||
}); |
||||
ga.l = +new Date(); |
||||
ga('create', options.googleAnalyticsId, 'auto'); |
||||
ga('set', 'anonymizeIp', true); |
||||
} |
||||
|
||||
addEvent = (e: PageviewEchoEvent) => { |
||||
if (!(window as any).ga) { |
||||
return; |
||||
} |
||||
|
||||
(window as any).ga('set', { page: e.payload.page }); |
||||
(window as any).ga('send', 'pageview'); |
||||
}; |
||||
|
||||
// Not using Echo buffering, addEvent above sends events to GA as soon as they appear
|
||||
flush = () => {}; |
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
import $ from 'jquery'; |
||||
import { EchoBackend, EchoEventType, isInteractionEvent, isPageviewEvent, PageviewEchoEvent } from '@grafana/runtime'; |
||||
import { User } from '../sentry/types'; |
||||
|
||||
export interface RudderstackBackendOptions { |
||||
writeKey: string; |
||||
dataPlaneUrl: string; |
||||
user?: User; |
||||
} |
||||
|
||||
export class RudderstackBackend implements EchoBackend<PageviewEchoEvent, RudderstackBackendOptions> { |
||||
supportedEvents = [EchoEventType.Pageview, EchoEventType.Interaction]; |
||||
|
||||
constructor(public options: RudderstackBackendOptions) { |
||||
const url = `https://cdn.rudderlabs.com/v1/rudder-analytics.min.js`; |
||||
|
||||
$.ajax({ |
||||
url, |
||||
dataType: 'script', |
||||
cache: true, |
||||
}); |
||||
|
||||
const rds = ((window as any).rudderanalytics = []); |
||||
|
||||
var methods = [ |
||||
'load', |
||||
'page', |
||||
'track', |
||||
'identify', |
||||
'alias', |
||||
'group', |
||||
'ready', |
||||
'reset', |
||||
'getAnonymousId', |
||||
'setAnonymousId', |
||||
]; |
||||
|
||||
for (let i = 0; i < methods.length; i++) { |
||||
const method = methods[i]; |
||||
(rds as Record<string, any>)[method] = (function (methodName) { |
||||
return function () { |
||||
// @ts-ignore
|
||||
rds.push([methodName].concat(Array.prototype.slice.call(arguments))); |
||||
}; |
||||
})(method); |
||||
} |
||||
|
||||
(rds as any).load(options.writeKey, options.dataPlaneUrl); |
||||
|
||||
if (options.user) { |
||||
(rds as any).identify(String(options.user.id), { |
||||
email: options.user.email, |
||||
orgId: options.user.orgId, |
||||
}); |
||||
} |
||||
} |
||||
|
||||
addEvent = (e: PageviewEchoEvent) => { |
||||
if (!(window as any).rudderanalytics) { |
||||
return; |
||||
} |
||||
|
||||
if (isPageviewEvent(e)) { |
||||
(window as any).rudderanalytics.page(); |
||||
} |
||||
|
||||
if (isInteractionEvent(e)) { |
||||
(window as any).rudderanalytics.track(e.payload.interactionName, e.payload.properties); |
||||
} |
||||
}; |
||||
|
||||
// Not using Echo buffering, addEvent above sends events to GA as soon as they appear
|
||||
flush = () => {}; |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
import { attachDebugger, createLogger } from '@grafana/ui'; |
||||
|
||||
/** @internal */ |
||||
export const echoLogger = createLogger('EchoSrv'); |
||||
export const echoLog = echoLogger.logger; |
||||
|
||||
attachDebugger('echo', undefined, echoLogger); |
||||
Loading…
Reference in new issue