[Infra UI] Asset detail view telemetry (#166151)

Closes #156698

## Summary

This PR adds a new custom event to track asset details page views. The
event will have a parameter `integrations` which will show if `nginx` or
`kubernetes` integrations are enabled ( so extra charts will be
displayed in those cases) and the same parameters as the existing flyout
event.

## Testing 

- Open hosts view flyout:
- The `Asset Details Flyout Viewed` Event type should be tracked (same
as before)
   - Event properties in this example (same as before)
   ```
   "properties":{
       "componentName":"infraAssetDetailsFlyout",
       "assetType":"host",
       "tabId":"overview"
     }
   ```

![Image](7ab85302-1aed-487c-860c-b0a62ef06a70)

- Open asset details page for a host:
   - The `Asset Details Pade Viewed` Event type should be tracked
- The event properties should show the current enabled integrations
('nginx', 'kubernetes') if there are no integrations enabled the
integrations should not be visible in the event so in this example:
   ```
    "properties": {
       "componentName": "infraAssetDetailsPage",
       "assetType": "host",
       "tabId": "overview",
       "integrations": [
           "nginx"
       ]
   }
   ```

![Image](bc657298-cb14-4ac2-ba46-ac42e3bd3e37)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
jennypavlova 2023-09-13 10:13:32 +02:00 committed by GitHub
parent df08786c98
commit 39741a23c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 150 additions and 8 deletions

View file

@ -5,8 +5,17 @@
* 2.0.
*/
import { INTEGRATION_NAME } from './types';
export const ASSET_DETAILS_FLYOUT_COMPONENT_NAME = 'infraAssetDetailsFlyout';
export const ASSET_DETAILS_PAGE_COMPONENT_NAME = 'infraAssetDetailsPage';
export const METRIC_CHART_HEIGHT = 300;
export const APM_HOST_FILTER_FIELD = 'host.hostname';
export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails';
export const INTEGRATIONS = {
[INTEGRATION_NAME.nginx]: ['nginx.stubstatus', 'nginx.access'],
[INTEGRATION_NAME.kubernetes]: ['kubernetes.node'],
};

View file

@ -8,18 +8,55 @@
import { EuiFlexGroup, EuiPageTemplate } from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import React from 'react';
import React, { useEffect } from 'react';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { useKibanaHeader } from '../../../hooks/use_kibana_header';
import { InfraLoadingPanel } from '../../loading';
import { ASSET_DETAILS_PAGE_COMPONENT_NAME } from '../constants';
import { Content } from '../content/content';
import { useAssetDetailsRenderPropsContext } from '../hooks/use_asset_details_render_props';
import { useMetadataStateProviderContext } from '../hooks/use_metadata_state';
import { usePageHeader } from '../hooks/use_page_header';
import type { ContentTemplateProps } from '../types';
import { useTabSwitcherContext } from '../hooks/use_tab_switcher';
import { ContentTemplateProps } from '../types';
import { getIntegrationsAvailable } from '../utils';
export const Page = ({ header: { tabs = [], links = [] } }: ContentTemplateProps) => {
const { asset, loading } = useAssetDetailsRenderPropsContext();
const { loading } = useAssetDetailsRenderPropsContext();
const { metadata, loading: metadataLoading } = useMetadataStateProviderContext();
const { rightSideItems, tabEntries, breadcrumbs } = usePageHeader(tabs, links);
const { asset, assetType } = useAssetDetailsRenderPropsContext();
const { headerHeight } = useKibanaHeader();
const trackOnlyOnce = React.useRef(false);
const { activeTabId } = useTabSwitcherContext();
const {
services: { telemetry },
} = useKibanaContextForPlugin();
useEffect(() => {
if (trackOnlyOnce.current) {
return;
}
if (!metadataLoading && metadata) {
const integrations = getIntegrationsAvailable(metadata);
const telemetryParams = {
componentName: ASSET_DETAILS_PAGE_COMPONENT_NAME,
assetType,
tabId: activeTabId,
};
telemetry.reportAssetDetailsPageViewed(
integrations.length > 0
? {
...telemetryParams,
integrations,
}
: telemetryParams
);
trackOnlyOnce.current = true;
}
}, [activeTabId, assetType, metadata, metadataLoading, telemetry]);
return loading ? (
<EuiFlexGroup
@ -43,6 +80,8 @@ export const Page = ({ header: { tabs = [], links = [] } }: ContentTemplateProps
offset={0}
restrictWidth={false}
style={{ minBlockSize: `calc(100vh - ${headerHeight}px)` }}
data-component-name={ASSET_DETAILS_PAGE_COMPONENT_NAME}
data-asset-type={assetType}
>
<EuiPageTemplate.Section paddingSize="none">
<EuiPageTemplate.Header

View file

@ -88,3 +88,8 @@ export interface RouteState {
}
export type DataViewOrigin = 'logs' | 'metrics';
export enum INTEGRATION_NAME {
nginx = 'nginx',
kubernetes = 'kubernetes',
}

View file

@ -5,6 +5,9 @@
* 2.0.
*/
import type { InfraMetadata } from '../../../common/http_api';
import { INTEGRATIONS } from './constants';
export const toTimestampRange = ({ from, to }: { from: string; to: string }) => {
const fromTs = new Date(from).getTime();
const toTs = new Date(to).getTime();
@ -21,3 +24,13 @@ export const getDefaultDateRange = () => {
to: new Date(now).toISOString(),
};
};
export const getIntegrationsAvailable = (metadata?: InfraMetadata | null) => {
if (!metadata) {
return [];
}
return Object.entries(INTEGRATIONS)
.filter(([_, fields]) => metadata?.features?.some((f) => fields.includes(f.name)))
.map(([name]) => name);
};

View file

@ -14,4 +14,5 @@ export const createTelemetryClientMock = (): jest.Mocked<ITelemetryClient> => ({
reportHostFlyoutFilterAdded: jest.fn(),
reportHostsViewTotalHostCountRetrieved: jest.fn(),
reportAssetDetailsFlyoutViewed: jest.fn(),
reportAssetDetailsPageViewed: jest.fn(),
});

View file

@ -5,9 +5,10 @@
* 2.0.
*/
import { AnalyticsServiceSetup } from '@kbn/core-analytics-server';
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server';
import {
AssetDetailsFlyoutViewedParams,
AssetDetailsPageViewedParams,
HostEntryClickedParams,
HostFlyoutFilterActionParams,
HostsViewQueryHostsCountRetrievedParams,
@ -65,4 +66,8 @@ export class TelemetryClient implements ITelemetryClient {
public reportAssetDetailsFlyoutViewed = (params: AssetDetailsFlyoutViewedParams) => {
this.analytics.reportEvent(InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED, params);
};
public reportAssetDetailsPageViewed = (params: AssetDetailsPageViewedParams) => {
this.analytics.reportEvent(InfraTelemetryEventTypes.ASSET_DETAILS_PAGE_VIEWED, params);
};
}

View file

@ -118,21 +118,55 @@ const assetDetailsFlyoutViewed: InfraTelemetryEvent = {
componentName: {
type: 'keyword',
_meta: {
description: 'Hostname for the clicked host.',
description: 'Name of the parent react component for the clicked asset.',
optional: false,
},
},
assetType: {
type: 'keyword',
_meta: {
description: 'Cloud provider for the clicked host.',
description: 'Asset type for the clicked asset.',
optional: false,
},
},
tabId: {
type: 'keyword',
_meta: {
description: 'Cloud provider for the clicked host.',
description: 'Tab id for the clicked asset.',
optional: true,
},
},
},
};
const assetDetailsPageViewed: InfraTelemetryEvent = {
eventType: InfraTelemetryEventTypes.ASSET_DETAILS_PAGE_VIEWED,
schema: {
componentName: {
type: 'keyword',
_meta: {
description: 'Name of the parent react component for the clicked asset.',
optional: false,
},
},
assetType: {
type: 'keyword',
_meta: {
description: 'Asset type for the clicked asset.',
optional: false,
},
},
tabId: {
type: 'keyword',
_meta: {
description: 'Tab id for the clicked asset.',
optional: true,
},
},
integrations: {
type: 'pass_through',
_meta: {
description: 'Integrations enabled for the displayed asset.',
optional: true,
},
},
@ -141,6 +175,7 @@ const assetDetailsFlyoutViewed: InfraTelemetryEvent = {
export const infraTelemetryEvents = [
assetDetailsFlyoutViewed,
assetDetailsPageViewed,
hostsViewQuerySubmittedEvent,
hostsEntryClickedEvent,
hostFlyoutRemoveFilter,

View file

@ -185,7 +185,7 @@ describe('TelemetryService', () => {
});
describe('#reportAssetDetailsFlyoutViewed', () => {
it('should report asset details viewed with properties', async () => {
it('should report asset details viewed in flyout with properties', async () => {
const setupParams = getSetupParams();
service.setup(setupParams);
const telemetry = service.start();
@ -207,4 +207,30 @@ describe('TelemetryService', () => {
);
});
});
describe('#reportAssetDetailsPageViewed', () => {
it('should report asset details viewed in full page with properties', async () => {
const setupParams = getSetupParams();
service.setup(setupParams);
const telemetry = service.start();
telemetry.reportAssetDetailsPageViewed({
componentName: 'infraAssetDetailsPage',
assetType: 'host',
tabId: 'overview',
integrations: ['nginx'],
});
expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1);
expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith(
InfraTelemetryEventTypes.ASSET_DETAILS_PAGE_VIEWED,
{
componentName: 'infraAssetDetailsPage',
assetType: 'host',
tabId: 'overview',
integrations: ['nginx'],
}
);
});
});
});

View file

@ -19,6 +19,7 @@ export enum InfraTelemetryEventTypes {
HOST_FLYOUT_FILTER_ADDED = 'Host Flyout Filter Added',
HOST_VIEW_TOTAL_HOST_COUNT_RETRIEVED = 'Host View Total Host Count Retrieved',
ASSET_DETAILS_FLYOUT_VIEWED = 'Asset Details Flyout Viewed',
ASSET_DETAILS_PAGE_VIEWED = 'Asset Details Page Viewed',
}
export interface HostsViewQuerySubmittedParams {
@ -47,6 +48,9 @@ export interface AssetDetailsFlyoutViewedParams {
componentName: string;
tabId?: string;
}
export interface AssetDetailsPageViewedParams extends AssetDetailsFlyoutViewedParams {
integrations?: string[];
}
export type InfraTelemetryEventParams =
| HostsViewQuerySubmittedParams
@ -62,6 +66,7 @@ export interface ITelemetryClient {
reportHostsViewTotalHostCountRetrieved(params: HostsViewQueryHostsCountRetrievedParams): void;
reportHostsViewQuerySubmitted(params: HostsViewQuerySubmittedParams): void;
reportAssetDetailsFlyoutViewed(params: AssetDetailsFlyoutViewedParams): void;
reportAssetDetailsPageViewed(params: AssetDetailsPageViewedParams): void;
}
export type InfraTelemetryEvent =
@ -88,4 +93,8 @@ export type InfraTelemetryEvent =
| {
eventType: InfraTelemetryEventTypes.ASSET_DETAILS_FLYOUT_VIEWED;
schema: RootSchema<AssetDetailsFlyoutViewedParams>;
}
| {
eventType: InfraTelemetryEventTypes.ASSET_DETAILS_PAGE_VIEWED;
schema: RootSchema<AssetDetailsPageViewedParams>;
};