From 9d3f4f5983380d9d905ae00c3b294a8c1c4fdfaa Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 15 Sep 2021 13:00:38 +0200 Subject: [PATCH] Analytics: Instrument Azure Application Insights (#38553) * add backend * read config and pass to backend * update config docs * change var name * use connection string instead of instrumentation key * add config for endpoint url. update docs * update default ini * add spaces * remove space * Update docs/sources/administration/configuration.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> --- conf/defaults.ini | 6 ++ docs/sources/administration/configuration.md | 7 ++ packages/grafana-runtime/src/config.ts | 2 + pkg/api/frontendsettings.go | 66 ++++++++++--------- pkg/setting/setting.go | 10 ++- public/app/app.ts | 10 +++ .../analytics/ApplicationInsightsBackend.ts | 48 ++++++++++++++ 7 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts diff --git a/conf/defaults.ini b/conf/defaults.ini index 583a3d90947..af81a9e01c9 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -208,6 +208,12 @@ rudderstack_write_key = # Rudderstack data plane url, enabled only if rudderstack_write_key is also set rudderstack_data_plane_url = +# Application Insights connection string. Specify an URL string to enable this feature. +application_insights_connection_string = + +# Optional. Specifies an Application Insights endpoint URL where the endpoint string is wrapped in backticks ``. +application_insights_endpoint_url = + #################################### Security ############################ [security] # disable creation of admin user on first start of grafana diff --git a/docs/sources/administration/configuration.md b/docs/sources/administration/configuration.md index 6a4a1eaeb5e..ccd81b165f7 100644 --- a/docs/sources/administration/configuration.md +++ b/docs/sources/administration/configuration.md @@ -468,6 +468,13 @@ Analytics ID here. By default this feature is disabled. Google Tag Manager ID, only enabled if you enter an ID here. +### application_insights_connection_string + +If you want to track Grafana usage via Azure Application Insights, then specify _your_ Application Insights connection string. Since the connection string contains semicolons, you need to wrap it in backticks (`). By default, tracking usage is disabled. + +### application_insights_endpoint_url + Optionally, use this option to override the default endpoint address for Application Insights data collecting. For details, refer to the [Azure documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/custom-endpoints?tabs=js). +
## [security] diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index ddd10809874..bbcb3d45357 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -94,6 +94,8 @@ export class GrafanaBootConfig implements GrafanaConfig { }; geomapDefaultBaseLayerConfig?: MapLayerOptions; geomapDisableCustomBaseLayer?: boolean; + applicationInsightsConnectionString?: string; + applicationInsightsEndpointUrl?: string; constructor(options: GrafanaBootConfig) { const mode = options.bootData.user.lightTheme ? 'light' : 'dark'; diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 1ef74759721..fbc4327b65c 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -197,38 +197,40 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i } jsonObj := map[string]interface{}{ - "defaultDatasource": defaultDS, - "datasources": dataSources, - "minRefreshInterval": setting.MinRefreshInterval, - "panels": panels, - "appUrl": hs.Cfg.AppURL, - "appSubUrl": hs.Cfg.AppSubURL, - "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, - "authProxyEnabled": setting.AuthProxyEnabled, - "ldapEnabled": hs.Cfg.LDAPEnabled, - "alertingEnabled": setting.AlertingEnabled, - "alertingErrorOrTimeout": setting.AlertingErrorOrTimeout, - "alertingNoDataOrNullValues": setting.AlertingNoDataOrNullValues, - "alertingMinInterval": setting.AlertingMinInterval, - "liveEnabled": hs.Cfg.LiveMaxConnections != 0, - "autoAssignOrg": setting.AutoAssignOrg, - "verifyEmailEnabled": setting.VerifyEmailEnabled, - "sigV4AuthEnabled": setting.SigV4AuthEnabled, - "exploreEnabled": setting.ExploreEnabled, - "googleAnalyticsId": setting.GoogleAnalyticsId, - "rudderstackWriteKey": setting.RudderstackWriteKey, - "rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl, - "disableLoginForm": setting.DisableLoginForm, - "disableUserSignUp": !setting.AllowUserSignUp, - "loginHint": setting.LoginHint, - "passwordHint": setting.PasswordHint, - "externalUserMngInfo": setting.ExternalUserMngInfo, - "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, - "externalUserMngLinkName": setting.ExternalUserMngLinkName, - "viewersCanEdit": setting.ViewersCanEdit, - "editorsCanAdmin": hs.Cfg.EditorsCanAdmin, - "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, - "pluginsToPreload": pluginsToPreload, + "defaultDatasource": defaultDS, + "datasources": dataSources, + "minRefreshInterval": setting.MinRefreshInterval, + "panels": panels, + "appUrl": hs.Cfg.AppURL, + "appSubUrl": hs.Cfg.AppSubURL, + "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, + "authProxyEnabled": setting.AuthProxyEnabled, + "ldapEnabled": hs.Cfg.LDAPEnabled, + "alertingEnabled": setting.AlertingEnabled, + "alertingErrorOrTimeout": setting.AlertingErrorOrTimeout, + "alertingNoDataOrNullValues": setting.AlertingNoDataOrNullValues, + "alertingMinInterval": setting.AlertingMinInterval, + "liveEnabled": hs.Cfg.LiveMaxConnections != 0, + "autoAssignOrg": setting.AutoAssignOrg, + "verifyEmailEnabled": setting.VerifyEmailEnabled, + "sigV4AuthEnabled": setting.SigV4AuthEnabled, + "exploreEnabled": setting.ExploreEnabled, + "googleAnalyticsId": setting.GoogleAnalyticsId, + "rudderstackWriteKey": setting.RudderstackWriteKey, + "rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl, + "applicationInsightsConnectionString": hs.Cfg.ApplicationInsightsConnectionString, + "applicationInsightsEndpointUrl": hs.Cfg.ApplicationInsightsEndpointUrl, + "disableLoginForm": setting.DisableLoginForm, + "disableUserSignUp": !setting.AllowUserSignUp, + "loginHint": setting.LoginHint, + "passwordHint": setting.PasswordHint, + "externalUserMngInfo": setting.ExternalUserMngInfo, + "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, + "externalUserMngLinkName": setting.ExternalUserMngLinkName, + "viewersCanEdit": setting.ViewersCanEdit, + "editorsCanAdmin": hs.Cfg.EditorsCanAdmin, + "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, + "pluginsToPreload": pluginsToPreload, "buildInfo": map[string]interface{}{ "hideVersion": hideVersion, "version": version, diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 1bbd5fce7eb..6948e10c638 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -369,9 +369,11 @@ type Cfg struct { Env string // Analytics - CheckForUpdates bool - ReportingDistributor string - ReportingEnabled bool + CheckForUpdates bool + ReportingDistributor string + ReportingEnabled bool + ApplicationInsightsConnectionString string + ApplicationInsightsEndpointUrl string // LDAP LDAPEnabled bool @@ -908,6 +910,8 @@ func (cfg *Cfg) Load(args CommandLineArgs) error { if len(cfg.ReportingDistributor) >= 100 { cfg.ReportingDistributor = cfg.ReportingDistributor[:100] } + cfg.ApplicationInsightsConnectionString = analytics.Key("application_insights_connection_string").String() + cfg.ApplicationInsightsEndpointUrl = analytics.Key("application_insights_endpoint_url").String() if err := readAlertingSettings(iniFile); err != nil { return err diff --git a/public/app/app.ts b/public/app/app.ts index 58fa46d24b8..c50432eb23b 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -48,6 +48,7 @@ import { getVariablesUrlParams } from './features/variables/getAllVariableValues import getDefaultMonacoLanguages from '../lib/monaco-languages'; import { contextSrv } from './core/services/context_srv'; import { GAEchoBackend } from './core/services/echo/backends/analytics/GABackend'; +import { ApplicationInsightsBackend } from './core/services/echo/backends/analytics/ApplicationInsightsBackend'; import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend'; import { getAllOptionEditors } from './core/components/editors/registry'; @@ -182,6 +183,15 @@ function initEchoSrv() { }) ); } + + if (config.applicationInsightsConnectionString) { + registerEchoBackend( + new ApplicationInsightsBackend({ + connectionString: config.applicationInsightsConnectionString, + endpointUrl: config.applicationInsightsEndpointUrl, + }) + ); + } } function addClassIfNoOverlayScrollbar() { diff --git a/public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts b/public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts new file mode 100644 index 00000000000..0405a4cecb7 --- /dev/null +++ b/public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts @@ -0,0 +1,48 @@ +import $ from 'jquery'; +import { EchoBackend, EchoEventType, isInteractionEvent, isPageviewEvent, PageviewEchoEvent } from '@grafana/runtime'; + +export interface ApplicationInsightsBackendOptions { + connectionString: string; + endpointUrl?: string; +} + +export class ApplicationInsightsBackend implements EchoBackend { + supportedEvents = [EchoEventType.Pageview]; + + constructor(public options: ApplicationInsightsBackendOptions) { + $.ajax({ + url: 'https://js.monitor.azure.com/scripts/b/ai.2.min.js', + dataType: 'script', + cache: true, + }).done(function () { + var applicationInsightsOpts = { + config: { + connectionString: options.connectionString, + endpointUrl: options.endpointUrl, + }, + }; + var init = new (window as any).Microsoft.ApplicationInsights.ApplicationInsights(applicationInsightsOpts); + (window as any).applicationInsights = init.loadAppInsights(); + }); + } + + addEvent = (e: PageviewEchoEvent) => { + if (!(window as any).applicationInsights) { + return; + } + + if (isPageviewEvent(e)) { + (window as any).applicationInsights.trackPageView(); + } + + if (isInteractionEvent(e)) { + (window as any).applicationInsights.trackPageView({ + name: e.payload.interactionName, + properties: e.payload.properties, + }); + } + }; + + // Not using Echo buffering, addEvent above sends events to Application Insights as soon as they appear + flush = () => {}; +}