[Logs UI] Show log analysis ML jobs in a list (#71132)
This modifies the ML job setup flyout of the anomalies tab to offer a list of the two available modules. Via the list each of the modules' jobs can be created or re-created.
This commit is contained in:
parent
29580bee4e
commit
ff7b736cc3
|
@ -14,18 +14,10 @@ export type JobStatus =
|
|||
| 'finished'
|
||||
| 'failed';
|
||||
|
||||
export type SetupStatusRequiredReason =
|
||||
| 'missing' // jobs are missing
|
||||
| 'reconfiguration' // the configurations don't match the source configurations
|
||||
| 'update'; // the definitions don't match the module definitions
|
||||
|
||||
export type SetupStatus =
|
||||
| { type: 'initializing' } // acquiring job statuses to determine setup status
|
||||
| { type: 'unknown' } // job status could not be acquired (failed request etc)
|
||||
| {
|
||||
type: 'required';
|
||||
reason: SetupStatusRequiredReason;
|
||||
} // setup required
|
||||
| { type: 'required' } // setup required
|
||||
| { type: 'pending' } // In the process of setting up the module for the first time or retrying, waiting for response
|
||||
| { type: 'succeeded' } // setup succeeded, notifying user
|
||||
| {
|
||||
|
|
|
@ -5,4 +5,5 @@
|
|||
*/
|
||||
|
||||
export * from './log_analysis_job_problem_indicator';
|
||||
export * from './notices_section';
|
||||
export * from './recreate_job_button';
|
||||
|
|
|
@ -11,19 +11,24 @@ import React from 'react';
|
|||
import { RecreateJobCallout } from './recreate_job_callout';
|
||||
|
||||
export const JobConfigurationOutdatedCallout: React.FC<{
|
||||
moduleName: string;
|
||||
onRecreateMlJob: () => void;
|
||||
}> = ({ onRecreateMlJob }) => (
|
||||
<RecreateJobCallout title={jobConfigurationOutdatedTitle} onRecreateMlJob={onRecreateMlJob}>
|
||||
}> = ({ moduleName, onRecreateMlJob }) => (
|
||||
<RecreateJobCallout
|
||||
title={i18n.translate('xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle', {
|
||||
defaultMessage: 'The {moduleName} ML job configuration is outdated',
|
||||
values: {
|
||||
moduleName,
|
||||
},
|
||||
})}
|
||||
onRecreateMlJob={onRecreateMlJob}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutMessage"
|
||||
defaultMessage="The ML job was created using a different source configuration. Recreate the job to apply the current configuration. This removes previously detected anomalies."
|
||||
defaultMessage="The {moduleName} ML job was created using a different source configuration. Recreate the job to apply the current configuration. This removes previously detected anomalies."
|
||||
values={{
|
||||
moduleName,
|
||||
}}
|
||||
/>
|
||||
</RecreateJobCallout>
|
||||
);
|
||||
|
||||
const jobConfigurationOutdatedTitle = i18n.translate(
|
||||
'xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle',
|
||||
{
|
||||
defaultMessage: 'ML job configuration outdated',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -11,19 +11,24 @@ import React from 'react';
|
|||
import { RecreateJobCallout } from './recreate_job_callout';
|
||||
|
||||
export const JobDefinitionOutdatedCallout: React.FC<{
|
||||
moduleName: string;
|
||||
onRecreateMlJob: () => void;
|
||||
}> = ({ onRecreateMlJob }) => (
|
||||
<RecreateJobCallout title={jobDefinitionOutdatedTitle} onRecreateMlJob={onRecreateMlJob}>
|
||||
}> = ({ moduleName, onRecreateMlJob }) => (
|
||||
<RecreateJobCallout
|
||||
title={i18n.translate('xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle', {
|
||||
defaultMessage: 'The {moduleName} ML job definition is outdated',
|
||||
values: {
|
||||
moduleName,
|
||||
},
|
||||
})}
|
||||
onRecreateMlJob={onRecreateMlJob}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutMessage"
|
||||
defaultMessage="A newer version of the ML job is available. Recreate the job to deploy the newer version. This removes previously detected anomalies."
|
||||
defaultMessage="A newer version of the {moduleName} ML job is available. Recreate the job to deploy the newer version. This removes previously detected anomalies."
|
||||
values={{
|
||||
moduleName,
|
||||
}}
|
||||
/>
|
||||
</RecreateJobCallout>
|
||||
);
|
||||
|
||||
const jobDefinitionOutdatedTitle = i18n.translate(
|
||||
'xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle',
|
||||
{
|
||||
defaultMessage: 'ML job definition outdated',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@ export const LogAnalysisJobProblemIndicator: React.FC<{
|
|||
hasOutdatedJobDefinitions: boolean;
|
||||
hasStoppedJobs: boolean;
|
||||
isFirstUse: boolean;
|
||||
moduleName: string;
|
||||
onRecreateMlJobForReconfiguration: () => void;
|
||||
onRecreateMlJobForUpdate: () => void;
|
||||
}> = ({
|
||||
|
@ -23,16 +24,23 @@ export const LogAnalysisJobProblemIndicator: React.FC<{
|
|||
hasOutdatedJobDefinitions,
|
||||
hasStoppedJobs,
|
||||
isFirstUse,
|
||||
moduleName,
|
||||
onRecreateMlJobForReconfiguration,
|
||||
onRecreateMlJobForUpdate,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{hasOutdatedJobDefinitions ? (
|
||||
<JobDefinitionOutdatedCallout onRecreateMlJob={onRecreateMlJobForUpdate} />
|
||||
<JobDefinitionOutdatedCallout
|
||||
moduleName={moduleName}
|
||||
onRecreateMlJob={onRecreateMlJobForUpdate}
|
||||
/>
|
||||
) : null}
|
||||
{hasOutdatedJobConfigurations ? (
|
||||
<JobConfigurationOutdatedCallout onRecreateMlJob={onRecreateMlJobForReconfiguration} />
|
||||
<JobConfigurationOutdatedCallout
|
||||
moduleName={moduleName}
|
||||
onRecreateMlJob={onRecreateMlJobForReconfiguration}
|
||||
/>
|
||||
) : null}
|
||||
{hasStoppedJobs ? <JobStoppedCallout /> : null}
|
||||
{isFirstUse ? <FirstUseCallout /> : null}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status';
|
||||
import { QualityWarning } from './quality_warnings';
|
||||
import { QualityWarning } from '../../../containers/logs/log_analysis/log_analysis_module_types';
|
||||
import { LogAnalysisJobProblemIndicator } from './log_analysis_job_problem_indicator';
|
||||
import { CategoryQualityWarnings } from './quality_warning_notices';
|
||||
|
||||
export const CategoryJobNoticesSection: React.FC<{
|
||||
|
@ -14,6 +14,7 @@ export const CategoryJobNoticesSection: React.FC<{
|
|||
hasOutdatedJobDefinitions: boolean;
|
||||
hasStoppedJobs: boolean;
|
||||
isFirstUse: boolean;
|
||||
moduleName: string;
|
||||
onRecreateMlJobForReconfiguration: () => void;
|
||||
onRecreateMlJobForUpdate: () => void;
|
||||
qualityWarnings: QualityWarning[];
|
||||
|
@ -22,6 +23,7 @@ export const CategoryJobNoticesSection: React.FC<{
|
|||
hasOutdatedJobDefinitions,
|
||||
hasStoppedJobs,
|
||||
isFirstUse,
|
||||
moduleName,
|
||||
onRecreateMlJobForReconfiguration,
|
||||
onRecreateMlJobForUpdate,
|
||||
qualityWarnings,
|
||||
|
@ -32,6 +34,7 @@ export const CategoryJobNoticesSection: React.FC<{
|
|||
hasOutdatedJobDefinitions={hasOutdatedJobDefinitions}
|
||||
hasStoppedJobs={hasStoppedJobs}
|
||||
isFirstUse={isFirstUse}
|
||||
moduleName={moduleName}
|
||||
onRecreateMlJobForReconfiguration={onRecreateMlJobForReconfiguration}
|
||||
onRecreateMlJobForUpdate={onRecreateMlJobForUpdate}
|
||||
/>
|
|
@ -8,7 +8,10 @@ import { EuiCallOut } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { CategoryQualityWarningReason, QualityWarning } from './quality_warnings';
|
||||
import type {
|
||||
CategoryQualityWarningReason,
|
||||
QualityWarning,
|
||||
} from '../../../containers/logs/log_analysis/log_analysis_module_types';
|
||||
|
||||
export const CategoryQualityWarnings: React.FC<{ qualityWarnings: QualityWarning[] }> = ({
|
||||
qualityWarnings,
|
|
@ -84,7 +84,7 @@ export const InitialConfigurationStep: React.FunctionComponent<InitialConfigurat
|
|||
);
|
||||
};
|
||||
|
||||
const editableFormStatus = ['required', 'failed'];
|
||||
const editableFormStatus = ['required', 'failed', 'skipped'];
|
||||
|
||||
const errorCalloutTitle = i18n.translate(
|
||||
'xpack.infra.analysisSetup.steps.initialConfigurationStep.errorCalloutTitle',
|
||||
|
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiButton, PropsOf } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
|
||||
export const ManageJobsButton: React.FunctionComponent<PropsOf<typeof EuiButton>> = (props) => (
|
||||
<EuiButton {...props}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.manageMlJobsButtonLabel"
|
||||
defaultMessage="Manage ML jobs"
|
||||
/>
|
||||
</EuiButton>
|
||||
);
|
|
@ -101,11 +101,10 @@ export const ProcessStep: React.FunctionComponent<ProcessStepProps> = ({
|
|||
/>
|
||||
</EuiButton>
|
||||
</>
|
||||
) : setupStatus.type === 'required' &&
|
||||
(setupStatus.reason === 'update' || setupStatus.reason === 'reconfiguration') ? (
|
||||
<RecreateMLJobsButton isDisabled={!isConfigurationValid} onClick={cleanUpAndSetUp} />
|
||||
) : (
|
||||
) : setupStatus.type === 'required' ? (
|
||||
<CreateMLJobsButton isDisabled={!isConfigurationValid} onClick={setUp} />
|
||||
) : (
|
||||
<RecreateMLJobsButton isDisabled={!isConfigurationValid} onClick={cleanUpAndSetUp} />
|
||||
)}
|
||||
</EuiText>
|
||||
);
|
||||
|
|
|
@ -3,3 +3,6 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './setup_flyout';
|
||||
export * from './setup_flyout_state';
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiSpacer, EuiSteps, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useLogEntryCategoriesSetup } from '../../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { createInitialConfigurationStep } from '../initial_configuration_step';
|
||||
import { createProcessStep } from '../process_step';
|
||||
|
||||
export const LogEntryCategoriesSetupView: React.FC<{
|
||||
onClose: () => void;
|
||||
}> = ({ onClose }) => {
|
||||
const {
|
||||
cleanUpAndSetUp,
|
||||
endTime,
|
||||
isValidating,
|
||||
lastSetupErrorMessages,
|
||||
moduleDescriptor,
|
||||
setEndTime,
|
||||
setStartTime,
|
||||
setValidatedIndices,
|
||||
setUp,
|
||||
setupStatus,
|
||||
startTime,
|
||||
validatedIndices,
|
||||
validationErrors,
|
||||
viewResults,
|
||||
} = useLogEntryCategoriesSetup();
|
||||
|
||||
const viewResultsAndClose = useCallback(() => {
|
||||
viewResults();
|
||||
onClose();
|
||||
}, [viewResults, onClose]);
|
||||
|
||||
const steps = useMemo(
|
||||
() => [
|
||||
createInitialConfigurationStep({
|
||||
setStartTime,
|
||||
setEndTime,
|
||||
startTime,
|
||||
endTime,
|
||||
isValidating,
|
||||
validatedIndices,
|
||||
setupStatus,
|
||||
setValidatedIndices,
|
||||
validationErrors,
|
||||
}),
|
||||
createProcessStep({
|
||||
cleanUpAndSetUp,
|
||||
errorMessages: lastSetupErrorMessages,
|
||||
isConfigurationValid: validationErrors.length <= 0 && !isValidating,
|
||||
setUp,
|
||||
setupStatus,
|
||||
viewResults: viewResultsAndClose,
|
||||
}),
|
||||
],
|
||||
[
|
||||
cleanUpAndSetUp,
|
||||
endTime,
|
||||
isValidating,
|
||||
lastSetupErrorMessages,
|
||||
setEndTime,
|
||||
setStartTime,
|
||||
setUp,
|
||||
setValidatedIndices,
|
||||
setupStatus,
|
||||
startTime,
|
||||
validatedIndices,
|
||||
validationErrors,
|
||||
viewResultsAndClose,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle size="s">
|
||||
<h3>{moduleDescriptor.moduleName} </h3>
|
||||
</EuiTitle>
|
||||
<EuiText size="s">{moduleDescriptor.moduleDescription}</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiSteps steps={steps} />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,37 +5,20 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutBody,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiSteps,
|
||||
} from '@elastic/eui';
|
||||
import { EuiTitle, EuiText, EuiSpacer, EuiSteps } from '@elastic/eui';
|
||||
import { createInitialConfigurationStep } from '../initial_configuration_step';
|
||||
import { createProcessStep } from '../process_step';
|
||||
import { useLogEntryRateSetup } from '../../../../containers/logs/log_analysis/modules/log_entry_rate';
|
||||
|
||||
import {
|
||||
createInitialConfigurationStep,
|
||||
createProcessStep,
|
||||
} from '../../../components/logging/log_analysis_setup';
|
||||
import { useLogEntryRateSetup } from './use_log_entry_rate_setup';
|
||||
|
||||
interface LogEntryRateSetupFlyoutProps {
|
||||
isOpen: boolean;
|
||||
export const LogEntryRateSetupView: React.FC<{
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const LogEntryRateSetupFlyout: React.FC<LogEntryRateSetupFlyoutProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
}) => {
|
||||
}> = ({ onClose }) => {
|
||||
const {
|
||||
cleanUpAndSetUp,
|
||||
endTime,
|
||||
isValidating,
|
||||
lastSetupErrorMessages,
|
||||
moduleDescriptor,
|
||||
setEndTime,
|
||||
setStartTime,
|
||||
setValidatedIndices,
|
||||
|
@ -91,39 +74,14 @@ export const LogEntryRateSetupFlyout: React.FC<LogEntryRateSetupFlyoutProps> = (
|
|||
]
|
||||
);
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<EuiFlyout onClose={onClose}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.setupFlyout.setupFlyoutTitle"
|
||||
defaultMessage="Anomaly detection with Machine Learning"
|
||||
/>
|
||||
</h3>
|
||||
<h3>{moduleDescriptor.moduleName} </h3>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.setupFlyout.logRateTitle"
|
||||
defaultMessage="Log rate"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiText size="s">
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.setupFlyout.logRateDescription"
|
||||
defaultMessage="Use Machine Learning to automatically detect anomalous log rate counts."
|
||||
/>
|
||||
</EuiText>
|
||||
<EuiText size="s">{moduleDescriptor.moduleDescription}</EuiText>
|
||||
<EuiSpacer />
|
||||
<EuiSteps steps={steps} />
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
logEntryCategoriesModule,
|
||||
useLogEntryCategoriesModuleContext,
|
||||
} from '../../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import {
|
||||
logEntryRateModule,
|
||||
useLogEntryRateModuleContext,
|
||||
} from '../../../../containers/logs/log_analysis/modules/log_entry_rate';
|
||||
import { LogAnalysisModuleListCard } from './module_list_card';
|
||||
import type { ModuleId } from './setup_flyout_state';
|
||||
|
||||
export const LogAnalysisModuleList: React.FC<{
|
||||
onViewModuleSetup: (module: ModuleId) => void;
|
||||
}> = ({ onViewModuleSetup }) => {
|
||||
const { setupStatus: logEntryRateSetupStatus } = useLogEntryRateModuleContext();
|
||||
const { setupStatus: logEntryCategoriesSetupStatus } = useLogEntryCategoriesModuleContext();
|
||||
|
||||
const viewLogEntryRateSetupFlyout = useCallback(() => {
|
||||
onViewModuleSetup('logs_ui_analysis');
|
||||
}, [onViewModuleSetup]);
|
||||
const viewLogEntryCategoriesSetupFlyout = useCallback(() => {
|
||||
onViewModuleSetup('logs_ui_categories');
|
||||
}, [onViewModuleSetup]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<LogAnalysisModuleListCard
|
||||
moduleDescription={logEntryRateModule.moduleDescription}
|
||||
moduleName={logEntryRateModule.moduleName}
|
||||
moduleStatus={logEntryRateSetupStatus}
|
||||
onViewSetup={viewLogEntryRateSetupFlyout}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<LogAnalysisModuleListCard
|
||||
moduleDescription={logEntryCategoriesModule.moduleDescription}
|
||||
moduleName={logEntryCategoriesModule.moduleName}
|
||||
moduleStatus={logEntryCategoriesSetupStatus}
|
||||
onViewSetup={viewLogEntryCategoriesSetupFlyout}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiCard, EuiIcon } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { RecreateJobButton } from '../../log_analysis_job_status';
|
||||
import { SetupStatus } from '../../../../../common/log_analysis';
|
||||
|
||||
export const LogAnalysisModuleListCard: React.FC<{
|
||||
moduleDescription: string;
|
||||
moduleName: string;
|
||||
moduleStatus: SetupStatus;
|
||||
onViewSetup: () => void;
|
||||
}> = ({ moduleDescription, moduleName, moduleStatus, onViewSetup }) => {
|
||||
const icon =
|
||||
moduleStatus.type === 'required' ? (
|
||||
<EuiIcon size="xxl" type="machineLearningApp" />
|
||||
) : (
|
||||
<EuiIcon color="secondary" size="xxl" type="check" />
|
||||
);
|
||||
const footerContent =
|
||||
moduleStatus.type === 'required' ? (
|
||||
<EuiButton onClick={onViewSetup}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.enableAnomalyDetectionButtonLabel"
|
||||
defaultMessage="Enable anomaly detection"
|
||||
/>
|
||||
</EuiButton>
|
||||
) : (
|
||||
<RecreateJobButton onClick={onViewSetup} />
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiCard
|
||||
description={moduleDescription}
|
||||
footer={<div>{footerContent}</div>}
|
||||
icon={icon}
|
||||
title={moduleName}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutHeader,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { LogEntryRateSetupView } from './log_entry_rate_setup_view';
|
||||
import { LogEntryCategoriesSetupView } from './log_entry_categories_setup_view';
|
||||
import { LogAnalysisModuleList } from './module_list';
|
||||
import { useLogAnalysisSetupFlyoutStateContext } from './setup_flyout_state';
|
||||
|
||||
const FLYOUT_HEADING_ID = 'logAnalysisSetupFlyoutHeading';
|
||||
|
||||
export const LogAnalysisSetupFlyout: React.FC = () => {
|
||||
const {
|
||||
closeFlyout,
|
||||
flyoutView,
|
||||
showModuleList,
|
||||
showModuleSetup,
|
||||
} = useLogAnalysisSetupFlyoutStateContext();
|
||||
|
||||
if (flyoutView.view === 'hidden') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlyout aria-labelledby={FLYOUT_HEADING_ID} maxWidth={800} onClose={closeFlyout}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle>
|
||||
<h2 id={FLYOUT_HEADING_ID}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.setupFlyoutTitle"
|
||||
defaultMessage="Anomaly detection with Machine Learning"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
{flyoutView.view === 'moduleList' ? (
|
||||
<LogAnalysisModuleList onViewModuleSetup={showModuleSetup} />
|
||||
) : flyoutView.view === 'moduleSetup' && flyoutView.module === 'logs_ui_analysis' ? (
|
||||
<LogAnalysisSetupFlyoutSubPage onViewModuleList={showModuleList}>
|
||||
<LogEntryRateSetupView onClose={closeFlyout} />
|
||||
</LogAnalysisSetupFlyoutSubPage>
|
||||
) : flyoutView.view === 'moduleSetup' && flyoutView.module === 'logs_ui_categories' ? (
|
||||
<LogAnalysisSetupFlyoutSubPage onViewModuleList={showModuleList}>
|
||||
<LogEntryCategoriesSetupView onClose={closeFlyout} />
|
||||
</LogAnalysisSetupFlyoutSubPage>
|
||||
) : null}
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
||||
|
||||
const LogAnalysisSetupFlyoutSubPage: React.FC<{
|
||||
onViewModuleList: () => void;
|
||||
}> = ({ children, onViewModuleList }) => (
|
||||
<EuiFlexGroup alignItems="flexStart" direction="column" gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty flush="left" iconSide="left" iconType="arrowLeft" onClick={onViewModuleList}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.logs.analysis.setupFlyoutGotoListButtonLabel"
|
||||
defaultMessage="All Machine Learning jobs"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{children}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import createContainer from 'constate';
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
export type ModuleId = 'logs_ui_analysis' | 'logs_ui_categories';
|
||||
|
||||
type FlyoutView =
|
||||
| { view: 'hidden' }
|
||||
| { view: 'moduleList' }
|
||||
| { view: 'moduleSetup'; module: ModuleId };
|
||||
|
||||
export const useLogAnalysisSetupFlyoutState = ({
|
||||
initialFlyoutView = { view: 'hidden' },
|
||||
}: {
|
||||
initialFlyoutView?: FlyoutView;
|
||||
}) => {
|
||||
const [flyoutView, setFlyoutView] = useState<FlyoutView>(initialFlyoutView);
|
||||
|
||||
const closeFlyout = useCallback(() => setFlyoutView({ view: 'hidden' }), []);
|
||||
const showModuleList = useCallback(() => setFlyoutView({ view: 'moduleList' }), []);
|
||||
const showModuleSetup = useCallback(
|
||||
(module: ModuleId) => {
|
||||
setFlyoutView({ view: 'moduleSetup', module });
|
||||
},
|
||||
[setFlyoutView]
|
||||
);
|
||||
|
||||
return {
|
||||
closeFlyout,
|
||||
flyoutView,
|
||||
setFlyoutView,
|
||||
showModuleList,
|
||||
showModuleSetup,
|
||||
};
|
||||
};
|
||||
|
||||
export const [
|
||||
LogAnalysisSetupFlyoutStateProvider,
|
||||
useLogAnalysisSetupFlyoutStateContext,
|
||||
] = createContainer(useLogAnalysisSetupFlyoutState);
|
|
@ -111,14 +111,6 @@ export const useLogAnalysisModule = <JobType extends string>({
|
|||
[cleanUpModule, dispatchModuleStatus, setUpModule]
|
||||
);
|
||||
|
||||
const viewSetupForReconfiguration = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'requestedJobConfigurationUpdate' });
|
||||
}, [dispatchModuleStatus]);
|
||||
|
||||
const viewSetupForUpdate = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'requestedJobDefinitionUpdate' });
|
||||
}, [dispatchModuleStatus]);
|
||||
|
||||
const viewResults = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'viewedResults' });
|
||||
}, [dispatchModuleStatus]);
|
||||
|
@ -143,7 +135,5 @@ export const useLogAnalysisModule = <JobType extends string>({
|
|||
setupStatus: moduleStatus.setupStatus,
|
||||
sourceConfiguration,
|
||||
viewResults,
|
||||
viewSetupForReconfiguration,
|
||||
viewSetupForUpdate,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -43,8 +43,6 @@ type StatusReducerAction =
|
|||
payload: FetchJobStatusResponsePayload;
|
||||
}
|
||||
| { type: 'failedFetchingJobStatuses' }
|
||||
| { type: 'requestedJobConfigurationUpdate' }
|
||||
| { type: 'requestedJobDefinitionUpdate' }
|
||||
| { type: 'viewedResults' };
|
||||
|
||||
const createInitialState = <JobType extends string>({
|
||||
|
@ -173,18 +171,6 @@ const createStatusReducer = <JobType extends string>(jobTypes: JobType[]) => (
|
|||
),
|
||||
};
|
||||
}
|
||||
case 'requestedJobConfigurationUpdate': {
|
||||
return {
|
||||
...state,
|
||||
setupStatus: { type: 'required', reason: 'reconfiguration' },
|
||||
};
|
||||
}
|
||||
case 'requestedJobDefinitionUpdate': {
|
||||
return {
|
||||
...state,
|
||||
setupStatus: { type: 'required', reason: 'update' },
|
||||
};
|
||||
}
|
||||
case 'viewedResults': {
|
||||
return {
|
||||
...state,
|
||||
|
@ -251,7 +237,7 @@ const getSetupStatus = <JobType extends string>(everyJobStatus: Record<JobType,
|
|||
): SetupStatus =>
|
||||
Object.entries<JobStatus>(everyJobStatus).reduce<SetupStatus>((setupStatus, [, jobStatus]) => {
|
||||
if (jobStatus === 'missing') {
|
||||
return { type: 'required', reason: 'missing' };
|
||||
return { type: 'required' };
|
||||
} else if (setupStatus.type === 'required' || setupStatus.type === 'succeeded') {
|
||||
return setupStatus;
|
||||
} else if (setupStatus.type === 'skipped' || isJobStatusWithResults(jobStatus)) {
|
||||
|
|
|
@ -4,18 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ValidateLogEntryDatasetsResponsePayload,
|
||||
ValidationIndicesResponsePayload,
|
||||
} from '../../../../common/http_api/log_analysis';
|
||||
import { DatasetFilter } from '../../../../common/log_analysis';
|
||||
import { DeleteJobsResponsePayload } from './api/ml_cleanup';
|
||||
import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api';
|
||||
import { GetMlModuleResponsePayload } from './api/ml_get_module';
|
||||
import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api';
|
||||
import {
|
||||
ValidationIndicesResponsePayload,
|
||||
ValidateLogEntryDatasetsResponsePayload,
|
||||
} from '../../../../common/http_api/log_analysis';
|
||||
import { DatasetFilter } from '../../../../common/log_analysis';
|
||||
|
||||
export { JobModelSizeStats, JobSummary } from './api/ml_get_jobs_summary_api';
|
||||
|
||||
export interface ModuleDescriptor<JobType extends string> {
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
moduleDescription: string;
|
||||
jobTypes: JobType[];
|
||||
bucketSpan: number;
|
||||
getJobIds: (spaceId: string, sourceId: string) => Record<JobType, string>;
|
||||
|
@ -46,3 +50,43 @@ export interface ModuleSourceConfiguration {
|
|||
spaceId: string;
|
||||
timestampField: string;
|
||||
}
|
||||
|
||||
interface ManyCategoriesWarningReason {
|
||||
type: 'manyCategories';
|
||||
categoriesDocumentRatio: number;
|
||||
}
|
||||
|
||||
interface ManyDeadCategoriesWarningReason {
|
||||
type: 'manyDeadCategories';
|
||||
deadCategoriesRatio: number;
|
||||
}
|
||||
|
||||
interface ManyRareCategoriesWarningReason {
|
||||
type: 'manyRareCategories';
|
||||
rareCategoriesRatio: number;
|
||||
}
|
||||
|
||||
interface NoFrequentCategoriesWarningReason {
|
||||
type: 'noFrequentCategories';
|
||||
}
|
||||
|
||||
interface SingleCategoryWarningReason {
|
||||
type: 'singleCategory';
|
||||
}
|
||||
|
||||
export type CategoryQualityWarningReason =
|
||||
| ManyCategoriesWarningReason
|
||||
| ManyDeadCategoriesWarningReason
|
||||
| ManyRareCategoriesWarningReason
|
||||
| NoFrequentCategoriesWarningReason
|
||||
| SingleCategoryWarningReason;
|
||||
|
||||
export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type'];
|
||||
|
||||
export interface CategoryQualityWarning {
|
||||
type: 'categoryQualityWarning';
|
||||
jobId: string;
|
||||
reasons: CategoryQualityWarningReason[];
|
||||
}
|
||||
|
||||
export type QualityWarning = CategoryQualityWarning;
|
||||
|
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './module_descriptor';
|
||||
export * from './use_log_entry_categories_module';
|
||||
export * from './use_log_entry_categories_quality';
|
||||
export * from './use_log_entry_categories_setup';
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
bucketSpan,
|
||||
categoriesMessageField,
|
||||
|
@ -12,19 +13,25 @@ import {
|
|||
LogEntryCategoriesJobType,
|
||||
logEntryCategoriesJobTypes,
|
||||
partitionField,
|
||||
} from '../../../../common/log_analysis';
|
||||
import {
|
||||
cleanUpJobsAndDatafeeds,
|
||||
ModuleDescriptor,
|
||||
ModuleSourceConfiguration,
|
||||
} from '../../../containers/logs/log_analysis';
|
||||
import { callJobsSummaryAPI } from '../../../containers/logs/log_analysis/api/ml_get_jobs_summary_api';
|
||||
import { callGetMlModuleAPI } from '../../../containers/logs/log_analysis/api/ml_get_module';
|
||||
import { callSetupMlModuleAPI } from '../../../containers/logs/log_analysis/api/ml_setup_module_api';
|
||||
import { callValidateDatasetsAPI } from '../../../containers/logs/log_analysis/api/validate_datasets';
|
||||
import { callValidateIndicesAPI } from '../../../containers/logs/log_analysis/api/validate_indices';
|
||||
} from '../../../../../../common/log_analysis';
|
||||
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
|
||||
import { callGetMlModuleAPI } from '../../api/ml_get_module';
|
||||
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
|
||||
import { callValidateDatasetsAPI } from '../../api/validate_datasets';
|
||||
import { callValidateIndicesAPI } from '../../api/validate_indices';
|
||||
import { cleanUpJobsAndDatafeeds } from '../../log_analysis_cleanup';
|
||||
import { ModuleDescriptor, ModuleSourceConfiguration } from '../../log_analysis_module_types';
|
||||
|
||||
const moduleId = 'logs_ui_categories';
|
||||
const moduleName = i18n.translate('xpack.infra.logs.analysis.logEntryCategoriesModuleName', {
|
||||
defaultMessage: 'Categorization',
|
||||
});
|
||||
const moduleDescription = i18n.translate(
|
||||
'xpack.infra.logs.analysis.logEntryCategoriesModuleDescription',
|
||||
{
|
||||
defaultMessage: 'Use Machine Learning to automatically categorize log messages.',
|
||||
}
|
||||
);
|
||||
|
||||
const getJobIds = (spaceId: string, sourceId: string) =>
|
||||
logEntryCategoriesJobTypes.reduce(
|
||||
|
@ -138,6 +145,8 @@ const validateSetupDatasets = async (
|
|||
|
||||
export const logEntryCategoriesModule: ModuleDescriptor<LogEntryCategoriesJobType> = {
|
||||
moduleId,
|
||||
moduleName,
|
||||
moduleDescription,
|
||||
jobTypes: logEntryCategoriesJobTypes,
|
||||
bucketSpan,
|
||||
getJobIds,
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
import createContainer from 'constate';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
ModuleSourceConfiguration,
|
||||
useLogAnalysisModule,
|
||||
useLogAnalysisModuleConfiguration,
|
||||
useLogAnalysisModuleDefinition,
|
||||
} from '../../../containers/logs/log_analysis';
|
||||
import { useLogAnalysisModule } from '../../log_analysis_module';
|
||||
import { useLogAnalysisModuleConfiguration } from '../../log_analysis_module_configuration';
|
||||
import { useLogAnalysisModuleDefinition } from '../../log_analysis_module_definition';
|
||||
import { ModuleSourceConfiguration } from '../../log_analysis_module_types';
|
||||
import { logEntryCategoriesModule } from './module_descriptor';
|
||||
import { useLogEntryCategoriesQuality } from './use_log_entry_categories_quality';
|
||||
|
|
@ -5,9 +5,12 @@
|
|||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { JobModelSizeStats, JobSummary } from '../../../containers/logs/log_analysis';
|
||||
import { QualityWarning, CategoryQualityWarningReason } from './sections/notices/quality_warnings';
|
||||
import {
|
||||
JobModelSizeStats,
|
||||
JobSummary,
|
||||
QualityWarning,
|
||||
CategoryQualityWarningReason,
|
||||
} from '../../log_analysis_module_types';
|
||||
|
||||
export const useLogEntryCategoriesQuality = ({ jobSummaries }: { jobSummaries: JobSummary[] }) => {
|
||||
const categoryQualityWarnings: QualityWarning[] = useMemo(
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useAnalysisSetupState } from '../../../containers/logs/log_analysis';
|
||||
import { useAnalysisSetupState } from '../../log_analysis_setup_state';
|
||||
import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module';
|
||||
|
||||
export const useLogEntryCategoriesSetup = () => {
|
||||
|
@ -41,6 +41,7 @@ export const useLogEntryCategoriesSetup = () => {
|
|||
endTime,
|
||||
isValidating,
|
||||
lastSetupErrorMessages,
|
||||
moduleDescriptor,
|
||||
setEndTime,
|
||||
setStartTime,
|
||||
setValidatedIndices,
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './module_descriptor';
|
||||
export * from './use_log_entry_rate_module';
|
||||
export * from './use_log_entry_rate_setup';
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
bucketSpan,
|
||||
DatasetFilter,
|
||||
|
@ -11,19 +12,25 @@ import {
|
|||
LogEntryRateJobType,
|
||||
logEntryRateJobTypes,
|
||||
partitionField,
|
||||
} from '../../../../common/log_analysis';
|
||||
import {
|
||||
cleanUpJobsAndDatafeeds,
|
||||
ModuleDescriptor,
|
||||
ModuleSourceConfiguration,
|
||||
} from '../../../containers/logs/log_analysis';
|
||||
import { callJobsSummaryAPI } from '../../../containers/logs/log_analysis/api/ml_get_jobs_summary_api';
|
||||
import { callGetMlModuleAPI } from '../../../containers/logs/log_analysis/api/ml_get_module';
|
||||
import { callSetupMlModuleAPI } from '../../../containers/logs/log_analysis/api/ml_setup_module_api';
|
||||
import { callValidateDatasetsAPI } from '../../../containers/logs/log_analysis/api/validate_datasets';
|
||||
import { callValidateIndicesAPI } from '../../../containers/logs/log_analysis/api/validate_indices';
|
||||
} from '../../../../../../common/log_analysis';
|
||||
import { ModuleDescriptor, ModuleSourceConfiguration } from '../../log_analysis_module_types';
|
||||
import { cleanUpJobsAndDatafeeds } from '../../log_analysis_cleanup';
|
||||
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
|
||||
import { callGetMlModuleAPI } from '../../api/ml_get_module';
|
||||
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
|
||||
import { callValidateDatasetsAPI } from '../../api/validate_datasets';
|
||||
import { callValidateIndicesAPI } from '../../api/validate_indices';
|
||||
|
||||
const moduleId = 'logs_ui_analysis';
|
||||
const moduleName = i18n.translate('xpack.infra.logs.analysis.logEntryRateModuleName', {
|
||||
defaultMessage: 'Log rate',
|
||||
});
|
||||
const moduleDescription = i18n.translate(
|
||||
'xpack.infra.logs.analysis.logEntryRateModuleDescription',
|
||||
{
|
||||
defaultMessage: 'Use Machine Learning to automatically detect anomalous log entry rates.',
|
||||
}
|
||||
);
|
||||
|
||||
const getJobIds = (spaceId: string, sourceId: string) =>
|
||||
logEntryRateJobTypes.reduce(
|
||||
|
@ -126,6 +133,8 @@ const validateSetupDatasets = async (
|
|||
|
||||
export const logEntryRateModule: ModuleDescriptor<LogEntryRateJobType> = {
|
||||
moduleId,
|
||||
moduleName,
|
||||
moduleDescription,
|
||||
jobTypes: logEntryRateJobTypes,
|
||||
bucketSpan,
|
||||
getJobIds,
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
import createContainer from 'constate';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
ModuleSourceConfiguration,
|
||||
useLogAnalysisModule,
|
||||
useLogAnalysisModuleConfiguration,
|
||||
useLogAnalysisModuleDefinition,
|
||||
} from '../../../containers/logs/log_analysis';
|
||||
import { ModuleSourceConfiguration } from '../../log_analysis_module_types';
|
||||
import { useLogAnalysisModule } from '../../log_analysis_module';
|
||||
import { useLogAnalysisModuleConfiguration } from '../../log_analysis_module_configuration';
|
||||
import { useLogAnalysisModuleDefinition } from '../../log_analysis_module_definition';
|
||||
import { logEntryRateModule } from './module_descriptor';
|
||||
|
||||
export const useLogEntryRateModule = ({
|
|
@ -4,7 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { useAnalysisSetupState } from '../../../containers/logs/log_analysis';
|
||||
import createContainer from 'constate';
|
||||
import { useAnalysisSetupState } from '../../log_analysis_setup_state';
|
||||
import { useLogEntryRateModuleContext } from './use_log_entry_rate_module';
|
||||
|
||||
export const useLogEntryRateSetup = () => {
|
||||
|
@ -41,6 +42,7 @@ export const useLogEntryRateSetup = () => {
|
|||
endTime,
|
||||
isValidating,
|
||||
lastSetupErrorMessages,
|
||||
moduleDescriptor,
|
||||
setEndTime,
|
||||
setStartTime,
|
||||
setValidatedIndices,
|
||||
|
@ -52,3 +54,7 @@ export const useLogEntryRateSetup = () => {
|
|||
viewResults,
|
||||
};
|
||||
};
|
||||
|
||||
export const [LogEntryRateSetupProvider, useLogEntryRateSetupContext] = createContainer(
|
||||
useLogEntryRateSetup
|
||||
);
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { isJobStatusWithResults } from '../../../../common/log_analysis';
|
||||
import { LoadingPage } from '../../../components/loading_page';
|
||||
import {
|
||||
|
@ -17,10 +17,10 @@ import {
|
|||
import { SourceErrorPage } from '../../../components/source_error_page';
|
||||
import { SourceLoadingPage } from '../../../components/source_loading_page';
|
||||
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
|
||||
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { useLogSourceContext } from '../../../containers/logs/log_source';
|
||||
import { LogEntryCategoriesResultsContent } from './page_results_content';
|
||||
import { LogEntryCategoriesSetupContent } from './page_setup_content';
|
||||
import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module';
|
||||
import { LogEntryCategoriesSetupFlyout } from './setup_flyout';
|
||||
|
||||
export const LogEntryCategoriesPageContent = () => {
|
||||
|
@ -50,13 +50,6 @@ export const LogEntryCategoriesPageContent = () => {
|
|||
}
|
||||
}, [fetchJobStatus, hasLogAnalysisReadCapabilities]);
|
||||
|
||||
// Open flyout if there are no ML jobs
|
||||
useEffect(() => {
|
||||
if (setupStatus.type === 'required' && setupStatus.reason === 'missing') {
|
||||
openFlyout();
|
||||
}
|
||||
}, [setupStatus, openFlyout]);
|
||||
|
||||
if (isLoading || isUninitialized) {
|
||||
return <SourceLoadingPage />;
|
||||
} else if (hasFailedLoadingSource) {
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { useLogSourceContext } from '../../../containers/logs/log_source';
|
||||
import { useKibanaSpaceId } from '../../../utils/use_kibana_space_id';
|
||||
import { LogEntryCategoriesModuleProvider } from './use_log_entry_categories_module';
|
||||
|
||||
export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ children }) => {
|
||||
const { sourceId, sourceConfiguration } = useLogSourceContext();
|
||||
|
|
|
@ -12,17 +12,17 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { euiStyled, useTrackPageview } from '../../../../../observability/public';
|
||||
import { TimeRange } from '../../../../common/http_api/shared/time_range';
|
||||
import { CategoryJobNoticesSection } from '../../../components/logging/log_analysis_job_status';
|
||||
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { ViewLogInContext } from '../../../containers/logs/view_log_in_context';
|
||||
import { useInterval } from '../../../hooks/use_interval';
|
||||
import { CategoryJobNoticesSection } from './sections/notices/notices_section';
|
||||
import { PageViewLogInContext } from '../stream/page_view_log_in_context';
|
||||
import { TopCategoriesSection } from './sections/top_categories';
|
||||
import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_module';
|
||||
import { useLogEntryCategoriesResults } from './use_log_entry_categories_results';
|
||||
import {
|
||||
StringTimeRange,
|
||||
useLogEntryCategoriesResultsUrlState,
|
||||
} from './use_log_entry_categories_results_url_state';
|
||||
import { PageViewLogInContext } from '../stream/page_view_log_in_context';
|
||||
import { ViewLogInContext } from '../../../containers/logs/view_log_in_context';
|
||||
|
||||
const JOB_STATUS_POLLING_INTERVAL = 30000;
|
||||
|
||||
|
@ -39,9 +39,8 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent<LogEntryC
|
|||
const {
|
||||
fetchJobStatus,
|
||||
fetchModuleDefinition,
|
||||
moduleDescriptor,
|
||||
setupStatus,
|
||||
viewSetupForReconfiguration,
|
||||
viewSetupForUpdate,
|
||||
hasOutdatedJobConfigurations,
|
||||
hasOutdatedJobDefinitions,
|
||||
hasStoppedJobs,
|
||||
|
@ -131,16 +130,6 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent<LogEntryC
|
|||
[setAutoRefresh]
|
||||
);
|
||||
|
||||
const viewSetupFlyoutForReconfiguration = useCallback(() => {
|
||||
viewSetupForReconfiguration();
|
||||
onOpenSetup();
|
||||
}, [onOpenSetup, viewSetupForReconfiguration]);
|
||||
|
||||
const viewSetupFlyoutForUpdate = useCallback(() => {
|
||||
viewSetupForUpdate();
|
||||
onOpenSetup();
|
||||
}, [onOpenSetup, viewSetupForUpdate]);
|
||||
|
||||
const hasResults = useMemo(() => topLogEntryCategories.length > 0, [
|
||||
topLogEntryCategories.length,
|
||||
]);
|
||||
|
@ -210,8 +199,9 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent<LogEntryC
|
|||
hasOutdatedJobDefinitions={hasOutdatedJobDefinitions}
|
||||
hasStoppedJobs={hasStoppedJobs}
|
||||
isFirstUse={isFirstUse}
|
||||
onRecreateMlJobForReconfiguration={viewSetupFlyoutForReconfiguration}
|
||||
onRecreateMlJobForUpdate={viewSetupFlyoutForUpdate}
|
||||
moduleName={moduleDescriptor.moduleName}
|
||||
onRecreateMlJobForReconfiguration={onOpenSetup}
|
||||
onRecreateMlJobForUpdate={onOpenSetup}
|
||||
qualityWarnings={categoryQualityWarnings}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
@ -223,7 +213,7 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent<LogEntryC
|
|||
isLoadingTopCategories={isLoadingTopLogEntryCategories}
|
||||
jobId={jobIds['log-entry-categories-count']}
|
||||
onChangeDatasetSelection={setCategoryQueryDatasets}
|
||||
onRequestRecreateMlJob={viewSetupFlyoutForReconfiguration}
|
||||
onRequestRecreateMlJob={onOpenSetup}
|
||||
selectedDatasets={categoryQueryDatasets}
|
||||
sourceId={sourceId}
|
||||
timeRange={categoryQueryTimeRange.timeRange}
|
||||
|
|
|
@ -1,45 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
interface ManyCategoriesWarningReason {
|
||||
type: 'manyCategories';
|
||||
categoriesDocumentRatio: number;
|
||||
}
|
||||
|
||||
interface ManyDeadCategoriesWarningReason {
|
||||
type: 'manyDeadCategories';
|
||||
deadCategoriesRatio: number;
|
||||
}
|
||||
|
||||
interface ManyRareCategoriesWarningReason {
|
||||
type: 'manyRareCategories';
|
||||
rareCategoriesRatio: number;
|
||||
}
|
||||
|
||||
interface NoFrequentCategoriesWarningReason {
|
||||
type: 'noFrequentCategories';
|
||||
}
|
||||
|
||||
interface SingleCategoryWarningReason {
|
||||
type: 'singleCategory';
|
||||
}
|
||||
|
||||
export type CategoryQualityWarningReason =
|
||||
| ManyCategoriesWarningReason
|
||||
| ManyDeadCategoriesWarningReason
|
||||
| ManyRareCategoriesWarningReason
|
||||
| NoFrequentCategoriesWarningReason
|
||||
| SingleCategoryWarningReason;
|
||||
|
||||
export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type'];
|
||||
|
||||
export interface CategoryQualityWarning {
|
||||
type: 'categoryQualityWarning';
|
||||
jobId: string;
|
||||
reasons: CategoryQualityWarningReason[];
|
||||
}
|
||||
|
||||
export type QualityWarning = CategoryQualityWarning;
|
|
@ -4,23 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutBody,
|
||||
EuiTitle,
|
||||
EuiText,
|
||||
EuiFlyoutHeader,
|
||||
EuiSpacer,
|
||||
EuiSteps,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
createInitialConfigurationStep,
|
||||
createProcessStep,
|
||||
} from '../../../components/logging/log_analysis_setup';
|
||||
import { useLogEntryCategoriesSetup } from './use_log_entry_categories_setup';
|
||||
import { useLogEntryCategoriesSetup } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
|
||||
interface LogEntryCategoriesSetupFlyoutProps {
|
||||
isOpen: boolean;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import React, { memo, useEffect, useCallback } from 'react';
|
||||
import { isJobStatusWithResults } from '../../../../common/log_analysis';
|
||||
import { LoadingPage } from '../../../components/loading_page';
|
||||
import {
|
||||
|
@ -14,16 +14,23 @@ import {
|
|||
MissingSetupPrivilegesPrompt,
|
||||
SubscriptionSplashContent,
|
||||
} from '../../../components/logging/log_analysis_setup';
|
||||
import {
|
||||
LogAnalysisSetupFlyout,
|
||||
useLogAnalysisSetupFlyoutStateContext,
|
||||
} from '../../../components/logging/log_analysis_setup/setup_flyout';
|
||||
import { SourceErrorPage } from '../../../components/source_error_page';
|
||||
import { SourceLoadingPage } from '../../../components/source_loading_page';
|
||||
import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis';
|
||||
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_rate';
|
||||
import { useLogSourceContext } from '../../../containers/logs/log_source';
|
||||
import { LogEntryRateResultsContent } from './page_results_content';
|
||||
import { LogEntryRateSetupContent } from './page_setup_content';
|
||||
import { useLogEntryRateModuleContext } from './use_log_entry_rate_module';
|
||||
import { LogEntryRateSetupFlyout } from './setup_flyout';
|
||||
import { useInterval } from '../../../hooks/use_interval';
|
||||
|
||||
export const LogEntryRatePageContent = () => {
|
||||
const JOB_STATUS_POLLING_INTERVAL = 30000;
|
||||
|
||||
export const LogEntryRatePageContent = memo(() => {
|
||||
const {
|
||||
hasFailedLoadingSource,
|
||||
isLoading,
|
||||
|
@ -38,24 +45,52 @@ export const LogEntryRatePageContent = () => {
|
|||
hasLogAnalysisSetupCapabilities,
|
||||
} = useLogAnalysisCapabilitiesContext();
|
||||
|
||||
const { fetchJobStatus, setupStatus, jobStatus } = useLogEntryRateModuleContext();
|
||||
const {
|
||||
fetchJobStatus: fetchLogEntryCategoriesJobStatus,
|
||||
fetchModuleDefinition: fetchLogEntryCategoriesModuleDefinition,
|
||||
jobStatus: logEntryCategoriesJobStatus,
|
||||
setupStatus: logEntryCategoriesSetupStatus,
|
||||
} = useLogEntryCategoriesModuleContext();
|
||||
const {
|
||||
fetchJobStatus: fetchLogEntryRateJobStatus,
|
||||
fetchModuleDefinition: fetchLogEntryRateModuleDefinition,
|
||||
jobStatus: logEntryRateJobStatus,
|
||||
setupStatus: logEntryRateSetupStatus,
|
||||
} = useLogEntryRateModuleContext();
|
||||
|
||||
const [isFlyoutOpen, setIsFlyoutOpen] = useState<boolean>(false);
|
||||
const openFlyout = useCallback(() => setIsFlyoutOpen(true), []);
|
||||
const closeFlyout = useCallback(() => setIsFlyoutOpen(false), []);
|
||||
const { showModuleList } = useLogAnalysisSetupFlyoutStateContext();
|
||||
|
||||
const fetchAllJobStatuses = useCallback(
|
||||
() => Promise.all([fetchLogEntryCategoriesJobStatus(), fetchLogEntryRateJobStatus()]),
|
||||
[fetchLogEntryCategoriesJobStatus, fetchLogEntryRateJobStatus]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasLogAnalysisReadCapabilities) {
|
||||
fetchJobStatus();
|
||||
fetchAllJobStatuses();
|
||||
}
|
||||
}, [fetchJobStatus, hasLogAnalysisReadCapabilities]);
|
||||
}, [fetchAllJobStatuses, hasLogAnalysisReadCapabilities]);
|
||||
|
||||
// Open flyout if there are no ML jobs
|
||||
useEffect(() => {
|
||||
if (setupStatus.type === 'required' && setupStatus.reason === 'missing') {
|
||||
openFlyout();
|
||||
if (hasLogAnalysisReadCapabilities) {
|
||||
fetchLogEntryCategoriesModuleDefinition();
|
||||
}
|
||||
}, [setupStatus, openFlyout]);
|
||||
}, [fetchLogEntryCategoriesModuleDefinition, hasLogAnalysisReadCapabilities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasLogAnalysisReadCapabilities) {
|
||||
fetchLogEntryRateModuleDefinition();
|
||||
}
|
||||
}, [fetchLogEntryRateModuleDefinition, hasLogAnalysisReadCapabilities]);
|
||||
|
||||
useInterval(() => {
|
||||
if (logEntryCategoriesSetupStatus.type !== 'pending' && hasLogAnalysisReadCapabilities) {
|
||||
fetchLogEntryCategoriesJobStatus();
|
||||
}
|
||||
if (logEntryRateSetupStatus.type !== 'pending' && hasLogAnalysisReadCapabilities) {
|
||||
fetchLogEntryRateJobStatus();
|
||||
}
|
||||
}, JOB_STATUS_POLLING_INTERVAL);
|
||||
|
||||
if (isLoading || isUninitialized) {
|
||||
return <SourceLoadingPage />;
|
||||
|
@ -65,7 +100,10 @@ export const LogEntryRatePageContent = () => {
|
|||
return <SubscriptionSplashContent />;
|
||||
} else if (!hasLogAnalysisReadCapabilities) {
|
||||
return <MissingResultsPrivilegesPrompt />;
|
||||
} else if (setupStatus.type === 'initializing') {
|
||||
} else if (
|
||||
logEntryCategoriesSetupStatus.type === 'initializing' ||
|
||||
logEntryRateSetupStatus.type === 'initializing'
|
||||
) {
|
||||
return (
|
||||
<LoadingPage
|
||||
message={i18n.translate('xpack.infra.logs.analysisPage.loadingMessage', {
|
||||
|
@ -73,13 +111,19 @@ export const LogEntryRatePageContent = () => {
|
|||
})}
|
||||
/>
|
||||
);
|
||||
} else if (setupStatus.type === 'unknown') {
|
||||
return <LogAnalysisSetupStatusUnknownPrompt retry={fetchJobStatus} />;
|
||||
} else if (isJobStatusWithResults(jobStatus['log-entry-rate'])) {
|
||||
} else if (
|
||||
logEntryCategoriesSetupStatus.type === 'unknown' ||
|
||||
logEntryRateSetupStatus.type === 'unknown'
|
||||
) {
|
||||
return <LogAnalysisSetupStatusUnknownPrompt retry={fetchAllJobStatuses} />;
|
||||
} else if (
|
||||
isJobStatusWithResults(logEntryCategoriesJobStatus['log-entry-categories-count']) ||
|
||||
isJobStatusWithResults(logEntryRateJobStatus['log-entry-rate'])
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<LogEntryRateResultsContent onOpenSetup={openFlyout} />
|
||||
<LogEntryRateSetupFlyout isOpen={isFlyoutOpen} onClose={closeFlyout} />
|
||||
<LogEntryRateResultsContent />
|
||||
<LogAnalysisSetupFlyout />
|
||||
</>
|
||||
);
|
||||
} else if (!hasLogAnalysisSetupCapabilities) {
|
||||
|
@ -87,9 +131,9 @@ export const LogEntryRatePageContent = () => {
|
|||
} else {
|
||||
return (
|
||||
<>
|
||||
<LogEntryRateSetupContent onOpenSetup={openFlyout} />
|
||||
<LogEntryRateSetupFlyout isOpen={isFlyoutOpen} onClose={closeFlyout} />
|
||||
<LogEntryRateSetupContent onOpenSetup={showModuleList} />
|
||||
<LogAnalysisSetupFlyout />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout';
|
||||
import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { LogEntryRateModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_rate';
|
||||
import { useLogSourceContext } from '../../../containers/logs/log_source';
|
||||
import { useKibanaSpaceId } from '../../../utils/use_kibana_space_id';
|
||||
import { LogEntryRateModuleProvider } from './use_log_entry_rate_module';
|
||||
|
||||
export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) => {
|
||||
const { sourceId, sourceConfiguration } = useLogSourceContext();
|
||||
|
@ -21,7 +22,14 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children })
|
|||
spaceId={spaceId}
|
||||
timestampField={sourceConfiguration?.configuration.fields.timestamp ?? ''}
|
||||
>
|
||||
{children}
|
||||
<LogEntryCategoriesModuleProvider
|
||||
indexPattern={sourceConfiguration?.configuration.logAlias ?? ''}
|
||||
sourceId={sourceId}
|
||||
spaceId={spaceId}
|
||||
timestampField={sourceConfiguration?.configuration.fields.timestamp ?? ''}
|
||||
>
|
||||
<LogAnalysisSetupFlyoutStateProvider>{children}</LogAnalysisSetupFlyoutStateProvider>
|
||||
</LogEntryCategoriesModuleProvider>
|
||||
</LogEntryRateModuleProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,19 +11,23 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||
import { euiStyled, useTrackPageview } from '../../../../../observability/public';
|
||||
import { TimeRange } from '../../../../common/http_api/shared/time_range';
|
||||
import { bucketSpan } from '../../../../common/log_analysis';
|
||||
import { LogAnalysisJobProblemIndicator } from '../../../components/logging/log_analysis_job_status';
|
||||
import {
|
||||
CategoryJobNoticesSection,
|
||||
LogAnalysisJobProblemIndicator,
|
||||
} from '../../../components/logging/log_analysis_job_status';
|
||||
import { useLogAnalysisSetupFlyoutStateContext } from '../../../components/logging/log_analysis_setup/setup_flyout';
|
||||
import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_categories';
|
||||
import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analysis/modules/log_entry_rate';
|
||||
import { useLogSourceContext } from '../../../containers/logs/log_source';
|
||||
import { useInterval } from '../../../hooks/use_interval';
|
||||
import { AnomaliesResults } from './sections/anomalies';
|
||||
import { useLogEntryRateModuleContext } from './use_log_entry_rate_module';
|
||||
import { useLogEntryRateResults } from './use_log_entry_rate_results';
|
||||
import { useLogEntryAnomaliesResults } from './use_log_entry_anomalies_results';
|
||||
import { useLogEntryRateResults } from './use_log_entry_rate_results';
|
||||
import {
|
||||
StringTimeRange,
|
||||
useLogAnalysisResultsUrlState,
|
||||
} from './use_log_entry_rate_results_url_state';
|
||||
|
||||
const JOB_STATUS_POLLING_INTERVAL = 30000;
|
||||
|
||||
export const SORT_DEFAULTS = {
|
||||
direction: 'desc' as const,
|
||||
field: 'anomalyScore' as const,
|
||||
|
@ -33,28 +37,29 @@ export const PAGINATION_DEFAULTS = {
|
|||
pageSize: 25,
|
||||
};
|
||||
|
||||
interface LogEntryRateResultsContentProps {
|
||||
onOpenSetup: () => void;
|
||||
}
|
||||
|
||||
export const LogEntryRateResultsContent: React.FunctionComponent<LogEntryRateResultsContentProps> = ({
|
||||
onOpenSetup,
|
||||
}) => {
|
||||
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 { sourceId } = useLogSourceContext();
|
||||
|
||||
const {
|
||||
fetchJobStatus,
|
||||
fetchModuleDefinition,
|
||||
setupStatus,
|
||||
viewSetupForReconfiguration,
|
||||
viewSetupForUpdate,
|
||||
hasOutdatedJobConfigurations,
|
||||
hasOutdatedJobDefinitions,
|
||||
hasStoppedJobs,
|
||||
sourceConfiguration: { sourceId },
|
||||
hasOutdatedJobConfigurations: hasOutdatedLogEntryRateJobConfigurations,
|
||||
hasOutdatedJobDefinitions: hasOutdatedLogEntryRateJobDefinitions,
|
||||
hasStoppedJobs: hasStoppedLogEntryRateJobs,
|
||||
moduleDescriptor: logEntryRateModuleDescriptor,
|
||||
setupStatus: logEntryRateSetupStatus,
|
||||
} = useLogEntryRateModuleContext();
|
||||
|
||||
const {
|
||||
categoryQualityWarnings,
|
||||
hasOutdatedJobConfigurations: hasOutdatedLogEntryCategoriesJobConfigurations,
|
||||
hasOutdatedJobDefinitions: hasOutdatedLogEntryCategoriesJobDefinitions,
|
||||
hasStoppedJobs: hasStoppedLogEntryCategoriesJobs,
|
||||
moduleDescriptor: logEntryCategoriesModuleDescriptor,
|
||||
setupStatus: logEntryCategoriesSetupStatus,
|
||||
} = useLogEntryCategoriesModuleContext();
|
||||
|
||||
const {
|
||||
timeRange: selectedTimeRange,
|
||||
setTimeRange: setSelectedTimeRange,
|
||||
|
@ -145,41 +150,33 @@ export const LogEntryRateResultsContent: React.FunctionComponent<LogEntryRateRes
|
|||
[setAutoRefresh]
|
||||
);
|
||||
|
||||
const viewSetupFlyoutForReconfiguration = useCallback(() => {
|
||||
viewSetupForReconfiguration();
|
||||
onOpenSetup();
|
||||
}, [viewSetupForReconfiguration, onOpenSetup]);
|
||||
const { showModuleList, showModuleSetup } = useLogAnalysisSetupFlyoutStateContext();
|
||||
|
||||
const viewSetupFlyoutForUpdate = useCallback(() => {
|
||||
viewSetupForUpdate();
|
||||
onOpenSetup();
|
||||
}, [viewSetupForUpdate, onOpenSetup]);
|
||||
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
const hasResults = useMemo(() => (logEntryRate?.histogramBuckets?.length ?? 0) > 0, [
|
||||
logEntryRate,
|
||||
const showLogEntryRateSetup = useCallback(() => showModuleSetup('logs_ui_analysis'), [
|
||||
showModuleSetup,
|
||||
]);
|
||||
const showLogEntryCategoriesSetup = useCallback(() => showModuleSetup('logs_ui_categories'), [
|
||||
showModuleSetup,
|
||||
]);
|
||||
|
||||
const hasLogRateResults = (logEntryRate?.histogramBuckets?.length ?? 0) > 0;
|
||||
const hasAnomalyResults = logEntryAnomalies.length > 0;
|
||||
|
||||
const isFirstUse = useMemo(
|
||||
() =>
|
||||
((setupStatus.type === 'skipped' && !!setupStatus.newlyCreated) ||
|
||||
setupStatus.type === 'succeeded') &&
|
||||
!hasResults,
|
||||
[hasResults, setupStatus]
|
||||
((logEntryCategoriesSetupStatus.type === 'skipped' &&
|
||||
!!logEntryCategoriesSetupStatus.newlyCreated) ||
|
||||
logEntryCategoriesSetupStatus.type === 'succeeded' ||
|
||||
(logEntryRateSetupStatus.type === 'skipped' && !!logEntryRateSetupStatus.newlyCreated) ||
|
||||
logEntryRateSetupStatus.type === 'succeeded') &&
|
||||
!(hasLogRateResults || hasAnomalyResults),
|
||||
[hasAnomalyResults, hasLogRateResults, logEntryCategoriesSetupStatus, logEntryRateSetupStatus]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getLogEntryRate();
|
||||
}, [getLogEntryRate, queryTimeRange.lastChangedTime]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchModuleDefinition();
|
||||
}, [fetchModuleDefinition]);
|
||||
|
||||
useInterval(() => {
|
||||
fetchJobStatus();
|
||||
}, JOB_STATUS_POLLING_INTERVAL);
|
||||
|
||||
useInterval(
|
||||
() => {
|
||||
handleQueryTimeRangeChange({
|
||||
|
@ -209,12 +206,23 @@ export const LogEntryRateResultsContent: React.FunctionComponent<LogEntryRateRes
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LogAnalysisJobProblemIndicator
|
||||
hasOutdatedJobConfigurations={hasOutdatedJobConfigurations}
|
||||
hasOutdatedJobDefinitions={hasOutdatedJobDefinitions}
|
||||
hasStoppedJobs={hasStoppedJobs}
|
||||
hasOutdatedJobConfigurations={hasOutdatedLogEntryRateJobConfigurations}
|
||||
hasOutdatedJobDefinitions={hasOutdatedLogEntryRateJobDefinitions}
|
||||
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}
|
||||
hasStoppedJobs={hasStoppedLogEntryCategoriesJobs}
|
||||
isFirstUse={isFirstUse}
|
||||
onRecreateMlJobForReconfiguration={viewSetupFlyoutForReconfiguration}
|
||||
onRecreateMlJobForUpdate={viewSetupFlyoutForUpdate}
|
||||
moduleName={logEntryCategoriesModuleDescriptor.moduleName}
|
||||
onRecreateMlJobForReconfiguration={showLogEntryCategoriesSetup}
|
||||
onRecreateMlJobForUpdate={showLogEntryCategoriesSetup}
|
||||
qualityWarnings={categoryQualityWarnings}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -222,7 +230,7 @@ export const LogEntryRateResultsContent: React.FunctionComponent<LogEntryRateRes
|
|||
<AnomaliesResults
|
||||
isLoadingLogRateResults={isLoading}
|
||||
isLoadingAnomaliesResults={isLoadingLogEntryAnomalies}
|
||||
viewSetupForReconfiguration={viewSetupFlyoutForReconfiguration}
|
||||
onViewModuleList={showModuleList}
|
||||
logEntryRateResults={logEntryRate}
|
||||
anomalies={logEntryAnomalies}
|
||||
setTimeRange={handleChartTimeRangeChange}
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiStat } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiTitle } from '@elastic/eui';
|
||||
import numeral from '@elastic/numeral';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { useMount } from 'react-use';
|
||||
import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
|
||||
import { LogEntryAnomaly } from '../../../../../../common/http_api';
|
||||
import { useLogEntryExamples } from '../../use_log_entry_examples';
|
||||
import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples';
|
||||
import { LogEntryExampleMessage, LogEntryExampleMessageHeaders } from './log_entry_example';
|
||||
import { euiStyled } from '../../../../../../../observability/public';
|
||||
import { LogEntryAnomaly } from '../../../../../../common/http_api';
|
||||
import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
|
||||
import { LogEntryExampleMessages } from '../../../../../components/logging/log_entry_examples/log_entry_examples';
|
||||
import { useLogSourceContext } from '../../../../../containers/logs/log_source';
|
||||
import { useLogEntryExamples } from '../../use_log_entry_examples';
|
||||
import { LogEntryExampleMessage, LogEntryExampleMessageHeaders } from './log_entry_example';
|
||||
|
||||
const EXAMPLE_COUNT = 5;
|
||||
|
||||
|
|
|
@ -20,16 +20,16 @@ import { TimeRange } from '../../../../../../common/http_api/shared/time_range';
|
|||
import { getAnnotationsForAll, getLogEntryRateCombinedSeries } from '../helpers/data_formatters';
|
||||
import { AnomaliesChart } from './chart';
|
||||
import { AnomaliesTable } from './table';
|
||||
import { RecreateJobButton } from '../../../../../components/logging/log_analysis_job_status';
|
||||
import { ManageJobsButton } from '../../../../../components/logging/log_analysis_setup/manage_jobs_button';
|
||||
import {
|
||||
Page,
|
||||
ChangePaginationOptions,
|
||||
ChangeSortOptions,
|
||||
FetchNextPage,
|
||||
FetchPreviousPage,
|
||||
ChangeSortOptions,
|
||||
ChangePaginationOptions,
|
||||
SortOptions,
|
||||
PaginationOptions,
|
||||
LogEntryAnomalies,
|
||||
Page,
|
||||
PaginationOptions,
|
||||
SortOptions,
|
||||
} from '../../use_log_entry_anomalies_results';
|
||||
import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper';
|
||||
|
||||
|
@ -40,7 +40,7 @@ export const AnomaliesResults: React.FunctionComponent<{
|
|||
anomalies: LogEntryAnomalies;
|
||||
setTimeRange: (timeRange: TimeRange) => void;
|
||||
timeRange: TimeRange;
|
||||
viewSetupForReconfiguration: () => void;
|
||||
onViewModuleList: () => void;
|
||||
page: Page;
|
||||
fetchNextPage?: FetchNextPage;
|
||||
fetchPreviousPage?: FetchPreviousPage;
|
||||
|
@ -54,7 +54,7 @@ export const AnomaliesResults: React.FunctionComponent<{
|
|||
logEntryRateResults,
|
||||
setTimeRange,
|
||||
timeRange,
|
||||
viewSetupForReconfiguration,
|
||||
onViewModuleList,
|
||||
anomalies,
|
||||
changeSortOptions,
|
||||
sortOptions,
|
||||
|
@ -93,7 +93,7 @@ export const AnomaliesResults: React.FunctionComponent<{
|
|||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<RecreateJobButton onClick={viewSetupForReconfiguration} size="s" />
|
||||
<ManageJobsButton onClick={onViewModuleList} size="s" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
|
|
|
@ -42,10 +42,10 @@ export const LogsPageContent: React.FunctionComponent = () => {
|
|||
pathname: '/stream',
|
||||
};
|
||||
|
||||
const logRateTab = {
|
||||
const anomaliesTab = {
|
||||
app: 'logs',
|
||||
title: logRateTabTitle,
|
||||
pathname: '/log-rate',
|
||||
title: anomaliesTabTitle,
|
||||
pathname: '/anomalies',
|
||||
};
|
||||
|
||||
const logCategoriesTab = {
|
||||
|
@ -77,7 +77,7 @@ export const LogsPageContent: React.FunctionComponent = () => {
|
|||
<AppNavigation aria-label={pageTitle}>
|
||||
<EuiFlexGroup gutterSize={'none'} alignItems={'center'}>
|
||||
<EuiFlexItem>
|
||||
<RoutedTabs tabs={[streamTab, logRateTab, logCategoriesTab, settingsTab]} />
|
||||
<RoutedTabs tabs={[streamTab, anomaliesTab, logCategoriesTab, settingsTab]} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AlertDropdown />
|
||||
|
@ -96,10 +96,11 @@ export const LogsPageContent: React.FunctionComponent = () => {
|
|||
</AppNavigation>
|
||||
<Switch>
|
||||
<Route path={streamTab.pathname} component={StreamPage} />
|
||||
<Route path={logRateTab.pathname} component={LogEntryRatePage} />
|
||||
<Route path={anomaliesTab.pathname} component={LogEntryRatePage} />
|
||||
<Route path={logCategoriesTab.pathname} component={LogEntryCategoriesPage} />
|
||||
<Route path={settingsTab.pathname} component={LogsSettingsPage} />
|
||||
<RedirectWithQueryParams from={'/analysis'} to={logRateTab.pathname} exact />
|
||||
<RedirectWithQueryParams from={'/analysis'} to={anomaliesTab.pathname} exact />
|
||||
<RedirectWithQueryParams from={'/log-rate'} to={anomaliesTab.pathname} exact />
|
||||
<RedirectWithQueryParams from={'/'} to={streamTab.pathname} exact />
|
||||
</Switch>
|
||||
</ColumnarPage>
|
||||
|
@ -114,8 +115,8 @@ const streamTabTitle = i18n.translate('xpack.infra.logs.index.streamTabTitle', {
|
|||
defaultMessage: 'Stream',
|
||||
});
|
||||
|
||||
const logRateTabTitle = i18n.translate('xpack.infra.logs.index.logRateBetaBadgeTitle', {
|
||||
defaultMessage: 'Log Rate',
|
||||
const anomaliesTabTitle = i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
|
||||
defaultMessage: 'Anomalies',
|
||||
});
|
||||
|
||||
const logCategoriesTabTitle = i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
|
||||
|
|
|
@ -7469,14 +7469,9 @@
|
|||
"xpack.infra.logs.alerting.threshold.fired": "実行",
|
||||
"xpack.infra.logs.analysis.analyzeInMlButtonLabel": "ML で分析",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionLineSeriesName": "15 分ごとのログエントリー (平均)",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel": "異常を読み込み中",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionTitle": "異常",
|
||||
"xpack.infra.logs.analysis.anomalySectionNoDataBody": "時間範囲を調整する必要があるかもしれません。",
|
||||
"xpack.infra.logs.analysis.anomalySectionNoDataTitle": "表示するデータがありません。",
|
||||
"xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutMessage": "異なるソース構成を使用して ML ジョブが作成されました。現在の構成を適用するにはジョブを再作成してください。これにより以前検出された異常が削除されます。",
|
||||
"xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle": "古い ML ジョブ構成",
|
||||
"xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutMessage": "ML ジョブの新しいバージョンが利用可能です。新しいバージョンをデプロイするにはジョブを再作成してください。これにより以前検出された異常が削除されます。",
|
||||
"xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle": "古い ML ジョブ定義",
|
||||
"xpack.infra.logs.analysis.jobStoppedCalloutMessage": "ML ジョブが手動またはリソース不足により停止しました。新しいログエントリーはジョブが再起動するまで処理されません。",
|
||||
"xpack.infra.logs.analysis.jobStoppedCalloutTitle": "ML ジョブが停止しました",
|
||||
"xpack.infra.logs.analysis.missingMlResultsPrivilegesBody": "本機能は機械学習ジョブを利用し、そのステータスと結果にアクセスするためには、少なくとも{machineLearningUserRole}ロールが必要です。",
|
||||
|
@ -7517,7 +7512,6 @@
|
|||
"xpack.infra.logs.highlights.highlightsPopoverButtonLabel": "ハイライト",
|
||||
"xpack.infra.logs.highlights.highlightTermsFieldLabel": "ハイライトする用語",
|
||||
"xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "カテゴリー",
|
||||
"xpack.infra.logs.index.logRateBetaBadgeTitle": "ログレート",
|
||||
"xpack.infra.logs.index.settingsTabTitle": "設定",
|
||||
"xpack.infra.logs.index.streamTabTitle": "ストリーム",
|
||||
"xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動",
|
||||
|
|
|
@ -7474,14 +7474,9 @@
|
|||
"xpack.infra.logs.alerting.threshold.fired": "已触发",
|
||||
"xpack.infra.logs.analysis.analyzeInMlButtonLabel": "在 ML 中分析",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionLineSeriesName": "每 15 分钟日志条目数(平均值)",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel": "正在加载异常",
|
||||
"xpack.infra.logs.analysis.anomaliesSectionTitle": "异常",
|
||||
"xpack.infra.logs.analysis.anomalySectionNoDataBody": "您可能想调整时间范围。",
|
||||
"xpack.infra.logs.analysis.anomalySectionNoDataTitle": "没有可显示的数据。",
|
||||
"xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutMessage": "创建 ML 作业时所使用的源配置不同。重新创建作业以应用当前配置。这将移除以前检测到的异常。",
|
||||
"xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle": "ML 作业配置已过期",
|
||||
"xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutMessage": "ML 作业有更新的版本可用。重新创建作业以部署更新的版本。这将移除以前检测到的异常。",
|
||||
"xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle": "ML 作业定义已过期",
|
||||
"xpack.infra.logs.analysis.jobStoppedCalloutMessage": "ML 作业已手动停止或由于缺乏资源而停止。作业重新启动后,才会处理新的日志条目。",
|
||||
"xpack.infra.logs.analysis.jobStoppedCalloutTitle": "ML 作业已停止",
|
||||
"xpack.infra.logs.analysis.missingMlResultsPrivilegesBody": "此功能使用 Machine Learning 作业,要访问这些作业的状态和结果,至少需要 {machineLearningUserRole} 角色。",
|
||||
|
@ -7522,7 +7517,6 @@
|
|||
"xpack.infra.logs.highlights.highlightsPopoverButtonLabel": "突出显示",
|
||||
"xpack.infra.logs.highlights.highlightTermsFieldLabel": "要突出显示的词",
|
||||
"xpack.infra.logs.index.logCategoriesBetaBadgeTitle": "类别",
|
||||
"xpack.infra.logs.index.logRateBetaBadgeTitle": "日志速率",
|
||||
"xpack.infra.logs.index.settingsTabTitle": "设置",
|
||||
"xpack.infra.logs.index.streamTabTitle": "流式传输",
|
||||
"xpack.infra.logs.jumpToTailText": "跳到最近的条目",
|
||||
|
|
Loading…
Reference in a new issue