Skip to content

Commit

Permalink
[7.x] [Logs UI] View log details for anomaly log examples (#75425) (#…
Browse files Browse the repository at this point in the history
…75954)

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
Alejandro Fernández Gómez and elasticmachine authored Aug 27, 2020
1 parent 6e624bf commit 0c01321
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -40,27 +38,27 @@ export const LogEntryFlyout = ({
loading,
setFlyoutVisibility,
setFilter,
setTarget,
}: Props) => {
}: LogEntryFlyoutProps) => {
const createFilterHandler = useCallback(
(field: LogEntriesItemField) => () => {
if (!flyoutItem) {
return;
}

const filter = `${field.field}:"${field.value}"`;
setFilter(filter);
const timestampMoment = moment(flyoutItem.key.time);
let target;

if (flyoutItem && flyoutItem.key) {
const timestampMoment = moment(flyoutItem.key.time);
if (timestampMoment.isValid()) {
setTarget(
{
time: timestampMoment.valueOf(),
tiebreaker: flyoutItem.key.tiebreaker,
},
flyoutItem.id
);
}
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]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -23,20 +24,22 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children })
}

return (
<LogEntryRateModuleProvider
indexPattern={sourceConfiguration?.configuration.logAlias ?? ''}
sourceId={sourceId}
spaceId={space.id}
timestampField={sourceConfiguration?.configuration.fields.timestamp ?? ''}
>
<LogEntryCategoriesModuleProvider
<LogFlyout.Provider>
<LogEntryRateModuleProvider
indexPattern={sourceConfiguration?.configuration.logAlias ?? ''}
sourceId={sourceId}
spaceId={space.id}
timestampField={sourceConfiguration?.configuration.fields.timestamp ?? ''}
>
<LogAnalysisSetupFlyoutStateProvider>{children}</LogAnalysisSetupFlyoutStateProvider>
</LogEntryCategoriesModuleProvider>
</LogEntryRateModuleProvider>
<LogEntryCategoriesModuleProvider
indexPattern={sourceConfiguration?.configuration.logAlias ?? ''}
sourceId={sourceId}
spaceId={space.id}
timestampField={sourceConfiguration?.configuration.fields.timestamp ?? ''}
>
<LogAnalysisSetupFlyoutStateProvider>{children}</LogAnalysisSetupFlyoutStateProvider>
</LogEntryCategoriesModuleProvider>
</LogEntryRateModuleProvider>
</LogFlyout.Provider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand All @@ -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();

Expand Down Expand Up @@ -79,6 +85,30 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
lastChangedTime: Date.now(),
}));

const linkToLogStream = useCallback<LogEntryFlyoutProps['setFilter']>(
(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]
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -198,75 +232,86 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
);

