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

## 📓 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

<img width="949" alt="Screenshot 2024-10-02 at 10 22 09"
src="https://github.com/user-attachments/assets/99fd5554-004b-45e4-81db-cb23947e210e">

### Unavailable setting in serverless


https://github.com/user-attachments/assets/91bf6c37-0845-4918-a485-b6250bbd96bf

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Mike Birnstiehl <114418652+mdbirnstiehl@users.noreply.github.com>
This commit is contained in:
Marco Antonio Ghiani 2024-10-09 10:14:06 +02:00 committed by GitHub
parent 9975fd63d3
commit 9907601dd1
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."
}
},
"banners:placement": {
"type": "keyword",
"_meta": {

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

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

View file

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

View file

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

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