diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/common/telemetry_events.ts b/x-pack/solutions/observability/plugins/observability_onboarding/common/telemetry_events.ts index ca84d0a6f0379..26b5aefbad5c2 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/common/telemetry_events.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/common/telemetry_events.ts @@ -7,19 +7,23 @@ import type { EventTypeOpts, SchemaValue } from '@elastic/ebt/client'; -interface ObservabilityOnboardingIntegrationTelemetryFields { - installSource: string; - pkgName: string; - pkgVersion: string; - title: string; -} - interface FlowEventFields { - flow?: string; - step?: string; - step_status?: string; + flow_type: string; + flow_id: string; + step: string; + step_status: string; step_message?: string; - uses_legacy_onboarding_page: boolean; + payload?: { + integrations?: Array<{ + installSource: string; + pkgName: string; + pkgVersion: string; + title: string; + }>; + agentId?: string; + os?: string; + arch?: string; + }; } type ObservabilityOnboardingTelemetryEvent = EventTypeOpts; @@ -27,26 +31,29 @@ type ObservabilityOnboardingTelemetryEvent = EventTypeOpts; export const OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT: ObservabilityOnboardingTelemetryEvent = { eventType: 'observability_onboarding', schema: { - flow: { + flow_type: { 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, + "The current onboarding flow type user is going through (e.g. 'autoDetect', 'logFiles', 'kubernetes'). If not present, user is on the landing screen.", + }, + }, + flow_id: { + type: 'keyword', + _meta: { + description: 'Unique identifier of the current onboarding session', }, }, 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: { @@ -56,10 +63,62 @@ export const OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT: ObservabilityOnboardingTe optional: true, }, }, - uses_legacy_onboarding_page: { - type: 'boolean', + payload: { + properties: { + integrations: { + type: 'array', + items: { + properties: { + installSource: { + type: 'keyword', + _meta: { + description: + 'The source of the package used to create the integration. Usually "registry" or "custom".', + }, + }, + pkgName: { + type: 'keyword', + _meta: { + description: 'The name of the package used to create the integration.', + }, + }, + pkgVersion: { + type: 'keyword', + _meta: { + description: 'The version of the package used to create the integration.', + }, + }, + title: { type: 'keyword', _meta: { description: 'The visual name of the package.' } }, + }, + }, + _meta: { + optional: true, + }, + }, + agentId: { + type: 'keyword', + _meta: { + description: 'The ID of the Elastic Agent installed on the host.', + optional: true, + }, + }, + os: { + type: 'keyword', + _meta: { + description: 'OS used by the host.', + optional: true, + }, + }, + arch: { + type: 'keyword', + _meta: { + description: 'Architecture used by the host.', + optional: true, + }, + }, + }, _meta: { - description: 'Whether the user is using the legacy onboarding page or the new one', + optional: true, }, }, }, @@ -87,48 +146,6 @@ export const OBSERVABILITY_ONBOARDING_FEEDBACK_TELEMETRY_EVENT: EventTypeOpts<{ }, }; -type ObservabilityOnboardingAutodetectTelemetryEvent = EventTypeOpts< - FlowEventFields & { - integrations?: ObservabilityOnboardingIntegrationTelemetryFields[]; - } ->; - -export const OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT: ObservabilityOnboardingAutodetectTelemetryEvent = - { - eventType: 'observability_onboarding_autodetect', - schema: { - ...OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.schema, - integrations: { - type: 'array', - items: { - properties: { - installSource: { - type: 'keyword', - _meta: { - description: - 'The source of the package used to create the integration. Usually "registry" or "custom".', - }, - }, - pkgName: { - type: 'keyword', - _meta: { - description: 'The name of the package used to create the integration.', - }, - }, - pkgVersion: { - type: 'keyword', - _meta: { description: 'The version of the package used to create the integration.' }, - }, - title: { type: 'keyword', _meta: { description: 'The visual name of the package.' } }, - }, - }, - _meta: { - optional: true, - }, - }, - }, - }; - interface OnboardingFirehoseFlowEventContext { selectedCreateStackOption?: string; cloudServiceProvider?: string; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/app.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/app.tsx index 688d4cc3ce9bb..c2163bda2f2da 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/app.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/app.tsx @@ -15,7 +15,6 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Router } from '@kbn/shared-ux-router'; import React from 'react'; import ReactDOM from 'react-dom'; -import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../common/telemetry_events'; import { AppContext, ConfigSchema, ObservabilityOnboardingAppServices } from '..'; import { ObservabilityOnboardingHeaderActionMenu } from './shared/header_action_menu'; import { @@ -53,10 +52,6 @@ export function ObservabilityOnboardingAppRoot({ context, }; - core.analytics.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { - uses_legacy_onboarding_page: false, - }); - return (
diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx index 47282ba9b8d69..6b5fc0e4eaf17 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx @@ -32,7 +32,6 @@ import { GetStartedPanel } from '../shared/get_started_panel'; import { isSupportedLogo, LogoIcon } from '../../shared/logo_icon'; import { FeedbackButtons } from '../shared/feedback_buttons'; import { ObservabilityOnboardingContextValue } from '../../../plugin'; -import { useAutoDetectTelemetry } from './use_auto_detect_telemetry'; import { SupportedIntegrationsList } from './supported_integrations_list'; export const AutoDetectPanel: FunctionComponent = () => { @@ -43,16 +42,6 @@ export const AutoDetectPanel: FunctionComponent = () => { services: { share }, } = useKibana(); - useAutoDetectTelemetry( - status, - installedIntegrations.map(({ title, pkgName, pkgVersion, installSource }) => ({ - title, - pkgName, - pkgVersion, - installSource, - })) - ); - if (error) { return ; } diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.test.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.test.ts deleted file mode 100644 index f77db3b8aad3d..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { useAutoDetectTelemetry } from './use_auto_detect_telemetry'; -import { ObservabilityOnboardingFlowStatus } from './get_onboarding_status'; -import { OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT } from '../../../../common/telemetry_events'; - -jest.mock('@kbn/kibana-react-plugin/public', () => ({ - useKibana: jest.fn(), -})); - -describe('useAutoDetectTelemetry', () => { - let reportEventMock: any; - - beforeEach(() => { - reportEventMock = jest.fn(); - (useKibana as jest.Mock).mockReturnValue({ - services: { - analytics: { - reportEvent: reportEventMock, - }, - }, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it(`should report "awaiting_data" event when status is "awaitingData"`, () => { - const expectedIntegration = { - installSource: 'source1', - pkgName: 'pkgName1', - pkgVersion: 'pkgVersion1', - title: 'title', - }; - renderHook(() => useAutoDetectTelemetry('awaitingData', [expectedIntegration])); - - expect(reportEventMock).toHaveBeenCalledWith( - OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, - { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'awaiting_data', - integrations: [expectedIntegration], - } - ); - }); - - it(`should report "data_shipped" event when status is "dataReceived"`, () => { - const expectedIntegration = { - installSource: 'source2', - pkgName: 'pkgName2', - pkgVersion: 'pkgVersion2', - title: 'title2', - }; - renderHook(() => useAutoDetectTelemetry('dataReceived', [expectedIntegration])); - - // The effect runs after initial render - expect(reportEventMock).toHaveBeenCalledWith( - OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, - { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'data_shipped', - integrations: [expectedIntegration], - } - ); - }); - - it('should not report the same event more than once', () => { - const expectedIntegration = { - installSource: 'source1', - pkgName: 'pkgName1', - pkgVersion: 'pkgVersion1', - title: 'title', - }; - const { rerender } = renderHook( - ({ status }: { status: ObservabilityOnboardingFlowStatus }) => - useAutoDetectTelemetry(status, [expectedIntegration]), - { initialProps: { status: 'awaitingData' } } - ); - - expect(reportEventMock).toHaveBeenCalledWith( - OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, - { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'awaiting_data', - integrations: [expectedIntegration], - } - ); - - rerender({ status: 'awaitingData' }); - expect(reportEventMock).toHaveBeenCalledTimes(1); - - rerender({ status: 'dataReceived' }); - expect(reportEventMock).toHaveBeenCalledWith( - OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, - { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'data_shipped', - integrations: [expectedIntegration], - } - ); - expect(reportEventMock).toHaveBeenCalledTimes(2); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.ts deleted file mode 100644 index 8fb4df118ebaf..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_auto_detect_telemetry.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ObservabilityOnboardingFlowStatus } from './get_onboarding_status'; -import { OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT } from '../../../../common/telemetry_events'; - -interface IntegrationFields { - installSource: string; - pkgName: string; - pkgVersion: string; - title: string; -} - -export function useAutoDetectTelemetry( - status: ObservabilityOnboardingFlowStatus, - integrations: IntegrationFields[] -) { - const [waitingMessageSent, setWaitingMessageSent] = useState(false); - const [dataShippedMessageSent, setDataShippedMessageSent] = useState(false); - const { - services: { analytics }, - } = useKibana(); - - useEffect(() => { - if (status === 'awaitingData' && !waitingMessageSent) { - analytics?.reportEvent(OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'awaiting_data', - integrations, - }); - setWaitingMessageSent(true); - } - if (status === 'dataReceived' && !dataShippedMessageSent) { - analytics?.reportEvent(OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT.eventType, { - uses_legacy_onboarding_page: false, - flow: 'auto_detect', - step: 'data_shipped', - integrations, - }); - setDataShippedMessageSent(true); - } - }, [analytics, dataShippedMessageSent, integrations, status, waitingMessageSent]); -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_onboarding_flow.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_onboarding_flow.tsx index 7434141a76d35..c6aae1f3e959e 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_onboarding_flow.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/auto_detect/use_onboarding_flow.tsx @@ -9,6 +9,8 @@ import useInterval from 'react-use/lib/useInterval'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import useAsync from 'react-use/lib/useAsync'; import { type AssetSOObject, type GetBulkAssetsResponse } from '@kbn/fleet-plugin/common'; +import { useEffect, useState } from 'react'; +import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../../../common/telemetry_events'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { getOnboardingStatus } from './get_onboarding_status'; import { getInstalledIntegrations } from './get_installed_integrations'; @@ -32,8 +34,9 @@ export const DASHBOARDS = { export function useOnboardingFlow() { const { - services: { fleet }, + services: { fleet, analytics }, } = useKibana(); + const [dataReceivedTelemetrySent, setDataReceivedTelemetrySent] = useState(false); // Create onboarding session const { data, error, refetch } = useFetcher( @@ -105,6 +108,18 @@ export function useOnboardingFlow() { progressStatus === FETCH_STATUS.SUCCESS && status !== 'dataReceived' ? 3000 : null ); + useEffect(() => { + if (status === 'dataReceived' && !dataReceivedTelemetrySent) { + setDataReceivedTelemetrySent(true); + analytics.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { + flow_type: 'autoDetect', + flow_id: onboardingId, + step: 'logs-ingest', + step_status: 'complete', + }); + } + }, [analytics, dataReceivedTelemetrySent, onboardingId, status]); + return { data, error, diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/custom_logs/install_elastic_agent.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/custom_logs/install_elastic_agent.tsx index e297bd2076536..ae07e39cf7c8d 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/custom_logs/install_elastic_agent.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/quickstart_flows/custom_logs/install_elastic_agent.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { default as React, useCallback, useEffect, useState } from 'react'; import { type LogsLocatorParams, LOGS_LOCATOR_ID } from '@kbn/logs-shared-plugin/common'; +import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../../../common/telemetry_events'; import { ObservabilityOnboardingPluginSetupDeps } from '../../../plugin'; import { useWizard } from '.'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; @@ -26,14 +27,14 @@ 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 = ''; export function InstallElasticAgent() { const { - services: { share }, + services: { share, analytics }, } = useKibana(); + const [dataReceivedTelemetrySent, setDataReceivedTelemetrySent] = useState(false); const logsLocator = share.url.locators.get(LOGS_LOCATOR_ID); @@ -181,8 +182,6 @@ export function InstallElasticAgent() { } }, [progressSucceded, refetchProgress]); - useFlowProgressTelemetry(progressData?.progress, 'custom_logs'); - const getCheckLogsStep = useCallback(() => { const progress = progressData?.progress; if (progress) { @@ -209,6 +208,19 @@ export function InstallElasticAgent() { const isInstallCompleted = progressData?.progress?.['ea-status']?.status === 'complete'; const autoDownloadConfigStatus = (progressData?.progress?.['ea-config']?.status ?? 'incomplete') as EuiStepStatus; + const isIngestCompleted = progressData?.progress?.['logs-ingest']?.status === 'complete'; + + useEffect(() => { + if (isIngestCompleted && !dataReceivedTelemetrySent) { + setDataReceivedTelemetrySent(true); + analytics?.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { + flow_type: 'logFiles', + flow_id: onboardingId, + step: 'logs-ingest', + step_status: 'complete', + }); + } + }, [analytics, dataReceivedTelemetrySent, isIngestCompleted, onboardingId]); return ( (undefined); const [checkDataStartTime] = useState(Date.now()); + const [dataReceivedTelemetrySent, setDataReceivedTelemetrySent] = useState(false); const { - services: { share }, + services: { share, analytics }, } = useKibana(); const dashboardLocator = share.url.locators.get(DASHBOARD_APP_LOCATOR); @@ -61,12 +58,16 @@ export function DataIngestStatus({ onboardingId }: Props) { }, [data?.hasData, refetch, status]); useEffect(() => { - if (data?.hasData === true) { - setProgress({ 'logs-ingest': { status: 'complete' } }); + if (data?.hasData === true && !dataReceivedTelemetrySent) { + setDataReceivedTelemetrySent(true); + analytics.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { + flow_type: 'kubernetes', + flow_id: onboardingId, + step: 'logs-ingest', + step_status: 'complete', + }); } - }, [data?.hasData]); - - useFlowProgressTelemetry(progress, onboardingId); + }, [analytics, data?.hasData, dataReceivedTelemetrySent, onboardingId]); const isTroubleshootingVisible = data?.hasData === false && Date.now() - checkDataStartTime > SHOW_TROUBLESHOOTING_DELAY; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/assets/auto_detect.sh b/x-pack/solutions/observability/plugins/observability_onboarding/public/assets/auto_detect.sh index 06f6a90f82a45..beff090673976 100755 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/assets/auto_detect.sh +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/assets/auto_detect.sh @@ -90,6 +90,34 @@ ensure_argument "$kibana_api_endpoint" "--kibana-url" ensure_argument "$onboarding_flow_id" "--id" ensure_argument "$elastic_agent_version" "--ea-version" +update_step_progress() { + local STEPNAME="$1" + local STATUS="$2" # "incomplete" | "complete" | "disabled" | "loading" | "warning" | "danger" | "current" + local MESSAGE=${3:-} + local PAYLOAD=${4:-} + local data="" + + MESSAGE=$(echo "$MESSAGE" | sed 's/"/\\"/g') + + if [ -z "$PAYLOAD" ]; then + data="{\"status\":\"${STATUS}\", \"message\":\"${MESSAGE}\"}" + else + data="{\"status\":\"${STATUS}\", \"message\":\"${MESSAGE}\", \"payload\":${PAYLOAD}}" + fi + curl --request POST \ + --url "${kibana_api_endpoint}/internal/observability_onboarding/flow/${onboarding_flow_id}/step/${STEPNAME}" \ + --header "Authorization: ApiKey ${install_api_key_encoded}" \ + --header "Content-Type: application/json" \ + --header "kbn-xsrf: true" \ + --header "x-elastic-internal-origin: Kibana" \ + --data "$data" \ + --output /dev/null \ + --no-progress-meter \ + --fail +} + +update_step_progress "logs-detect" "initialize" + known_integrations_list_string="" selected_known_integrations_array=() detected_patterns=() @@ -122,34 +150,12 @@ elif [ "${OS}" == "Darwin" ]; then fi elastic_agent_config_path=/Library/Elastic/Agent/elastic-agent.yml else + update_step_progress "logs-detect" "danger" "Unable to run auto-detect script on ${os} (${arch})" fail "This script is only supported on Linux and macOS" fi elastic_agent_artifact_name="elastic-agent-${elastic_agent_version}-${os}-${arch}" -update_step_progress() { - local STEPNAME="$1" - local STATUS="$2" # "incomplete" | "complete" | "disabled" | "loading" | "warning" | "danger" | "current" - local MESSAGE=${3:-} - local PAYLOAD=${4:-} - local data="" - if [ -z "$PAYLOAD" ]; then - data="{\"status\":\"${STATUS}\", \"message\":\"${MESSAGE}\"}" - else - data="{\"status\":\"${STATUS}\", \"message\":\"${MESSAGE}\", \"payload\":${PAYLOAD}}" - fi - curl --request POST \ - --url "${kibana_api_endpoint}/internal/observability_onboarding/flow/${onboarding_flow_id}/step/${STEPNAME}" \ - --header "Authorization: ApiKey ${install_api_key_encoded}" \ - --header "Content-Type: application/json" \ - --header "kbn-xsrf: true" \ - --header "x-elastic-internal-origin: Kibana" \ - --data "$data" \ - --output /dev/null \ - --no-progress-meter \ - --fail -} - download_elastic_agent() { local download_url="https://artifacts.elastic.co/downloads/beats/elastic-agent/${elastic_agent_artifact_name}.tar.gz" rm -rf "./${elastic_agent_artifact_name}" "./${elastic_agent_artifact_name}.tar.gz" @@ -244,7 +250,7 @@ backup_elastic_agent_config() { if [ "$?" -eq 0 ]; then printf "\n\e[32;1m✓\e[0m %s \e[36m%s\e[0m\n" "Backup saved to" "$backup_path" else - update_step_progress "ea-config" "warning" "Failed to backup existing configuration" + update_step_progress "ea-config" "danger" "Failed to backup existing configuration" fail "Failed to backup existing config - Try manually creating a backup or delete your existing config before re-running this script" fi else @@ -289,7 +295,7 @@ install_integrations() { if [ "$?" -eq 0 ]; then printf "\n\e[32;1m✓\e[0m %s\n" "Integrations installed" else - update_step_progress "ea-config" "warning" "Failed to install integrations" + update_step_progress "install-integrations" "danger" "Failed to install integrations" fail "Failed to install integrations" fi } @@ -318,7 +324,7 @@ apply_elastic_agent_config() { update_step_progress "ea-config" "complete" else - update_step_progress "ea-config" "warning" "Failed to configure Elastic Agent" + update_step_progress "ea-config" "danger" "Failed to configure Elastic Agent" fail "Failed to configure Elastic Agent" fi } @@ -588,7 +594,7 @@ generate_custom_integration_name() { } printf "\e[1m%s\e[0m\n" "Looking for log files..." -update_step_progress "logs-detect" "loading" +update_step_progress "logs-detect" "loading" "" "{\"os\": \"${os}\", \"arch\": \"${arch}\"}" detect_known_integrations # Check if LSOF_PATH is executable @@ -596,6 +602,7 @@ if [ -x "$LSOF_PATH" ]; then read_open_log_file_list build_unknown_log_file_patterns else + update_step_progress "logs-detect" "warning" "lsof is not available on the host" echo -e "\nlsof is required to detect custom log files. Looking for known integrations only." fi diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.test.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.test.tsx deleted file mode 100644 index 45ca17d878991..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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'; -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: false, - 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); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts deleted file mode 100644 index a580055f8e3c7..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/hooks/use_flow_progress_telemetry.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 { EuiStepsProps } from '@elastic/eui'; -import { type LogsFlowProgressStepId } from '../../common/logs_flow_progress_step_id'; -import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../common/telemetry_events'; -import { useKibana } from './use_kibana'; - -export type EuiStepStatus = EuiStepsProps['steps'][number]['status']; - -export type StepsProgress = Partial< - Record ->; - -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 [previousReportedSteps] = useState>(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: false, - flow: flowId, - step: stepId, - step_status: step.status, - step_message: step.message, - }); - previousReportedSteps.set(stepId, step.status); - }); - }, [analytics, flowId, progress, previousReportedSteps]); -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts index 0f0880dde4199..ee4527d693558 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts @@ -35,7 +35,6 @@ import { ConfigSchema } from '.'; import { OBSERVABILITY_ONBOARDING_FEEDBACK_TELEMETRY_EVENT, OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT, - OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT, OBSERVABILITY_ONBOARDING_FLOW_PROGRESS_TELEMETRY_EVENT, OBSERVABILITY_ONBOARDING_FLOW_ERROR_TELEMETRY_EVENT, OBSERVABILITY_ONBOARDING_FLOW_DATASET_DETECTED_TELEMETRY_EVENT, @@ -125,7 +124,6 @@ export class ObservabilityOnboardingPlugin core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT); core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_FEEDBACK_TELEMETRY_EVENT); - core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_AUTODETECT_TELEMETRY_EVENT); core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_FLOW_PROGRESS_TELEMETRY_EVENT); core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_FLOW_ERROR_TELEMETRY_EVENT); core.analytics.registerEventType( diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/server/plugin.ts b/x-pack/solutions/observability/plugins/observability_onboarding/server/plugin.ts index 60b33eb3dd601..2a9f282ed14b2 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/server/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/server/plugin.ts @@ -26,6 +26,7 @@ import { import { observabilityOnboardingFlow } from './saved_objects/observability_onboarding_status'; import { EsLegacyConfigService } from './services/es_legacy_config_service'; import { ObservabilityOnboardingConfig } from './config'; +import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../common/telemetry_events'; export class ObservabilityOnboardingPlugin implements @@ -116,6 +117,8 @@ export class ObservabilityOnboardingPlugin isBeta: true, }); + core.analytics.registerEventType(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT); + return {}; } diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/flow/route.ts b/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/flow/route.ts index 290e003d72661..d61e90c2ef4ed 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/flow/route.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/flow/route.ts @@ -15,6 +15,7 @@ import { import { dump } from 'js-yaml'; import { PackageDataStreamTypes, Output } from '@kbn/fleet-plugin/common/types'; import { transformOutputToFullPolicyOutput } from '@kbn/fleet-plugin/server/services/output_client'; +import { OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT } from '../../../common/telemetry_events'; import { getObservabilityOnboardingFlow, saveObservabilityOnboardingFlow } from '../../lib/state'; import type { SavedObservabilityOnboardingFlow } from '../../saved_objects/observability_onboarding_status'; import { ObservabilityOnboardingFlow } from '../../saved_objects/observability_onboarding_status'; @@ -134,6 +135,16 @@ const stepProgressUpdateRoute = createObservabilityOnboardingServerRoute({ }, }, }); + + coreStart.analytics.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { + flow_type: observabilityOnboardingState.type, + flow_id: id, + step: name, + step_status: status, + step_message: message, + payload, + }); + return { name, status, message, payload }; }, }); @@ -415,6 +426,23 @@ const integrationsInstallRoute = createObservabilityOnboardingServerRoute({ }, }); + coreStart.analytics.reportEvent(OBSERVABILITY_ONBOARDING_TELEMETRY_EVENT.eventType, { + flow_type: savedObservabilityOnboardingState.type, + flow_id: params.path.onboardingId, + step: 'install-integrations', + step_status: 'complete', + payload: { + integrations: installedIntegrations.map( + ({ title, pkgName, pkgVersion, installSource }) => ({ + title, + pkgName, + pkgVersion, + installSource, + }) + ), + }, + }); + return response.ok({ headers: { 'content-type': 'application/x-tar', diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/types.ts b/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/types.ts index 4a1e818995409..531caf436c5a5 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/types.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/server/routes/types.ts @@ -76,6 +76,11 @@ export const IntegrationRT = t.intersection([ export type InstalledIntegration = t.TypeOf; +export const LogsDetectLoadingStepPayloadRT = t.type({ + os: t.string, + arch: t.string, +}); + export const ElasticAgentStepPayloadRT = t.type({ agentId: t.string, }); @@ -87,6 +92,7 @@ export const InstallIntegrationsStepPayloadRT = t.array(IntegrationRT); export type InstallIntegrationsStepPayload = t.TypeOf; export const StepProgressPayloadRT = t.union([ + LogsDetectLoadingStepPayloadRT, ElasticAgentStepPayloadRT, InstallIntegrationsStepPayloadRT, ]); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/server/saved_objects/observability_onboarding_status.ts b/x-pack/solutions/observability/plugins/observability_onboarding/server/saved_objects/observability_onboarding_status.ts index 03be370e6cf6b..81f5919a7c1ac 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/server/saved_objects/observability_onboarding_status.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/server/saved_objects/observability_onboarding_status.ts @@ -53,6 +53,11 @@ const SystemLogsStateSchema = schema.object({ namespace: schema.string(), }); +const LogsDetectLoadingStepPayloadSchema = schema.object({ + os: schema.string(), + arch: schema.string(), +}); + const ElasticAgentStepPayloadSchema = schema.object({ agentId: schema.string(), }); @@ -128,7 +133,11 @@ export const observabilityOnboardingFlow: SavedObjectsType = { status: schema.string(), message: schema.maybe(schema.string()), payload: schema.maybe( - schema.oneOf([ElasticAgentStepPayloadSchema, InstallIntegrationsStepPayloadSchema]) + schema.oneOf([ + ElasticAgentStepPayloadSchema, + InstallIntegrationsStepPayloadSchema, + LogsDetectLoadingStepPayloadSchema, + ]) ), }) ),