[8.x] [LogsUI] Add UI setting to hide Logs Stream and dashboard panel option (#194519) (#195542)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[LogsUI] Add UI setting to hide Logs Stream and dashboard panel
option (#194519)](https://github.com/elastic/kibana/pull/194519)

<!--- Backport version: 8.9.8 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Marco Antonio
Ghiani","email":"marcoantonio.ghiani01@gmail.com"},"sourceCommit":{"committedDate":"2024-10-09T08:14:06Z","message":"[LogsUI]
Add UI setting to hide Logs Stream and dashboard panel option
(#194519)\n\n## 📓 Summary\r\n\r\nCloses #193319 \r\nCloses #193320
\r\n\r\nThis work is part of the effort to progressively deprecate the
existing\r\nLogs Stream feature.\r\n\r\nChanges taken on this PR consist
of:\r\n- Create a new uiSettings `observability:enableLogsStream`
which\r\ndefaults to `false` on the stateful/cloud deployments and is
not\r\navailable in serverless ones (still, defaults to `false` behind
the\r\nscene).\r\n- When `observability:enableLogsStream` is `false`,
the Logs Stream page\r\nroute is not registered, and neither is its deep
link for global search.\r\n- When `observability:enableLogsStream` is
`false`, the panels list on\r\nDashboard won't show anymore the option
`Logs Stream (Deprecated)` to\r\nprevent usage of this embeddable in new
dashboards. The embeddable is\r\nstill registered for
retro-compatibility with active dashboards, and it\r\nhas now a callout
explaining the status of this
embeddable\r\n(unmaintained/deprecated).\r\n- Rename logs ML to \"Logs
Anomalies\" and \"Logs Categories\".\r\n\r\nOther minor improvements
regard:\r\n- Remove duplicate Xstate utils and use the relative package
instead.\r\n- Remove the duplicated `useBoolean` hook used in the
deprecation\r\ncallout.\r\n- Sync deep links registration with available
routes through a single\r\n`getLogsRoutes` util.\r\n\r\n## 🎥
Recordings\r\n\r\n### Logs Stream app
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/f4173294-8789-4abd-9972-29c9b7c197ed\r\n\r\n###
Logs Stream dashboard panel entry
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f99ca2a-c030-4867-b976-8fdc1df09d29\r\n\r\n###
Logs Stream app removed from project
nav\r\n\r\n\r\nhttps://github.com/user-attachments/assets/de51bdd6-820a-4c03-8b64-fb1a6ced0a12\r\n\r\n###
Embeddable deprecation callout\r\n\r\n<img width=\"949\"
alt=\"Screenshot 2024-10-02 at 10 22
09\"\r\nsrc=\"https://github.com/user-attachments/assets/99fd5554-004b-45e4-81db-cb23947e210e\">\r\n\r\n###
Unavailable setting in
serverless\r\n\r\n\r\nhttps://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf\r\n\r\n---------\r\n\r\nCo-authored-by:
Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Mike Birnstiehl
<114418652+mdbirnstiehl@users.noreply.github.com>","sha":"9907601dd148ba59420bffda45ff584686f47b43","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:deprecation","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-logs","Team:obs-ux-management","apm:review"],"number":194519,"url":"https://github.com/elastic/kibana/pull/194519","mergeCommit":{"message":"[LogsUI]
Add UI setting to hide Logs Stream and dashboard panel option
(#194519)\n\n## 📓 Summary\r\n\r\nCloses #193319 \r\nCloses #193320
\r\n\r\nThis work is part of the effort to progressively deprecate the
existing\r\nLogs Stream feature.\r\n\r\nChanges taken on this PR consist
of:\r\n- Create a new uiSettings `observability:enableLogsStream`
which\r\ndefaults to `false` on the stateful/cloud deployments and is
not\r\navailable in serverless ones (still, defaults to `false` behind
the\r\nscene).\r\n- When `observability:enableLogsStream` is `false`,
the Logs Stream page\r\nroute is not registered, and neither is its deep
link for global search.\r\n- When `observability:enableLogsStream` is
`false`, the panels list on\r\nDashboard won't show anymore the option
`Logs Stream (Deprecated)` to\r\nprevent usage of this embeddable in new
dashboards. The embeddable is\r\nstill registered for
retro-compatibility with active dashboards, and it\r\nhas now a callout
explaining the status of this
embeddable\r\n(unmaintained/deprecated).\r\n- Rename logs ML to \"Logs
Anomalies\" and \"Logs Categories\".\r\n\r\nOther minor improvements
regard:\r\n- Remove duplicate Xstate utils and use the relative package
instead.\r\n- Remove the duplicated `useBoolean` hook used in the
deprecation\r\ncallout.\r\n- Sync deep links registration with available
routes through a single\r\n`getLogsRoutes` util.\r\n\r\n## 🎥
Recordings\r\n\r\n### Logs Stream app
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/f4173294-8789-4abd-9972-29c9b7c197ed\r\n\r\n###
Logs Stream dashboard panel entry
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f99ca2a-c030-4867-b976-8fdc1df09d29\r\n\r\n###
Logs Stream app removed from project
nav\r\n\r\n\r\nhttps://github.com/user-attachments/assets/de51bdd6-820a-4c03-8b64-fb1a6ced0a12\r\n\r\n###
Embeddable deprecation callout\r\n\r\n<img width=\"949\"
alt=\"Screenshot 2024-10-02 at 10 22
09\"\r\nsrc=\"https://github.com/user-attachments/assets/99fd5554-004b-45e4-81db-cb23947e210e\">\r\n\r\n###
Unavailable setting in
serverless\r\n\r\n\r\nhttps://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf\r\n\r\n---------\r\n\r\nCo-authored-by:
Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Mike Birnstiehl
<114418652+mdbirnstiehl@users.noreply.github.com>","sha":"9907601dd148ba59420bffda45ff584686f47b43"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194519","number":194519,"mergeCommit":{"message":"[LogsUI]
Add UI setting to hide Logs Stream and dashboard panel option
(#194519)\n\n## 📓 Summary\r\n\r\nCloses #193319 \r\nCloses #193320
\r\n\r\nThis work is part of the effort to progressively deprecate the
existing\r\nLogs Stream feature.\r\n\r\nChanges taken on this PR consist
of:\r\n- Create a new uiSettings `observability:enableLogsStream`
which\r\ndefaults to `false` on the stateful/cloud deployments and is
not\r\navailable in serverless ones (still, defaults to `false` behind
the\r\nscene).\r\n- When `observability:enableLogsStream` is `false`,
the Logs Stream page\r\nroute is not registered, and neither is its deep
link for global search.\r\n- When `observability:enableLogsStream` is
`false`, the panels list on\r\nDashboard won't show anymore the option
`Logs Stream (Deprecated)` to\r\nprevent usage of this embeddable in new
dashboards. The embeddable is\r\nstill registered for
retro-compatibility with active dashboards, and it\r\nhas now a callout
explaining the status of this
embeddable\r\n(unmaintained/deprecated).\r\n- Rename logs ML to \"Logs
Anomalies\" and \"Logs Categories\".\r\n\r\nOther minor improvements
regard:\r\n- Remove duplicate Xstate utils and use the relative package
instead.\r\n- Remove the duplicated `useBoolean` hook used in the
deprecation\r\ncallout.\r\n- Sync deep links registration with available
routes through a single\r\n`getLogsRoutes` util.\r\n\r\n## 🎥
Recordings\r\n\r\n### Logs Stream app
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/f4173294-8789-4abd-9972-29c9b7c197ed\r\n\r\n###
Logs Stream dashboard panel entry
removed\r\n\r\n\r\nhttps://github.com/user-attachments/assets/7f99ca2a-c030-4867-b976-8fdc1df09d29\r\n\r\n###
Logs Stream app removed from project
nav\r\n\r\n\r\nhttps://github.com/user-attachments/assets/de51bdd6-820a-4c03-8b64-fb1a6ced0a12\r\n\r\n###
Embeddable deprecation callout\r\n\r\n<img width=\"949\"
alt=\"Screenshot 2024-10-02 at 10 22
09\"\r\nsrc=\"https://github.com/user-attachments/assets/99fd5554-004b-45e4-81db-cb23947e210e\">\r\n\r\n###
Unavailable setting in
serverless\r\n\r\n\r\nhttps://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf\r\n\r\n---------\r\n\r\nCo-authored-by:
Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Mike Birnstiehl
<114418652+mdbirnstiehl@users.noreply.github.com>","sha":"9907601dd148ba59420bffda45ff584686f47b43"}}]}]
BACKPORT-->
This commit is contained in:
Marco Antonio Ghiani 2024-10-09 12:05:38 +02:00 committed by GitHub
parent b8d7596193
commit ef3ebf0ae3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 387 additions and 406 deletions

View file

@ -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 =

View file

@ -510,6 +510,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
_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.' },

View file

@ -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;

View file

@ -10486,6 +10486,12 @@
}
}
},
"observability:enableLogsStream": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
}
},
"observability:searchExcludedDataTiers": {
"type": "array",
"items": {

View file

@ -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();

View file

@ -76,5 +76,6 @@
"@kbn/default-nav-management",
"@kbn/default-nav-devtools",
"@kbn/core-saved-objects-import-export-server-internal",
"@kbn/management-settings-ids",
]
}

View file

@ -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<string, UiSettingsParams> = {
[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,
},
};

View file

@ -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$}
>
<EuiThemeProvider darkMode={darkMode}>
<div style={{ width: '100%' }}>
<div style={{ width: '100%', position: 'relative' }}>
<LogStream
logView={{ type: 'log-view-reference', logViewId: 'default' }}
startTimestamp={startTimestamp}
@ -90,6 +95,7 @@ export function getLogStreamEmbeddableFactory(services: Services) {
query={query as Query | undefined}
filters={filters}
/>
<DeprecationCallout />
</div>
</EuiThemeProvider>
</LogStreamEmbeddableProviders>
@ -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 (
<EuiCallOut
color="warning"
iconType="help"
onDismiss={() => setDismissed(true)}
css={{
position: 'absolute',
bottom: euiThemeVars.euiSizeM,
right: euiThemeVars.euiSizeM,
width: 'min(100%, 40ch)',
}}
>
<p>
<FormattedMessage
id="xpack.infra.logsStreamEmbeddable.deprecationWarningDescription"
defaultMessage="Logs Stream panels are no longer maintained. Try using {savedSearchDocsLink} for a similar visualization."
values={{
savedSearchDocsLink: (
<EuiLink
data-test-subj="infraDeprecationCalloutSavedSearchesLink"
href={SAVED_SEARCH_DOCS_URL}
target="_blank"
>
{i18n.translate(
'xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel',
{ defaultMessage: 'saved searches' }
)}
</EuiLink>
),
}}
/>
</p>
</EuiCallOut>
);
};
export interface LogStreamEmbeddableProvidersProps {
core: CoreStart;
pluginStart: InfraClientStartExports;

View file

@ -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.',
}
),
},
};
export const LogsDeprecationCallout = () => {
interface LogsDeprecationCalloutProps {
page: keyof typeof pageConfigurations;
}
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}
>
<p>
{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.',
})}
</p>
<p>{message}</p>
<EuiButton
fill
data-test-subj="infraLogsDeprecationCalloutTryLogsExplorerButton"

View file

@ -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 { useMemo } from 'react';
import useToggle from 'react-use/lib/useToggle';
export type VoidHandler = () => void;
export type DispatchWithOptionalAction<Type> = (_arg?: Type | unknown) => void;
export interface UseBooleanHandlers {
on: VoidHandler;
off: VoidHandler;
toggle: DispatchWithOptionalAction<boolean>;
}
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];
};

View file

@ -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 =

View file

@ -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,

View file

@ -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 {

View file

@ -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,

View file

@ -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';

View file

@ -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<TContext, TEvent extends EventObject, TSentEvent> {
createService: () => Subscribable<TSentEvent>;
notify: (
eventExpr: Expr<TContext, TEvent, TSentEvent | undefined>
) => ActionFunction<TContext, TEvent>;
}
export const createNotificationChannel = <
TContext,
TEvent extends EventObject,
TSentEvent
>(): NotificationChannel<TContext, TEvent, TSentEvent> => {
const eventsSubject = new ReplaySubject<TSentEvent>(1);
const createService = () => eventsSubject.asObservable();
const notify =
(eventExpr: Expr<TContext, TEvent, TSentEvent | undefined>) =>
(context: TContext, event: TEvent) => {
const eventToSend = eventExpr(context, event);
if (eventToSend != null) {
eventsSubject.next(eventToSend);
}
};
return {
createService,
notify,
};
};

View file

@ -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 = <T extends EventObject>(): ActorRef<T> => ({
getSnapshot: jest.fn(),
id: 'mockActor',
send: jest.fn(),
subscribe: jest.fn(),
[Symbol.observable]() {
return this;
},
});

View file

@ -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 =
<TSentEvent extends EventObject = AnyEventObject>(target: string | ActorRef<TSentEvent>) =>
<TContext, TEvent extends EventObject>(
eventExpr: Expr<TContext, TEvent, TSentEvent | undefined>,
options?: SendActionOptions<TContext, TEvent>
): PureAction<TContext, TEvent> => {
return actions.pure((context, event) => {
const targetEvent = eventExpr(context, event);
return targetEvent != null
? [
actions.send(targetEvent, {
...options,
to: target,
}),
]
: undefined;
});
};

View file

@ -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<T extends ActorRefWithDeprecatedState<any, any, any, any>> = Omit<
T,
'state'
>;
export type MatchedState<
TState extends State<any, any, any, any, any>,
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<any, any>,
TStateValue extends StateValue
> = MatchedState<EmittedFrom<TActorRef>, TStateValue>;

View file

@ -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 (

View file

@ -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(() => {
<SubscriptionSplashPage
data-test-subj="logsLogEntryRatePage"
pageHeader={{
pageTitle: anomaliesTitle,
pageTitle: logsAnomaliesTitle,
}}
/>
);
@ -137,7 +137,7 @@ export const LogEntryRatePageContent = memo(() => {
) {
return (
<>
<LogEntryRateResultsContent idFormats={idFormats} pageTitle={anomaliesTitle} />
<LogEntryRateResultsContent idFormats={idFormats} pageTitle={logsAnomaliesTitle} />
<LogAnalysisSetupFlyout />
</>
);
@ -172,7 +172,7 @@ const AnomaliesPageTemplate: React.FC<LazyObservabilityPageTemplateProps> = ({
rest.isEmptyState
? undefined
: {
pageTitle: anomaliesTitle,
pageTitle: logsAnomaliesTitle,
}
}
{...rest}

View file

@ -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<ObservabilityOnboardingLocatorParams>(
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>
<Route path={streamTab.pathname} component={StreamPage} />
<Route path={anomaliesTab.pathname} component={LogEntryRatePage} />
<Route path={logCategoriesTab.pathname} component={LogEntryCategoriesPage} />
<Route path={settingsTab.pathname} component={LogsSettingsPage} />
{routes.stream ? (
<Route path={routes.stream.path} component={StreamPage} />
) : (
<Route
path="/stream"
exact
render={() => {
share.url.locators
.get<AllDatasetsLocatorParams>(ALL_DATASETS_LOCATOR_ID)
?.navigate({});
return null;
}}
/>
)}
<Route path={routes.logsAnomalies.path} component={LogEntryRatePage} />
<Route path={routes.logsCategories.path} component={LogEntryCategoriesPage} />
<Route path={routes.settings.path} component={LogsSettingsPage} />
{enableDeveloperRoutes && (
<Route path={'/state-machine-playground'} component={StateMachinePlayground} />
)}
<RedirectWithQueryParams from={'/analysis'} to={anomaliesTab.pathname} exact />
<RedirectWithQueryParams from={'/log-rate'} to={anomaliesTab.pathname} exact />
<RedirectWithQueryParams from={'/'} to={streamTab.pathname} exact />
<Route
render={() => (
<NotFoundPage
title={i18n.translate('xpack.infra.logs.index.logsLabel', {
defaultMessage: 'Logs',
})}
/>
)}
<RedirectWithQueryParams from={'/analysis'} to={routes.logsAnomalies.path} exact />
<RedirectWithQueryParams from={'/log-rate'} to={routes.logsAnomalies.path} exact />
<RedirectWithQueryParams
from={'/'}
to={routes.stream?.path ?? routes.logsAnomalies.path}
exact
/>
<Route render={() => <NotFoundPage title={pageTitle} />} />
</Routes>
</>
);
@ -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',
});

View file

@ -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;
};

View file

@ -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
>
<LogsDeprecationCallout page="settings" />
<Prompt
prompt={sourceConfigurationFormElement.isDirty ? unsavedFormPromptMessage : undefined}
/>

View file

@ -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 (
<>
<LogsDeprecationCallout />
<LogsDeprecationCallout page="stream" />
<WithLogTextviewUrlState />
<WithFlyoutOptionsUrlState />
<LogsToolbar />

View file

@ -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<PropsWithChildren<unknown>> = ({ children }) => {
const { startTimestamp, endTimestamp } = useLogPositionStateContext();

View file

@ -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';

View file

@ -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$<boolean>(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<EmbeddableApiContext>({
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<LogStreamSerializedState>(
{
panelType: LOG_STREAM_EMBEDDABLE,
initialState: {
title: i18n.translate('xpack.infra.logStreamEmbeddable.title', {
defaultMessage: 'Log stream',
}),
if (isLogsStreamEnabled) {
plugins.uiActions.registerAction<EmbeddableApiContext>({
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<LogStreamSerializedState>(
{
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,
});

View file

@ -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',
}
);

View file

@ -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;
}

View file

@ -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<InfraConfig> = {
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);

View file

@ -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"

View file

@ -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' },

View file

@ -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/**/*"]
}

View file

@ -23215,7 +23215,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",

View file

@ -22965,7 +22965,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": "カテゴリー",

View file

@ -22996,7 +22996,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": "类别",

View file

@ -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 = {

View file

@ -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 () => {

View file

@ -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', () => {

View file

@ -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');