mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[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:
parent
9975fd63d3
commit
9907601dd1
41 changed files with 387 additions and 406 deletions
|
@ -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 =
|
||||
|
|
|
@ -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.' },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10486,6 +10486,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"observability:enableLogsStream": {
|
||||
"type": "boolean",
|
||||
"_meta": {
|
||||
"description": "Non-default value of setting."
|
||||
}
|
||||
},
|
||||
"banners:placement": {
|
||||
"type": "keyword",
|
||||
"_meta": {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -76,5 +76,6 @@
|
|||
"@kbn/default-nav-management",
|
||||
"@kbn/default-nav-devtools",
|
||||
"@kbn/core-saved-objects-import-export-server-internal",
|
||||
"@kbn/management-settings-ids",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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];
|
||||
};
|
|
@ -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 =
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
|
@ -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;
|
||||
},
|
||||
});
|
|
@ -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;
|
||||
});
|
||||
};
|
|
@ -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>;
|
|
@ -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 (
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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/**/*"]
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "カテゴリー",
|
||||
|
|
|
@ -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": "类别",
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue