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