diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
index 521fbf209870..f11d6cdb8d26 100644
--- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/index.tsx
@@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { LogEntryFlyout } from './log_entry_flyout';
+export * from './log_entry_flyout';
diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
index 57f27ee76184..76ffada510e5 100644
--- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
@@ -26,12 +26,10 @@ import { InfraLoadingPanel } from '../../loading';
import { LogEntryActionsMenu } from './log_entry_actions_menu';
import { LogEntriesItem, LogEntriesItemField } from '../../../../common/http_api';
-interface Props {
+export interface LogEntryFlyoutProps {
flyoutItem: LogEntriesItem | null;
setFlyoutVisibility: (visible: boolean) => void;
- setFilter: (filter: string) => void;
- setTarget: (timeKey: TimeKey, flyoutItemId: string) => void;
-
+ setFilter: (filter: string, flyoutItemId: string, timeKey?: TimeKey) => void;
loading: boolean;
}
@@ -40,27 +38,27 @@ export const LogEntryFlyout = ({
loading,
setFlyoutVisibility,
setFilter,
- setTarget,
-}: Props) => {
+}: LogEntryFlyoutProps) => {
const createFilterHandler = useCallback(
(field: LogEntriesItemField) => () => {
- const filter = `${field.field}:"${field.value}"`;
- setFilter(filter);
-
- if (flyoutItem && flyoutItem.key) {
- const timestampMoment = moment(flyoutItem.key.time);
- if (timestampMoment.isValid()) {
- setTarget(
- {
- time: timestampMoment.valueOf(),
- tiebreaker: flyoutItem.key.tiebreaker,
- },
- flyoutItem.id
- );
- }
+ if (!flyoutItem) {
+ return;
}
+
+ const filter = `${field.field}:"${field.value}"`;
+ const timestampMoment = moment(flyoutItem.key.time);
+ let target;
+
+ if (timestampMoment.isValid()) {
+ target = {
+ time: timestampMoment.valueOf(),
+ tiebreaker: flyoutItem.key.tiebreaker,
+ };
+ }
+
+ setFilter(filter, flyoutItem.id, target);
},
- [flyoutItem, setFilter, setTarget]
+ [flyoutItem, setFilter]
);
const closeFlyout = useCallback(() => setFlyoutVisibility(false), [setFlyoutVisibility]);
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
index e986fa37c2b2..4ad654614237 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx
@@ -10,6 +10,7 @@ import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_a
import { LogEntryRateModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_rate';
import { useLogSourceContext } from '../../../containers/logs/log_source';
import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space';
+import { LogFlyout } from '../../../containers/logs/log_flyout';
export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) => {
const { sourceId, sourceConfiguration } = useLogSourceContext();
@@ -23,20 +24,22 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children })
}
return (
-
-
+
- {children}
-
-
+
+ {children}
+
+
+
);
};
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
index 65cc4a6c4a70..de72ac5c5a57 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
@@ -7,7 +7,9 @@
import datemath from '@elastic/datemath';
import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel, EuiSuperDatePicker } from '@elastic/eui';
import moment from 'moment';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { encode, RisonValue } from 'rison-node';
+import { stringify } from 'query-string';
+import React, { useCallback, useEffect, useMemo, useState, useContext } from 'react';
import { euiStyled, useTrackPageview } from '../../../../../observability/public';
import { TimeRange } from '../../../../common/http_api/shared/time_range';
import { bucketSpan } from '../../../../common/log_analysis';
@@ -29,6 +31,9 @@ import {
StringTimeRange,
useLogAnalysisResultsUrlState,
} from './use_log_entry_rate_results_url_state';
+import { LogEntryFlyout, LogEntryFlyoutProps } from '../../../components/logging/log_entry_flyout';
+import { LogFlyout } from '../../../containers/logs/log_flyout';
+import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
export const SORT_DEFAULTS = {
direction: 'desc' as const,
@@ -42,6 +47,7 @@ export const PAGINATION_DEFAULTS = {
export const LogEntryRateResultsContent: React.FunctionComponent = () => {
useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_results' });
useTrackPageview({ app: 'infra_logs', path: 'log_entry_rate_results', delay: 15000 });
+ const navigateToApp = useKibana().services.application?.navigateToApp;
const { sourceId } = useLogSourceContext();
@@ -79,6 +85,30 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
lastChangedTime: Date.now(),
}));
+ const linkToLogStream = useCallback(
+ (filter, id, timeKey) => {
+ const params = {
+ logPosition: encode({
+ end: moment(queryTimeRange.value.endTime).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ position: timeKey as RisonValue,
+ start: moment(queryTimeRange.value.startTime).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
+ streamLive: false,
+ }),
+ flyoutOptions: encode({
+ surroundingLogsId: id,
+ }),
+ logFilter: encode({
+ expression: filter,
+ kind: 'kuery',
+ }),
+ };
+
+ // eslint-disable-next-line no-unused-expressions
+ navigateToApp?.('logs', { path: `/stream?${stringify(params)}` });
+ },
+ [queryTimeRange, navigateToApp]
+ );
+
const bucketDuration = useMemo(
() => getBucketDuration(queryTimeRange.value.startTime, queryTimeRange.value.endTime),
[queryTimeRange.value.endTime, queryTimeRange.value.startTime]
@@ -115,6 +145,10 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
filteredDatasets: selectedDatasets,
});
+ const { flyoutVisible, setFlyoutVisibility, flyoutItem, isLoading: isFlyoutLoading } = useContext(
+ LogFlyout.Context
+ );
+
const handleQueryTimeRangeChange = useCallback(
({ start: startTime, end: endTime }: { start: string; end: string }) => {
setQueryTimeRange({
@@ -198,75 +232,86 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
);
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {flyoutVisible ? (
+
+ ) : null}
+ >
);
};
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
index fece2522de57..a543f95bf4ff 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/log_entry_example.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useMemo, useCallback, useState } from 'react';
+import React, { useMemo, useCallback, useState, useContext } from 'react';
import moment from 'moment';
import { encode } from 'rison-node';
import { i18n } from '@kbn/i18n';
@@ -37,6 +37,7 @@ import {
} from '../../../../../utils/source_configuration';
import { localizedDate } from '../../../../../../common/formatters/datetime';
import { LogEntryAnomaly } from '../../../../../../common/http_api';
+import { LogFlyout } from '../../../../../containers/logs/log_flyout';
export const exampleMessageScale = 'medium' as const;
export const exampleTimestampFormat = 'time' as const;
@@ -45,6 +46,13 @@ const MENU_LABEL = i18n.translate('xpack.infra.logAnomalies.logEntryExamplesMenu
defaultMessage: 'View actions for log entry',
});
+const VIEW_DETAILS_LABEL = i18n.translate(
+ 'xpack.infra.logs.analysis.logEntryExamplesViewDetailsLabel',
+ {
+ defaultMessage: 'View details',
+ }
+);
+
const VIEW_IN_STREAM_LABEL = i18n.translate(
'xpack.infra.logs.analysis.logEntryExamplesViewInStreamLabel',
{
@@ -80,6 +88,8 @@ export const LogEntryExampleMessage: React.FunctionComponent = ({
const setItemIsHovered = useCallback(() => setIsHovered(true), []);
const setItemIsNotHovered = useCallback(() => setIsHovered(false), []);
+ const { setFlyoutVisibility, setFlyoutId } = useContext(LogFlyout.Context);
+
// handle special cases for the dataset value
const humanFriendlyDataset = getFriendlyNameForPartitionId(dataset);
@@ -116,6 +126,13 @@ export const LogEntryExampleMessage: React.FunctionComponent = ({
}
return [
+ {
+ label: VIEW_DETAILS_LABEL,
+ onClick: () => {
+ setFlyoutId(id);
+ setFlyoutVisibility(true);
+ },
+ },
{
label: VIEW_IN_STREAM_LABEL,
onClick: viewInStreamLinkProps.onClick,
@@ -127,7 +144,13 @@ export const LogEntryExampleMessage: React.FunctionComponent = ({
href: viewAnomalyInMachineLearningLinkProps.href,
},
];
- }, [viewInStreamLinkProps, viewAnomalyInMachineLearningLinkProps]);
+ }, [
+ id,
+ setFlyoutId,
+ setFlyoutVisibility,
+ viewInStreamLinkProps,
+ viewAnomalyInMachineLearningLinkProps,
+ ]);
return (
{
const [, { setContextEntry }] = useContext(ViewLogInContext.Context);
+ const setFilter = useCallback(
+ (filter, flyoutItemId, timeKey) => {
+ applyLogFilterQuery(filter);
+ if (timeKey) {
+ jumpToTargetPosition(timeKey);
+ }
+ setSurroundingLogsId(flyoutItemId);
+ stopLiveStreaming();
+ },
+ [applyLogFilterQuery, jumpToTargetPosition, setSurroundingLogsId, stopLiveStreaming]
+ );
+
return (
<>
@@ -65,12 +77,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
{flyoutVisible ? (
{
- jumpToTargetPosition(timeKey);
- setSurroundingLogsId(flyoutItemId);
- stopLiveStreaming();
- }}
+ setFilter={setFilter}
setFlyoutVisibility={setFlyoutVisibility}
flyoutItem={flyoutItem}
loading={isLoading}