Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions x-pack/plugins/monitoring/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { CommonAlertParamDetail, ExpressionConfig } from './types/alerts';
import { AlertParamType } from './enums';
import { validateDuration } from './validate_duration';

export const USAGE_COLLECTION_APP_NAME = 'stack_monitoring';

/**
* Helper string to add as a tag in every logging call
*/
Expand Down
4 changes: 1 addition & 3 deletions x-pack/plugins/monitoring/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
"features",
"data",
"navigation",
"observability",
"observabilityShared",
"dataViews",
"unifiedSearch",
"share"
Expand All @@ -42,4 +40,4 @@
"kibanaReact",
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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, useMemo } from 'react';
import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { USAGE_COLLECTION_APP_NAME } from '../../../common/constants';

/**
* Note: The usage_collection plugin will take care of sending this data to the telemetry server.
* You can find the metrics that are collected by these hooks in Stack Telemetry.
* Search the index `kibana-ui-counter`. You can filter for `eventName` and/or `appName`.
*/

interface TrackOptions {
metricType?: UiCounterMetricType;
delay?: number; // in ms
}
type EffectDeps = unknown[];

interface ServiceDeps {
usageCollection: UsageCollectionSetup; // TODO: This should really be start. Looking into it.
}

export type TrackMetricOptions = TrackOptions & { metric: string };
export type UiTracker = ReturnType<typeof useUiTracker>;
export type TrackEvent = (options: TrackMetricOptions) => void;

export { METRIC_TYPE };

export function useUiTracker<Services extends ServiceDeps>(): TrackEvent {
const reportUiCounter = useKibana<Services>().services?.usageCollection?.reportUiCounter;
const trackEvent = useMemo(() => {
return ({ metric, metricType = METRIC_TYPE.COUNT }: TrackMetricOptions) => {
if (reportUiCounter) {
reportUiCounter(USAGE_COLLECTION_APP_NAME, metricType, metric);
}
};
}, [reportUiCounter]);
return trackEvent;
}

export function useTrackMetric<Services extends ServiceDeps>(
{ metric, metricType = METRIC_TYPE.COUNT, delay = 0 }: TrackMetricOptions,
effectDependencies: EffectDeps = []
) {
const reportUiCounter = useKibana<Services>().services?.usageCollection?.reportUiCounter;

useEffect(() => {
if (!reportUiCounter) {
// eslint-disable-next-line no-console
console.log(
'usageCollection.reportUiCounter is unavailable. Ensure this is setup via <KibanaContextProvider />.'
);
} else {
let decoratedMetric = metric;
if (delay > 0) {
decoratedMetric += `__delayed_${delay}ms`;
}
const id = setTimeout(
() => reportUiCounter(USAGE_COLLECTION_APP_NAME, metricType, decoratedMetric),
Math.max(delay, 0)
);
return () => clearTimeout(id);
}
// the dependencies are managed externally
// eslint-disable-next-line react-hooks/exhaustive-deps
}, effectDependencies);
}

/**
* useTrackPageview is a convenience wrapper for tracking a pageview
* Its metrics will be found at:
* stack_stats.kibana.plugins.ui_metric.{app}.pageview__{path}(__delayed_{n}ms)?
*/
type TrackPageviewProps = TrackOptions & { path: string };

