grafana/public/app/plugins/datasource/elasticsearch/tracking.ts
Sven Grossmann ae05c6180c
Loki/ES: Add further items to query tracking (#60347)
* add more tracking to queries from ES and Loki

* improve tracking elasticsearch

* update type assertion

* remove unused import
2022-12-15 09:59:50 +01:00

143 lines
5.3 KiB
TypeScript

import { CoreApp, DashboardLoadedEvent, DataQueryRequest, DataQueryResponse } from '@grafana/data';
import { config, 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,
request: DataQueryRequest<ElasticsearchQuery> & { targets: ElasticsearchQuery[] },
startTime: Date
): void {
const { targets: queries, app } = request;
if (app === CoreApp.Dashboard || app === CoreApp.PanelViewer) {
return;
}
for (const query of queries) {
if (shouldNotReportBasedOnRefId(query.refId)) {
return;
}
reportInteraction('grafana_elasticsearch_query_executed', {
app,
grafana_version: config.buildInfo.version,
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,
time_range_from: request?.range?.from?.toISOString(),
time_range_to: request?.range?.to?.toISOString(),
time_taken: Date.now() - startTime.getTime(),
});
}
}