Skip to content

Commit

Permalink
[ECO][Infra] Add callout for ingesting metrics data in Host and Conta…
Browse files Browse the repository at this point in the history
…iner views (elastic#195378)

## Summary

Closes elastic#193703

This PR introduces a callout designed to prompt users to ingest metrics
data in the Host and Container views.
The callout will be displayed on the following tabs:

- **Hosts**: Overview, Metrics, Processes
- **Containers**: Overview, Metrics

The primary condition for showing the callout is that the asset does not
currently have any metrics data available. This enhancement aims to
encourage users to take action and improve their monitoring experience.

Additional details include:

- The callout will be positioned below the date picker for better
visibility.
- Links for "Add Metrics" will guide users to the appropriate onboarding
pages based on their asset type.
- The callout will be dismissible on the Overview tab, and the KPI
section will be hidden in favor of the callout for a cleaner interface.
- Custom telemetry events will be tracked to measure user interactions
with the callout.
- Only Docker and K8 containers will show the callout.

**Host**
|Tab||
|-|-|
|Overview|![Screenshot 2024-10-08 at 12 19
22](https://github.com/user-attachments/assets/e357d6c6-2423-40f9-a513-361c642dc07c)|
|Metrics|![Screenshot 2024-10-08 at 12 19
31](https://github.com/user-attachments/assets/559a6e71-344a-4b4a-9ad6-8d229a1d9bcb)|
|Processes|![Screenshot 2024-10-08 at 12 19
39](https://github.com/user-attachments/assets/070f6fb1-0756-4b21-abce-4b395be943df)|

**Container**
|Tab||
|-|-|
|Overview|![Screenshot 2024-10-08 at 12 24
10](https://github.com/user-attachments/assets/101cfc7b-f445-44e7-9aa3-bec8928c3ed5)|
|Metrics|![Screenshot 2024-10-08 at 12 21
22](https://github.com/user-attachments/assets/d516d449-2af4-441f-9047-39c9362c5a86)|

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Caue Marcondes <[email protected]>
(cherry picked from commit 96966c5)
  • Loading branch information
iblancof committed Oct 14, 2024
1 parent 7aa0e2a commit 8d70612
Show file tree
Hide file tree
Showing 38 changed files with 1,508 additions and 226 deletions.
1 change: 1 addition & 0 deletions .buildkite/scripts/steps/storybooks/build_and_upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const STORYBOOKS = [
'lists',
'observability',
'observability_ai_assistant',
'observability_shared',
'presentation',
'security_solution',
'security_solution_packages',
Expand Down
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const storybookAliases = {
'x-pack/plugins/observability_solution/observability_ai_assistant/.storybook',
observability_ai_assistant_app:
'x-pack/plugins/observability_solution/observability_ai_assistant_app/.storybook',
observability_shared: 'x-pack/plugins/observability_solution/observability_shared/.storybook',
observability_slo: 'x-pack/plugins/observability_solution/slo/.storybook',
presentation: 'src/plugins/presentation_util/storybook',
random_sampling: 'x-pack/packages/kbn-random-sampling/.storybook',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import { Meta, Story } from '@storybook/react';
import React from 'react';
import { EntityDataStreamType } from '@kbn/observability-shared-plugin/common';
import { ServiceOverview } from '.';
import { MockApmPluginStorybook } from '../../../context/apm_plugin/mock_apm_plugin_storybook';
import { APMServiceContextValue } from '../../../context/apm_service/apm_service_context';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
import { mockApmApiCallResponse } from '../../../services/rest/call_apm_api_spy';
import { EntityDataStreamType } from '../../../../common/entities/types';

const stories: Meta<{}> = {
title: 'app/ServiceOverview',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { EntityDataStreamType } from '../../common/entities/types';
import { EntityDataStreamType } from '@kbn/observability-shared-plugin/common';

export function isApmSignal(dataStreamTypes: EntityDataStreamType[]) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/

import { compact, uniq } from 'lodash';
import { EntityDataStreamType } from '@kbn/observability-shared-plugin/common';
import type { EntityLatestServiceRaw } from '../types';
import type { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
import type { EntityDataStreamType } from '../../../../common/entities/types';

export interface MergedServiceEntity {
serviceName: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { ObservabilityOnboardingLocatorParams } from '@kbn/deeplinks-observability';
import { i18n } from '@kbn/i18n';
import { AddDataPanelProps } from '@kbn/observability-shared-plugin/public';
import { LocatorPublic } from '@kbn/share-plugin/common';
import { OnboardingFlow } from '../../shared/templates/no_data_config';

export type AddMetricsCalloutKey =
| 'hostOverview'
| 'hostMetrics'
| 'hostProcesses'
| 'containerOverview'
| 'containerMetrics';

const defaultPrimaryActionLabel = i18n.translate(
'xpack.infra.addDataCallout.hostOverviewPrimaryActionLabel',
{
defaultMessage: 'Add Metrics',
}
);

const defaultContent = {
content: {
title: i18n.translate('xpack.infra.addDataCallout.defaultTitle', {
defaultMessage: 'View core metrics to understand your host performance',
}),
content: i18n.translate('xpack.infra.addDataCallout.defaultContent', {
defaultMessage:
'Collect metrics such as CPU and memory usage to identify performance bottlenecks that could be affecting your users.',
}),
},
};

const hostDefaultActions = (
locator: LocatorPublic<ObservabilityOnboardingLocatorParams> | undefined
) => {
return {
actions: {
primary: {
href: locator?.getRedirectUrl({ category: OnboardingFlow.Hosts }),
label: defaultPrimaryActionLabel,
},
secondary: {
href: 'https://ela.st/demo-cluster-hosts',
},
link: {
href: 'https://ela.st/docs-hosts-add-metrics',
},
},
};
};

const containerDefaultActions = (
locator: LocatorPublic<ObservabilityOnboardingLocatorParams> | undefined
) => {
return {
actions: {
primary: {
href: locator?.getRedirectUrl({ category: OnboardingFlow.Infra }),
label: defaultPrimaryActionLabel,
},
link: {
href: 'https://ela.st/docs-containers-add-metrics',
},
},
};
};

export const addMetricsCalloutDefinitions = (
locator: LocatorPublic<ObservabilityOnboardingLocatorParams> | undefined
): Record<
AddMetricsCalloutKey,
Omit<AddDataPanelProps, 'onDismiss' | 'onAddData' | 'onLearnMore' | 'onTryIt'>
> => {
return {
hostOverview: {
...defaultContent,
...hostDefaultActions(locator),
},
hostMetrics: {
...defaultContent,
...hostDefaultActions(locator),
},
hostProcesses: {
content: {
title: i18n.translate('xpack.infra.addDataCallout.hostProcessesTitle', {
defaultMessage: 'View host processes to identify performance bottlenecks',
}),
content: i18n.translate('xpack.infra.addDataCallout.hostProcessesContent', {
defaultMessage:
'Collect process data to understand what is consuming resource on your hosts.',
}),
},
...hostDefaultActions(locator),
},
containerOverview: {
...defaultContent,
...containerDefaultActions(locator),
},
containerMetrics: {
...defaultContent,
...containerDefaultActions(locator),
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { AddDataPanel } from '@kbn/observability-shared-plugin/public';
import {
OBSERVABILITY_ONBOARDING_LOCATOR,
ObservabilityOnboardingLocatorParams,
} from '@kbn/deeplinks-observability';
import { AddMetricsCalloutEventParams } from '../../../services/telemetry';
import { addMetricsCalloutDefinitions, AddMetricsCalloutKey } from './constants';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';

export interface AddMetricsCalloutProps {
id: AddMetricsCalloutKey;
onDismiss?: () => void;
}

const defaultEventParams: AddMetricsCalloutEventParams = { view: 'add_metrics_cta' };

export function AddMetricsCallout({ id, onDismiss }: AddMetricsCalloutProps) {
const {
services: { telemetry, share },
} = useKibanaContextForPlugin();

const onboardingLocator = share.url.locators.get<ObservabilityOnboardingLocatorParams>(
OBSERVABILITY_ONBOARDING_LOCATOR
);

function handleAddMetricsClick() {
telemetry.reportAddMetricsCalloutAddMetricsClicked(defaultEventParams);
}

function handleTryItClick() {
telemetry.reportAddMetricsCalloutTryItClicked(defaultEventParams);
}

function handleLearnMoreClick() {
telemetry.reportAddMetricsCalloutLearnMoreClicked(defaultEventParams);
}

function handleDismiss() {
telemetry.reportAddMetricsCalloutDismissed(defaultEventParams);
onDismiss?.();
}

return (
<AddDataPanel
data-test-subj="infraAddMetricsCallout"
content={addMetricsCalloutDefinitions(onboardingLocator)[id].content}
actions={addMetricsCalloutDefinitions(onboardingLocator)[id].actions}
onAddData={handleAddMetricsClick}
onTryIt={handleTryItClick}
onLearnMore={handleLearnMoreClick}
onDissmiss={onDismiss && handleDismiss}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import * as z from '@kbn/zod';
import { EntityDataStreamType, EntityType } from '@kbn/observability-shared-plugin/common';
import { useFetcher } from '../../../hooks/use_fetcher';

const EntityTypeSchema = z.union([z.literal(EntityType.HOST), z.literal(EntityType.CONTAINER)]);
const EntityDataStreamSchema = z.union([
z.literal(EntityDataStreamType.METRICS),
z.literal(EntityDataStreamType.LOGS),
]);

const EntitySummarySchema = z.object({
entityType: EntityTypeSchema,
entityId: z.string(),
sourceDataStreams: z.array(EntityDataStreamSchema),
});

export type EntitySummary = z.infer<typeof EntitySummarySchema>;

export function useEntitySummary({
entityType,
entityId,
}: {
entityType: string;
entityId: string;
}) {
const { data, status } = useFetcher(
async (callApi) => {
if (!entityType || !entityId) {
return undefined;
}

const response = await callApi(`/api/infra/entities/${entityType}/${entityId}/summary`, {
method: 'GET',
});

return EntitySummarySchema.parse(response);
},
[entityType, entityId]
);

return { dataStreams: data?.sourceDataStreams ?? [], status };
}
Loading

0 comments on commit 8d70612

Please sign in to comment.