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