mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Observability Onboarding] Add telemetry events for logs onboarding flows (#180439)
Closes https://github.com/elastic/kibana/issues/179555 Closes https://github.com/elastic/kibana/issues/179786 Depends on: https://github.com/elastic/kibana/pull/180301 > [!NOTE] > The current flow in `main` is a bit broken and does not report the final completed status. Make sure to wait until the above PR is merged or cherry pick its commit before testing. ## Summary * Adds a new schema for the `observability_onboarding` event * Adds logic to trigger the event on the onboarding landing pages (old and new) * Adds logic to trigger the event during the system/custom logs flows when: user has downloaded the agent, when agent has reported it's status, in case of warning/errors and finally when the flow has been completed. ## How to test * Run run serverless Kibana localy * Set the new onboarding feature flag on or off depending on which one you want to test: ``` # kibana.dev.yml xpack.cloud_integrations.experiments.enabled: true xpack.cloud_integrations.experiments.flag_overrides: "observability_onboarding.experimental_onboarding_flow_enabled": true ``` * (Annoying workaround 🙈) In order to make Elastic Agent to communicate with ES over https, modify `outputs.default` configuration in `x-pack/plugins/observability_solution/observability_onboarding/common/elastic_agent_logs/system_logs/generate_system_logs_yml.ts` and `x-pack/plugins/observability_solution/observability_onboarding/common/elastic_agent_logs/custom_logs/generate_custom_logs_yml.ts` to use your local Kiabana SSL certificate: ``` outputs: { default: { ... ssl: { enabled: true, certificate_authorities: [ // Replace with you local path to Kibana repo '/Users/mykolaharmash/Developer/kibana/packages/kbn-dev-utils/certs/ca.crt', ], }, } } ``` * Go trough the onboarding flow and make sure you see `/kibana-browser` requests in the "Network" with the correct payload. --------- Co-authored-by: Thom Heymann <190132+thomheymann@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ce14d99f3b
commit
891358ff80
15 changed files with 299 additions and 20 deletions
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type EaInstallProgressStepId =
|
||||
| 'ea-download'
|
||||
| 'ea-extract'
|
||||
| 'ea-install'
|
||||
| 'ea-status'
|
||||
| 'ea-config';
|
||||
|
||||
export type LogsFlowProgressStepId = EaInstallProgressStepId | 'logs-ingest';
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { type EventTypeOpts } from '@kbn/analytics-client';
|
||||
|
||||
export const OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT: EventTypeOpts<{
|
||||
flow?: string;
|
||||
step?: string;
|
||||
step_status?: string;
|
||||
step_message?: string;
|
||||
uses_legacy_onboarding_page: boolean;
|
||||
}> = {
|
||||
eventType: 'observability_onboarding',
|
||||
schema: {
|
||||
flow: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description:
|
||||
"The current onboarding flow user is going through (e.g. 'system_logs', 'nginx'). If not present, user is on the landing screen.",
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
step: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'The current step in the onboarding flow.',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
step_status: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description: 'The status of the step in the onboarding flow.',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
step_message: {
|
||||
type: 'keyword',
|
||||
_meta: {
|
||||
description:
|
||||
'Error or warning message of the current step in the onboarding flow',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
uses_legacy_onboarding_page: {
|
||||
type: 'boolean',
|
||||
_meta: {
|
||||
description:
|
||||
'Whether the user is using the legacy onboarding page or the new one',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -24,6 +24,7 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
|
|||
import { euiDarkVars, euiLightVars } from '@kbn/ui-theme';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../common/telemetry_events';
|
||||
import { ConfigSchema } from '..';
|
||||
import { customLogsRoutes } from '../components/app/custom_logs';
|
||||
import { systemLogsRoutes } from '../components/app/system_logs';
|
||||
|
@ -36,6 +37,7 @@ import { baseRoutes, routes } from '../routes';
|
|||
import { CustomLogs } from '../routes/templates/custom_logs';
|
||||
import { SystemLogs } from '../routes/templates/system_logs';
|
||||
import { ExperimentalOnboardingFlow } from './experimental_onboarding_flow';
|
||||
import { ExperimentalOnboardingFeatureFlag } from '../context/experimental_onboarding_enabled';
|
||||
|
||||
export const onBoardingTitle = i18n.translate(
|
||||
'xpack.observability_onboarding.breadcrumbs.onboarding',
|
||||
|
@ -145,6 +147,13 @@ export function ObservabilityOnboardingAppRoot({
|
|||
|
||||
const renderFeedbackLinkAsPortal = !config.serverless.enabled;
|
||||
|
||||
core.analytics.reportEvent(
|
||||
OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType,
|
||||
{
|
||||
uses_legacy_onboarding_page: !experimentalOnboardingFlowEnabled,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={APP_WRAPPER_CLASS}>
|
||||
<RedirectAppLinks
|
||||
|
@ -181,11 +190,15 @@ export function ObservabilityOnboardingAppRoot({
|
|||
<ObservabilityOnboardingHeaderActionMenu />
|
||||
</HeaderMenuPortal>
|
||||
)}
|
||||
{experimentalOnboardingFlowEnabled ? (
|
||||
<ExperimentalOnboardingFlow />
|
||||
) : (
|
||||
<ObservabilityOnboardingApp />
|
||||
)}
|
||||
<ExperimentalOnboardingFeatureFlag.Provider
|
||||
value={experimentalOnboardingFlowEnabled}
|
||||
>
|
||||
{experimentalOnboardingFlowEnabled ? (
|
||||
<ExperimentalOnboardingFlow />
|
||||
) : (
|
||||
<ObservabilityOnboardingApp />
|
||||
)}
|
||||
</ExperimentalOnboardingFeatureFlag.Provider>
|
||||
</EuiErrorBoundary>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React from 'react';
|
||||
|
|
|
@ -35,6 +35,7 @@ import { StepModal } from '../shared/step_panel';
|
|||
import { ApiKeyBanner } from './api_key_banner';
|
||||
import { WindowsInstallStep } from '../shared/windows_install_step';
|
||||
import { TroubleshootingLink } from '../shared/troubleshooting_link';
|
||||
import { useFlowProgressTelemetry } from '../../../hooks/use_flow_progress_telemetry';
|
||||
|
||||
const defaultDatasetName = '';
|
||||
|
||||
|
@ -215,6 +216,8 @@ export function InstallElasticAgent() {
|
|||
}
|
||||
}, [progressSucceded, refetchProgress]);
|
||||
|
||||
useFlowProgressTelemetry(progressData?.progress, 'custom_logs');
|
||||
|
||||
const getCheckLogsStep = useCallback(() => {
|
||||
const progress = progressData?.progress;
|
||||
if (progress) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
SystemIntegrationBanner,
|
||||
SystemIntegrationBannerState,
|
||||
} from './system_integration_banner';
|
||||
import { useFlowProgressTelemetry } from '../../../hooks/use_flow_progress_telemetry';
|
||||
|
||||
export function InstallElasticAgent() {
|
||||
const {
|
||||
|
@ -174,6 +175,8 @@ export function InstallElasticAgent() {
|
|||
}
|
||||
}, [progressSucceded, refetchProgress]);
|
||||
|
||||
useFlowProgressTelemetry(progressData?.progress, 'system_logs');
|
||||
|
||||
const getCheckLogsStep = useCallback(() => {
|
||||
const progress = progressData?.progress;
|
||||
if (progress) {
|
||||
|
|
|
@ -19,6 +19,8 @@ import {
|
|||
SingleDatasetLocatorParams,
|
||||
SINGLE_DATASET_LOCATOR_ID,
|
||||
} from '@kbn/deeplinks-observability/locators';
|
||||
import { EaInstallProgressStepId } from '../../../../common/logs_flow_progress_step_id';
|
||||
import { useFlowProgressTelemetry } from '../../../hooks/use_flow_progress_telemetry';
|
||||
import { ObservabilityOnboardingPluginSetupDeps } from '../../../plugin';
|
||||
import { useWizard } from '.';
|
||||
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
|
||||
|
@ -28,7 +30,6 @@ import {
|
|||
} from '../../shared/get_elastic_agent_setup_command';
|
||||
import {
|
||||
InstallElasticAgentSteps,
|
||||
ProgressStepId,
|
||||
EuiStepStatus,
|
||||
} from '../../shared/install_elastic_agent_steps';
|
||||
import {
|
||||
|
@ -220,6 +221,8 @@ export function InstallElasticAgent() {
|
|||
}
|
||||
}, [progressSucceded, refetchProgress]);
|
||||
|
||||
useFlowProgressTelemetry(progressData?.progress, 'custom_logs');
|
||||
|
||||
const getCheckLogsStep = useCallback(() => {
|
||||
const progress = progressData?.progress;
|
||||
if (progress) {
|
||||
|
@ -372,7 +375,7 @@ export function InstallElasticAgent() {
|
|||
installProgressSteps={
|
||||
(progressData?.progress ?? {}) as Partial<
|
||||
Record<
|
||||
ProgressStepId,
|
||||
EaInstallProgressStepId,
|
||||
{ status: EuiStepStatus; message?: string }
|
||||
>
|
||||
>
|
||||
|
|
|
@ -22,6 +22,8 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { default as React, useCallback, useEffect, useState } from 'react';
|
||||
import { EaInstallProgressStepId } from '../../../../common/logs_flow_progress_step_id';
|
||||
import { useFlowProgressTelemetry } from '../../../hooks/use_flow_progress_telemetry';
|
||||
import { useWizard } from '.';
|
||||
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
|
||||
import { ObservabilityOnboardingPluginSetupDeps } from '../../../plugin';
|
||||
|
@ -33,7 +35,6 @@ import {
|
|||
import {
|
||||
EuiStepStatus,
|
||||
InstallElasticAgentSteps,
|
||||
ProgressStepId,
|
||||
} from '../../shared/install_elastic_agent_steps';
|
||||
import {
|
||||
StepPanel,
|
||||
|
@ -186,6 +187,8 @@ export function InstallElasticAgent() {
|
|||
}
|
||||
}, [progressSucceded, refetchProgress]);
|
||||
|
||||
useFlowProgressTelemetry(progressData?.progress, 'system_logs');
|
||||
|
||||
const getCheckLogsStep = useCallback(() => {
|
||||
const progress = progressData?.progress;
|
||||
if (progress) {
|
||||
|
@ -326,7 +329,7 @@ export function InstallElasticAgent() {
|
|||
installProgressSteps={
|
||||
(progressData?.progress ?? {}) as Partial<
|
||||
Record<
|
||||
ProgressStepId,
|
||||
EaInstallProgressStepId,
|
||||
{ status: EuiStepStatus; message?: string }
|
||||
>
|
||||
>
|
||||
|
|
|
@ -26,17 +26,11 @@ import { Buffer } from 'buffer';
|
|||
import React, { ReactNode } from 'react';
|
||||
import { intersection } from 'lodash';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EaInstallProgressStepId } from '../../../common/logs_flow_progress_step_id';
|
||||
import { StepStatus } from './step_status';
|
||||
|
||||
export type EuiStepStatus = EuiStepsProps['steps'][number]['status'];
|
||||
|
||||
export type ProgressStepId =
|
||||
| 'ea-download'
|
||||
| 'ea-extract'
|
||||
| 'ea-install'
|
||||
| 'ea-status'
|
||||
| 'ea-config';
|
||||
|
||||
interface Props<PlatformId extends string> {
|
||||
installAgentPlatformOptions: Array<{
|
||||
label: string;
|
||||
|
@ -53,7 +47,7 @@ interface Props<PlatformId extends string> {
|
|||
installAgentStatus: EuiStepStatus;
|
||||
showInstallProgressSteps: boolean;
|
||||
installProgressSteps: Partial<
|
||||
Record<ProgressStepId, { status: EuiStepStatus; message?: string }>
|
||||
Record<EaInstallProgressStepId, { status: EuiStepStatus; message?: string }>
|
||||
>;
|
||||
configureAgentStatus: EuiStepStatus;
|
||||
configureAgentYaml: string;
|
||||
|
@ -343,7 +337,7 @@ export function InstallElasticAgentSteps<PlatformId extends string>({
|
|||
}
|
||||
|
||||
function getStep(
|
||||
id: ProgressStepId,
|
||||
id: EaInstallProgressStepId,
|
||||
installProgressSteps: Props<string>['installProgressSteps'],
|
||||
configPath: string
|
||||
): { title: string; status: EuiStepStatus; message?: string } {
|
||||
|
@ -374,7 +368,7 @@ function getStep(
|
|||
const PROGRESS_STEP_TITLES: (
|
||||
configPath: string
|
||||
) => Record<
|
||||
ProgressStepId,
|
||||
EaInstallProgressStepId,
|
||||
Record<'incompleteTitle' | 'loadingTitle' | 'completedTitle', string>
|
||||
> = (configPath: string) => ({
|
||||
'ea-download': {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 { createContext } from 'react';
|
||||
|
||||
export const ExperimentalOnboardingFeatureFlag = createContext(false);
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { useContext } from 'react';
|
||||
import { ExperimentalOnboardingFeatureFlag } from '../context/experimental_onboarding_enabled';
|
||||
|
||||
export const useExperimentalOnboardingFlag = () => {
|
||||
return useContext(ExperimentalOnboardingFeatureFlag);
|
||||
};
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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 { renderHook } from '@testing-library/react-hooks';
|
||||
import { useFlowProgressTelemetry } from './use_flow_progress_telemetry';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
jest.mock('./use_kibana', () => {
|
||||
return {
|
||||
useKibana: jest.fn().mockReturnValue({
|
||||
...jest.requireActual('./use_kibana'),
|
||||
services: {
|
||||
analytics: { reportEvent: jest.fn() },
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('useFlowProgressTelemetry', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('does not trigger an event if there is no progress', () => {
|
||||
const render = renderHook(() => ({
|
||||
analytics: useKibana().services.analytics,
|
||||
flowProgress: useFlowProgressTelemetry(undefined, 'test-flow'),
|
||||
}));
|
||||
|
||||
expect(render.result.current.analytics.reportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('triggers an event when there is a progress change', () => {
|
||||
const render = renderHook(() => ({
|
||||
analytics: useKibana().services.analytics,
|
||||
flowProgress: useFlowProgressTelemetry(
|
||||
{ 'ea-download': { status: 'complete' } },
|
||||
'test-flow'
|
||||
),
|
||||
}));
|
||||
|
||||
expect(render.result.current.analytics.reportEvent).toHaveBeenCalledTimes(
|
||||
1
|
||||
);
|
||||
expect(render.result.current.analytics.reportEvent).toHaveBeenCalledWith(
|
||||
'observability_onboarding',
|
||||
{
|
||||
uses_legacy_onboarding_page: true,
|
||||
flow: 'test-flow',
|
||||
step: 'ea-download',
|
||||
step_status: 'complete',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('does not trigger an event for unsupported steps', () => {
|
||||
const render = renderHook(() => ({
|
||||
analytics: useKibana().services.analytics,
|
||||
flowProgress: useFlowProgressTelemetry(
|
||||
{ 'ea-extract': { status: 'complete' } },
|
||||
'test-flow'
|
||||
),
|
||||
}));
|
||||
|
||||
expect(render.result.current.analytics.reportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not trigger an event if the status of a step has not changed', () => {
|
||||
const render = renderHook(() => ({
|
||||
analytics: useKibana().services.analytics,
|
||||
flowProgress: useFlowProgressTelemetry(
|
||||
{ 'ea-download': { status: 'complete' } },
|
||||
'test-flow'
|
||||
),
|
||||
}));
|
||||
|
||||
render.rerender();
|
||||
|
||||
expect(render.result.current.analytics.reportEvent).toHaveBeenCalledTimes(
|
||||
1
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 { useEffect, useState } from 'react';
|
||||
import { type LogsFlowProgressStepId } from '../../common/logs_flow_progress_step_id';
|
||||
import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../common/telemetry_events';
|
||||
import { type EuiStepStatus } from '../components/shared/install_elastic_agent_steps';
|
||||
import { useExperimentalOnboardingFlag } from './use_experimental_onboarding_flag';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
type StepsProgress = Partial<
|
||||
Record<LogsFlowProgressStepId, { status: EuiStepStatus; message?: string }>
|
||||
>;
|
||||
|
||||
const TRACKED_STEPS: LogsFlowProgressStepId[] = [
|
||||
'ea-download',
|
||||
'ea-status',
|
||||
'logs-ingest',
|
||||
];
|
||||
const TRACKED_STATUSES: EuiStepStatus[] = ['danger', 'warning', 'complete'];
|
||||
|
||||
export function useFlowProgressTelemetry(
|
||||
progress: StepsProgress | undefined,
|
||||
flowId: string
|
||||
) {
|
||||
const {
|
||||
services: { analytics },
|
||||
} = useKibana();
|
||||
const experimentalOnboardingFlowEnabled = useExperimentalOnboardingFlag();
|
||||
const [previousReportedSteps] = useState<
|
||||
Map<LogsFlowProgressStepId, EuiStepStatus>
|
||||
>(new Map());
|
||||
|
||||
useEffect(() => {
|
||||
if (!progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
TRACKED_STEPS.forEach((stepId) => {
|
||||
const step = progress[stepId];
|
||||
|
||||
if (
|
||||
!step ||
|
||||
!TRACKED_STATUSES.includes(step.status) ||
|
||||
previousReportedSteps.get(stepId) === step.status
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
analytics.reportEvent(
|
||||
OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType,
|
||||
{
|
||||
uses_legacy_onboarding_page: !experimentalOnboardingFlowEnabled,
|
||||
flow: flowId,
|
||||
step: stepId,
|
||||
step_status: step.status,
|
||||
step_message: step.message,
|
||||
}
|
||||
);
|
||||
previousReportedSteps.set(stepId, step.status);
|
||||
});
|
||||
}, [
|
||||
analytics,
|
||||
experimentalOnboardingFlowEnabled,
|
||||
flowId,
|
||||
progress,
|
||||
previousReportedSteps,
|
||||
]);
|
||||
}
|
|
@ -29,6 +29,7 @@ import { PLUGIN_ID } from '../common';
|
|||
import { ObservabilityOnboardingLocatorDefinition } from './locators/onboarding_locator/locator_definition';
|
||||
import { ObservabilityOnboardingPluginLocators } from './locators';
|
||||
import { ConfigSchema } from '.';
|
||||
import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../common/telemetry_events';
|
||||
|
||||
export type ObservabilityOnboardingPluginSetup = void;
|
||||
export type ObservabilityOnboardingPluginStart = void;
|
||||
|
@ -164,6 +165,8 @@ export class ObservabilityOnboardingPlugin
|
|||
),
|
||||
};
|
||||
|
||||
core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT);
|
||||
|
||||
return {
|
||||
locators: this.locators,
|
||||
};
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
"@kbn/deeplinks-observability",
|
||||
"@kbn/fleet-plugin",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/cloud-experiments-plugin"
|
||||
"@kbn/cloud-experiments-plugin",
|
||||
"@kbn/analytics-client"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue