mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Log Explorer] Flyout details ai assistant registration (#170658)
## 📓 Summary
Closes #169506
This PR introduces a mechanism to apply customizations on the
LogExplorer component.
The first necessary customization which is implemented is for the flyout
detail, allowing the consumer to display additional content on top of
what is already displayed.
This is a temporary solution which will be updated and embedded in a
more structured customization system as a result of the work done for
https://github.com/elastic/kibana/issues/165255.
The current solution creates already a context to allow granular
consumption of the customizations only for those subtrees where a
specific customization should apply.
The LogAIAssistant is used to customize the current LogExplorer as the
first usage of this customization.
c9e6b40e
-e636-456a-9e19-1778c26142db
---------
Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e00566fa98
commit
43f9712dd5
19 changed files with 215 additions and 25 deletions
|
@ -17,6 +17,7 @@ import { createLogExplorerProfileCustomizations } from '../../customizations/log
|
|||
import { createPropertyGetProxy } from '../../utils/proxies';
|
||||
import { LogExplorerProfileContext } from '../../state_machines/log_explorer_profile';
|
||||
import { LogExplorerStartDeps } from '../../types';
|
||||
import { LogExplorerCustomizations } from './types';
|
||||
|
||||
export interface CreateLogExplorerArgs {
|
||||
core: CoreStart;
|
||||
|
@ -29,6 +30,7 @@ export interface LogExplorerStateContainer {
|
|||
}
|
||||
|
||||
export interface LogExplorerProps {
|
||||
customizations?: LogExplorerCustomizations;
|
||||
scopedHistory: ScopedHistory;
|
||||
state$?: BehaviorSubject<LogExplorerStateContainer>;
|
||||
}
|
||||
|
@ -44,10 +46,10 @@ export const createLogExplorer = ({ core, plugins }: CreateLogExplorerArgs) => {
|
|||
uiSettings: createUiSettingsServiceProxy(core.uiSettings),
|
||||
};
|
||||
|
||||
return ({ scopedHistory, state$ }: LogExplorerProps) => {
|
||||
return ({ customizations = {}, scopedHistory, state$ }: LogExplorerProps) => {
|
||||
const logExplorerCustomizations = useMemo(
|
||||
() => [createLogExplorerProfileCustomizations({ core, plugins, state$ })],
|
||||
[state$]
|
||||
() => [createLogExplorerProfileCustomizations({ core, customizations, plugins, state$ })],
|
||||
[customizations, state$]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 { DataTableRecord } from '@kbn/discover-utils/types';
|
||||
|
||||
export type RenderPreviousContent = () => React.ReactNode;
|
||||
|
||||
export interface LogExplorerFlyoutContentProps {
|
||||
doc: DataTableRecord;
|
||||
}
|
||||
|
||||
export type FlyoutRenderContent = (
|
||||
renderPreviousContent: RenderPreviousContent,
|
||||
props: LogExplorerFlyoutContentProps
|
||||
) => React.ReactNode;
|
||||
|
||||
export interface LogExplorerCustomizations {
|
||||
flyout?: {
|
||||
renderContent?: FlyoutRenderContent;
|
||||
};
|
||||
}
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FlyoutDetail } from '../components/flyout_detail/flyout_detail';
|
||||
import { FlyoutProps } from '../components/flyout_detail';
|
||||
import { useLogExplorerCustomizationsContext } from '../hooks/use_log_explorer_customizations';
|
||||
|
||||
export const CustomFlyoutContent = ({
|
||||
actions,
|
||||
|
@ -16,12 +17,28 @@ export const CustomFlyoutContent = ({
|
|||
doc,
|
||||
renderDefaultContent,
|
||||
}: FlyoutProps) => {
|
||||
const { flyout } = useLogExplorerCustomizationsContext();
|
||||
|
||||
const renderPreviousContent = useCallback(
|
||||
() => (
|
||||
<>
|
||||
{/* Apply custom Log Explorer detail */}
|
||||
<EuiFlexItem>
|
||||
<FlyoutDetail actions={actions} dataView={dataView} doc={doc} />
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
),
|
||||
[actions, dataView, doc]
|
||||
);
|
||||
|
||||
const content = flyout?.renderContent
|
||||
? flyout?.renderContent(renderPreviousContent, { doc })
|
||||
: renderPreviousContent();
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column">
|
||||
{/* Apply custom Log Explorer detail */}
|
||||
<EuiFlexItem>
|
||||
<FlyoutDetail actions={actions} dataView={dataView} doc={doc} />
|
||||
</EuiFlexItem>
|
||||
{content}
|
||||
{/* Restore default content */}
|
||||
<EuiFlexItem>{renderDefaultContent()}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -14,6 +14,8 @@ import { LogExplorerProfileStateService } from '../state_machines/log_explorer_p
|
|||
import { LogExplorerStateContainer } from '../components/log_explorer';
|
||||
import { LogExplorerStartDeps } from '../types';
|
||||
import { useKibanaContextForPluginProvider } from '../utils/use_kibana';
|
||||
import { LogExplorerCustomizations } from '../components/log_explorer/types';
|
||||
import { LogExplorerCustomizationsProvider } from '../hooks/use_log_explorer_customizations';
|
||||
|
||||
const LazyCustomDatasetFilters = dynamic(() => import('./custom_dataset_filters'));
|
||||
const LazyCustomDatasetSelector = dynamic(() => import('./custom_dataset_selector'));
|
||||
|
@ -21,12 +23,18 @@ const LazyCustomFlyoutContent = dynamic(() => import('./custom_flyout_content'))
|
|||
|
||||
export interface CreateLogExplorerProfileCustomizationsDeps {
|
||||
core: CoreStart;
|
||||
customizations: LogExplorerCustomizations;
|
||||
plugins: LogExplorerStartDeps;
|
||||
state$?: BehaviorSubject<LogExplorerStateContainer>;
|
||||
}
|
||||
|
||||
export const createLogExplorerProfileCustomizations =
|
||||
({ core, plugins, state$ }: CreateLogExplorerProfileCustomizationsDeps): CustomizationCallback =>
|
||||
({
|
||||
core,
|
||||
customizations: logExplorerCustomizations,
|
||||
plugins,
|
||||
state$,
|
||||
}: CreateLogExplorerProfileCustomizationsDeps): CustomizationCallback =>
|
||||
async ({ customizations, stateContainer }) => {
|
||||
const { data, dataViews, discover } = plugins;
|
||||
// Lazy load dependencies
|
||||
|
@ -127,7 +135,9 @@ export const createLogExplorerProfileCustomizations =
|
|||
|
||||
return (
|
||||
<KibanaContextProviderForPlugin>
|
||||
<LazyCustomFlyoutContent {...props} dataView={internalState.dataView} />
|
||||
<LogExplorerCustomizationsProvider value={logExplorerCustomizations}>
|
||||
<LazyCustomFlyoutContent {...props} dataView={internalState.dataView} />
|
||||
</LogExplorerCustomizationsProvider>
|
||||
</KibanaContextProviderForPlugin>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 createContainer from 'constate';
|
||||
import { LogExplorerCustomizations } from '../components/log_explorer/types';
|
||||
|
||||
interface UseLogExplorerCustomizationsDeps {
|
||||
value: LogExplorerCustomizations;
|
||||
}
|
||||
|
||||
const useLogExplorerCustomizations = ({ value }: UseLogExplorerCustomizationsDeps) => value;
|
||||
|
||||
export const [LogExplorerCustomizationsProvider, useLogExplorerCustomizationsContext] =
|
||||
createContainer(useLogExplorerCustomizations);
|
|
@ -10,6 +10,10 @@ import type { LogExplorerConfig } from '../common/plugin_config';
|
|||
import { LogExplorerPlugin } from './plugin';
|
||||
export type { LogExplorerPluginSetup, LogExplorerPluginStart } from './types';
|
||||
export type { LogExplorerStateContainer } from './components/log_explorer';
|
||||
export type {
|
||||
LogExplorerCustomizations,
|
||||
LogExplorerFlyoutContentProps,
|
||||
} from './components/log_explorer/types';
|
||||
|
||||
export function plugin(context: PluginInitializerContext<LogExplorerConfig>) {
|
||||
return new LogExplorerPlugin(context);
|
||||
|
|
|
@ -4,22 +4,25 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { Optional } from '@kbn/utility-types';
|
||||
import { dynamic } from '../../../common/dynamic';
|
||||
import type { LogAIAssistantProps } from './log_ai_assistant';
|
||||
import type { LogAIAssistantDeps } from './log_ai_assistant';
|
||||
|
||||
export const LogAIAssistant = dynamic(() => import('./log_ai_assistant'));
|
||||
|
||||
interface LogAIAssistantFactoryDeps {
|
||||
observabilityAIAssistant: LogAIAssistantProps['aiAssistant'];
|
||||
observabilityAIAssistant: LogAIAssistantDeps['observabilityAIAssistant'];
|
||||
}
|
||||
|
||||
export function createLogAIAssistant({ observabilityAIAssistant }: LogAIAssistantFactoryDeps) {
|
||||
return ({
|
||||
aiAssistant = observabilityAIAssistant,
|
||||
...props
|
||||
}: Optional<LogAIAssistantProps, 'aiAssistant'>) => (
|
||||
<LogAIAssistant aiAssistant={aiAssistant} {...props} />
|
||||
export type LogAIAssistantComponent = ComponentType<
|
||||
Optional<LogAIAssistantDeps, 'observabilityAIAssistant'>
|
||||
>;
|
||||
|
||||
export function createLogAIAssistant({
|
||||
observabilityAIAssistant: aiAssistant,
|
||||
}: LogAIAssistantFactoryDeps): LogAIAssistantComponent {
|
||||
return ({ observabilityAIAssistant = aiAssistant, ...props }) => (
|
||||
<LogAIAssistant observabilityAIAssistant={observabilityAIAssistant} {...props} />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
|
||||
export const createLogAIAssistantMock = () => jest.fn().mockReturnValue(<div />);
|
|
@ -12,6 +12,8 @@ import {
|
|||
type Message,
|
||||
ObservabilityAIAssistantPluginStart,
|
||||
MessageRole,
|
||||
ObservabilityAIAssistantProvider,
|
||||
useObservabilityAIAssistant,
|
||||
} from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import { LogEntryField } from '../../../common';
|
||||
import { explainLogMessageTitle, similarLogMessagesTitle } from './translations';
|
||||
|
@ -21,11 +23,16 @@ export interface LogAIAssistantDocument {
|
|||
}
|
||||
|
||||
export interface LogAIAssistantProps {
|
||||
aiAssistant: ObservabilityAIAssistantPluginStart;
|
||||
doc: LogAIAssistantDocument | undefined;
|
||||
}
|
||||
|
||||
export function LogAIAssistant({ aiAssistant, doc }: LogAIAssistantProps) {
|
||||
export interface LogAIAssistantDeps extends LogAIAssistantProps {
|
||||
observabilityAIAssistant: ObservabilityAIAssistantPluginStart;
|
||||
}
|
||||
|
||||
export const LogAIAssistant = withProviders(({ doc }: LogAIAssistantProps) => {
|
||||
const aiAssistant = useObservabilityAIAssistant();
|
||||
|
||||
const explainLogMessageMessages = useMemo<Message[] | undefined>(() => {
|
||||
if (!doc) {
|
||||
return undefined;
|
||||
|
@ -80,7 +87,20 @@ export function LogAIAssistant({ aiAssistant, doc }: LogAIAssistantProps) {
|
|||
) : null}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default LogAIAssistant;
|
||||
|
||||
function withProviders(Component: React.FunctionComponent<LogAIAssistantProps>) {
|
||||
return function ComponentWithProviders({
|
||||
observabilityAIAssistant,
|
||||
...props
|
||||
}: LogAIAssistantDeps) {
|
||||
return (
|
||||
<ObservabilityAIAssistantProvider value={observabilityAIAssistant}>
|
||||
<Component {...props} />
|
||||
</ObservabilityAIAssistantProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ export const LogEntryFlyout = ({
|
|||
>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<LogAIAssistant aiAssistant={observabilityAIAssistant} doc={logEntry} />
|
||||
<LogAIAssistant observabilityAIAssistant={observabilityAIAssistant} doc={logEntry} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LogEntryFieldsTable logEntry={logEntry} onSetFieldFilter={onSetFieldFilter} />
|
||||
|
|
|
@ -37,6 +37,7 @@ export { useLogSummary, WithSummary } from './containers/logs/log_summary';
|
|||
export { useLogEntryFlyout } from './components/logging/log_entry_flyout';
|
||||
|
||||
// Shared components
|
||||
export type { LogAIAssistantDocument } from './components/log_ai_assistant/log_ai_assistant';
|
||||
export type {
|
||||
LogEntryStreamItem,
|
||||
LogEntryColumnWidths,
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createLogAIAssistantMock } from './components/log_ai_assistant/log_ai_assistant.mock';
|
||||
import { createLogViewsServiceStartMock } from './services/log_views/log_views_service.mock';
|
||||
import { LogsSharedClientStartExports } from './types';
|
||||
|
||||
export const createLogsSharedPluginStartMock = (): jest.Mocked<LogsSharedClientStartExports> => ({
|
||||
logViews: createLogViewsServiceStartMock(),
|
||||
LogAIAssistant: createLogAIAssistantMock(),
|
||||
});
|
||||
|
||||
export const _ensureTypeCompatibility = (): LogsSharedClientStartExports =>
|
||||
|
|
|
@ -17,6 +17,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
|||
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { LogAIAssistantComponent } from './components/log_ai_assistant';
|
||||
// import type { OsqueryPluginStart } from '../../osquery/public';
|
||||
import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views';
|
||||
|
||||
|
@ -27,6 +28,7 @@ export interface LogsSharedClientSetupExports {
|
|||
|
||||
export interface LogsSharedClientStartExports {
|
||||
logViews: LogViewsServiceStart;
|
||||
LogAIAssistant: LogAIAssistantComponent;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"data",
|
||||
"discover",
|
||||
"logExplorer",
|
||||
"logsShared",
|
||||
"observabilityShared",
|
||||
"share",
|
||||
"kibanaUtils",
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { EuiFlexItem } from '@elastic/eui';
|
||||
import {
|
||||
LogExplorerCustomizations,
|
||||
LogExplorerFlyoutContentProps,
|
||||
} from '@kbn/log-explorer-plugin/public';
|
||||
import type { LogAIAssistantDocument } from '@kbn/logs-shared-plugin/public';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useKibanaContextForPlugin } from '../utils/use_kibana';
|
||||
|
||||
const ObservabilityLogAIAssistant = ({ doc }: LogExplorerFlyoutContentProps) => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { LogAIAssistant } = services.logsShared;
|
||||
|
||||
const mappedDoc = useMemo(() => mapDocToAIAssistantFormat(doc), [doc]);
|
||||
|
||||
return <LogAIAssistant key={doc.id} doc={mappedDoc} />;
|
||||
};
|
||||
|
||||
export const renderFlyoutContent: Required<LogExplorerCustomizations>['flyout']['renderContent'] = (
|
||||
renderPreviousContent,
|
||||
props
|
||||
) => {
|
||||
return (
|
||||
<>
|
||||
{renderPreviousContent()}
|
||||
<EuiFlexItem>
|
||||
<ObservabilityLogAIAssistant {...props} />
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
const mapDocToAIAssistantFormat = (doc: LogExplorerFlyoutContentProps['doc']) => {
|
||||
if (!doc) return;
|
||||
|
||||
return {
|
||||
fields: Object.entries(doc.flattened).map(([field, value]) => ({
|
||||
field,
|
||||
value,
|
||||
})) as LogAIAssistantDocument['fields'],
|
||||
};
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { LogExplorerCustomizations } from '@kbn/log-explorer-plugin/public';
|
||||
import { renderFlyoutContent } from './flyout_content';
|
||||
|
||||
export const createLogExplorerCustomizations = (): LogExplorerCustomizations => ({
|
||||
flyout: {
|
||||
renderContent: renderFlyoutContent,
|
||||
},
|
||||
});
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LogExplorerTopNavMenu } from '../../components/log_explorer_top_nav_menu';
|
||||
import { ObservabilityLogExplorerPageTemplate } from '../../components/page_template';
|
||||
|
@ -14,6 +14,7 @@ import { noBreadcrumbs, useBreadcrumbs } from '../../utils/breadcrumbs';
|
|||
import { useKibanaContextForPlugin } from '../../utils/use_kibana';
|
||||
import { ObservabilityLogExplorerAppMountParameters } from '../../types';
|
||||
import { LazyOriginInterpreter } from '../../state_machines/origin_interpreter/src/lazy_component';
|
||||
import { createLogExplorerCustomizations } from '../../log_explorer_customizations';
|
||||
export interface ObservablityLogExplorerMainRouteProps {
|
||||
appParams: ObservabilityLogExplorerAppMountParameters;
|
||||
core: CoreStart;
|
||||
|
@ -31,6 +32,8 @@ export const ObservablityLogExplorerMainRoute = ({
|
|||
|
||||
const [state$] = useState(() => new BehaviorSubject({}));
|
||||
|
||||
const customizations = useMemo(() => createLogExplorerCustomizations(), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LogExplorerTopNavMenu
|
||||
|
@ -41,7 +44,11 @@ export const ObservablityLogExplorerMainRoute = ({
|
|||
/>
|
||||
<LazyOriginInterpreter history={history} toasts={core.notifications.toasts} />
|
||||
<ObservabilityLogExplorerPageTemplate observabilityShared={observabilityShared}>
|
||||
<logExplorer.LogExplorer scopedHistory={history} state$={state$} />
|
||||
<logExplorer.LogExplorer
|
||||
customizations={customizations}
|
||||
scopedHistory={history}
|
||||
state$={state$}
|
||||
/>
|
||||
</ObservabilityLogExplorerPageTemplate>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -12,6 +12,7 @@ import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin
|
|||
import { ServerlessPluginStart } from '@kbn/serverless/public';
|
||||
import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import { AppMountParameters, ScopedHistory } from '@kbn/core/public';
|
||||
import { LogsSharedClientStartExports } from '@kbn/logs-shared-plugin/public';
|
||||
import {
|
||||
ObservabilityLogExplorerLocators,
|
||||
ObservabilityLogExplorerLocationState,
|
||||
|
@ -33,6 +34,7 @@ export interface ObservabilityLogExplorerStartDeps {
|
|||
data: DataPublicPluginStart;
|
||||
discover: DiscoverStart;
|
||||
logExplorer: LogExplorerPluginStart;
|
||||
logsShared: LogsSharedClientStartExports;
|
||||
observabilityShared: ObservabilitySharedPluginStart;
|
||||
serverless?: ServerlessPluginStart;
|
||||
share: SharePluginStart;
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
"@kbn/core-mount-utils-browser-internal",
|
||||
"@kbn/xstate-utils",
|
||||
"@kbn/shared-ux-utility",
|
||||
"@kbn/ui-theme"
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/logs-shared-plugin"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue