From fdc3adb2c920cbf5f06a8092a57151b6b6bd1e00 Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Tue, 6 Dec 2022 21:15:55 +0100 Subject: [PATCH] Loki: Add tracking for on dashboard load (#59914) Loki: Add on dashboard load tracking --- .../plugins/datasource/loki/module.test.ts | 90 +++++++++++++ public/app/plugins/datasource/loki/module.ts | 8 +- .../app/plugins/datasource/loki/tracking.ts | 118 ++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 public/app/plugins/datasource/loki/module.test.ts create mode 100644 public/app/plugins/datasource/loki/tracking.ts diff --git a/public/app/plugins/datasource/loki/module.test.ts b/public/app/plugins/datasource/loki/module.test.ts new file mode 100644 index 00000000000..a99b7fb1987 --- /dev/null +++ b/public/app/plugins/datasource/loki/module.test.ts @@ -0,0 +1,90 @@ +import { DashboardLoadedEvent } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; +import './module'; + +jest.mock('@grafana/runtime', () => { + return { + ...jest.requireActual('@grafana/runtime'), + reportInteraction: jest.fn(), + getAppEvents: () => ({ + subscribe: jest.fn((_, handler) => { + // Trigger test event + handler( + new DashboardLoadedEvent({ + dashboardId: 'dashboard123', + orgId: 1, + userId: 2, + grafanaVersion: 'v9.0.0', + queries: { + loki: [ + { + datasource: { type: 'loki', uid: 'abc' }, + editorMode: 'builder', + expr: '{place="$place"} |= ``', + queryType: 'range', + refId: 'A', + }, + { + datasource: { type: 'loki', uid: 'abc' }, + editorMode: 'builder', + expr: '{place="$place"} |= `error`', + maxLines: 60, + queryType: 'instant', + refId: 'A', + }, + { + datasource: { type: 'loki', uid: 'abc' }, + editorMode: 'builder', + expr: 'count_over_time({place="$place"} [5m])', + legendFormat: '{{place}}', + maxLines: 60, + queryType: 'range', + refId: 'A', + resolution: 1, + }, + { + datasource: { type: 'loki', uid: 'abc' }, + editorMode: 'code', + expr: 'count_over_time({place="moon"} [5m])', + legendFormat: '{{place}}', + queryType: 'range', + refId: 'A', + }, + { + datasource: { type: 'loki', uid: 'abc' }, + editorMode: 'code', + expr: 'count_over_time({place="luna"} [5m])', + legendFormat: '{{place}}', + queryType: 'range', + refId: 'A', + }, + ], + }, + }) + ); + }), + }), + }; +}); + +describe('queriesOnInitDashboard', () => { + it('should report a grafana_loki_dashboard_loaded interaction ', () => { + // subscribeDashboardLoadedEvent(); + expect(reportInteraction).toHaveBeenCalledWith('grafana_loki_dashboard_loaded', { + builder_mode_queries_count: 3, + grafana_version: 'v9.0.0', + dashboard_id: 'dashboard123', + org_id: 1, + code_mode_queries_count: 2, + instant_queries_count: 1, + logs_queries_count: 2, + metric_queries_count: 3, + queries_count: 5, + queries_with_changed_legend_count: 3, + queries_with_changed_line_limit_count: 5, + queries_with_changed_resolution_count: 0, + queries_with_template_variables_count: 2, + range_queries_count: 4, + }); + }); +}); diff --git a/public/app/plugins/datasource/loki/module.ts b/public/app/plugins/datasource/loki/module.ts index dddfa023aed..10ee51588d3 100644 --- a/public/app/plugins/datasource/loki/module.ts +++ b/public/app/plugins/datasource/loki/module.ts @@ -1,11 +1,17 @@ -import { DataSourcePlugin } from '@grafana/data'; +import { DashboardLoadedEvent, DataSourcePlugin } from '@grafana/data'; +import { getAppEvents } from '@grafana/runtime'; import LokiCheatSheet from './components/LokiCheatSheet'; import LokiQueryEditorByApp from './components/LokiQueryEditorByApp'; import { ConfigEditor } from './configuration/ConfigEditor'; import { LokiDatasource } from './datasource'; +import { onDashboardLoadedHandler } from './tracking'; +import { LokiQuery } from './types'; export const plugin = new DataSourcePlugin(LokiDatasource) .setQueryEditor(LokiQueryEditorByApp) .setConfigEditor(ConfigEditor) .setQueryEditorHelp(LokiCheatSheet); + +// Subscribe to on dashboard loaded event so that we can track plugin adoption +getAppEvents().subscribe>(DashboardLoadedEvent, onDashboardLoadedHandler); diff --git a/public/app/plugins/datasource/loki/tracking.ts b/public/app/plugins/datasource/loki/tracking.ts new file mode 100644 index 00000000000..99f32fa4a27 --- /dev/null +++ b/public/app/plugins/datasource/loki/tracking.ts @@ -0,0 +1,118 @@ +import { DashboardLoadedEvent } from '@grafana/data'; +import { reportInteraction } from '@grafana/runtime'; +import { variableRegex } from 'app/features/variables/utils'; + +import { QueryEditorMode } from '../prometheus/querybuilder/shared/types'; + +import pluginJson from './plugin.json'; +import { getNormalizedLokiQuery, isLogsQuery } from './queryUtils'; +import { LokiQuery, LokiQueryType } from './types'; + +type LokiOnDashboardLoadedTrackingEvent = { + grafana_version?: string; + dashboard_id?: string; + org_id?: number; + + /* The number of Loki queries present in the dashboard*/ + queries_count: number; + + /* The number of Loki logs queries present in the dashboard*/ + logs_queries_count: number; + + /* The number of Loki metric queries present in the dashboard*/ + metric_queries_count: number; + + /* The number of Loki instant queries present in the dashboard*/ + instant_queries_count: number; + + /* The number of Loki range queries present in the dashboard*/ + range_queries_count: number; + + /* The number of Loki queries created in builder mode present in the dashboard*/ + builder_mode_queries_count: number; + + /* The number of Loki queries created in code mode present in the dashboard*/ + code_mode_queries_count: number; + + /* The number of Loki queries with used template variables present in the dashboard*/ + queries_with_template_variables_count: number; + + /* The number of Loki queries with changed resolution present in the dashboard*/ + queries_with_changed_resolution_count: number; + + /* The number of Loki queries with changed line limit present in the dashboard*/ + queries_with_changed_line_limit_count: number; + + /* The number of Loki queries with changed legend present in the dashboard*/ + queries_with_changed_legend_count: number; +}; + +export const onDashboardLoadedHandler = ({ + payload: { dashboardId, orgId, grafanaVersion, queries }, +}: DashboardLoadedEvent) => { + try { + // We only want to track visible Loki queries + const lokiQueries = queries[pluginJson.id] + .filter((query) => !query.hide) + .map((query) => getNormalizedLokiQuery(query)); + + if (!lokiQueries?.length) { + return; + } + + const logsQueries = lokiQueries.filter((query) => isLogsQuery(query.expr)); + const metricQueries = lokiQueries.filter((query) => !isLogsQuery(query.expr)); + const instantQueries = lokiQueries.filter((query) => query.queryType === LokiQueryType.Instant); + const rangeQueries = lokiQueries.filter((query) => query.queryType === LokiQueryType.Range); + const builderModeQueries = lokiQueries.filter((query) => query.editorMode === QueryEditorMode.Builder); + const codeModeQueries = lokiQueries.filter((query) => query.editorMode === QueryEditorMode.Code); + const queriesWithTemplateVariables = lokiQueries.filter(isQueryWithTemplateVariables); + const queriesWithChangedResolution = lokiQueries.filter(isQueryWithChangedResolution); + const queriesWithChangedLineLimit = lokiQueries.filter(isQueryWithChangedLineLimit); + const queriesWithChangedLegend = lokiQueries.filter(isQueryWithChangedLegend); + + const event: LokiOnDashboardLoadedTrackingEvent = { + grafana_version: grafanaVersion, + dashboard_id: dashboardId, + org_id: orgId, + queries_count: lokiQueries.length, + logs_queries_count: logsQueries.length, + metric_queries_count: metricQueries.length, + instant_queries_count: instantQueries.length, + range_queries_count: rangeQueries.length, + builder_mode_queries_count: builderModeQueries.length, + code_mode_queries_count: codeModeQueries.length, + queries_with_template_variables_count: queriesWithTemplateVariables.length, + queries_with_changed_resolution_count: queriesWithChangedResolution.length, + queries_with_changed_line_limit_count: queriesWithChangedLineLimit.length, + queries_with_changed_legend_count: queriesWithChangedLegend.length, + }; + + reportInteraction('grafana_loki_dashboard_loaded', event); + } catch (error) { + console.error('error in loki tracking handler', error); + } +}; + +const isQueryWithTemplateVariables = (query: LokiQuery): boolean => { + return variableRegex.test(query.expr); +}; + +const isQueryWithChangedResolution = (query: LokiQuery): boolean => { + if (!query.resolution) { + return false; + } + // 1 is the default resolution + return query.resolution !== 1; +}; + +const isQueryWithChangedLineLimit = (query: LokiQuery): boolean => { + return query.maxLines !== null || query.maxLines !== undefined; +}; + +const isQueryWithChangedLegend = (query: LokiQuery): boolean => { + if (!query.legendFormat) { + return false; + } + return query.legendFormat !== ''; +};