From 45ae4a7c087ea0b64db91b1d7311cbb6a0d791c4 Mon Sep 17 00:00:00 2001
From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com>
Date: Tue, 5 Nov 2024 12:39:34 +0000
Subject: [PATCH] Add new saved search component
---
.github/CODEOWNERS | 1 +
package.json | 1 +
packages/kbn-saved-search-component/README.md | 26 +++
packages/kbn-saved-search-component/index.ts | 18 ++
.../kbn-saved-search-component/jest.config.js | 14 ++
.../kbn-saved-search-component/kibana.jsonc | 5 +
.../kbn-saved-search-component/package.json | 7 +
.../src/components/saved_search.tsx | 160 ++++++++++++++++++
.../kbn-saved-search-component/src/types.ts | 30 ++++
.../kbn-saved-search-component/tsconfig.json | 28 +++
.../presentation_publishing/index.ts | 6 +-
.../interfaces/publishes_data_views.ts | 4 +
.../initialize_search_embeddable_api.tsx | 28 ++-
.../discover/public/embeddable/types.ts | 9 +-
src/plugins/discover/public/index.ts | 2 +
tsconfig.base.json | 2 +
.../log_category_details_flyout.tsx | 42 ++++-
.../log_category_document_examples_table.tsx | 151 +++--------------
.../observability/logs_overview/tsconfig.json | 7 +-
.../observability_solution/apm/kibana.jsonc | 3 +-
.../components/app/service_logs/index.tsx | 45 +++--
.../apm/public/plugin.ts | 4 +
.../observability_solution/apm/tsconfig.json | 2 +
.../services/log_sources_service/index.ts | 45 +++--
.../logs_shared/kibana.jsonc | 5 +-
.../logs_shared/public/plugin.tsx | 4 +-
.../logs_shared/public/types.ts | 4 +
.../logs_shared/tsconfig.json | 2 +
yarn.lock | 4 +
29 files changed, 483 insertions(+), 176 deletions(-)
create mode 100644 packages/kbn-saved-search-component/README.md
create mode 100644 packages/kbn-saved-search-component/index.ts
create mode 100644 packages/kbn-saved-search-component/jest.config.js
create mode 100644 packages/kbn-saved-search-component/kibana.jsonc
create mode 100644 packages/kbn-saved-search-component/package.json
create mode 100644 packages/kbn-saved-search-component/src/components/saved_search.tsx
create mode 100644 packages/kbn-saved-search-component/src/types.ts
create mode 100644 packages/kbn-saved-search-component/tsconfig.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 73a670d14534..2f3429e52324 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -462,6 +462,7 @@ packages/kbn-rrule @elastic/response-ops
packages/kbn-rule-data-utils @elastic/security-detections-response @elastic/response-ops @elastic/obs-ux-management-team
packages/kbn-safer-lodash-set @elastic/kibana-security
packages/kbn-saved-objects-settings @elastic/appex-sharedux
+packages/kbn-saved-search-component @elastic/obs-ux-logs-team
packages/kbn-screenshotting-server @elastic/appex-sharedux
packages/kbn-search-api-keys-components @elastic/search-kibana
packages/kbn-search-api-keys-server @elastic/search-kibana
diff --git a/package.json b/package.json
index afda7cd4c912..13d45d425e57 100644
--- a/package.json
+++ b/package.json
@@ -777,6 +777,7 @@
"@kbn/saved-objects-settings": "link:packages/kbn-saved-objects-settings",
"@kbn/saved-objects-tagging-oss-plugin": "link:src/plugins/saved_objects_tagging_oss",
"@kbn/saved-objects-tagging-plugin": "link:x-pack/plugins/saved_objects_tagging",
+ "@kbn/saved-search-component": "link:packages/kbn-saved-search-component",
"@kbn/saved-search-plugin": "link:src/plugins/saved_search",
"@kbn/screenshot-mode-example-plugin": "link:examples/screenshot_mode_example",
"@kbn/screenshot-mode-plugin": "link:src/plugins/screenshot_mode",
diff --git a/packages/kbn-saved-search-component/README.md b/packages/kbn-saved-search-component/README.md
new file mode 100644
index 000000000000..296ddb9079bc
--- /dev/null
+++ b/packages/kbn-saved-search-component/README.md
@@ -0,0 +1,26 @@
+# @kbn/saved-search-component
+
+A component wrapper around Discover's Saved Search embeddable. This can be used in solutions without being within a Dasboard context.
+
+This can be used to render a context-aware (logs etc) "document table".
+
+In the past you may have used the Log Stream Component to achieve this, this component supersedes that.
+
+## Basic usage
+
+```
+import { LazySavedSearchComponent } from '@kbn/saved-search-component';
+
+
+```
\ No newline at end of file
diff --git a/packages/kbn-saved-search-component/index.ts b/packages/kbn-saved-search-component/index.ts
new file mode 100644
index 000000000000..80617ebfa46e
--- /dev/null
+++ b/packages/kbn-saved-search-component/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { dynamic } from '@kbn/shared-ux-utility';
+
+export type { SavedSearchComponentDependencies, SavedSearchComponentProps } from './src/types';
+
+export const LazySavedSearchComponent = dynamic(() =>
+ import('./src/components/saved_search').then((mod) => ({
+ default: mod.SavedSearchComponent,
+ }))
+);
diff --git a/packages/kbn-saved-search-component/jest.config.js b/packages/kbn-saved-search-component/jest.config.js
new file mode 100644
index 000000000000..dedff3f69781
--- /dev/null
+++ b/packages/kbn-saved-search-component/jest.config.js
@@ -0,0 +1,14 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+module.exports = {
+ preset: '@kbn/test',
+ rootDir: '../..',
+ roots: ['/packages/kbn-saved-search-component'],
+};
diff --git a/packages/kbn-saved-search-component/kibana.jsonc b/packages/kbn-saved-search-component/kibana.jsonc
new file mode 100644
index 000000000000..d0de843443d1
--- /dev/null
+++ b/packages/kbn-saved-search-component/kibana.jsonc
@@ -0,0 +1,5 @@
+{
+ "type": "shared-browser",
+ "id": "@kbn/saved-search-component",
+ "owner": "@elastic/obs-ux-logs-team"
+}
diff --git a/packages/kbn-saved-search-component/package.json b/packages/kbn-saved-search-component/package.json
new file mode 100644
index 000000000000..917956fd69ba
--- /dev/null
+++ b/packages/kbn-saved-search-component/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@kbn/saved-search-component",
+ "private": true,
+ "version": "1.0.0",
+ "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0",
+ "sideEffects": false
+}
\ No newline at end of file
diff --git a/packages/kbn-saved-search-component/src/components/saved_search.tsx b/packages/kbn-saved-search-component/src/components/saved_search.tsx
new file mode 100644
index 000000000000..ac88fcdeb47a
--- /dev/null
+++ b/packages/kbn-saved-search-component/src/components/saved_search.tsx
@@ -0,0 +1,160 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import React, { useEffect, useMemo, useRef, useState } from 'react';
+import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
+import { SEARCH_EMBEDDABLE_TYPE } from '@kbn/discover-utils';
+import type {
+ SearchEmbeddableSerializedState,
+ SearchEmbeddableRuntimeState,
+ SearchEmbeddableApi,
+} from '@kbn/discover-plugin/public';
+import { SerializedPanelState } from '@kbn/presentation-containers';
+import { SavedSearchComponentProps } from '../types';
+
+const TIMESTAMP_FIELD = '@timestamp';
+
+export const SavedSearchComponent: React.FC = (props) => {
+ // Creates our *initial* search source and set of attributes.
+ // Future changes to these properties will be facilitated by the Parent API from the embeddable.
+ const [initialSerializedState, setInitialSerializedState] =
+ useState>();
+
+ const { dependencies, timeRange, query, filters, index, timestampField, height } = props;
+
+ useEffect(() => {
+ // Ensure we get a stabilised set of initial state incase dependencies change, as
+ // the data view creation process is async.
+ const abortController = new AbortController();
+
+ async function createInitialSerializedState() {
+ const { dataViews, searchSource: searchSourceService } = dependencies;
+ // Ad-hoc data view
+ const dataView = await dataViews.create({
+ title: index,
+ timeFieldName: timestampField ?? TIMESTAMP_FIELD,
+ });
+ if (!abortController.signal.aborted) {
+ // Search source
+ const searchSource = searchSourceService.createEmpty();
+ searchSource.setField('index', dataView);
+ searchSource.setField('query', query);
+ searchSource.setField('filter', filters);
+ const { searchSourceJSON, references } = searchSource.serialize();
+ // By-value saved object structure
+ const attributes = {
+ kibanaSavedObjectMeta: {
+ searchSourceJSON,
+ },
+ };
+ setInitialSerializedState({
+ rawState: {
+ attributes: { ...attributes, references },
+ timeRange,
+ } as SearchEmbeddableSerializedState,
+ references,
+ });
+ }
+ }
+
+ createInitialSerializedState();
+
+ return () => {
+ abortController.abort();
+ };
+ }, [dependencies, filters, index, query, timeRange, timestampField]);
+
+ return initialSerializedState ? (
+
+
+
+ ) : null;
+};
+
+const SavedSearchComponentTable: React.FC<
+ SavedSearchComponentProps & { initialSerializedState: any }
+> = (props) => {
+ const { dependencies, initialSerializedState, filters, query, timeRange, timestampField, index } =
+ props;
+ const embeddableApi = useRef(undefined);
+
+ const parentApi = useMemo(() => {
+ return {
+ getSerializedStateForChild: () => {
+ return initialSerializedState;
+ },
+ };
+ }, [initialSerializedState]);
+
+ useEffect(
+ function syncIndex() {
+ if (!embeddableApi.current) return;
+
+ const abortController = new AbortController();
+
+ async function updateDataView(indexPattern: string) {
+ const { dataViews } = dependencies;
+ // Ad-hoc data view
+ const dataView = await dataViews.create({
+ title: index,
+ timeFieldName: timestampField ?? TIMESTAMP_FIELD,
+ });
+ if (!abortController.signal.aborted) {
+ embeddableApi?.current?.setDataViews([dataView]);
+ }
+ }
+
+ updateDataView(index);
+
+ return () => {
+ abortController.abort();
+ };
+ },
+ [dependencies, index, timestampField]
+ );
+
+ useEffect(
+ function syncFilters() {
+ if (!embeddableApi.current) return;
+ embeddableApi.current.setFilters(filters);
+ },
+ [filters]
+ );
+
+ useEffect(
+ function syncQuery() {
+ if (!embeddableApi.current) return;
+ embeddableApi.current.setQuery(query);
+ },
+ [query]
+ );
+
+ useEffect(
+ function syncTimeRange() {
+ if (!embeddableApi.current) return;
+ embeddableApi.current.setTimeRange(timeRange);
+ },
+ [timeRange]
+ );
+
+ return (
+
+ maybeId={undefined}
+ type={SEARCH_EMBEDDABLE_TYPE}
+ getParentApi={() => parentApi}
+ onApiAvailable={(api) => {
+ embeddableApi.current = api;
+ }}
+ />
+ );
+};
diff --git a/packages/kbn-saved-search-component/src/types.ts b/packages/kbn-saved-search-component/src/types.ts
new file mode 100644
index 000000000000..0e0ba8363659
--- /dev/null
+++ b/packages/kbn-saved-search-component/src/types.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import { EmbeddableStart } from '@kbn/embeddable-plugin/public';
+import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
+import { Filter, Query, TimeRange } from '@kbn/es-query';
+import { DataViewsContract, ISearchStartSearchSource } from '@kbn/data-plugin/public';
+
+export interface SavedSearchComponentDependencies {
+ embeddable: EmbeddableStart;
+ savedSearch: SavedSearchPublicPluginStart;
+ searchSource: ISearchStartSearchSource;
+ dataViews: DataViewsContract;
+}
+
+export interface SavedSearchComponentProps {
+ dependencies: SavedSearchComponentDependencies;
+ index: string;
+ timeRange?: TimeRange;
+ query?: Query;
+ filters?: Filter[];
+ timestampField?: string;
+ height?: string | number;
+}
diff --git a/packages/kbn-saved-search-component/tsconfig.json b/packages/kbn-saved-search-component/tsconfig.json
new file mode 100644
index 000000000000..7230bab371c5
--- /dev/null
+++ b/packages/kbn-saved-search-component/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "target/types",
+ "types": [
+ "jest",
+ "node",
+ "react"
+ ]
+ },
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ ],
+ "exclude": [
+ "target/**/*"
+ ],
+ "kbn_references": [
+ "@kbn/embeddable-plugin",
+ "@kbn/shared-ux-utility",
+ "@kbn/discover-utils",
+ "@kbn/saved-search-plugin",
+ "@kbn/es-query",
+ "@kbn/data-plugin",
+ "@kbn/discover-plugin",
+ "@kbn/presentation-containers",
+ ]
+}
diff --git a/packages/presentation/presentation_publishing/index.ts b/packages/presentation/presentation_publishing/index.ts
index 2b96c6d353ee..801b64e10d90 100644
--- a/packages/presentation/presentation_publishing/index.ts
+++ b/packages/presentation/presentation_publishing/index.ts
@@ -97,7 +97,11 @@ export {
apiPublishesDataLoading,
type PublishesDataLoading,
} from './interfaces/publishes_data_loading';
-export { apiPublishesDataViews, type PublishesDataViews } from './interfaces/publishes_data_views';
+export {
+ apiPublishesDataViews,
+ type PublishesDataViews,
+ type PublishesWritableDataViews,
+} from './interfaces/publishes_data_views';
export {
apiPublishesDisabledActionIds,
type PublishesDisabledActionIds,
diff --git a/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts b/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts
index d160cdb6e419..50649b1764c3 100644
--- a/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts
+++ b/packages/presentation/presentation_publishing/interfaces/publishes_data_views.ts
@@ -14,6 +14,10 @@ export interface PublishesDataViews {
dataViews: PublishingSubject;
}
+export type PublishesWritableDataViews = PublishesDataViews & {
+ setDataViews: (dataViews: DataView[]) => void;
+};
+
export const apiPublishesDataViews = (
unknownApi: null | unknown
): unknownApi is PublishesDataViews => {
diff --git a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx
index e824fb6fdc02..c9c7dfe922e0 100644
--- a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx
+++ b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx
@@ -15,8 +15,8 @@ import { ISearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/co
import { DataView } from '@kbn/data-views-plugin/common';
import { DataTableRecord } from '@kbn/discover-utils/types';
import type {
- PublishesDataViews,
- PublishesUnifiedSearch,
+ PublishesWritableUnifiedSearch,
+ PublishesWritableDataViews,
StateComparators,
} from '@kbn/presentation-publishing';
import { DiscoverGridSettings, SavedSearch } from '@kbn/saved-search-plugin/common';
@@ -71,7 +71,7 @@ export const initializeSearchEmbeddableApi = async (
discoverServices: DiscoverServices;
}
): Promise<{
- api: PublishesSavedSearch & PublishesDataViews & Partial;
+ api: PublishesSavedSearch & PublishesWritableDataViews & Partial;
stateManager: SearchEmbeddableStateManager;
comparators: StateComparators;
cleanup: () => void;
@@ -144,6 +144,25 @@ export const initializeSearchEmbeddableApi = async (
pick(stateManager, EDITABLE_SAVED_SEARCH_KEYS)
);
+ /** APIs for updating search source properties */
+ const setDataViews = async (nextDataViews: DataView[]) => {
+ searchSource.setField('index', nextDataViews[0]);
+ dataViews.next(nextDataViews);
+ searchSource$.next(searchSource);
+ };
+
+ const setFilters = (filters: Filter[] | undefined) => {
+ searchSource.setField('filter', filters);
+ filters$.next(filters);
+ searchSource$.next(searchSource);
+ };
+
+ const setQuery = (query: Query | AggregateQuery | undefined) => {
+ searchSource.setField('query', query);
+ query$.next(query);
+ searchSource$.next(searchSource);
+ };
+
/** Keep the saved search in sync with any state changes */
const syncSavedSearch = combineLatest([onAnyStateChange, searchSource$])
.pipe(
@@ -163,10 +182,13 @@ export const initializeSearchEmbeddableApi = async (
syncSavedSearch.unsubscribe();
},
api: {
+ setDataViews,
dataViews,
savedSearch$,
filters$,
+ setFilters,
query$,
+ setQuery,
},
stateManager,
comparators: {
diff --git a/src/plugins/discover/public/embeddable/types.ts b/src/plugins/discover/public/embeddable/types.ts
index 1b7ab4a96c2d..030ae2e82633 100644
--- a/src/plugins/discover/public/embeddable/types.ts
+++ b/src/plugins/discover/public/embeddable/types.ts
@@ -15,10 +15,9 @@ import {
HasInPlaceLibraryTransforms,
PublishesBlockingError,
PublishesDataLoading,
- PublishesDataViews,
PublishesSavedObjectId,
- PublishesUnifiedSearch,
PublishesWritablePanelTitle,
+ PublishesWritableUnifiedSearch,
PublishingSubject,
SerializedTimeRange,
SerializedTitles,
@@ -30,6 +29,7 @@ import {
} from '@kbn/saved-search-plugin/common/types';
import { DataTableColumnsMeta } from '@kbn/unified-data-table';
import { BehaviorSubject } from 'rxjs';
+import { PublishesWritableDataViews } from '@kbn/presentation-publishing/interfaces/publishes_data_views';
import { EDITABLE_SAVED_SEARCH_KEYS } from './constants';
export type SearchEmbeddableState = Pick<
@@ -80,14 +80,13 @@ export type SearchEmbeddableApi = DefaultEmbeddableApi<
SearchEmbeddableSerializedState,
SearchEmbeddableRuntimeState
> &
- PublishesDataViews &
PublishesSavedObjectId &
PublishesDataLoading &
PublishesBlockingError &
PublishesWritablePanelTitle &
PublishesSavedSearch &
- PublishesDataViews &
- PublishesUnifiedSearch &
+ PublishesWritableDataViews &
+ PublishesWritableUnifiedSearch &
HasInPlaceLibraryTransforms &
HasTimeRange &
Partial;
diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts
index b5d4308010f1..2d3fe785579c 100644
--- a/src/plugins/discover/public/index.ts
+++ b/src/plugins/discover/public/index.ts
@@ -35,6 +35,8 @@ export {
type PublishesSavedSearch,
type HasTimeRange,
type SearchEmbeddableSerializedState,
+ type SearchEmbeddableRuntimeState,
+ type SearchEmbeddableApi,
} from './embeddable';
export { loadSharingDataHelpers } from './utils';
export { LogsExplorerTabs, type LogsExplorerTabsProps } from './components/logs_explorer_tabs';
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 68faf44ed74d..42b98f074c8a 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -1512,6 +1512,8 @@
"@kbn/saved-objects-tagging-oss-plugin/*": ["src/plugins/saved_objects_tagging_oss/*"],
"@kbn/saved-objects-tagging-plugin": ["x-pack/plugins/saved_objects_tagging"],
"@kbn/saved-objects-tagging-plugin/*": ["x-pack/plugins/saved_objects_tagging/*"],
+ "@kbn/saved-search-component": ["packages/kbn-saved-search-component"],
+ "@kbn/saved-search-component/*": ["packages/kbn-saved-search-component/*"],
"@kbn/saved-search-plugin": ["src/plugins/saved_search"],
"@kbn/saved-search-plugin/*": ["src/plugins/saved_search/*"],
"@kbn/screenshot-mode-example-plugin": ["examples/screenshot_mode_example"],
diff --git a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx
index 2f478c771dbf..cf57ec3eab35 100644
--- a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx
+++ b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_details_flyout.tsx
@@ -20,6 +20,8 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { StateFrom } from 'xstate5';
import { i18n } from '@kbn/i18n';
import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types';
+import { css } from '@emotion/react';
+import { FilterStateStore, buildCustomFilter } from '@kbn/es-query';
import { LogCategory } from '../../types';
import { LogCategoryPattern } from '../shared/log_category_pattern';
import { categoryDetailsService } from '../../services/category_details_service';
@@ -30,10 +32,17 @@ import {
import { type ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source';
import { LogCategoryDetailsLoadingContent } from './log_category_details_loading_content';
import { LogCategoryDetailsErrorContent } from './log_category_details_error_content';
-import { DiscoverLink } from '../discover_link';
+import { DiscoverLink, DiscoverLinkDependencies } from '../discover_link';
import { createCategoryQuery } from '../../services/categorize_logs_service/queries';
-export type LogCategoriesFlyoutDependencies = LogCategoryDocumentExamplesTableDependencies;
+export type LogCategoriesFlyoutDependencies = LogCategoryDocumentExamplesTableDependencies &
+ DiscoverLinkDependencies;
+
+const flyoutBodyCss = css`
+ .euiFlyoutBody__overflowContent {
+ height: 100%;
+ }
+`;
interface LogCategoryDetailsFlyoutProps {
onCloseFlyout: () => void;
@@ -61,11 +70,19 @@ export const LogCategoryDetailsFlyout: React.FC =
prefix: 'flyoutTitle',
});
+ const categoryFilter = useMemo(() => {
+ return createCategoryQuery(logsSource.messageField)(logCategory.terms);
+ }, [logCategory.terms, logsSource.messageField]);
+
+ const documentAndCategoryFilters = useMemo(() => {
+ return [...(documentFilters ?? []), categoryFilter];
+ }, [categoryFilter, documentFilters]);
+
const linkFilters = useMemo(() => {
return [
...(documentFilters ? documentFilters.map((filter) => ({ filter })) : []),
{
- filter: createCategoryQuery(logsSource.messageField)(logCategory.terms),
+ filter: categoryFilter,
meta: {
name: i18n.translate(
'xpack.observabilityLogsOverview.logCategoryDetailsFlyout.discoverLinkFilterName',
@@ -79,7 +96,20 @@ export const LogCategoryDetailsFlyout: React.FC =
},
},
];
- }, [documentFilters, logCategory.terms, logsSource.messageField]);
+ }, [categoryFilter, documentFilters, logCategory.terms]);
+
+ const filters = useMemo(() => {
+ return documentAndCategoryFilters.map((filter) =>
+ buildCustomFilter(
+ logsSource.indexName,
+ filter,
+ false,
+ false,
+ 'Document filters',
+ FilterStateStore.APP_STATE
+ )
+ );
+ }, [documentAndCategoryFilters, logsSource.indexName]);
return (
onCloseFlyout()} aria-labelledby={flyoutTitleId}>
@@ -107,7 +137,7 @@ export const LogCategoryDetailsFlyout: React.FC =
-
+
{categoryDetailsServiceState.matches({ hasCategory: 'fetchingDocuments' }) ? (
=
) : (
)}
diff --git a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx
index 6b43fa86fe49..cdd9ad3f3804 100644
--- a/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx
+++ b/x-pack/packages/observability/logs_overview/src/components/log_category_details/log_category_document_examples_table.tsx
@@ -5,147 +5,46 @@
* 2.0.
*/
-import { EuiBasicTable, EuiBasicTableColumn, EuiSpacer, EuiText } from '@elastic/eui';
-import React, { useMemo } from 'react';
-import { i18n } from '@kbn/i18n';
-import { DataGridDensity, ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table';
-import moment from 'moment';
-import type { SettingsStart } from '@kbn/core-ui-settings-browser';
-import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
-import type { SharePluginStart } from '@kbn/share-plugin/public';
+import React from 'react';
import { CoreStart } from '@kbn/core-lifecycle-browser';
-import { getLogLevelBadgeCell, LazySummaryColumn } from '@kbn/discover-contextual-components';
-import type { LogCategoryDocument } from '../../services/category_details_service/types';
-import { type ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source';
+import { LazySavedSearchComponent } from '@kbn/saved-search-component';
+import { EmbeddableStart } from '@kbn/embeddable-plugin/public';
+import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
+import { DataViewsContract } from '@kbn/data-views-plugin/public';
+import { ISearchStartSearchSource } from '@kbn/data-plugin/common';
+import { Filter } from '@kbn/es-query';
+import { ResolvedIndexNameLogsSourceConfiguration } from '../../utils/logs_source';
export interface LogCategoryDocumentExamplesTableDependencies {
core: CoreStart;
- uiSettings: SettingsStart;
- fieldFormats: FieldFormatsStart;
- share: SharePluginStart;
+ embeddable: EmbeddableStart;
+ savedSearch: SavedSearchPublicPluginStart;
+ dataViews: DataViewsContract;
+ searchSource: ISearchStartSearchSource;
}
export interface LogCategoryDocumentExamplesTableProps {
dependencies: LogCategoryDocumentExamplesTableDependencies;
- categoryDocuments: LogCategoryDocument[];
logsSource: ResolvedIndexNameLogsSourceConfiguration;
+ filters: Filter[];
}
-const TimestampCell = ({
- dependencies,
- timestamp,
-}: {
- dependencies: LogCategoryDocumentExamplesTableDependencies;
- timestamp?: string | number;
-}) => {
- const dateFormat = useMemo(
- () => dependencies.uiSettings.client.get('dateFormat'),
- [dependencies.uiSettings.client]
- );
- if (!timestamp) return null;
-
- if (dateFormat) {
- return <>{moment(timestamp).format(dateFormat)}>;
- } else {
- return <>{timestamp}>;
- }
-};
-
-const LogLevelBadgeCell = getLogLevelBadgeCell('log.level');
-
export const LogCategoryDocumentExamplesTable: React.FC = ({
- categoryDocuments,
dependencies,
+ filters,
logsSource,
}) => {
- const columns: Array> = [
- {
- field: 'row',
- name: 'Timestamp',
- width: '25%',
- render: (row: any) => {
- return (
-
- );
- },
- },
- {
- field: 'row',
- name: 'Log level',
- width: '10%',
- render: (row: any) => {
- return (
- {}}
- closePopover={() => {}}
- />
- );
- },
- },
- {
- field: 'row',
- name: 'Summary',
- width: '65%',
- render: (row: any) => {
- return (
- {}}
- closePopover={() => {}}
- density={DataGridDensity.COMPACT}
- rowHeight={ROWS_HEIGHT_OPTIONS.single}
- shouldShowFieldHandler={() => false}
- core={dependencies.core}
- share={dependencies.share}
- />
- );
- },
- },
- ];
return (
- <>
-
- {i18n.translate(
- 'xpack.observabilityLogsOverview.logCategoryDocumentExamplesTable.documentCountText',
- {
- defaultMessage: 'Displaying the latest {documentsCount} documents.',
- values: {
- documentsCount: categoryDocuments.length,
- },
- }
- )}
-
-
-
- >
+
);
};
diff --git a/x-pack/packages/observability/logs_overview/tsconfig.json b/x-pack/packages/observability/logs_overview/tsconfig.json
index 29595ce0162f..03e2c8b09af7 100644
--- a/x-pack/packages/observability/logs_overview/tsconfig.json
+++ b/x-pack/packages/observability/logs_overview/tsconfig.json
@@ -34,12 +34,13 @@
"@kbn/es-query",
"@kbn/router-utils",
"@kbn/share-plugin",
- "@kbn/field-formats-plugin",
"@kbn/data-service",
"@kbn/discover-utils",
"@kbn/discover-plugin",
- "@kbn/unified-data-table",
- "@kbn/discover-contextual-components",
"@kbn/core-lifecycle-browser",
+ "@kbn/embeddable-plugin",
+ "@kbn/saved-search-plugin",
+ "@kbn/data-plugin",
+ "@kbn/saved-search-component",
]
}
diff --git a/x-pack/plugins/observability_solution/apm/kibana.jsonc b/x-pack/plugins/observability_solution/apm/kibana.jsonc
index 656f898f2406..bcb0801fc639 100644
--- a/x-pack/plugins/observability_solution/apm/kibana.jsonc
+++ b/x-pack/plugins/observability_solution/apm/kibana.jsonc
@@ -37,7 +37,8 @@
"lens",
"maps",
"uiActions",
- "logsDataAccess"
+ "logsDataAccess",
+ "savedSearch",
],
"optionalPlugins": [
"actions",
diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx
index a1dadbf186b9..4e2bfa430a2c 100644
--- a/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx
+++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_logs/index.tsx
@@ -6,9 +6,9 @@
*/
import React, { useMemo } from 'react';
-import moment from 'moment';
import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types';
-import { LogStream } from '@kbn/logs-shared-plugin/public';
+import { LazySavedSearchComponent } from '@kbn/saved-search-component';
+import useAsync from 'react-use/lib/useAsync';
import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values';
import { CONTAINER_ID, SERVICE_ENVIRONMENT, SERVICE_NAME } from '../../../../common/es_fields/apm';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
@@ -35,6 +35,20 @@ export function ServiceLogs() {
}
export function ClassicServiceLogsStream() {
+ const {
+ services: {
+ logsDataAccess: {
+ services: { logSourcesService },
+ },
+ embeddable,
+ savedSearch,
+ dataViews,
+ data: {
+ search: { searchSource },
+ },
+ },
+ } = useKibana();
+
const { serviceName } = useApmServiceContext();
const {
@@ -62,17 +76,26 @@ export function ClassicServiceLogsStream() {
[environment, kuery, serviceName, start, end]
);
- return (
- {
+ return { embeddable, savedSearch, searchSource, dataViews };
+ }, [dataViews, embeddable, savedSearch, searchSource]);
+
+ const timeRange = useMemo(() => ({ from: start, to: end }), [start, end]);
+
+ return logSources.value ? (
+
- );
+ ) : null;
}
export function ServiceLogsOverview() {
diff --git a/x-pack/plugins/observability_solution/apm/public/plugin.ts b/x-pack/plugins/observability_solution/apm/public/plugin.ts
index b21bdedac9ef..f9627692b2e3 100644
--- a/x-pack/plugins/observability_solution/apm/public/plugin.ts
+++ b/x-pack/plugins/observability_solution/apm/public/plugin.ts
@@ -70,6 +70,8 @@ import { map } from 'rxjs';
import type { CloudSetup } from '@kbn/cloud-plugin/public';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import { LogsSharedClientStartExports } from '@kbn/logs-shared-plugin/public';
+import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
+import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
import type { ConfigSchema } from '.';
import { registerApmRuleTypes } from './components/alerting/rule_types/register_apm_rule_types';
import { registerEmbeddables } from './embeddable/register_embeddables';
@@ -144,6 +146,8 @@ export interface ApmPluginStartDeps {
metricsDataAccess: MetricsDataPluginStart;
uiSettings: IUiSettingsClient;
logsShared: LogsSharedClientStartExports;
+ logsDataAccess: LogsDataAccessPluginStart;
+ savedSearch: SavedSearchPublicPluginStart;
}
const applicationsTitle = i18n.translate('xpack.apm.navigation.rootTitle', {
diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json
index 0f08bf3143cd..2dc38f84130b 100644
--- a/x-pack/plugins/observability_solution/apm/tsconfig.json
+++ b/x-pack/plugins/observability_solution/apm/tsconfig.json
@@ -128,6 +128,8 @@
"@kbn/router-utils",
"@kbn/react-hooks",
"@kbn/alerting-comparators",
+ "@kbn/saved-search-component",
+ "@kbn/saved-search-plugin",
],
"exclude": ["target/**/*"]
}
diff --git a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts
index f329907f145e..e44b9cadcae0 100644
--- a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts
+++ b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts
@@ -12,23 +12,32 @@ import { RegisterServicesParams } from '../register_services';
export function createLogSourcesService(params: RegisterServicesParams): LogSourcesService {
const { uiSettings } = params.deps;
- return {
- async getLogSources() {
- const logSources = uiSettings.get(OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID);
- return logSources.map((logSource) => ({
- indexPattern: logSource,
- }));
- },
- async getFlattenedLogSources() {
- const logSources = await this.getLogSources();
- return flattenLogSources(logSources);
- },
- async setLogSources(sources: LogSource[]) {
- await uiSettings.set(
- OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID,
- sources.map((source) => source.indexPattern)
- );
- return;
- },
+
+ const getLogSources = async () => {
+ const logSources = uiSettings.get(OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID);
+ return logSources.map((logSource) => ({
+ indexPattern: logSource,
+ }));
+ };
+
+ const getFlattenedLogSources = async () => {
+ const logSources = await getLogSources();
+ return flattenLogSources(logSources);
+ };
+
+ const setLogSources = async (sources: LogSource[]) => {
+ await uiSettings.set(
+ OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID,
+ sources.map((source) => source.indexPattern)
+ );
+ return;
+ };
+
+ const logSourcesService: LogSourcesService = {
+ getLogSources,
+ getFlattenedLogSources,
+ setLogSources,
};
+
+ return logSourcesService;
}
diff --git a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc
index f5e9f76c2ace..a31d27bed8dd 100644
--- a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc
+++ b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc
@@ -18,11 +18,14 @@
"observabilityShared",
"share",
"usageCollection",
+ "embeddable",
+ "savedSearch"
],
"optionalPlugins": [
"observabilityAIAssistant",
],
- "requiredBundles": ["kibanaUtils", "kibanaReact", "unifiedDocViewer"],
+ "requiredBundles": ["kibanaUtils", "kibanaReact"],
"extraPublicDirs": ["common"]
}
}
+
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx b/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx
index 0321651607ed..5f86c26941ce 100644
--- a/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx
+++ b/x-pack/plugins/observability_solution/logs_shared/public/plugin.tsx
@@ -76,10 +76,12 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass {
charts,
logsDataAccess,
search: data.search.search,
+ searchSource: data.search.searchSource,
uiSettings: settings,
share,
dataViews,
- fieldFormats,
+ embeddable: plugins.embeddable,
+ savedSearch: plugins.savedSearch,
});
if (!observabilityAIAssistant) {
diff --git a/x-pack/plugins/observability_solution/logs_shared/public/types.ts b/x-pack/plugins/observability_solution/logs_shared/public/types.ts
index e2435fa1f491..90bbd89f2481 100644
--- a/x-pack/plugins/observability_solution/logs_shared/public/types.ts
+++ b/x-pack/plugins/observability_solution/logs_shared/public/types.ts
@@ -15,6 +15,8 @@ import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-
import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
import type { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
+import { EmbeddableStart } from '@kbn/embeddable-plugin/public';
+import { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
import type { LogsSharedLocators } from '../common/locators';
import type { LogAIAssistantProps } from './components/log_ai_assistant/log_ai_assistant';
import type { SelfContainedLogsOverview } from './components/logs_overview';
@@ -46,6 +48,8 @@ export interface LogsSharedClientStartDeps {
share: SharePluginStart;
uiActions: UiActionsStart;
fieldFormats: FieldFormatsStart;
+ embeddable: EmbeddableStart;
+ savedSearch: SavedSearchPublicPluginStart;
}
export type LogsSharedClientCoreSetup = CoreSetup<
diff --git a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json
index f171c79afccd..acaed5073a17 100644
--- a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json
+++ b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json
@@ -49,5 +49,7 @@
"@kbn/charts-plugin",
"@kbn/core-ui-settings-common",
"@kbn/field-formats-plugin",
+ "@kbn/embeddable-plugin",
+ "@kbn/saved-search-plugin",
]
}
diff --git a/yarn.lock b/yarn.lock
index 5faf426cf4d2..6b9e4804b467 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6278,6 +6278,10 @@
version "0.0.0"
uid ""
+"@kbn/saved-search-component@link:packages/kbn-saved-search-component":
+ version "0.0.0"
+ uid ""
+
"@kbn/saved-search-plugin@link:src/plugins/saved_search":
version "0.0.0"
uid ""