Skip to content

Commit

Permalink
[Entity Analytics] [Entity Store] Telemetry (elastic#196880)
Browse files Browse the repository at this point in the history
## Summary

This PR adds telemetry for the Entity Store.
Client side tracks UI enablement actions, whilst Kibana side tracks
execution time of the store initialisation process and execution time of
the enrich policy task.
Finally we also track number of entities in the store
  • Loading branch information
tiansivive authored Oct 29, 2024
1 parent b4ed7a1 commit 67d96e3
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export enum TelemetryEventTypes {
EntityDetailsClicked = 'Entity Details Clicked',
EntityAlertsClicked = 'Entity Alerts Clicked',
EntityRiskFiltered = 'Entity Risk Filtered',
EntityStoreEnablementToggleClicked = 'Entity Store Enablement Toggle Clicked',
EntityStoreDashboardInitButtonClicked = 'Entity Store Initialization Button Clicked',
MLJobUpdate = 'ML Job Update',
AddRiskInputToTimelineClicked = 'Add Risk Input To Timeline Clicked',
ToggleRiskSummaryClicked = 'Toggle Risk Summary Clicked',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,36 @@ export const assetCriticalityCsvImportedEvent: TelemetryEvent = {
},
},
};

export const entityStoreInitEvent: TelemetryEvent = {
eventType: TelemetryEventTypes.EntityStoreDashboardInitButtonClicked,
schema: {
timestamp: {
type: 'date',
_meta: {
description: 'Timestamp of the event',
optional: false,
},
},
},
};

