From 490bf6b09933a5b24b393de397ce831495b0a63d Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Tue, 30 Jun 2020 13:56:35 -0400 Subject: [PATCH] [Logs and Metrics UI] Initial setup for registering observability overview data fetchers (#69999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Switches mount callbacks to only use start deps Fixes #58014 * Sets up skeleton logs data fetchers for overview * Fixes type hacks for logs fetcher * Prevent kibana from crashing on initial load * Fixes types and linting errors * Fixes some linting import/export issues Co-authored-by: Alejandro Fernández Gómez --- src/plugins/usage_collection/public/index.ts | 2 +- .../services/rest/observability_dashboard.ts | 7 +- .../get_transaction_coordinates.ts | 2 +- x-pack/plugins/infra/kibana.json | 4 +- .../infra/public/apps/common_providers.tsx | 4 +- x-pack/plugins/infra/public/apps/logs_app.tsx | 6 +- .../plugins/infra/public/apps/metrics_app.tsx | 6 +- x-pack/plugins/infra/public/index.ts | 17 ++-- x-pack/plugins/infra/public/plugin.ts | 63 ++++++------- x-pack/plugins/infra/public/types.ts | 30 +++++- .../public/utils/logs_overview_fetchers.ts | 93 +++++++++++++++++++ .../observability/public/data_handler.ts | 22 +---- x-pack/plugins/observability/public/index.ts | 8 +- x-pack/plugins/observability/public/plugin.ts | 4 +- .../index.ts} | 22 +++++ .../observability/public/typings/index.ts | 1 + 16 files changed, 201 insertions(+), 90 deletions(-) create mode 100644 x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts rename x-pack/plugins/observability/public/typings/{fetch_data_response/index.d.ts => fetch_overview_data/index.ts} (68%) diff --git a/src/plugins/usage_collection/public/index.ts b/src/plugins/usage_collection/public/index.ts index 712e6a76152a2..c6c6ba64e6630 100644 --- a/src/plugins/usage_collection/public/index.ts +++ b/src/plugins/usage_collection/public/index.ts @@ -21,7 +21,7 @@ import { PluginInitializerContext } from '../../../core/public'; import { UsageCollectionPlugin } from './plugin'; export { METRIC_TYPE } from '@kbn/analytics'; -export { UsageCollectionSetup } from './plugin'; +export { UsageCollectionSetup, UsageCollectionStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new UsageCollectionPlugin(initializerContext); diff --git a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts index 2221904932b63..4614e06cbd45d 100644 --- a/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts +++ b/x-pack/plugins/apm/public/services/rest/observability_dashboard.ts @@ -6,9 +6,10 @@ import { i18n } from '@kbn/i18n'; import { sum } from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FetchDataParams } from '../../../../observability/public/data_handler'; -import { ApmFetchDataResponse } from '../../../../observability/public/typings/fetch_data_response'; +import { + ApmFetchDataResponse, + FetchDataParams, +} from '../../../../observability/public'; import { callApmApi } from './createCallApmApi'; import { Theme } from '../../utils/get_theme'; diff --git a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts index 78ed11d839ad2..e78a3c1cec24a 100644 --- a/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts +++ b/x-pack/plugins/apm/server/lib/observability_dashboard/get_transaction_coordinates.ts @@ -9,7 +9,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { rangeFilter } from '../../../common/utils/range_filter'; -import { Coordinates } from '../../../../observability/public/typings/fetch_data_response'; +import { Coordinates } from '../../../../observability/public'; import { PROCESSOR_EVENT } from '../../../common/elasticsearch_fieldnames'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { ProcessorEvent } from '../../../common/processor_event'; diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json index 4e23f1985d450..e5ce1b1cd96f8 100644 --- a/x-pack/plugins/infra/kibana.json +++ b/x-pack/plugins/infra/kibana.json @@ -13,9 +13,7 @@ "alerts", "triggers_actions_ui" ], - "optionalPlugins": [ - "ml" - ], + "optionalPlugins": ["ml", "observability"], "server": true, "ui": true, "configPath": ["xpack", "infra"] diff --git a/x-pack/plugins/infra/public/apps/common_providers.tsx b/x-pack/plugins/infra/public/apps/common_providers.tsx index facb0f1539a10..9e4917856d8b2 100644 --- a/x-pack/plugins/infra/public/apps/common_providers.tsx +++ b/x-pack/plugins/infra/public/apps/common_providers.tsx @@ -12,7 +12,7 @@ import { KibanaContextProvider, } from '../../../../../src/plugins/kibana_react/public'; import { TriggersActionsProvider } from '../utils/triggers_actions_context'; -import { ClientPluginDeps } from '../types'; +import { InfraClientStartDeps } from '../types'; import { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public'; import { ApolloClientContext } from '../utils/apollo_context'; import { EuiThemeProvider } from '../../../observability/public'; @@ -37,7 +37,7 @@ export const CommonInfraProviders: React.FC<{ export const CoreProviders: React.FC<{ core: CoreStart; - plugins: ClientPluginDeps; + plugins: InfraClientStartDeps; }> = ({ children, core, plugins }) => { return ( diff --git a/x-pack/plugins/infra/public/apps/logs_app.tsx b/x-pack/plugins/infra/public/apps/logs_app.tsx index e0251522bb24c..528d90b2a3a23 100644 --- a/x-pack/plugins/infra/public/apps/logs_app.tsx +++ b/x-pack/plugins/infra/public/apps/logs_app.tsx @@ -15,14 +15,14 @@ import '../index.scss'; import { NotFoundPage } from '../pages/404'; import { LinkToLogsPage } from '../pages/link_to/link_to_logs'; import { LogsPage } from '../pages/logs'; -import { ClientPluginDeps } from '../types'; +import { InfraClientStartDeps } from '../types'; import { createApolloClient } from '../utils/apollo_client'; import { CommonInfraProviders, CoreProviders } from './common_providers'; import { prepareMountElement } from './common_styles'; export const renderApp = ( core: CoreStart, - plugins: ClientPluginDeps, + plugins: InfraClientStartDeps, { element, history }: AppMountParameters ) => { const apolloClient = createApolloClient(core.http.fetch); @@ -43,7 +43,7 @@ const LogsApp: React.FC<{ apolloClient: ApolloClient<{}>; core: CoreStart; history: History; - plugins: ClientPluginDeps; + plugins: InfraClientStartDeps; }> = ({ apolloClient, core, history, plugins }) => { const uiCapabilities = core.application.capabilities; diff --git a/x-pack/plugins/infra/public/apps/metrics_app.tsx b/x-pack/plugins/infra/public/apps/metrics_app.tsx index 8713abe0510a6..3069490466938 100644 --- a/x-pack/plugins/infra/public/apps/metrics_app.tsx +++ b/x-pack/plugins/infra/public/apps/metrics_app.tsx @@ -16,7 +16,7 @@ import { NotFoundPage } from '../pages/404'; import { LinkToMetricsPage } from '../pages/link_to/link_to_metrics'; import { InfrastructurePage } from '../pages/metrics'; import { MetricDetail } from '../pages/metrics/metric_detail'; -import { ClientPluginDeps } from '../types'; +import { InfraClientStartDeps } from '../types'; import { createApolloClient } from '../utils/apollo_client'; import { RedirectWithQueryParams } from '../utils/redirect_with_query_params'; import { CommonInfraProviders, CoreProviders } from './common_providers'; @@ -24,7 +24,7 @@ import { prepareMountElement } from './common_styles'; export const renderApp = ( core: CoreStart, - plugins: ClientPluginDeps, + plugins: InfraClientStartDeps, { element, history }: AppMountParameters ) => { const apolloClient = createApolloClient(core.http.fetch); @@ -45,7 +45,7 @@ const MetricsApp: React.FC<{ apolloClient: ApolloClient<{}>; core: CoreStart; history: History; - plugins: ClientPluginDeps; + plugins: InfraClientStartDeps; }> = ({ apolloClient, core, history, plugins }) => { const uiCapabilities = core.application.capabilities; diff --git a/x-pack/plugins/infra/public/index.ts b/x-pack/plugins/infra/public/index.ts index 8f2d37fa1daa9..cadf9a4837866 100644 --- a/x-pack/plugins/infra/public/index.ts +++ b/x-pack/plugins/infra/public/index.ts @@ -5,14 +5,19 @@ */ import { PluginInitializer, PluginInitializerContext } from 'kibana/public'; -import { ClientSetup, ClientStart, Plugin } from './plugin'; -import { ClientPluginsSetup, ClientPluginsStart } from './types'; +import { Plugin } from './plugin'; +import { + InfraClientSetupExports, + InfraClientStartExports, + InfraClientSetupDeps, + InfraClientStartDeps, +} from './types'; export const plugin: PluginInitializer< - ClientSetup, - ClientStart, - ClientPluginsSetup, - ClientPluginsStart + InfraClientSetupExports, + InfraClientStartExports, + InfraClientSetupDeps, + InfraClientStartDeps > = (context: PluginInitializerContext) => { return new Plugin(context); }; diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 496e788efc060..1b28945320bb6 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -4,35 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { - AppMountParameters, - CoreSetup, - CoreStart, - Plugin as PluginClass, - PluginInitializerContext, -} from 'kibana/public'; +import { AppMountParameters, PluginInitializerContext } from 'kibana/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { createMetricThresholdAlertType } from './alerting/metric_threshold'; import { createInventoryMetricAlertType } from './alerting/inventory'; import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type'; import { registerStartSingleton } from './legacy_singletons'; import { registerFeatures } from './register_feature'; -import { ClientPluginsSetup, ClientPluginsStart } from './types'; - -export type ClientSetup = void; -export type ClientStart = void; +import { + InfraClientSetupDeps, + InfraClientStartDeps, + InfraClientCoreSetup, + InfraClientCoreStart, + InfraClientPluginClass, +} from './types'; +import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers'; -export class Plugin - implements PluginClass { +export class Plugin implements InfraClientPluginClass { constructor(_context: PluginInitializerContext) {} - setup(core: CoreSetup, pluginsSetup: ClientPluginsSetup) { + setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) { registerFeatures(pluginsSetup.home); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createInventoryMetricAlertType()); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getLogsAlertType()); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createMetricThresholdAlertType()); + if (pluginsSetup.observability) { + pluginsSetup.observability.dashboard.register({ + appName: 'infra_logs', + hasData: getLogsHasDataFetcher(core.getStartServices), + fetchData: getLogsOverviewDataFetcher(core.getStartServices), + }); + } + core.application.register({ id: 'logs', title: i18n.translate('xpack.infra.logs.pluginTitle', { @@ -43,20 +48,11 @@ export class Plugin appRoute: '/app/logs', category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { + // mount callback should not use setup dependencies, get start dependencies instead const [coreStart, pluginsStart] = await core.getStartServices(); const { renderApp } = await import('./apps/logs_app'); - return renderApp( - coreStart, - { - data: pluginsStart.data, - dataEnhanced: pluginsSetup.dataEnhanced, - home: pluginsSetup.home, - triggers_actions_ui: pluginsStart.triggers_actions_ui, - usageCollection: pluginsSetup.usageCollection, - }, - params - ); + return renderApp(coreStart, pluginsStart, params); }, }); @@ -70,20 +66,11 @@ export class Plugin appRoute: '/app/metrics', category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { + // mount callback should not use setup dependencies, get start dependencies instead const [coreStart, pluginsStart] = await core.getStartServices(); const { renderApp } = await import('./apps/metrics_app'); - return renderApp( - coreStart, - { - data: pluginsStart.data, - dataEnhanced: pluginsSetup.dataEnhanced, - home: pluginsSetup.home, - triggers_actions_ui: pluginsStart.triggers_actions_ui, - usageCollection: pluginsSetup.usageCollection, - }, - params - ); + return renderApp(coreStart, pluginsStart, params); }, }); @@ -102,7 +89,9 @@ export class Plugin }); } - start(core: CoreStart, _plugins: ClientPluginsStart) { + start(core: InfraClientCoreStart, _plugins: InfraClientStartDeps) { registerStartSingleton(core); } + + stop() {} } diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 8181da3301c92..357f07265ac6e 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -4,22 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ +import { CoreSetup, CoreStart, Plugin as PluginClass } from 'kibana/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; -import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import { + UsageCollectionSetup, + UsageCollectionStart, +} from '../../../../src/plugins/usage_collection/public'; import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public'; -import { DataEnhancedSetup } from '../../data_enhanced/public'; +import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public'; +import { ObservabilityPluginSetup, ObservabilityPluginStart } from '../../observability/public'; -export interface ClientPluginsSetup { +// Our own setup and start contract values +export type InfraClientSetupExports = void; +export type InfraClientStartExports = void; + +export interface InfraClientSetupDeps { dataEnhanced: DataEnhancedSetup; home: HomePublicPluginSetup; + observability: ObservabilityPluginSetup; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; usageCollection: UsageCollectionSetup; } -export interface ClientPluginsStart { +export interface InfraClientStartDeps { data: DataPublicPluginStart; + dataEnhanced: DataEnhancedStart; + observability: ObservabilityPluginStart; triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; + usageCollection: UsageCollectionStart; } -export type ClientPluginDeps = ClientPluginsSetup & ClientPluginsStart; +export type InfraClientCoreSetup = CoreSetup; +export type InfraClientCoreStart = CoreStart; +export type InfraClientPluginClass = PluginClass< + InfraClientSetupExports, + InfraClientStartExports, + InfraClientSetupDeps, + InfraClientStartDeps +>; diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts new file mode 100644 index 0000000000000..46a0edf75b756 --- /dev/null +++ b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { InfraClientCoreSetup } from '../types'; +import { LogsFetchDataResponse } from '../../../observability/public'; + +export function getLogsHasDataFetcher(getStartServices: InfraClientCoreSetup['getStartServices']) { + return async () => { + // if you need the data plugin, this is how you get it + // const [, startPlugins] = await getStartServices(); + // const { data } = startPlugins; + + // if you need a core dep, we need to pass in more than just getStartServices + + // perform query + return true; + }; +} + +export function getLogsOverviewDataFetcher( + getStartServices: InfraClientCoreSetup['getStartServices'] +) { + return async (): Promise => { + // if you need the data plugin, this is how you get it + // const [, startPlugins] = await getStartServices(); + // const { data } = startPlugins; + + // if you need a core dep, we need to pass in more than just getStartServices + + // perform query + return { + title: 'Log rate', + appLink: 'TBD', // TODO: what format should this be in, relative I assume? + stats: { + nginx: { + type: 'number', + label: 'nginx', + value: 345341, + }, + 'elasticsearch.audit': { + type: 'number', + label: 'elasticsearch.audit', + value: 164929, + }, + 'haproxy.log': { + type: 'number', + label: 'haproxy.log', + value: 51101, + }, + }, + // Note: My understanding is that these series coordinates will be + // combined into objects that look like: + // { x: timestamp, y: value, g: label (e.g. nginx) } + // so they fit the stacked bar chart API + // https://elastic.github.io/elastic-charts/?path=/story/bar-chart--stacked-with-axis-and-legend + series: { + nginx: { + label: 'nginx', + coordinates: [ + { x: 1593000000000, y: 10014 }, + { x: 1593000900000, y: 12827 }, + { x: 1593001800000, y: 2946 }, + { x: 1593002700000, y: 14298 }, + { x: 1593003600000, y: 4096 }, + ], + }, + 'elasticsearch.audit': { + label: 'elasticsearch.audit', + coordinates: [ + { x: 1593000000000, y: 5676 }, + { x: 1593000900000, y: 6783 }, + { x: 1593001800000, y: 2394 }, + { x: 1593002700000, y: 4554 }, + { x: 1593003600000, y: 5659 }, + ], + }, + 'haproxy.log': { + label: 'haproxy.log', + coordinates: [ + { x: 1593000000000, y: 9085 }, + { x: 1593000900000, y: 9002 }, + { x: 1593001800000, y: 3940 }, + { x: 1593002700000, y: 5451 }, + { x: 1593003600000, y: 9133 }, + ], + }, + }, + }; + }; +} diff --git a/x-pack/plugins/observability/public/data_handler.ts b/x-pack/plugins/observability/public/data_handler.ts index 65f2c52a4e320..39e702a332a8e 100644 --- a/x-pack/plugins/observability/public/data_handler.ts +++ b/x-pack/plugins/observability/public/data_handler.ts @@ -4,29 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ObservabilityFetchDataResponse, FetchDataResponse } from './typings/fetch_data_response'; +import { DataHandler } from './typings/fetch_overview_data'; import { ObservabilityApp } from '../typings/common'; -export interface FetchDataParams { - // The start timestamp in milliseconds of the queried time interval - startTime: string; - // The end timestamp in milliseconds of the queried time interval - endTime: string; - // The aggregation bucket size in milliseconds if applicable to the data source - bucketSize: string; -} - -export type FetchData = ( - fetchDataParams: FetchDataParams -) => Promise; - -export type HasData = () => Promise; - -interface DataHandler { - fetchData: FetchData; - hasData: HasData; -} - const dataHandlers: Partial> = {}; export function registerDataHandler({ diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index fcb569f535d76..d2f1d246f79ec 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -5,16 +5,16 @@ */ import { PluginInitializerContext, PluginInitializer } from 'kibana/public'; -import { Plugin, ObservabilityPluginSetup } from './plugin'; +import { Plugin, ObservabilityPluginSetup, ObservabilityPluginStart } from './plugin'; -export const plugin: PluginInitializer = ( +export { ObservabilityPluginSetup, ObservabilityPluginStart }; + +export const plugin: PluginInitializer = ( context: PluginInitializerContext ) => { return new Plugin(context); }; -export { ObservabilityPluginSetup }; - export * from './components/action_menu'; export { diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index c20e8c7b75d49..bbda1026606f1 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -16,7 +16,9 @@ export interface ObservabilityPluginSetup { dashboard: { register: typeof registerDataHandler }; } -export class Plugin implements PluginClass { +export type ObservabilityPluginStart = void; + +export class Plugin implements PluginClass { constructor(context: PluginInitializerContext) {} public setup(core: CoreSetup) { diff --git a/x-pack/plugins/observability/public/typings/fetch_data_response/index.d.ts b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts similarity index 68% rename from x-pack/plugins/observability/public/typings/fetch_data_response/index.d.ts rename to x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts index 06e86d1096cfc..e65d1779520cf 100644 --- a/x-pack/plugins/observability/public/typings/fetch_data_response/index.d.ts +++ b/x-pack/plugins/observability/public/typings/fetch_overview_data/index.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ObservabilityApp } from '../../../typings/common'; + interface Stat { type: 'number' | 'percent' | 'bytesPerSecond'; label: string; @@ -22,6 +24,26 @@ interface Series { color?: string; } +export interface FetchDataParams { + // The start timestamp in milliseconds of the queried time interval + startTime: string; + // The end timestamp in milliseconds of the queried time interval + endTime: string; + // The aggregation bucket size in milliseconds if applicable to the data source + bucketSize: string; +} + +export type FetchData = ( + fetchDataParams: FetchDataParams +) => Promise; + +export type HasData = () => Promise; + +export interface DataHandler { + fetchData: FetchData; + hasData: HasData; +} + export interface FetchDataResponse { title: string; appLink: string; diff --git a/x-pack/plugins/observability/public/typings/index.ts b/x-pack/plugins/observability/public/typings/index.ts index 3da2febc73efd..5cc2c613881df 100644 --- a/x-pack/plugins/observability/public/typings/index.ts +++ b/x-pack/plugins/observability/public/typings/index.ts @@ -6,3 +6,4 @@ export * from './eui_draggable'; export * from './eui_styled_components'; +export * from './fetch_overview_data';