mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
UsageInsights: Record events for Explore queries (#59931)
* usageinsights: record events for Explore queries * usageinsights: make the source field optional It is not logical to have it for an event like the dashboard-view * usageinsights: add comment to Explore test Explain why we are reversing a previous decision
This commit is contained in:
parent
69ffce4c42
commit
74167b4d44
@ -44,6 +44,7 @@ Logs of usage insights contain the following fields, where the fields followed b
|
|||||||
| `panelName` | string | Name of the panel of the query. |
|
| `panelName` | string | Name of the panel of the query. |
|
||||||
| `error` | string | Error returned by the query. |
|
| `error` | string | Error returned by the query. |
|
||||||
| `duration` | number | Duration of the query. |
|
| `duration` | number | Duration of the query. |
|
||||||
|
| `source` | string | Source of the query. For example, `dashboard` or `explore`. |
|
||||||
| `orgId`\* | number | ID of the user’s organization. |
|
| `orgId`\* | number | ID of the user’s organization. |
|
||||||
| `orgName`\* | string | Name of the user’s organization. |
|
| `orgName`\* | string | Name of the user’s organization. |
|
||||||
| `timestamp`\* | string | The date and time that the request was made, in Coordinated Universal Time (UTC) in [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) format. |
|
| `timestamp`\* | string | The date and time that the request was made, in Coordinated Universal Time (UTC) in [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) format. |
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { CoreApp } from '@grafana/data';
|
||||||
|
|
||||||
import { EchoEvent, EchoEventType } from '../services/EchoSrv';
|
import { EchoEvent, EchoEventType } from '../services/EchoSrv';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,6 +22,7 @@ export interface DashboardInfo {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface DataRequestInfo extends Partial<DashboardInfo> {
|
export interface DataRequestInfo extends Partial<DashboardInfo> {
|
||||||
|
source?: CoreApp | string;
|
||||||
datasourceName: string;
|
datasourceName: string;
|
||||||
datasourceId: number;
|
datasourceId: number;
|
||||||
datasourceUid: string;
|
datasourceUid: string;
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
import { CoreApp, DataFrame, DataQueryRequest, DataSourceApi, dateTime, LoadingState, PanelData } from '@grafana/data';
|
import {
|
||||||
|
CoreApp,
|
||||||
|
DataFrame,
|
||||||
|
DataQueryError,
|
||||||
|
DataQueryRequest,
|
||||||
|
DataSourceApi,
|
||||||
|
dateTime,
|
||||||
|
LoadingState,
|
||||||
|
PanelData,
|
||||||
|
} from '@grafana/data';
|
||||||
import { MetaAnalyticsEventName, reportMetaAnalytics } from '@grafana/runtime';
|
import { MetaAnalyticsEventName, reportMetaAnalytics } from '@grafana/runtime';
|
||||||
|
|
||||||
import { DashboardModel } from '../../dashboard/state';
|
import { DashboardModel } from '../../dashboard/state';
|
||||||
@ -12,6 +21,7 @@ beforeEach(() => {
|
|||||||
const datasource = {
|
const datasource = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
id: 1,
|
id: 1,
|
||||||
|
uid: 'test',
|
||||||
} as DataSourceApi;
|
} as DataSourceApi;
|
||||||
|
|
||||||
const dashboardModel = new DashboardModel(
|
const dashboardModel = new DashboardModel(
|
||||||
@ -94,6 +104,28 @@ function getTestData(requestApp: string, series: DataFrame[] = []): PanelData {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTestDataForExplore(requestApp: string, series: DataFrame[] = []): PanelData {
|
||||||
|
const now = dateTime();
|
||||||
|
const error: DataQueryError = { message: 'test error' };
|
||||||
|
|
||||||
|
return {
|
||||||
|
request: {
|
||||||
|
app: requestApp,
|
||||||
|
dashboardId: 0,
|
||||||
|
startTime: now.unix(),
|
||||||
|
endTime: now.add(1, 's').unix(),
|
||||||
|
} as DataQueryRequest,
|
||||||
|
series,
|
||||||
|
state: LoadingState.Done,
|
||||||
|
timeRange: {
|
||||||
|
from: dateTime(),
|
||||||
|
to: dateTime(),
|
||||||
|
raw: { from: '1h', to: 'now' },
|
||||||
|
},
|
||||||
|
error: error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe('emitDataRequestEvent - from a dashboard panel', () => {
|
describe('emitDataRequestEvent - from a dashboard panel', () => {
|
||||||
it('Should report meta analytics', () => {
|
it('Should report meta analytics', () => {
|
||||||
const data = getTestData(CoreApp.Dashboard);
|
const data = getTestData(CoreApp.Dashboard);
|
||||||
@ -185,10 +217,35 @@ describe('emitDataRequestEvent - from a dashboard panel', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Previously we filtered out Explore events due to too many errors being generated while a user is building a query
|
||||||
|
// This tests that we send an event for Explore queries but do not record errors
|
||||||
describe('emitDataRequestEvent - from Explore', () => {
|
describe('emitDataRequestEvent - from Explore', () => {
|
||||||
const data = getTestData(CoreApp.Explore);
|
it('Should report meta analytics', () => {
|
||||||
it('Should not report meta analytics', () => {
|
const data = getTestDataForExplore(CoreApp.Explore);
|
||||||
emitDataRequestEvent(datasource)(data);
|
emitDataRequestEvent(datasource)(data);
|
||||||
expect(reportMetaAnalytics).not.toBeCalled();
|
|
||||||
|
expect(reportMetaAnalytics).toBeCalledTimes(1);
|
||||||
|
expect(reportMetaAnalytics).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
eventName: MetaAnalyticsEventName.DataRequest,
|
||||||
|
source: 'explore',
|
||||||
|
datasourceName: 'test',
|
||||||
|
datasourceId: 1,
|
||||||
|
datasourceUid: 'test',
|
||||||
|
dataSize: 0,
|
||||||
|
duration: 1,
|
||||||
|
totalQueries: 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('emitDataRequestEvent - from Explore', () => {
|
||||||
|
it('Should not report errors', () => {
|
||||||
|
const data = getTestDataForExplore(CoreApp.Explore);
|
||||||
|
emitDataRequestEvent(datasource)(data);
|
||||||
|
|
||||||
|
expect(reportMetaAnalytics).toBeCalledTimes(1);
|
||||||
|
expect(reportMetaAnalytics).toBeCalledWith(expect.not.objectContaining({ error: 'test error' }));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ export function emitDataRequestEvent(datasource: DataSourceApi) {
|
|||||||
let done = false;
|
let done = false;
|
||||||
|
|
||||||
return (data: PanelData) => {
|
return (data: PanelData) => {
|
||||||
if (!data.request || done || data.request.app === CoreApp.Explore) {
|
if (!data.request || done) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,6 +21,41 @@ export function emitDataRequestEvent(datasource: DataSourceApi) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventData: DataRequestEventPayload = {
|
||||||
|
eventName: MetaAnalyticsEventName.DataRequest,
|
||||||
|
source: data.request.app,
|
||||||
|
datasourceName: datasource.name,
|
||||||
|
datasourceId: datasource.id,
|
||||||
|
datasourceUid: datasource.uid,
|
||||||
|
datasourceType: datasource.type,
|
||||||
|
dataSize: 0,
|
||||||
|
duration: data.request.endTime! - data.request.startTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.request.app === CoreApp.Explore) {
|
||||||
|
enrichWithExploreInfo(eventData, data);
|
||||||
|
} else {
|
||||||
|
enrichWithDashboardInfo(eventData, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.series && data.series.length > 0) {
|
||||||
|
// estimate size
|
||||||
|
eventData.dataSize = data.series.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
reportMetaAnalytics(eventData);
|
||||||
|
|
||||||
|
// this done check is to make sure we do not double emit events in case
|
||||||
|
// there are multiple responses with done state
|
||||||
|
done = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function enrichWithExploreInfo(eventData: DataRequestEventPayload, data: PanelData) {
|
||||||
|
const totalQueries = Object.keys(data.series).length;
|
||||||
|
eventData.totalQueries = totalQueries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enrichWithDashboardInfo(eventData: DataRequestEventPayload, data: PanelData) {
|
||||||
const queryCacheStatus: { [key: string]: boolean } = {};
|
const queryCacheStatus: { [key: string]: boolean } = {};
|
||||||
for (let i = 0; i < data.series.length; i++) {
|
for (let i = 0; i < data.series.length; i++) {
|
||||||
const refId = data.series[i].refId;
|
const refId = data.series[i].refId;
|
||||||
@ -31,21 +66,11 @@ export function emitDataRequestEvent(datasource: DataSourceApi) {
|
|||||||
const totalQueries = Object.keys(queryCacheStatus).length;
|
const totalQueries = Object.keys(queryCacheStatus).length;
|
||||||
const cachedQueries = Object.values(queryCacheStatus).filter((val) => val === true).length;
|
const cachedQueries = Object.values(queryCacheStatus).filter((val) => val === true).length;
|
||||||
|
|
||||||
const eventData: DataRequestEventPayload = {
|
eventData.panelId = data.request!.panelId;
|
||||||
eventName: MetaAnalyticsEventName.DataRequest,
|
eventData.dashboardId = data.request!.dashboardId;
|
||||||
datasourceName: datasource.name,
|
eventData.totalQueries = totalQueries;
|
||||||
datasourceId: datasource.id,
|
eventData.cachedQueries = cachedQueries;
|
||||||
datasourceUid: datasource.uid,
|
|
||||||
datasourceType: datasource.type,
|
|
||||||
panelId: data.request.panelId,
|
|
||||||
dashboardId: data.request.dashboardId,
|
|
||||||
dataSize: 0,
|
|
||||||
duration: data.request.endTime! - data.request.startTime,
|
|
||||||
totalQueries,
|
|
||||||
cachedQueries,
|
|
||||||
};
|
|
||||||
|
|
||||||
// enrich with dashboard info
|
|
||||||
const dashboard = getDashboardSrv().getCurrent();
|
const dashboard = getDashboardSrv().getCurrent();
|
||||||
if (dashboard) {
|
if (dashboard) {
|
||||||
eventData.dashboardId = dashboard.id;
|
eventData.dashboardId = dashboard.id;
|
||||||
@ -58,19 +83,8 @@ export function emitDataRequestEvent(datasource: DataSourceApi) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.series && data.series.length > 0) {
|
|
||||||
// estimate size
|
|
||||||
eventData.dataSize = data.series.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
eventData.error = data.error.message;
|
eventData.error = data.error.message;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
reportMetaAnalytics(eventData);
|
|
||||||
|
|
||||||
// this done check is to make sure we do not double emit events in case
|
|
||||||
// there are multiple responses with done state
|
|
||||||
done = true;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user