export const entityStoreEnablementEvent: TelemetryEvent = {
eventType: TelemetryEventTypes.EntityStoreEnablementToggleClicked,
schema: {
timestamp: {
type: 'date',
_meta: {
description: 'Timestamp of the event',
optional: false,
},
},
action: {
type: 'keyword',
_meta: {
description: 'Event toggle action',
optional: false,
},
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ export interface ReportAssetCriticalityCsvImportedParams {
};
}

export interface ReportEntityStoreEnablementParams {
timestamp: string;
action: 'start' | 'stop';
}

export interface ReportEntityStoreInitParams {
timestamp: string;
}

export type ReportEntityAnalyticsTelemetryEventParams =
| ReportEntityDetailsClickedParams
| ReportEntityAlertsClickedParams
Expand All @@ -68,7 +77,9 @@ export type ReportEntityAnalyticsTelemetryEventParams =
| ReportAddRiskInputToTimelineClickedParams
| ReportAssetCriticalityCsvPreviewGeneratedParams
| ReportAssetCriticalityFileSelectedParams
| ReportAssetCriticalityCsvImportedParams;
| ReportAssetCriticalityCsvImportedParams
| ReportEntityStoreEnablementParams
| ReportEntityStoreInitParams;

export type EntityAnalyticsTelemetryEvent =
| {
Expand Down Expand Up @@ -106,4 +117,12 @@ export type EntityAnalyticsTelemetryEvent =
| {
eventType: TelemetryEventTypes.AssetCriticalityCsvImported;
schema: RootSchema<ReportAssetCriticalityCsvImportedParams>;
}
| {
eventType: TelemetryEventTypes.EntityStoreEnablementToggleClicked;
schema: RootSchema<ReportEntityStoreEnablementParams>;
}
| {
eventType: TelemetryEventTypes.EntityStoreDashboardInitButtonClicked;
schema: RootSchema<ReportEntityStoreInitParams>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
assetCriticalityCsvPreviewGeneratedEvent,
assetCriticalityFileSelectedEvent,
assetCriticalityCsvImportedEvent,
entityStoreEnablementEvent,
entityStoreInitEvent,
} from './entity_analytics';
import {
assistantInvokedEvent,
Expand Down Expand Up @@ -172,6 +174,8 @@ export const telemetryEvents = [
assetCriticalityCsvPreviewGeneratedEvent,
assetCriticalityFileSelectedEvent,
assetCriticalityCsvImportedEvent,
entityStoreEnablementEvent,
entityStoreInitEvent,
toggleRiskSummaryClickedEvent,
RiskInputsExpandedFlyoutOpenedEvent,
addRiskInputToTimelineClickedEvent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ export const createTelemetryClientMock = (): jest.Mocked<TelemetryClientStart> =
reportOpenNoteInExpandableFlyoutClicked: jest.fn(),
reportAddNoteFromExpandableFlyoutClicked: jest.fn(),
reportPreviewRule: jest.fn(),
reportEntityStoreEnablement: jest.fn(),
reportEntityStoreInit: jest.fn(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import type {
ReportEventLogShowSourceEventDateRangeParams,
ReportEventLogFilterByRunTypeParams,
PreviewRuleParams,
ReportEntityStoreEnablementParams,
ReportEntityStoreInitParams,
} from './types';
import { TelemetryEventTypes } from './constants';

Expand Down Expand Up @@ -216,4 +218,12 @@ export class TelemetryClient implements TelemetryClientStart {
public reportPreviewRule = (params: PreviewRuleParams) => {
this.analytics.reportEvent(TelemetryEventTypes.PreviewRule, params);
};

public reportEntityStoreEnablement = (params: ReportEntityStoreEnablementParams) => {
this.analytics.reportEvent(TelemetryEventTypes.EntityStoreEnablementToggleClicked, params);
};

public reportEntityStoreInit = (params: ReportEntityStoreInitParams) => {
this.analytics.reportEvent(TelemetryEventTypes.EntityStoreDashboardInitButtonClicked, params);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import type {
ReportAssetCriticalityCsvPreviewGeneratedParams,
ReportAssetCriticalityFileSelectedParams,
ReportAssetCriticalityCsvImportedParams,
ReportEntityStoreEnablementParams,
ReportEntityStoreInitParams,
} from './events/entity_analytics/types';
import type {
AssistantTelemetryEvent,
Expand Down Expand Up @@ -78,17 +80,7 @@ export * from './events/ai_assistant/types';
export * from './events/alerts_grouping/types';
export * from './events/data_quality/types';
export * from './events/onboarding/types';
export type {
ReportEntityAlertsClickedParams,
ReportEntityDetailsClickedParams,
ReportEntityRiskFilteredParams,
ReportRiskInputsExpandedFlyoutOpenedParams,
ReportToggleRiskSummaryClickedParams,
ReportAddRiskInputToTimelineClickedParams,
ReportAssetCriticalityCsvPreviewGeneratedParams,
ReportAssetCriticalityFileSelectedParams,
ReportAssetCriticalityCsvImportedParams,
} from './events/entity_analytics/types';
export * from './events/entity_analytics/types';
export * from './events/document_details/types';
export * from './events/manual_rule_run/types';
export * from './events/event_log/types';
Expand Down Expand Up @@ -168,6 +160,9 @@ export interface TelemetryClientStart {
): void;
reportAssetCriticalityCsvImported(params: ReportAssetCriticalityCsvImportedParams): void;
reportCellActionClicked(params: ReportCellActionClickedParams): void;
// Entity Analytics Entity Store
reportEntityStoreEnablement(params: ReportEntityStoreEnablementParams): void;
reportEntityStoreInit(params: ReportEntityStoreInitParams): void;

reportAnomaliesCountClicked(params: ReportAnomaliesCountClickedParams): void;
reportDataQualityIndexChecked(params: ReportDataQualityIndexCheckedParams): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { UseMutationOptions } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback, useState } from 'react';

import { useKibana } from '../../../../common/lib/kibana/kibana_react';
import type {
DeleteEntityEngineResponse,
InitEntityEngineResponse,
Expand All @@ -21,6 +22,7 @@ const ENTITY_STORE_ENABLEMENT_INIT = 'ENTITY_STORE_ENABLEMENT_INIT';

export const useEntityStoreEnablement = () => {
const [polling, setPolling] = useState(false);
const { telemetry } = useKibana().services;

useEntityEngineStatus({
disabled: !polling,
Expand All @@ -46,8 +48,11 @@ export const useEntityStoreEnablement = () => {
});

const enable = useCallback(() => {
telemetry?.reportEntityStoreInit({
timestamp: new Date().toISOString(),
});
initialize().then(() => setPolling(true));
}, [initialize]);
}, [initialize, telemetry]);

return { enable };
};
Expand All @@ -65,10 +70,17 @@ export const useInvalidateEntityEngineStatusQuery = () => {
};

export const useInitEntityEngineMutation = (options?: UseMutationOptions<{}>) => {
const { telemetry } = useKibana().services;
const invalidateEntityEngineStatusQuery = useInvalidateEntityEngineStatusQuery();
const { initEntityStore } = useEntityStoreRoutes();
return useMutation<InitEntityEngineResponse[]>(
() => Promise.all([initEntityStore('user'), initEntityStore('host')]),
() => {
telemetry?.reportEntityStoreEnablement({
timestamp: new Date().toISOString(),
action: 'start',
});
return Promise.all([initEntityStore('user'), initEntityStore('host')]);
},
{
...options,
mutationKey: INIT_ENTITY_ENGINE_STATUS_KEY,
Expand All @@ -86,10 +98,17 @@ export const useInitEntityEngineMutation = (options?: UseMutationOptions<{}>) =>
export const STOP_ENTITY_ENGINE_STATUS_KEY = ['POST', 'STOP_ENTITY_ENGINE'];

export const useStopEntityEngineMutation = (options?: UseMutationOptions<{}>) => {
const { telemetry } = useKibana().services;
const invalidateEntityEngineStatusQuery = useInvalidateEntityEngineStatusQuery();
const { stopEntityStore } = useEntityStoreRoutes();
return useMutation<StopEntityEngineResponse[]>(
() => Promise.all([stopEntityStore('user'), stopEntityStore('host')]),
() => {
telemetry?.reportEntityStoreEnablement({
timestamp: new Date().toISOString(),
action: 'stop',
});
return Promise.all([stopEntityStore('user'), stopEntityStore('host')]);
},
{
...options,
mutationKey: STOP_ENTITY_ENGINE_STATUS_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import type {
SavedObjectsClientContract,
AuditLogger,
IScopedClusterClient,
AnalyticsServiceSetup,
} from '@kbn/core/server';
import { EntityClient } from '@kbn/entityManager-plugin/server/lib/entity_client';
import type { SortOrder } from '@elastic/elasticsearch/lib/api/types';
import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server';
import type { DataViewsService } from '@kbn/data-views-plugin/common';
import { isEqual } from 'lodash/fp';
import moment from 'moment';
import type { AppClient } from '../../..';
import type {
Entity,
Expand Down Expand Up @@ -53,6 +55,10 @@ import {
isPromiseFulfilled,
isPromiseRejected,
} from './utils';
import {
ENTITY_ENGINE_INITIALIZATION_EVENT,
ENTITY_ENGINE_RESOURCE_INIT_FAILURE_EVENT,
} from '../../telemetry/event_based/events';
import type { EntityRecord } from './types';
import { CRITICALITY_VALUES } from '../asset_criticality/constants';

Expand All @@ -66,6 +72,7 @@ interface EntityStoreClientOpts {
kibanaVersion: string;
dataViewsService: DataViewsService;
appClient: AppClient;
telemetry?: AnalyticsServiceSetup;
}

interface SearchEntitiesParams {
Expand Down Expand Up @@ -170,6 +177,7 @@ export class EntityStoreDataClient {
filter: string,
pipelineDebugMode: boolean
) {
const setupStartTime = moment().utc().toISOString();
const { logger, namespace, appClient, dataViewsService } = this.options;
const indexPatterns = await buildIndexPatterns(namespace, appClient, dataViewsService);

Expand Down Expand Up @@ -249,12 +257,22 @@ export class EntityStoreDataClient {
});
logger.info(`Entity store initialized for ${entityType}`);

const setupEndTime = moment().utc().toISOString();
const duration = moment(setupEndTime).diff(moment(setupStartTime), 'seconds');
this.options.telemetry?.reportEvent(ENTITY_ENGINE_INITIALIZATION_EVENT.eventType, {
duration,
});

return updated;
} catch (err) {
this.options.logger.error(
`Error initializing entity store for ${entityType}: ${err.message}`
);

this.options.telemetry?.reportEvent(ENTITY_ENGINE_RESOURCE_INIT_FAILURE_EVENT.eventType, {
error: err.message,
});

await this.engineClient.update(entityType, ENGINE_STATUS.ERROR);

await this.delete(entityType, taskManager, { deleteData: true, deleteEngine: false });
Expand Down
Loading

0 comments on commit 67d96e3

Please sign in to comment.