return (
<ResultsContentPage>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem>
<DatasetsSelector
availableDatasets={datasets}
isLoading={isLoadingDatasets}
selectedDatasets={selectedDatasets}
onChangeDatasetSelection={setSelectedDatasets}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSuperDatePicker
start={selectedTimeRange.startTime}
end={selectedTimeRange.endTime}
onTimeChange={handleSelectedTimeRangeChange}
isPaused={autoRefresh.isPaused}
refreshInterval={autoRefresh.interval}
onRefreshChange={handleAutoRefreshChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<LogAnalysisJobProblemIndicator
hasOutdatedJobConfigurations={hasOutdatedLogEntryRateJobConfigurations}
hasOutdatedJobDefinitions={hasOutdatedLogEntryRateJobDefinitions}
hasSetupCapabilities={hasLogAnalysisSetupCapabilities}
hasStoppedJobs={hasStoppedLogEntryRateJobs}
isFirstUse={false /* the first use message is already shown by the section below */}
moduleName={logEntryRateModuleDescriptor.moduleName}
onRecreateMlJobForReconfiguration={showLogEntryRateSetup}
onRecreateMlJobForUpdate={showLogEntryRateSetup}
/>
<CategoryJobNoticesSection
hasOutdatedJobConfigurations={hasOutdatedLogEntryCategoriesJobConfigurations}
hasOutdatedJobDefinitions={hasOutdatedLogEntryCategoriesJobDefinitions}
hasSetupCapabilities={hasLogAnalysisSetupCapabilities}
hasStoppedJobs={hasStoppedLogEntryCategoriesJobs}
isFirstUse={isFirstUse}
moduleName={logEntryCategoriesModuleDescriptor.moduleName}
onRecreateMlJobForReconfiguration={showLogEntryCategoriesSetup}
onRecreateMlJobForUpdate={showLogEntryCategoriesSetup}
qualityWarnings={categoryQualityWarnings}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="m">
<AnomaliesResults
isLoadingLogRateResults={isLoading}
isLoadingAnomaliesResults={isLoadingLogEntryAnomalies}
onViewModuleList={showModuleList}
logEntryRateResults={logEntryRate}
anomalies={logEntryAnomalies}
setTimeRange={handleChartTimeRangeChange}
timeRange={queryTimeRange.value}
page={page}
fetchNextPage={fetchNextPage}
fetchPreviousPage={fetchPreviousPage}
changeSortOptions={changeSortOptions}
changePaginationOptions={changePaginationOptions}
sortOptions={sortOptions}
paginationOptions={paginationOptions}
<>
<ResultsContentPage>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem>
<DatasetsSelector
availableDatasets={datasets}
isLoading={isLoadingDatasets}
selectedDatasets={selectedDatasets}
onChangeDatasetSelection={setSelectedDatasets}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSuperDatePicker
start={selectedTimeRange.startTime}
end={selectedTimeRange.endTime}
onTimeChange={handleSelectedTimeRangeChange}
isPaused={autoRefresh.isPaused}
refreshInterval={autoRefresh.interval}
onRefreshChange={handleAutoRefreshChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<LogAnalysisJobProblemIndicator
hasOutdatedJobConfigurations={hasOutdatedLogEntryRateJobConfigurations}
hasOutdatedJobDefinitions={hasOutdatedLogEntryRateJobDefinitions}
hasSetupCapabilities={hasLogAnalysisSetupCapabilities}
hasStoppedJobs={hasStoppedLogEntryRateJobs}
isFirstUse={false /* the first use message is already shown by the section below */}
moduleName={logEntryRateModuleDescriptor.moduleName}
onRecreateMlJobForReconfiguration={showLogEntryRateSetup}
onRecreateMlJobForUpdate={showLogEntryRateSetup}
/>
<CategoryJobNoticesSection
hasOutdatedJobConfigurations={hasOutdatedLogEntryCategoriesJobConfigurations}
hasOutdatedJobDefinitions={hasOutdatedLogEntryCategoriesJobDefinitions}
hasSetupCapabilities={hasLogAnalysisSetupCapabilities}
hasStoppedJobs={hasStoppedLogEntryCategoriesJobs}
isFirstUse={isFirstUse}
moduleName={logEntryCategoriesModuleDescriptor.moduleName}
onRecreateMlJobForReconfiguration={showLogEntryCategoriesSetup}
onRecreateMlJobForUpdate={showLogEntryCategoriesSetup}
qualityWarnings={categoryQualityWarnings}
/>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</ResultsContentPage>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiPanel paddingSize="m">
<AnomaliesResults
isLoadingLogRateResults={isLoading}
isLoadingAnomaliesResults={isLoadingLogEntryAnomalies}
onViewModuleList={showModuleList}
logEntryRateResults={logEntryRate}
anomalies={logEntryAnomalies}
setTimeRange={handleChartTimeRangeChange}
timeRange={queryTimeRange.value}
page={page}
fetchNextPage={fetchNextPage}
fetchPreviousPage={fetchPreviousPage}
changeSortOptions={changeSortOptions}
changePaginationOptions={changePaginationOptions}
sortOptions={sortOptions}
paginationOptions={paginationOptions}
/>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</ResultsContentPage>

{flyoutVisible ? (
<LogEntryFlyout
flyoutItem={flyoutItem}
setFlyoutVisibility={setFlyoutVisibility}
loading={isFlyoutLoading}
setFilter={linkToLogStream}
/>
) : null}
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -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',
{
Expand Down Expand Up @@ -80,6 +88,8 @@ export const LogEntryExampleMessage: React.FunctionComponent<Props> = ({
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);

Expand Down Expand Up @@ -116,6 +126,13 @@ export const LogEntryExampleMessage: React.FunctionComponent<Props> = ({
}

return [
{
label: VIEW_DETAILS_LABEL,
onClick: () => {
setFlyoutId(id);
setFlyoutVisibility(true);
},
},
{
label: VIEW_IN_STREAM_LABEL,
onClick: viewInStreamLinkProps.onClick,
Expand All @@ -127,7 +144,13 @@ export const LogEntryExampleMessage: React.FunctionComponent<Props> = ({
href: viewAnomalyInMachineLearningLinkProps.href,
},
];
}, [viewInStreamLinkProps, viewAnomalyInMachineLearningLinkProps]);
}, [
id,
setFlyoutId,
setFlyoutVisibility,
viewInStreamLinkProps,
viewAnomalyInMachineLearningLinkProps,
]);

return (
<LogEntryRowWrapper
Expand Down
Loading

0 comments on commit 0c01321

Please sign in to comment.