mirror of https://github.com/grafana/grafana
Elasticsearch: Add tracking for plugin adoption stats (#59954)
* Elasticsearch: Add tracking * Update public/app/plugins/datasource/elasticsearch/tracking.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * Update public/app/plugins/datasource/elasticsearch/tracking.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * Update public/app/plugins/datasource/elasticsearch/tracking.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * Refactor getLineLimit * Update public/app/plugins/datasource/elasticsearch/tracking.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * Update public/app/plugins/datasource/elasticsearch/tracking.ts Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com> * Update not tracking for volume queries Co-authored-by: Sven Grossmann <sven.grossmann@grafana.com>pull/60017/head
parent
c5ee4e4ae1
commit
4b56493789
@ -0,0 +1,152 @@ |
||||
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: { |
||||
elasticsearch: [ |
||||
{ |
||||
alias: '', |
||||
bucketAggs: [], |
||||
datasource: { |
||||
type: 'elasticsearch', |
||||
uid: 'PE50363A9B6833EE7', |
||||
}, |
||||
metrics: [ |
||||
{ |
||||
id: '1', |
||||
settings: { |
||||
limit: '501', |
||||
}, |
||||
type: 'logs', |
||||
}, |
||||
], |
||||
query: 'abc:def', |
||||
refId: 'A', |
||||
timeField: '@timestamp', |
||||
}, |
||||
{ |
||||
alias: '', |
||||
bucketAggs: [], |
||||
datasource: { |
||||
type: 'elasticsearch', |
||||
uid: 'es1', |
||||
}, |
||||
metrics: [ |
||||
{ |
||||
id: '1', |
||||
settings: { |
||||
size: '600', |
||||
}, |
||||
type: 'raw_data', |
||||
}, |
||||
], |
||||
query: '', |
||||
}, |
||||
{ |
||||
alias: 'alias', |
||||
bucketAggs: [ |
||||
{ |
||||
field: '@timestamp', |
||||
id: '2', |
||||
settings: { |
||||
interval: 'auto', |
||||
}, |
||||
type: 'date_histogram', |
||||
}, |
||||
], |
||||
datasource: { |
||||
type: 'elasticsearch', |
||||
uid: 'es1', |
||||
}, |
||||
metrics: [ |
||||
{ |
||||
id: '3', |
||||
type: 'count', |
||||
}, |
||||
], |
||||
query: 'abc:def', |
||||
}, |
||||
{ |
||||
alias: '', |
||||
bucketAggs: [], |
||||
datasource: { |
||||
type: 'elasticsearch', |
||||
uid: 'PE50363A9B6833EE7', |
||||
}, |
||||
metrics: [ |
||||
{ |
||||
id: '1', |
||||
settings: { |
||||
size: '600', |
||||
}, |
||||
type: 'raw_document', |
||||
}, |
||||
], |
||||
query: '', |
||||
refId: 'A', |
||||
timeField: '@timestamp', |
||||
}, |
||||
{ |
||||
alias: '', |
||||
bucketAggs: [ |
||||
{ |
||||
field: '@timestamp', |
||||
id: '2', |
||||
settings: { |
||||
interval: 'auto', |
||||
}, |
||||
type: 'date_histogram', |
||||
}, |
||||
], |
||||
datasource: { |
||||
type: 'elasticsearch', |
||||
uid: 'es1', |
||||
}, |
||||
metrics: [ |
||||
{ |
||||
field: 'counter', |
||||
id: '1', |
||||
type: 'avg', |
||||
}, |
||||
], |
||||
query: '$test:abc', |
||||
}, |
||||
], |
||||
}, |
||||
}) |
||||
); |
||||
}), |
||||
}), |
||||
}; |
||||
}); |
||||
|
||||
describe('queriesOnInitDashboard', () => { |
||||
it('should report a grafana_elasticsearch_dashboard_loaded interaction ', () => { |
||||
expect(reportInteraction).toHaveBeenCalledWith('grafana_elasticsearch_dashboard_loaded', { |
||||
grafana_version: 'v9.0.0', |
||||
dashboard_id: 'dashboard123', |
||||
org_id: 1, |
||||
queries_count: 5, |
||||
queries_with_changed_line_limit_count: 1, |
||||
queries_with_lucene_query_count: 3, |
||||
queries_with_template_variables_count: 1, |
||||
raw_data_queries_count: 1, |
||||
raw_document_queries_count: 1, |
||||
logs_queries_count: 1, |
||||
metric_queries_count: 2, |
||||
}); |
||||
}); |
||||
}); |
||||
@ -1,7 +1,13 @@ |
||||
import { DataSourcePlugin } from '@grafana/data'; |
||||
import { DashboardLoadedEvent, DataSourcePlugin } from '@grafana/data'; |
||||
import { getAppEvents } from '@grafana/runtime'; |
||||
|
||||
import { QueryEditor } from './components/QueryEditor'; |
||||
import { ConfigEditor } from './configuration/ConfigEditor'; |
||||
import { ElasticDatasource } from './datasource'; |
||||
import { onDashboardLoadedHandler } from './tracking'; |
||||
import { ElasticsearchQuery } from './types'; |
||||
|
||||
export const plugin = new DataSourcePlugin(ElasticDatasource).setQueryEditor(QueryEditor).setConfigEditor(ConfigEditor); |
||||
|
||||
// Subscribe to on dashboard loaded event so that we can track plugin adoption
|
||||
getAppEvents().subscribe<DashboardLoadedEvent<ElasticsearchQuery>>(DashboardLoadedEvent, onDashboardLoadedHandler); |
||||
|
||||
@ -0,0 +1,129 @@ |
||||
import { DashboardLoadedEvent, DataQueryResponse } from '@grafana/data'; |
||||
import { reportInteraction } from '@grafana/runtime'; |
||||
import { variableRegex } from 'app/features/variables/utils'; |
||||
|
||||
import { REF_ID_STARTER_LOG_VOLUME } from './datasource'; |
||||
import pluginJson from './plugin.json'; |
||||
import { ElasticsearchQuery } from './types'; |
||||
|
||||
type ElasticSearchOnDashboardLoadedTrackingEvent = { |
||||
grafana_version?: string; |
||||
dashboard_id?: string; |
||||
org_id?: number; |
||||
|
||||
/* The number of Elasticsearch queries present in the dashboard*/ |
||||
queries_count: number; |
||||
|
||||
/* The number of Elasticsearch logs queries present in the dashboard*/ |
||||
logs_queries_count: number; |
||||
|
||||
/* The number of Elasticsearch metric queries present in the dashboard*/ |
||||
metric_queries_count: number; |
||||
|
||||
/* The number of Elasticsearch raw data queries present in the dashboard*/ |
||||
raw_data_queries_count: number; |
||||
|
||||
/* The number of Elasticsearch raw documents queries present in the dashboard*/ |
||||
raw_document_queries_count: number; |
||||
|
||||
/* The number of Elasticsearch queries with used template variables present in the dashboard*/ |
||||
queries_with_template_variables_count: number; |
||||
|
||||
/* The number of Elasticsearch queries with changed line limit present in the dashboard*/ |
||||
queries_with_changed_line_limit_count: number; |
||||
|
||||
/* The number of Elasticsearch queries with lucene query present in the dashboard*/ |
||||
queries_with_lucene_query_count: number; |
||||
}; |
||||
|
||||
export const onDashboardLoadedHandler = ({ |
||||
payload: { dashboardId, orgId, grafanaVersion, queries }, |
||||
}: DashboardLoadedEvent<ElasticsearchQuery>) => { |
||||
try { |
||||
// We only want to track visible ElasticSearch queries
|
||||
const elasticsearchQueries = queries[pluginJson.id].filter((query) => !query.hide); |
||||
if (!elasticsearchQueries?.length) { |
||||
return; |
||||
} |
||||
|
||||
const queriesWithTemplateVariables = elasticsearchQueries.filter(isQueryWithTemplateVariables); |
||||
const queriesWithLuceneQuery = elasticsearchQueries.filter((query) => !!query.query); |
||||
const logsQueries = elasticsearchQueries.filter((query) => getQueryType(query) === 'logs'); |
||||
const metricQueries = elasticsearchQueries.filter((query) => getQueryType(query) === 'metric'); |
||||
const rawDataQueries = elasticsearchQueries.filter((query) => getQueryType(query) === 'raw_data'); |
||||
const rawDocumentQueries = elasticsearchQueries.filter((query) => getQueryType(query) === 'raw_document'); |
||||
const queriesWithChangedLineLimit = elasticsearchQueries.filter(isQueryWithChangedLineLimit); |
||||
|
||||
const event: ElasticSearchOnDashboardLoadedTrackingEvent = { |
||||
grafana_version: grafanaVersion, |
||||
dashboard_id: dashboardId, |
||||
org_id: orgId, |
||||
queries_count: elasticsearchQueries.length, |
||||
logs_queries_count: logsQueries.length, |
||||
metric_queries_count: metricQueries.length, |
||||
raw_data_queries_count: rawDataQueries.length, |
||||
raw_document_queries_count: rawDocumentQueries.length, |
||||
queries_with_template_variables_count: queriesWithTemplateVariables.length, |
||||
queries_with_changed_line_limit_count: queriesWithChangedLineLimit.length, |
||||
queries_with_lucene_query_count: queriesWithLuceneQuery.length, |
||||
}; |
||||
|
||||
reportInteraction('grafana_elasticsearch_dashboard_loaded', event); |
||||
} catch (error) { |
||||
console.error('error in elasticsearch tracking handler', error); |
||||
} |
||||
}; |
||||
|
||||
const getQueryType = (query: ElasticsearchQuery): string | undefined => { |
||||
if (!query.metrics || !query.metrics.length) { |
||||
return undefined; |
||||
} |
||||
const nonMetricQueryTypes = ['logs', 'raw_data', 'raw_document']; |
||||
if (nonMetricQueryTypes.includes(query.metrics[0].type)) { |
||||
return query.metrics[0].type; |
||||
} |
||||
return 'metric'; |
||||
}; |
||||
|
||||
const getLineLimit = (query: ElasticsearchQuery): number | undefined => { |
||||
if (query.metrics?.[0]?.type !== 'logs') { |
||||
return undefined; |
||||
} |
||||
|
||||
const lineLimit = query.metrics?.[0].settings?.limit; |
||||
return lineLimit ? parseInt(lineLimit, 10) : undefined; |
||||
}; |
||||
|
||||
const isQueryWithChangedLineLimit = (query: ElasticsearchQuery): boolean => { |
||||
const lineLimit = getLineLimit(query); |
||||
return lineLimit !== undefined && lineLimit !== 500; |
||||
}; |
||||
|
||||
const isQueryWithTemplateVariables = (query: ElasticsearchQuery): boolean => { |
||||
return variableRegex.test(query.query ?? ''); |
||||
}; |
||||
|
||||
const shouldNotReportBasedOnRefId = (refId: string): boolean => { |
||||
if (refId.startsWith(REF_ID_STARTER_LOG_VOLUME)) { |
||||
return true; |
||||
} |
||||
return false; |
||||
}; |
||||
|
||||
export function trackQuery(response: DataQueryResponse, queries: ElasticsearchQuery[], app: string): void { |
||||
for (const query of queries) { |
||||
if (shouldNotReportBasedOnRefId(query.refId)) { |
||||
return; |
||||
} |
||||
reportInteraction('grafana_elasticsearch_query_executed', { |
||||
app, |
||||
with_lucene_query: query.query ? true : false, |
||||
query_type: getQueryType(query), |
||||
line_limit: getLineLimit(query), |
||||
alias: query.alias, |
||||
has_error: response.error !== undefined, |
||||
has_data: response.data.some((frame) => frame.length > 0), |
||||
simultaneously_sent_query_count: queries.length, |
||||
}); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue