From 9907601dd148ba59420bffda45ff584686f47b43 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Wed, 9 Oct 2024 10:14:06 +0200 Subject: [PATCH] [LogsUI] Add UI setting to hide Logs Stream and dashboard panel option (#194519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Closes #193319 Closes #193320 This work is part of the effort to progressively deprecate the existing Logs Stream feature. Changes taken on this PR consist of: - Create a new uiSettings `observability:enableLogsStream` which defaults to `false` on the stateful/cloud deployments and is not available in serverless ones (still, defaults to `false` behind the scene). - When `observability:enableLogsStream` is `false`, the Logs Stream page route is not registered, and neither is its deep link for global search. - When `observability:enableLogsStream` is `false`, the panels list on Dashboard won't show anymore the option `Logs Stream (Deprecated)` to prevent usage of this embeddable in new dashboards. The embeddable is still registered for retro-compatibility with active dashboards, and it has now a callout explaining the status of this embeddable (unmaintained/deprecated). - Rename logs ML to "Logs Anomalies" and "Logs Categories". Other minor improvements regard: - Remove duplicate Xstate utils and use the relative package instead. - Remove the duplicated `useBoolean` hook used in the deprecation callout. - Sync deep links registration with available routes through a single `getLogsRoutes` util. ## 🎥 Recordings ### Logs Stream app removed https://github.com/user-attachments/assets/f4173294-8789-4abd-9972-29c9b7c197ed ### Logs Stream dashboard panel entry removed https://github.com/user-attachments/assets/7f99ca2a-c030-4867-b976-8fdc1df09d29 ### Logs Stream app removed from project nav https://github.com/user-attachments/assets/de51bdd6-820a-4c03-8b64-fb1a6ced0a12 ### Embeddable deprecation callout Screenshot 2024-10-02 at 10 22 09 ### Unavailable setting in serverless https://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf --------- Co-authored-by: Marco Antonio Ghiani Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mike Birnstiehl <114418652+mdbirnstiehl@users.noreply.github.com> --- .../settings/setting_ids/index.ts | 1 + .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../group1/create_and_add_embeddables.ts | 12 +- test/tsconfig.json | 1 + .../infra/common/ui_settings.ts | 30 +++ .../log_stream_react_embeddable.tsx | 55 +++++- .../components/logs_deprecation_callout.tsx | 37 +++- .../infra/public/hooks/use_boolean.ts | 36 ---- .../log_stream_page/state/src/selectors.ts | 2 +- .../state/src/state_machine.ts | 2 +- .../src/state_machine.ts | 2 +- .../src/state_machine.ts | 2 +- .../xstate_helpers/src/index.ts | 3 - .../src/notification_channel.ts | 41 ---- .../xstate_helpers/src/send_actions.test.ts | 67 ------- .../xstate_helpers/src/send_actions.ts | 36 ---- .../xstate_helpers/src/types.ts | 43 ---- .../public/pages/logs/log_entry_rate/page.tsx | 4 +- .../logs/log_entry_rate/page_content.tsx | 8 +- .../infra/public/pages/logs/page_content.tsx | 103 +++++----- .../infra/public/pages/logs/routes.ts | 56 ++++++ .../source_configuration_settings.tsx | 2 + .../pages/logs/stream/page_logs_content.tsx | 4 +- .../pages/logs/stream/page_providers.tsx | 2 +- .../waffle/waffle_group_by_controls.tsx | 2 +- .../infra/public/plugin.ts | 183 ++++++++++-------- .../infra/public/translations.ts | 6 +- .../lib/adapters/framework/adapter_types.ts | 2 + .../infra/server/plugin.ts | 4 + .../infra/tsconfig.json | 3 + .../observability/public/navigation_tree.ts | 6 + .../observability/tsconfig.json | 2 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../functional/apps/infra/logs/log_stream.ts | 4 + .../apps/infra/logs/log_stream_date_nano.ts | 4 + .../infra/logs/logs_source_configuration.ts | 3 + .../functional/apps/infra/page_not_found.ts | 11 +- 41 files changed, 387 insertions(+), 406 deletions(-) create mode 100644 x-pack/plugins/observability_solution/infra/common/ui_settings.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_boolean.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index cb32dbd4a450..a7051804289b 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -144,6 +144,7 @@ export const OBSERVABILITY_LOGS_EXPLORER_ALLOWED_DATA_VIEWS_ID = 'observability:logsExplorer:allowedDataViews'; export const OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE = 'observability:entityCentricExperience'; export const OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID = 'observability:logSources'; +export const OBSERVABILITY_ENABLE_LOGS_STREAM = 'observability:enableLogsStream'; export const OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING = 'observability:aiAssistantSimulatedFunctionCalling'; export const OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN = diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 0ece5f004c23..dc2d2ad2c5de 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -510,6 +510,10 @@ export const stackManagementSchema: MakeSchemaFrom = { _meta: { description: 'Non-default value of setting.' }, }, }, + 'observability:enableLogsStream': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'banners:placement': { type: 'keyword', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index ca1df3c95e87..ef20ab223dfb 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -55,6 +55,7 @@ export interface UsageStats { 'observability:apmEnableServiceInventoryTableSearchBar': boolean; 'observability:logsExplorer:allowedDataViews': string[]; 'observability:logSources': string[]; + 'observability:enableLogsStream': boolean; 'observability:aiAssistantSimulatedFunctionCalling': boolean; 'observability:aiAssistantSearchConnectorIndexPattern': string; 'visualization:heatmap:maxBuckets': number; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index f25c29e5f695..958280d9eba0 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10486,6 +10486,12 @@ } } }, + "observability:enableLogsStream": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "banners:placement": { "type": "keyword", "_meta": { diff --git a/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts index 61929b19ff46..34e78f3e6863 100644 --- a/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts +++ b/test/functional/apps/dashboard/group1/create_and_add_embeddables.ts @@ -8,7 +8,7 @@ */ import expect from '@kbn/expect'; - +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { VisualizeConstants } from '@kbn/visualizations-plugin/common/constants'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -28,6 +28,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); + }); + + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('ensure toolbar popover closes on add', async () => { @@ -39,10 +45,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardAddPanel.expectEditorMenuClosed(); }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - describe('add new visualization link', () => { before(async () => { await dashboard.navigateToApp(); diff --git a/test/tsconfig.json b/test/tsconfig.json index 8b0d946bded6..1d8c301c44a2 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -76,5 +76,6 @@ "@kbn/default-nav-management", "@kbn/default-nav-devtools", "@kbn/core-saved-objects-import-export-server-internal", + "@kbn/management-settings-ids", ] } diff --git a/x-pack/plugins/observability_solution/infra/common/ui_settings.ts b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts new file mode 100644 index 000000000000..95f1ee0a44ba --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts @@ -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. + */ + +/** + * uiSettings definitions for the logs_data_access plugin. + */ +import { schema } from '@kbn/config-schema'; +import { UiSettingsParams } from '@kbn/core-ui-settings-common'; +import { i18n } from '@kbn/i18n'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; + +export const uiSettings: Record = { + [OBSERVABILITY_ENABLE_LOGS_STREAM]: { + category: ['observability'], + name: i18n.translate('xpack.infra.enableLogsStream', { + defaultMessage: 'Logs Stream', + }), + value: false, + description: i18n.translate('xpack.infra.enableLogsStreamDescription', { + defaultMessage: 'Enables the legacy Logs Stream application and dashboard panel. ', + }), + type: 'boolean', + schema: schema.boolean(), + requiresPageReload: true, + }, +}; diff --git a/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx b/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx index bbb2e09d8660..1193b8137921 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/log_stream/log_stream_react_embeddable.tsx @@ -6,6 +6,8 @@ */ import React, { FC, PropsWithChildren, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut, EuiLink } from '@elastic/eui'; import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; import { initializeTimeRange, @@ -17,6 +19,9 @@ import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { Query } from '@kbn/es-query'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { euiThemeVars } from '@kbn/ui-theme'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { FormattedMessage } from '@kbn/i18n-react'; import type { LogStreamApi, LogStreamSerializedState, Services } from './types'; import { datemathToEpochMillis } from '../../utils/datemath'; import { LOG_STREAM_EMBEDDABLE } from './constants'; @@ -81,7 +86,7 @@ export function getLogStreamEmbeddableFactory(services: Services) { theme$={services.coreStart.theme.theme$} > -
+
+
@@ -101,6 +107,53 @@ export function getLogStreamEmbeddableFactory(services: Services) { return factory; } +const DISMISSAL_STORAGE_KEY = 'observability:logStreamEmbeddableDeprecationCalloutDismissed'; +const SAVED_SEARCH_DOCS_URL = + 'https://www.elastic.co/guide/en/kibana/current/save-open-search.html'; + +const DeprecationCallout = () => { + const [isDismissed, setDismissed] = useLocalStorage(DISMISSAL_STORAGE_KEY, false); + + if (isDismissed) { + return null; + } + + return ( + setDismissed(true)} + css={{ + position: 'absolute', + bottom: euiThemeVars.euiSizeM, + right: euiThemeVars.euiSizeM, + width: 'min(100%, 40ch)', + }} + > +

+ + {i18n.translate( + 'xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel', + { defaultMessage: 'saved searches' } + )} + + ), + }} + /> +