export function useTrackPageview<Services extends ServiceDeps>(
{ path, ...rest }: TrackPageviewProps,
effectDependencies: EffectDeps = []
) {
useTrackMetric<Services>({ ...rest, metric: `pageview__${path}` }, effectDependencies);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { EuiPage, EuiPageBody, EuiPageTemplate, EuiTab, EuiTabs, EuiSpacer } fro
import React, { useContext, useState, useEffect, useCallback, FC, PropsWithChildren } from 'react';
import { useHistory } from 'react-router-dom';
import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser';
import { HeaderMenuPortal } from '@kbn/observability-shared-plugin/public';
import { useTitle } from '../hooks/use_title';
import { MonitoringToolbar } from '../../components/shared/toolbar';
import { useMonitoringTimeContainerContext } from '../hooks/use_monitoring_time';
Expand All @@ -25,6 +24,7 @@ import { AlertsDropdown } from '../../alerts/alerts_dropdown';
import { useRequestErrorHandler } from '../hooks/use_request_error_handler';
import { SetupModeToggleButton } from '../../components/setup_mode/toggle_button';
import { HeaderActionMenuContext } from '../contexts/header_action_menu_context';
import { HeaderMenuPortal } from '../../components/header_menu';

export interface TabMenuItem {
id: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { render } from '@testing-library/react';
import React from 'react';
import HeaderMenuPortal from './header_menu_portal';
import { themeServiceMock } from '@kbn/core/public/mocks';

describe('HeaderMenuPortal', () => {
describe('when unmounted', () => {
it('calls setHeaderActionMenu with undefined', () => {
const setHeaderActionMenu = jest.fn();
const theme$ = themeServiceMock.createTheme$();

const { unmount } = render(
<HeaderMenuPortal setHeaderActionMenu={setHeaderActionMenu} theme$={theme$}>
test
</HeaderMenuPortal>
);

unmount();

expect(setHeaderActionMenu).toHaveBeenCalledWith(undefined);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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, { useEffect, useMemo } from 'react';
import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import type { HeaderMenuPortalProps } from '../../types';

// eslint-disable-next-line import/no-default-export
export default function HeaderMenuPortal({
children,
setHeaderActionMenu,
theme$,
}: HeaderMenuPortalProps) {
const portalNode = useMemo(() => createHtmlPortalNode(), []);

useEffect(() => {
setHeaderActionMenu((element) => {
const mount = toMountPoint(<OutPortal node={portalNode} />, { theme$ });
return mount(element);
});

return () => {
portalNode.unmount();
setHeaderActionMenu(undefined);
};
}, [portalNode, setHeaderActionMenu, theme$]);

return <InPortal node={portalNode}>{children}</InPortal>;
}
20 changes: 20 additions & 0 deletions x-pack/plugins/monitoring/public/components/header_menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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, { lazy, Suspense } from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
import { HeaderMenuPortalProps } from '../../types';

const HeaderMenuPortalLazy = lazy(() => import('./header_menu_portal'));

export function HeaderMenuPortal(props: HeaderMenuPortalProps) {
return (
<Suspense fallback={<EuiLoadingSpinner />}>
<HeaderMenuPortalLazy {...props} />
</Suspense>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React from 'react';
import { EuiPage, EuiPageBody, EuiPageTemplate, EuiLoadingSpinner } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useTrackPageview } from '@kbn/observability-shared-plugin/public';
import { useTrackPageview } from '../../application/hooks/use_track_metric';

function PageLoadingUI() {
return (
Expand All @@ -29,8 +29,8 @@ const PageLoadingTracking: React.FunctionComponent<{ pageViewTitle: string }> =
pageViewTitle,
}) => {
const path = pageViewTitle.toLowerCase().replace(/-/g, '').replace(/\s+/g, '_');
useTrackPageview({ app: 'stack_monitoring', path });
useTrackPageview({ app: 'stack_monitoring', path, delay: 15000 });
useTrackPageview({ path });
useTrackPageview({ path, delay: 15000 });
return <PageLoadingUI />;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import React from 'react';
import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE, useUiTracker } from '@kbn/observability-shared-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { TELEMETRY_METRIC_BUTTON_CLICK } from '../../../common/constants';
import { SetupModeExitButton } from './exit_button';
import { useUiTracker } from '../../application/hooks/use_track_metric';

export interface SetupModeToggleButtonProps {
enabled: boolean;
Expand All @@ -21,7 +22,7 @@ export const SetupModeToggleButton: React.FC<SetupModeToggleButtonProps> = (
props: SetupModeToggleButtonProps
) => {
const [isLoading, setIsLoading] = React.useState(false);
const trackStat = useUiTracker({ app: 'stack_monitoring' });
const trackStat = useUiTracker();

function toggleSetupMode(enabled: boolean, stat: string) {
setIsLoading(true);
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/monitoring/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { DashboardStart } from '@kbn/dashboard-plugin/public';
import { FleetStart } from '@kbn/fleet-plugin/public';
import type { InfraClientStartExports } from '@kbn/infra-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
import { ReactNode } from 'react';

export interface MonitoringStartPluginDependencies {
navigation: NavigationStart;
Expand All @@ -43,3 +44,9 @@ export type LegacyMonitoringStartPluginDependencies = MonitoringStartPluginDepen
LegacyStartDependencies;

export type MonitoringStartServices = CoreStart & MonitoringStartPluginDependencies;

export interface HeaderMenuPortalProps {
children: ReactNode;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
theme$: AppMountParameters['theme$'];
}
2 changes: 1 addition & 1 deletion x-pack/plugins/monitoring/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"@kbn/dashboard-plugin",
"@kbn/fleet-plugin",
"@kbn/shared-ux-router",
"@kbn/observability-shared-plugin",
"@kbn/shared-ux-link-redirect-app",
"@kbn/alerts-as-data-utils",
"@kbn/rule-data-utils",
Expand All @@ -48,6 +47,7 @@
"@kbn/ui-theme",
"@kbn/core-elasticsearch-server",
"@kbn/share-plugin",
"@kbn/analytics",
],
"exclude": [
"target/**/*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,7 @@ export type HasData<T extends ObservabilityFetchDataPlugins> = (

export type ObservabilityFetchDataPlugins = Exclude<
ObservabilityApp,
| 'observability-overview'
| 'stack_monitoring'
| 'fleet'
| 'synthetics'
| 'profiling'
| 'observability-onboarding'
'observability-overview' | 'fleet' | 'synthetics' | 'profiling' | 'observability-onboarding'
>;

export interface DataHandler<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export type HasData<T extends ObservabilityFetchDataPlugins> = (

export type ObservabilityFetchDataPlugins = Exclude<
ObservabilityApp,
'observability-overview' | 'stack_monitoring' | 'fleet' | 'synthetics'
'observability-overview' | 'fleet' | 'synthetics'
>;

export interface DataHandler<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export type ObservabilityApp =
| 'uptime'
| 'synthetics'
| 'observability-overview'
| 'stack_monitoring'
| 'ux'
| 'fleet'
| 'universal_profiling';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export type ObservabilityApp =
| 'uptime'
| 'synthetics'
| 'observability-overview'
| 'stack_monitoring'
| 'ux'
| 'fleet'
| 'profiling'
Expand Down