[Data-Plugin] Add telemetry around query/filter bar usage (#82709)

* [Discover] Add telemetry around query/filter bar usage

* Updating documentation

* Adding a separate method for tracking query submit

* Updating doc changes

* Adding trackUiMetric to Data plugin

* Doc changes

* Do not make usageCollection a start dependency

* Updating documentation

* Adding metrics for filter inclusion, edit, and disabled

* updating docs

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Maja Grubic 2020-11-11 12:18:41 +00:00 committed by GitHub
parent ac150da49d
commit cb27a7295b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 39 additions and 5 deletions

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
}
```

View file

@ -72,6 +72,7 @@ import {
import { SavedObjectsClientPublicToCommon } from './index_patterns';
import { indexPatternLoad } from './index_patterns/expressions/load_index_pattern';
import { UsageCollectionSetup } from '../../usage_collection/public';
declare module '../../ui_actions/public' {
export interface ActionContextMapping {
@ -94,6 +95,7 @@ export class DataPublicPlugin
private readonly fieldFormatsService: FieldFormatsService;
private readonly queryService: QueryService;
private readonly storage: IStorageWrapper;
private usageCollection: UsageCollectionSetup | undefined;
constructor(initializerContext: PluginInitializerContext<ConfigSchema>) {
this.searchService = new SearchService(initializerContext);
@ -112,6 +114,8 @@ export class DataPublicPlugin
expressions.registerFunction(esaggs);
expressions.registerFunction(indexPatternLoad);
this.usageCollection = usageCollection;
const queryService = this.queryService.setup({
uiSettings: core.uiSettings,
storage: this.storage,
@ -208,6 +212,7 @@ export class DataPublicPlugin
core,
data: dataServices,
storage: this.storage,
trackUiMetric: this.usageCollection?.reportUiStats.bind(this.usageCollection, 'data_plugin'),
});
return {

View file

@ -84,6 +84,7 @@ import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { TypeOf } from '@kbn/config-schema';
import { UiActionsSetup } from 'src/plugins/ui_actions/public';
import { UiActionsStart } from 'src/plugins/ui_actions/public';
import { UiStatsMetricType } from '@kbn/analytics';
import { Unit } from '@elastic/datemath';
import { UnregisterCallback } from 'history';
import { UserProvidedValues } from 'src/core/server/types';
@ -1998,8 +1999,8 @@ export const search: {
// Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
export const SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "isLoading" | "filters" | "onRefresh" | "onRefreshChange" | "refreshInterval" | "intl" | "indexPatterns" | "dataTestSubj" | "screenTitle" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
};
// Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts

View file

@ -22,6 +22,7 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
import classNames from 'classnames';
import React, { useState } from 'react';
import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics';
import { FilterEditor } from './filter_editor';
import { FILTER_EDITOR_WIDTH, FilterItem } from './filter_item';
import { FilterOptions } from './filter_options';
@ -45,6 +46,9 @@ interface Props {
className: string;
indexPatterns: IIndexPattern[];
intl: InjectedIntl;
appName: string;
// Track UI Metrics
trackUiMetric?: (metricType: UiStatsMetricType, eventName: string | string[]) => void;
}
function FilterBarUI(props: Props) {
@ -128,6 +132,9 @@ function FilterBarUI(props: Props) {
function onAdd(filter: Filter) {
setIsAddFilterPopoverOpen(false);
if (props.trackUiMetric) {
props.trackUiMetric(METRIC_TYPE.CLICK, `${props.appName}:filter_added`);
}
const filters = [...props.filters, filter];
onFiltersUpdated(filters);
}
@ -139,6 +146,9 @@ function FilterBarUI(props: Props) {
}
function onUpdate(i: number, filter: Filter) {
if (props.trackUiMetric) {
props.trackUiMetric(METRIC_TYPE.CLICK, `${props.appName}:filter_edited`);
}
const filters = [...props.filters];
filters[i] = filter;
onFiltersUpdated(filters);
@ -165,11 +175,17 @@ function FilterBarUI(props: Props) {
}
function onToggleAllNegated() {
if (props.trackUiMetric) {
props.trackUiMetric(METRIC_TYPE.CLICK, `${props.appName}:filter_invertInclusion`);
}
const filters = props.filters.map(toggleFilterNegated);
onFiltersUpdated(filters);
}
function onToggleAllDisabled() {
if (props.trackUiMetric) {
props.trackUiMetric(METRIC_TYPE.CLICK, `${props.appName}:filter_toggleAllDisabled`);
}
const filters = props.filters.map(toggleFilterDisabled);
onFiltersUpdated(filters);
}

View file

@ -21,6 +21,7 @@ import _ from 'lodash';
import React, { useEffect, useRef } from 'react';
import { CoreStart } from 'src/core/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { UiStatsMetricType } from '@kbn/analytics';
import { KibanaContextProvider } from '../../../../kibana_react/public';
import { QueryStart, SavedQuery } from '../../query';
import { SearchBar, SearchBarOwnProps } from './';
@ -35,6 +36,7 @@ interface StatefulSearchBarDeps {
core: CoreStart;
data: Omit<DataPublicPluginStart, 'ui'>;
storage: IStorageWrapper;
trackUiMetric?: (metricType: UiStatsMetricType, eventName: string | string[]) => void;
}
export type StatefulSearchBarProps = SearchBarOwnProps & {
@ -119,7 +121,7 @@ const overrideDefaultBehaviors = (props: StatefulSearchBarProps) => {
return props.useDefaultBehaviors ? {} : props;
};
export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps) {
export function createSearchBar({ core, storage, data, trackUiMetric }: StatefulSearchBarDeps) {
// App name should come from the core application service.
// Until it's available, we'll ask the user to provide it for the pre-wired component.
return (props: StatefulSearchBarProps) => {
@ -197,6 +199,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
onClearSavedQuery={defaultOnClearSavedQuery(props, clearSavedQuery)}
onSavedQueryUpdated={defaultOnSavedQueryUpdated(props, setSavedQuery)}
onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)}
trackUiMetric={trackUiMetric}
{...overrideDefaultBehaviors(props)}
/>
</KibanaContextProvider>

View file

@ -24,6 +24,7 @@ import React, { Component } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import { get, isEqual } from 'lodash';
import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics';
import { withKibana, KibanaReactContextValue } from '../../../../kibana_react/public';
import QueryBarTopRow from '../query_string_input/query_bar_top_row';
@ -78,6 +79,8 @@ export interface SearchBarOwnProps {
onRefresh?: (payload: { dateRange: TimeRange }) => void;
indicateNoData?: boolean;
// Track UI Metrics
trackUiMetric?: (metricType: UiStatsMetricType, eventName: string | string[]) => void;
}
export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps;
@ -331,6 +334,9 @@ class SearchBarUI extends Component<SearchBarProps, State> {
},
});
}
if (this.props.trackUiMetric) {
this.props.trackUiMetric(METRIC_TYPE.CLICK, `${this.services.appName}:query_submitted`);
}
}
);
};
@ -432,6 +438,8 @@ class SearchBarUI extends Component<SearchBarProps, State> {
filters={this.props.filters!}
onFiltersUpdated={this.props.onFiltersUpdated}
indexPatterns={this.props.indexPatterns!}
appName={this.services.appName}
trackUiMetric={this.props.trackUiMetric}
/>
</div>
</div>

View file

@ -80,6 +80,7 @@ import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport';
import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import { TypeOf } from '@kbn/config-schema';
import { UiComponent } from 'src/plugins/kibana_utils/public';
import { UiStatsMetricType } from '@kbn/analytics';
import { UnregisterCallback } from 'history';
import { UserProvidedValues } from 'src/core/server/types';