+
+ ); +}; + export interface LogStreamEmbeddableProvidersProps { core: CoreStart; pluginStart: InfraClientStartExports; diff --git a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx index 71ae9698ea3b..63107f4a4d03 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx @@ -18,14 +18,38 @@ import { css } from '@emotion/css'; import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; import { useKibanaContextForPlugin } from '../hooks/use_kibana'; -const DISMISSAL_STORAGE_KEY = 'log_stream_deprecation_callout_dismissed'; +const pageConfigurations = { + stream: { + dismissalStorageKey: 'log_stream_deprecation_callout_dismissed', + message: i18n.translate('xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel', { + defaultMessage: + 'The new Logs Explorer makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation. We recommend switching to Logs Explorer, as it will replace Logs Stream in a future version.', + }), + }, + settings: { + dismissalStorageKey: 'log_settings_deprecation_callout_dismissed', + message: i18n.translate( + 'xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel', + { + defaultMessage: + 'These settings only apply to the legacy Logs Stream app, and we do not recommend configuring them. Instead, use Logs Explorer which makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation.', + } + ), + }, +}; + +interface LogsDeprecationCalloutProps { + page: keyof typeof pageConfigurations; +} -export const LogsDeprecationCallout = () => { +export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => { const { services: { share }, } = useKibanaContextForPlugin(); - const [isDismissed, setDismissed] = useLocalStorage(DISMISSAL_STORAGE_KEY, false); + const { dismissalStorageKey, message } = pageConfigurations[page]; + + const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); if (isDismissed) { return null; @@ -42,12 +66,7 @@ export const LogsDeprecationCallout = () => { onDismiss={() => setDismissed(true)} className={calloutStyle} > -

- {i18n.translate('xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel', { - defaultMessage: - 'The new Logs Explorer makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation. We recommend switching to Logs Explorer, as it will replace Logs Stream in a future version.', - })} -

+

{message}

void; - -export type DispatchWithOptionalAction = (_arg?: Type | unknown) => void; - -export interface UseBooleanHandlers { - on: VoidHandler; - off: VoidHandler; - toggle: DispatchWithOptionalAction; -} - -export type UseBooleanResult = [boolean, UseBooleanHandlers]; - -export const useBoolean = (initialValue: boolean = false): UseBooleanResult => { - const [value, toggle] = useToggle(initialValue); - - const handlers = useMemo( - () => ({ - toggle, - on: () => toggle(true), - off: () => toggle(false), - }), - [toggle] - ); - - return [value, handlers]; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts index c6bfafd020ab..6f00ce32097e 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/selectors.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogStreamQueryActorRef } from '../../../log_stream_query_state'; -import { MatchedStateFromActor } from '../../../xstate_helpers'; import { LogStreamPageActorRef } from './state_machine'; type LogStreamPageStateWithLogViewIndices = diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts index d2a71c65702e..e2755b29d21e 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts @@ -10,6 +10,7 @@ import { TimeRange } from '@kbn/es-query'; import { actions, ActorRefFrom, createMachine, EmittedFrom } from 'xstate'; import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; import type { LogViewNotificationChannel } from '@kbn/logs-shared-plugin/public'; +import { OmitDeprecatedState } from '@kbn/xstate-utils'; import { datemathToEpochMillis } from '../../../../utils/datemath'; import { createLogStreamPositionStateMachine } from '../../../log_stream_position_state/src/state_machine'; import { @@ -17,7 +18,6 @@ import { DEFAULT_TIMERANGE, LogStreamQueryStateMachineDependencies, } from '../../../log_stream_query_state'; -import { OmitDeprecatedState } from '../../../xstate_helpers'; import { waitForInitialQueryParameters, waitForInitialPositionParameters, diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts index 868f29a5c07e..0cc26d3e6ed3 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_position_state/src/state_machine.ts @@ -10,8 +10,8 @@ import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { convertISODateToNanoPrecision } from '@kbn/logs-shared-plugin/common'; import moment from 'moment'; import { actions, ActorRefFrom, createMachine, EmittedFrom, SpecialTargets } from 'xstate'; +import { OmitDeprecatedState, sendIfDefined } from '@kbn/xstate-utils'; import { isSameTimeKey } from '../../../../common/time'; -import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; import { DESIRED_BUFFER_PAGES, RELATIVE_END_UPDATE_DELAY } from './defaults'; import { LogStreamPositionNotificationEventSelectors } from './notifications'; import type { diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts index 1c0de464121c..5570faf16f3f 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts @@ -15,7 +15,7 @@ import { EsQueryConfig } from '@kbn/es-query'; import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { actions, ActorRefFrom, createMachine, SpecialTargets, send } from 'xstate'; import { DEFAULT_REFRESH_INTERVAL } from '@kbn/logs-shared-plugin/common'; -import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; +import { OmitDeprecatedState, sendIfDefined } from '@kbn/xstate-utils'; import { logStreamQueryNotificationEventSelectors } from './notifications'; import { subscribeToFilterSearchBarChanges, diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts index 8e6f993a9755..67b23e66b78e 100644 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts +++ b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/index.ts @@ -6,7 +6,4 @@ */ export * from './invalid_state_callout'; -export * from './notification_channel'; -export * from './send_actions'; -export * from './types'; export * from './state_machine_playground'; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts deleted file mode 100644 index 0108ab022517..000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/notification_channel.ts +++ /dev/null @@ -1,41 +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 { ReplaySubject } from 'rxjs'; -import { ActionFunction, EventObject, Expr, Subscribable } from 'xstate'; - -export interface NotificationChannel { - createService: () => Subscribable; - notify: ( - eventExpr: Expr - ) => ActionFunction; -} - -export const createNotificationChannel = < - TContext, - TEvent extends EventObject, - TSentEvent ->(): NotificationChannel => { - const eventsSubject = new ReplaySubject(1); - - const createService = () => eventsSubject.asObservable(); - - const notify = - (eventExpr: Expr) => - (context: TContext, event: TEvent) => { - const eventToSend = eventExpr(context, event); - - if (eventToSend != null) { - eventsSubject.next(eventToSend); - } - }; - - return { - createService, - notify, - }; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts deleted file mode 100644 index cf446fec63b3..000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.test.ts +++ /dev/null @@ -1,67 +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 { actions, ActorRef, EventObject } from 'xstate'; -import { sendIfDefined } from './send_actions'; - -describe('function sendIfDefined', () => { - it('sends the events to the specified target', () => { - const actor = createMockActor(); - const createEvent = (context: {}) => ({ - type: 'testEvent', - }); - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual([ - actions.send('testEvent', { - to: actor, - }), - ]); - }); - - it('sends the events created by the event expression', () => { - const actor = createMockActor(); - const createEvent = (context: {}) => ({ - type: 'testEvent', - payload: 'payload', - }); - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual([ - actions.send( - { - type: 'testEvent', - payload: 'payload', - }, - { - to: actor, - } - ), - ]); - }); - - it("doesn't send anything when the event expression returns undefined", () => { - const actor = createMockActor(); - const createEvent = (context: {}) => undefined; - - const action = sendIfDefined(actor)(createEvent).get({}, { type: 'triggeringEvent' }); - - expect(action).toEqual(undefined); - }); -}); - -const createMockActor = (): ActorRef => ({ - getSnapshot: jest.fn(), - id: 'mockActor', - send: jest.fn(), - subscribe: jest.fn(), - [Symbol.observable]() { - return this; - }, -}); diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts deleted file mode 100644 index 375fd831b030..000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/send_actions.ts +++ /dev/null @@ -1,36 +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 { - actions, - ActorRef, - AnyEventObject, - EventObject, - Expr, - PureAction, - SendActionOptions, -} from 'xstate'; - -export const sendIfDefined = - (target: string | ActorRef) => - ( - eventExpr: Expr, - options?: SendActionOptions - ): PureAction => { - return actions.pure((context, event) => { - const targetEvent = eventExpr(context, event); - - return targetEvent != null - ? [ - actions.send(targetEvent, { - ...options, - to: target, - }), - ] - : undefined; - }); - }; diff --git a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts b/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts deleted file mode 100644 index 05e75c5fe6e4..000000000000 --- a/x-pack/plugins/observability_solution/infra/public/observability_logs/xstate_helpers/src/types.ts +++ /dev/null @@ -1,43 +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 type { ActorRef, ActorRefWithDeprecatedState, EmittedFrom, State, StateValue } from 'xstate'; - -export type OmitDeprecatedState> = Omit< - T, - 'state' ->; - -export type MatchedState< - TState extends State, - TStateValue extends StateValue -> = TState extends State< - any, - infer TEvent, - infer TStateSchema, - infer TTypestate, - infer TResolvedTypesMeta -> - ? State< - (TTypestate extends any - ? { value: TStateValue; context: any } extends TTypestate - ? TTypestate - : never - : never)['context'], - TEvent, - TStateSchema, - TTypestate, - TResolvedTypesMeta - > & { - value: TStateValue; - } - : never; - -export type MatchedStateFromActor< - TActorRef extends ActorRef, - TStateValue extends StateValue -> = MatchedState, TStateValue>; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx index 50a4852c458c..97841745ae13 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page.tsx @@ -10,13 +10,13 @@ import React from 'react'; import { LogEntryRatePageContent } from './page_content'; import { LogEntryRatePageProviders } from './page_providers'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; -import { anomaliesTitle } from '../../../translations'; +import { logsAnomaliesTitle } from '../../../translations'; import { LogMlJobIdFormatsShimProvider } from '../shared/use_log_ml_job_id_formats_shim'; export const LogEntryRatePage = () => { useLogsBreadcrumbs([ { - text: anomaliesTitle, + text: logsAnomaliesTitle, }, ]); return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx index e4dc0694c3f7..3ac8d7d9137d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/page_content.tsx @@ -36,7 +36,7 @@ import { useLogMlJobIdFormatsShimContext } from '../shared/use_log_ml_job_id_for const JOB_STATUS_POLLING_INTERVAL = 30000; -const anomaliesTitle = i18n.translate('xpack.infra.logs.anomaliesPageTitle', { +const logsAnomaliesTitle = i18n.translate('xpack.infra.logs.anomaliesPageTitle', { defaultMessage: 'Anomalies', }); @@ -101,7 +101,7 @@ export const LogEntryRatePageContent = memo(() => { ); @@ -137,7 +137,7 @@ export const LogEntryRatePageContent = memo(() => { ) { return ( <> - + ); @@ -172,7 +172,7 @@ const AnomaliesPageTemplate: React.FC = ({ rest.isEmptyState ? undefined : { - pageTitle: anomaliesTitle, + pageTitle: logsAnomaliesTitle, } } {...rest} diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx index 5b5965bb2d5e..ecf5af5572b3 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx @@ -9,26 +9,36 @@ import { EuiFlexGroup, EuiFlexItem, EuiHeaderLink, EuiHeaderLinks } from '@elast import { i18n } from '@kbn/i18n'; import React, { useContext } from 'react'; import { Routes, Route } from '@kbn/shared-ux-router'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { SharePublicStart } from '@kbn/share-plugin/public/plugin'; import { ObservabilityOnboardingLocatorParams, OBSERVABILITY_ONBOARDING_LOCATOR, + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, } from '@kbn/deeplinks-observability'; import { dynamic } from '@kbn/shared-ux-utility'; +import { isDevMode } from '@kbn/xstate-utils'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { LazyAlertDropdownWrapper } from '../../alerting/log_threshold'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { HeaderActionMenuContext } from '../../containers/header_action_menu_provider'; import { RedirectWithQueryParams } from '../../utils/redirect_with_query_params'; -import { LogEntryCategoriesPage } from './log_entry_categories'; -import { LogEntryRatePage } from './log_entry_rate'; -import { LogsSettingsPage } from './settings'; -import { StreamPage } from './stream'; -import { isDevMode } from '../../utils/dev_mode'; import { NotFoundPage } from '../404'; +import { getLogsAppRoutes } from './routes'; +const StreamPage = dynamic(() => import('./stream').then((mod) => ({ default: mod.StreamPage }))); +const LogEntryCategoriesPage = dynamic(() => + import('./log_entry_categories').then((mod) => ({ default: mod.LogEntryCategoriesPage })) +); +const LogEntryRatePage = dynamic(() => + import('./log_entry_rate').then((mod) => ({ default: mod.LogEntryRatePage })) +); +const LogsSettingsPage = dynamic(() => + import('./settings').then((mod) => ({ default: mod.LogsSettingsPage })) +); const StateMachinePlayground = dynamic(() => import('../../observability_logs/xstate_helpers').then((mod) => ({ default: mod.StateMachinePlayground, @@ -37,6 +47,9 @@ const StateMachinePlayground = dynamic(() => export const LogsPageContent: React.FunctionComponent = () => { const { application, share } = useKibana<{ share: SharePublicStart }>().services; + + const isLogsStreamEnabled: boolean = useUiSetting(OBSERVABILITY_ENABLE_LOGS_STREAM, false); + const uiCapabilities = application?.capabilities; const onboardingLocator = share?.url.locators.get( OBSERVABILITY_ONBOARDING_LOCATOR @@ -47,30 +60,7 @@ export const LogsPageContent: React.FunctionComponent = () => { useReadOnlyBadge(!uiCapabilities?.logs?.save); - // !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts - const streamTab = { - app: 'logs', - title: streamTabTitle, - pathname: '/stream', - }; - - const anomaliesTab = { - app: 'logs', - title: anomaliesTabTitle, - pathname: '/anomalies', - }; - - const logCategoriesTab = { - app: 'logs', - title: logCategoriesTabTitle, - pathname: '/log-categories', - }; - - const settingsTab = { - app: 'logs', - title: settingsTabTitle, - pathname: '/settings', - }; + const routes = getLogsAppRoutes({ isLogsStreamEnabled }); const settingsLinkProps = useLinkProps({ app: 'logs', @@ -104,25 +94,36 @@ export const LogsPageContent: React.FunctionComponent = () => { )} - - - - + {routes.stream ? ( + + ) : ( + { + share.url.locators + .get(ALL_DATASETS_LOCATOR_ID) + ?.navigate({}); + + return null; + }} + /> + )} + + + {enableDeveloperRoutes && ( )} - - - - ( - - )} + + + + + } /> ); @@ -132,18 +133,6 @@ const pageTitle = i18n.translate('xpack.infra.header.logsTitle', { defaultMessage: 'Logs', }); -const streamTabTitle = i18n.translate('xpack.infra.logs.index.streamTabTitle', { - defaultMessage: 'Stream', -}); - -const anomaliesTabTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', -}); - -const logCategoriesTabTitle = i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', -}); - const settingsTabTitle = i18n.translate('xpack.infra.logs.index.settingsTabTitle', { defaultMessage: 'Settings', }); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts b/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts new file mode 100644 index 000000000000..a5c38672a8be --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/routes.ts @@ -0,0 +1,56 @@ +/* + * 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 { + logsAnomaliesTitle, + logCategoriesTitle, + settingsTitle, + streamTitle, +} from '../../translations'; + +export interface LogsRoute { + id: string; + title: string; + path: string; +} + +export interface LogsAppRoutes { + logsAnomalies: LogsRoute; + logsCategories: LogsRoute; + settings: LogsRoute; + stream?: LogsRoute; +} + +export const getLogsAppRoutes = ({ isLogsStreamEnabled }: { isLogsStreamEnabled: boolean }) => { + const routes: LogsAppRoutes = { + logsAnomalies: { + id: 'anomalies', + title: logsAnomaliesTitle, + path: '/anomalies', + }, + logsCategories: { + id: 'log-categories', + title: logCategoriesTitle, + path: '/log-categories', + }, + settings: { + id: 'settings', + title: settingsTitle, + path: '/settings', + }, + }; + + if (isLogsStreamEnabled) { + routes.stream = { + id: 'stream', + title: streamTitle, + path: '/stream', + }; + } + + return routes; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx index 557fe1cfab31..d1df2a5820dd 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -20,6 +20,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { Prompt } from '@kbn/observability-shared-plugin/public'; import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; +import { LogsDeprecationCallout } from '../../../components/logs_deprecation_callout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; import { settingsTitle } from '../../../translations'; @@ -98,6 +99,7 @@ export const LogsSettingsPage = () => { data-test-subj="sourceConfigurationContent" restrictWidth > + diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx index d40014ed4468..f59d3c1f03fb 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_logs_content.tsx @@ -26,6 +26,7 @@ import { useSelector } from '@xstate/react'; import stringify from 'json-stable-stringify'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogsDeprecationCallout } from '../../../components/logs_deprecation_callout'; import { TimeKey } from '../../../../common/time'; import { AutoSizer } from '../../../components/auto_sizer'; @@ -45,7 +46,6 @@ import { useLogStreamPageStateContext, } from '../../../observability_logs/log_stream_page/state'; import { type ParsedQuery } from '../../../observability_logs/log_stream_query_state'; -import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; import { LogsToolbar } from './page_toolbar'; import { PageViewLogInContext } from './page_view_log_in_context'; @@ -234,7 +234,7 @@ export const StreamPageLogsContent = React.memo<{ return ( <> - + diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx index a8fd0cecf448..497329782d87 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page_providers.tsx @@ -15,6 +15,7 @@ import { useLogStreamContext, useLogViewContext, } from '@kbn/logs-shared-plugin/public'; +import { MatchedStateFromActor } from '@kbn/xstate-utils'; import { LogStreamPageActorRef, LogStreamPageCallbacks, @@ -22,7 +23,6 @@ import { import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; import { LogViewConfigurationProvider } from '../../../containers/logs/log_view_configuration'; import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; -import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; const ViewLogInContext: FC> = ({ children }) => { const { startTimestamp, endTimestamp } = useLogPositionStateContext(); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx index 47e5f58a258d..bca1a3858f5c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx @@ -17,7 +17,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { css } from '@emotion/react'; -import { useBoolean } from '../../../../../hooks/use_boolean'; +import { useBoolean } from '@kbn/react-hooks'; import { InfraGroupByOptions } from '../../../../../common/inventory/types'; import { CustomFieldPanel } from './custom_field_panel'; import { SnapshotGroupBy } from '../../../../../../common/http_api/snapshot_api'; diff --git a/x-pack/plugins/observability_solution/infra/public/plugin.ts b/x-pack/plugins/observability_solution/infra/public/plugin.ts index 843a23bdfccc..daaa3510e166 100644 --- a/x-pack/plugins/observability_solution/infra/public/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/public/plugin.ts @@ -33,6 +33,8 @@ import { type AssetDetailsLocatorParams, type InventoryLocatorParams, } from '@kbn/observability-shared-plugin/common'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; +import { NavigationEntry } from '@kbn/observability-shared-plugin/public'; import type { InfraPublicConfig } from '../common/plugin_config_types'; import { createInventoryMetricRuleType } from './alerting/inventory'; import { createLogThresholdRuleType } from './alerting/log_threshold'; @@ -53,7 +55,14 @@ import type { } from './types'; import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers'; import type { LogStreamSerializedState } from './components/log_stream/types'; -import { hostsTitle, inventoryTitle, metricsExplorerTitle, metricsTitle } from './translations'; +import { + hostsTitle, + inventoryTitle, + logsTitle, + metricsExplorerTitle, + metricsTitle, +} from './translations'; +import { LogsAppRoutes, LogsRoute, getLogsAppRoutes } from './pages/logs/routes'; export class Plugin implements InfraClientPluginClass { public config: InfraPublicConfig; @@ -77,6 +86,8 @@ export class Plugin implements InfraClientPluginClass { } setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) { + const isLogsStreamEnabled = core.uiSettings.get(OBSERVABILITY_ENABLE_LOGS_STREAM, false); + if (pluginsSetup.home) { registerFeatures(pluginsSetup.home); } @@ -125,6 +136,8 @@ export class Plugin implements InfraClientPluginClass { core.settings.client.get$(enableInfrastructureHostsView), ]); + const logRoutes = getLogsAppRoutes({ isLogsStreamEnabled }); + /** !! Need to be kept in sync with the deepLinks in x-pack/plugins/observability_solution/infra/public/plugin.ts */ pluginsSetup.observabilityShared.navigation.registerSections( startDep$AndHostViewFlag$.pipe( @@ -137,32 +150,18 @@ export class Plugin implements InfraClientPluginClass { ], isInfrastructureHostsViewEnabled, ]) => { - const { infrastructure, logs, discover, fleet } = capabilities; + const { infrastructure, logs } = capabilities; return [ ...(logs.show ? [ { - label: 'Logs', + label: logsTitle, sortKey: 200, - entries: [ - ...(discover?.show && fleet?.read - ? [ - { - label: 'Explorer', - app: 'observability-logs-explorer', - path: '/', - isBetaFeature: true, - }, - ] - : []), - ...(this.config.featureFlags.logsUIEnabled - ? [ - { label: 'Stream', app: 'logs', path: '/stream' }, - { label: 'Anomalies', app: 'logs', path: '/anomalies' }, - { label: 'Categories', app: 'logs', path: '/log-categories' }, - ] - : []), - ], + entries: getLogsNavigationEntries({ + capabilities, + config: this.config, + routes: logRoutes, + }), }, ] : []), @@ -230,37 +229,7 @@ export class Plugin implements InfraClientPluginClass { euiIconType: 'logoObservability', order: 8100, appRoute: '/app/logs', - // !! Need to be kept in sync with the routes in x-pack/plugins/observability_solution/infra/public/pages/logs/page_content.tsx - deepLinks: [ - { - id: 'stream', - title: i18n.translate('xpack.infra.logs.index.streamTabTitle', { - defaultMessage: 'Stream', - }), - path: '/stream', - }, - { - id: 'anomalies', - title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', - }), - path: '/anomalies', - }, - { - id: 'log-categories', - title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', - }), - path: '/log-categories', - }, - { - id: 'settings', - title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', { - defaultMessage: 'Settings', - }), - path: '/settings', - }, - ], + deepLinks: Object.values(logRoutes), category: DEFAULT_APP_CATEGORIES.observability, mount: async (params: AppMountParameters) => { // mount callback should not use setup dependencies, get start dependencies instead @@ -384,44 +353,47 @@ export class Plugin implements InfraClientPluginClass { } start(core: InfraClientCoreStart, plugins: InfraClientStartDeps) { - const { http } = core; + const { http, uiSettings } = core; + const isLogsStreamEnabled = uiSettings.get(OBSERVABILITY_ENABLE_LOGS_STREAM, false); const inventoryViews = this.inventoryViews.start({ http }); const metricsExplorerViews = this.metricsExplorerViews?.start({ http }); const telemetry = this.telemetry.start(); - plugins.uiActions.registerAction({ - id: ADD_LOG_STREAM_ACTION_ID, - grouping: [COMMON_EMBEDDABLE_GROUPING.legacy], - order: 30, - getDisplayName: () => - i18n.translate('xpack.infra.logStreamEmbeddable.displayName', { - defaultMessage: 'Log stream (deprecated)', - }), - getDisplayNameTooltip: () => - i18n.translate('xpack.infra.logStreamEmbeddable.description', { - defaultMessage: - 'Add a table of live streaming logs. For a more efficient experience, we recommend using the Discover Page to create a saved search instead of using Log stream.', - }), - getIconType: () => 'logsApp', - isCompatible: async ({ embeddable }) => { - return apiCanAddNewPanel(embeddable); - }, - execute: async ({ embeddable }) => { - if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError(); - embeddable.addNewPanel( - { - panelType: LOG_STREAM_EMBEDDABLE, - initialState: { - title: i18n.translate('xpack.infra.logStreamEmbeddable.title', { - defaultMessage: 'Log stream', - }), + if (isLogsStreamEnabled) { + plugins.uiActions.registerAction({ + id: ADD_LOG_STREAM_ACTION_ID, + grouping: [COMMON_EMBEDDABLE_GROUPING.legacy], + order: 30, + getDisplayName: () => + i18n.translate('xpack.infra.logStreamEmbeddable.displayName', { + defaultMessage: 'Log stream (deprecated)', + }), + getDisplayNameTooltip: () => + i18n.translate('xpack.infra.logStreamEmbeddable.description', { + defaultMessage: + 'Add a table of live streaming logs. For a more efficient experience, we recommend using the Discover Page to create a saved search instead of using Log stream.', + }), + getIconType: () => 'logsApp', + isCompatible: async ({ embeddable }) => { + return apiCanAddNewPanel(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiCanAddNewPanel(embeddable)) throw new IncompatibleActionError(); + embeddable.addNewPanel( + { + panelType: LOG_STREAM_EMBEDDABLE, + initialState: { + title: i18n.translate('xpack.infra.logStreamEmbeddable.title', { + defaultMessage: 'Log stream', + }), + }, }, - }, - true - ); - }, - }); - plugins.uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LOG_STREAM_ACTION_ID); + true + ); + }, + }); + plugins.uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LOG_STREAM_ACTION_ID); + } const startContract: InfraClientStartExports = { inventoryViews, @@ -434,3 +406,42 @@ export class Plugin implements InfraClientPluginClass { stop() {} } + +const getLogsNavigationEntries = ({ + capabilities, + config, + routes, +}: { + capabilities: CoreStart['application']['capabilities']; + config: InfraPublicConfig; + routes: LogsAppRoutes; +}) => { + const entries: NavigationEntry[] = []; + + if (!config.featureFlags.logsUIEnabled) return entries; + + if (capabilities.discover?.show && capabilities.fleet?.read) { + entries.push({ + label: 'Explorer', + app: 'observability-logs-explorer', + path: '/', + isBetaFeature: true, + }); + } + + // Display Stream nav entry when Logs Stream is enabled + if (routes.stream) entries.push(createNavEntryFromRoute(routes.stream)); + // Display always Logs Anomalies and Logs Categories entries + entries.push(createNavEntryFromRoute(routes.logsAnomalies)); + entries.push(createNavEntryFromRoute(routes.logsCategories)); + // Display Logs Settings entry when Logs Stream is not enabled + if (!routes.stream) entries.push(createNavEntryFromRoute(routes.settings)); + + return entries; +}; + +const createNavEntryFromRoute = ({ path, title }: LogsRoute): NavigationEntry => ({ + app: 'logs', + label: title, + path, +}); diff --git a/x-pack/plugins/observability_solution/infra/public/translations.ts b/x-pack/plugins/observability_solution/infra/public/translations.ts index 2e9153ce171b..ecb72b3df4b0 100644 --- a/x-pack/plugins/observability_solution/infra/public/translations.ts +++ b/x-pack/plugins/observability_solution/infra/public/translations.ts @@ -19,14 +19,14 @@ export const streamTitle = i18n.translate('xpack.infra.logs.index.streamTabTitle defaultMessage: 'Stream', }); -export const anomaliesTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { - defaultMessage: 'Anomalies', +export const logsAnomaliesTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', { + defaultMessage: 'Logs Anomalies', }); export const logCategoriesTitle = i18n.translate( 'xpack.infra.logs.index.logCategoriesBetaBadgeTitle', { - defaultMessage: 'Categories', + defaultMessage: 'Logs Categories', } ); diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts index 2cbf6b61623c..b8dd11a17fb0 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts @@ -39,6 +39,7 @@ import { ApmDataAccessPluginStart, } from '@kbn/apm-data-access-plugin/server'; import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server'; +import { ServerlessPluginStart } from '@kbn/serverless/server'; import type { EntityManagerServerPluginStart, EntityManagerServerPluginSetup, @@ -60,6 +61,7 @@ export interface InfraServerPluginSetupDeps { metricsDataAccess: MetricsDataPluginSetup; profilingDataAccess?: ProfilingDataAccessPluginSetup; apmDataAccess: ApmDataAccessPluginSetup; + serverless?: ServerlessPluginStart; entityManager: EntityManagerServerPluginSetup; } diff --git a/x-pack/plugins/observability_solution/infra/server/plugin.ts b/x-pack/plugins/observability_solution/infra/server/plugin.ts index 73d49ed93854..b8becb916a4e 100644 --- a/x-pack/plugins/observability_solution/infra/server/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/server/plugin.ts @@ -59,6 +59,7 @@ import { } from './types'; import { UsageCollector } from './usage/usage_collector'; import { mapSourceToLogView } from './utils/map_source_to_log_view'; +import { uiSettings } from '../common/ui_settings'; export const config: PluginConfigDescriptor = { schema: schema.object({ @@ -211,6 +212,9 @@ export class InfraServerPlugin const inventoryViews = this.inventoryViews.setup(); const metricsExplorerViews = this.metricsExplorerViews?.setup(); + // Register uiSettings config + core.uiSettings.register(uiSettings); + // Register saved object types core.savedObjects.registerType(infraSourceConfigurationSavedObjectType); core.savedObjects.registerType(inventoryViewSavedObjectType); diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index e7aade296fa8..fea285b3a794 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -110,6 +110,9 @@ "@kbn/observability-alerting-rule-utils", "@kbn/core-application-browser", "@kbn/shared-ux-page-no-data-types", + "@kbn/xstate-utils", + "@kbn/management-settings-ids", + "@kbn/core-ui-settings-common", "@kbn/entityManager-plugin", "@kbn/observability-utils", "@kbn/entities-schema" diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index c661976fd076..0c6ceede0756 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -366,6 +366,12 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { defaultMessage: 'Logs categories', }), }, + { + link: 'logs:settings', + title: i18n.translate('xpack.observability.obltNav.otherTools.logsSettings', { + defaultMessage: 'Logs settings', + }), + }, { link: 'maps' }, { link: 'canvas' }, { link: 'graph' }, diff --git a/x-pack/plugins/observability_solution/observability/tsconfig.json b/x-pack/plugins/observability_solution/observability/tsconfig.json index d7a33cb6492c..cc8cef2a9716 100644 --- a/x-pack/plugins/observability_solution/observability/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability/tsconfig.json @@ -113,7 +113,7 @@ "@kbn/io-ts-utils", "@kbn/core-ui-settings-server-mocks", "@kbn/es-types", - "@kbn/logging-mocks" + "@kbn/logging-mocks", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d65dddae3947..e47a52ec5df5 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -23213,7 +23213,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "Termes à mettre en surbrillance", "xpack.infra.logs.index.anomaliesTabTitle": "Anomalies", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "Catégories", - "xpack.infra.logs.index.logsLabel": "Logs", "xpack.infra.logs.index.settingsTabTitle": "Paramètres", "xpack.infra.logs.index.streamTabTitle": "Flux", "xpack.infra.logs.logCategoriesTitle": "Catégories", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3883a4116483..8e2125deaf18 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22963,7 +22963,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "ハイライトする用語", "xpack.infra.logs.index.anomaliesTabTitle": "異常", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "カテゴリー", - "xpack.infra.logs.index.logsLabel": "ログ", "xpack.infra.logs.index.settingsTabTitle": "設定", "xpack.infra.logs.index.streamTabTitle": "ストリーム", "xpack.infra.logs.logCategoriesTitle": "カテゴリー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 823092906f7b..3b7a338fab71 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22994,7 +22994,6 @@ "xpack.infra.logs.highlights.highlightTermsFieldLabel": "要突出显示的词", "xpack.infra.logs.index.anomaliesTabTitle": "异常", "xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "类别", - "xpack.infra.logs.index.logsLabel": "日志", "xpack.infra.logs.index.settingsTabTitle": "设置", "xpack.infra.logs.index.streamTabTitle": "流式传输", "xpack.infra.logs.logCategoriesTitle": "类别", diff --git a/x-pack/test/functional/apps/infra/logs/log_stream.ts b/x-pack/test/functional/apps/infra/logs/log_stream.ts index 859228747782..16dcc038f7aa 100644 --- a/x-pack/test/functional/apps/infra/logs/log_stream.ts +++ b/x-pack/test/functional/apps/infra/logs/log_stream.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { URL } from 'url'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -16,17 +17,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const browser = getService('browser'); const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); describe('Log stream', function () { describe('Legacy URL handling', () => { describe('Correctly handles legacy versions of logFilter', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await esArchiver.unload( 'x-pack/test/functional/es_archives/infra/8.0.0/logs_and_metrics' ); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('Expression and kind', async () => { const location = { diff --git a/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts b/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts index ed1f85248b30..141d1bc38c3d 100644 --- a/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts +++ b/x-pack/test/functional/apps/infra/logs/log_stream_date_nano.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { DATES } from '../constants'; @@ -14,6 +15,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const logsUi = getService('logsUi'); const find = getService('find'); + const kibanaServer = getService('kibanaServer'); const logFilter = { timeRange: { from: DATES.metricsAndLogs.stream.startWithData, @@ -24,9 +26,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Log stream supports nano precision', function () { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/logs_with_nano_date'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/infra/logs_with_nano_date'); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); it('should display logs entries containing date_nano timestamps properly ', async () => { diff --git a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts index 4fdb4687faf6..84158051021c 100644 --- a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts @@ -10,6 +10,7 @@ import { ELASTIC_HTTP_VERSION_HEADER, X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { DATES } from '../constants'; import { FtrProviderContext } from '../../../ftr_provider_context'; @@ -31,9 +32,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Logs Source Configuration', function () { before(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); }); after(async () => { await kibanaServer.savedObjects.cleanStandardList(); + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); }); describe('Allows indices configuration', () => { diff --git a/x-pack/test/functional/apps/infra/page_not_found.ts b/x-pack/test/functional/apps/infra/page_not_found.ts index 479d9979b918..eb1fc77b4f9f 100644 --- a/x-pack/test/functional/apps/infra/page_not_found.ts +++ b/x-pack/test/functional/apps/infra/page_not_found.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { OBSERVABILITY_ENABLE_LOGS_STREAM } from '@kbn/management-settings-ids'; import { FtrProviderContext } from '../../ftr_provider_context'; const logsPages = ['logs/stream', 'logs/anomalies', 'logs/log-categories', 'logs/settings']; @@ -19,14 +20,22 @@ const metricsPages = [ ]; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const find = getService('find'); const pageObjects = getPageObjects(['common', 'infraHome']); + const find = getService('find'); + const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); describe('Infra Not Found page', function () { this.tags('includeFirefox'); describe('Logs', () => { + before(async () => { + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: true }); + }); + after(async () => { + await kibanaServer.uiSettings.update({ [OBSERVABILITY_ENABLE_LOGS_STREAM]: false }); + }); + it('should render the not found page when the route does not exist', async () => { await pageObjects.common.navigateToApp('logs/broken-link'); await testSubjects.existOrFail('infraNotFoundPage');