diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx index 83d4760259d9bb..aa66febc5b7c43 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx @@ -8,7 +8,7 @@ import createContainer from 'constate-latest'; import { useMemo, useCallback, useEffect } from 'react'; import { callGetMlModuleAPI } from './api/ml_get_module'; -import { bucketSpan } from '../../../../common/log_analysis'; +import { bucketSpan, getJobId } from '../../../../common/log_analysis'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callJobsSummaryAPI } from './api/ml_get_jobs_summary_api'; import { callSetupMlModuleAPI, SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; @@ -138,6 +138,12 @@ export const useLogAnalysisJobs = ({ fetchModuleDefinition(); }, [fetchModuleDefinition]); + const jobIds = useMemo(() => { + return { + 'log-entry-rate': getJobId(spaceId, sourceId, 'log-entry-rate'), + }; + }, [sourceId, spaceId]); + return { fetchJobStatus, isLoadingSetupStatus, @@ -149,6 +155,7 @@ export const useLogAnalysisJobs = ({ viewSetupForReconfiguration, viewSetupForUpdate, viewResults, + jobIds, }; }; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx index 33037aac9afe54..e846d4e9e4ac5e 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx @@ -19,9 +19,9 @@ import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; import React, { useCallback, useContext, useMemo, useState } from 'react'; +import euiStyled from '../../../../../../common/eui_styled_components'; import { TimeRange } from '../../../../common/http_api/shared/time_range'; import { bucketSpan } from '../../../../common/log_analysis'; -import euiStyled from '../../../../../../common/eui_styled_components'; import { LoadingPage } from '../../../components/loading_page'; import { LogAnalysisJobs, @@ -134,6 +134,7 @@ export const AnalysisResultsContent = ({ setupStatus, viewSetupForReconfiguration, viewSetupForUpdate, + jobIds, } = useContext(LogAnalysisJobs.Context); useInterval(() => { @@ -214,6 +215,7 @@ export const AnalysisResultsContent = ({ setTimeRange={handleChartTimeRangeChange} setupStatus={setupStatus} timeRange={queryTimeRange} + jobId={jobIds['log-entry-rate']} /> diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx new file mode 100644 index 00000000000000..852ce20e37d418 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/analyze_in_ml_button.tsx @@ -0,0 +1,101 @@ +/* + * 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 React from 'react'; +import url from 'url'; +import { EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import chrome from 'ui/chrome'; +import { QueryString } from 'ui/utils/query_string'; +import { encode } from 'rison-node'; +import { TimeRange } from '../../../../../common/http_api/shared/time_range'; + +export const AnalyzeInMlButton: React.FunctionComponent<{ + jobId: string; + partition?: string; + timeRange: TimeRange; +}> = ({ jobId, partition, timeRange }) => { + const pathname = chrome.addBasePath('/app/ml'); + const buttonLabel = ( + + ); + return partition ? ( + + {buttonLabel} + + ) : ( + + {buttonLabel} + + ); +}; + +const getOverallAnomalyExplorerLink = (pathname: string, jobId: string, timeRange: TimeRange) => { + const { from, to } = convertTimeRangeToParams(timeRange); + + const _g = encode({ + ml: { + jobIds: [jobId], + }, + time: { + from, + to, + }, + }); + + const hash = `/explorer?${QueryString.encode({ _g })}`; + + return url.format({ + pathname, + hash, + }); +}; + +const getPartitionSpecificSingleMetricViewerLink = ( + pathname: string, + jobId: string, + partition: string, + timeRange: TimeRange +) => { + const { from, to } = convertTimeRangeToParams(timeRange); + + const _g = encode({ + ml: { + jobIds: [jobId], + }, + time: { + from, + to, + mode: 'absolute', + }, + }); + + const _a = encode({ + mlTimeSeriesExplorer: { + entities: { 'event.dataset': partition }, + }, + }); + + const hash = `/timeseriesexplorer?${QueryString.encode({ _g, _a })}`; + + return url.format({ + pathname, + hash, + }); +}; + +const convertTimeRangeToParams = (timeRange: TimeRange): { from: string; to: string } => { + return { + from: new Date(timeRange.startTime).toISOString(), + to: new Date(timeRange.endTime).toISOString(), + }; +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx index a6f2ca71068c27..43cfbb306717d1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx @@ -7,7 +7,7 @@ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import numeral from '@elastic/numeral'; -import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiStat, EuiSpacer } from '@elastic/eui'; import { AnomaliesChart } from './chart'; import { GetLogEntryRateSuccessResponsePayload } from '../../../../../../common/http_api/log_analysis/results/log_entry_rate'; import { TimeRange } from '../../../../../../common/http_api/shared/time_range'; @@ -17,6 +17,7 @@ import { formatAnomalyScore, getTotalNumberOfLogEntriesForPartition, } from '../helpers/data_formatters'; +import { AnalyzeInMlButton } from '../analyze_in_ml_button'; export const AnomaliesTableExpandedRow: React.FunctionComponent<{ partitionId: string; @@ -24,7 +25,8 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; -}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId }) => { + jobId: string; +}> = ({ results, timeRange, setTimeRange, topAnomalyScore, partitionId, jobId }) => { const logEntryRateSeries = useMemo( () => results && results.histogramBuckets @@ -83,6 +85,12 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ )} reverse /> + + + + + + ); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx index 1bba8995f0c76a..5aa5891d7981d6 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx @@ -5,7 +5,6 @@ */ import { - EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, @@ -16,7 +15,6 @@ import { } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; import euiStyled from '../../../../../../../../common/eui_styled_components'; @@ -32,6 +30,7 @@ import { import { AnomaliesChart } from './chart'; import { AnomaliesTable } from './table'; import { LogAnalysisJobProblemIndicator } from '../../../../../components/logging/log_analysis_job_status'; +import { AnalyzeInMlButton } from '../analyze_in_ml_button'; export const AnomaliesResults: React.FunctionComponent<{ isLoading: boolean; @@ -42,6 +41,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange: TimeRange; viewSetupForReconfiguration: () => void; viewSetupForUpdate: () => void; + jobId: string; }> = ({ isLoading, jobStatus, @@ -51,6 +51,7 @@ export const AnomaliesResults: React.FunctionComponent<{ timeRange, viewSetupForReconfiguration, viewSetupForUpdate, + jobId, }) => { const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { defaultMessage: 'Anomalies', @@ -105,12 +106,7 @@ export const AnomaliesResults: React.FunctionComponent<{ - - - + @@ -193,7 +189,12 @@ export const AnomaliesResults: React.FunctionComponent<{ - + )} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx index 2a8ac44d09083b..88ffcae6e9dea1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/table.tsx @@ -52,7 +52,8 @@ export const AnomaliesTable: React.FunctionComponent<{ results: GetLogEntryRateSuccessResponsePayload['data']; setTimeRange: (timeRange: TimeRange) => void; timeRange: TimeRange; -}> = ({ results, timeRange, setTimeRange }) => { + jobId: string; +}> = ({ results, timeRange, setTimeRange, jobId }) => { const tableItems: TableItem[] = useMemo(() => { return Object.entries(getTopAnomalyScoresByPartition(results)).map(([key, value]) => { return { @@ -112,6 +113,7 @@ export const AnomaliesTable: React.FunctionComponent<{ topAnomalyScore={item.topAnomalyScore} setTimeRange={setTimeRange} timeRange={timeRange} + jobId={jobId} /> ), }; diff --git a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts b/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts index bcd36f72bef076..4afcb4fd884303 100644 --- a/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts +++ b/x-pack/legacy/plugins/infra/public/utils/use_kibana_injected_var.ts @@ -14,6 +14,5 @@ import { npSetup } from 'ui/new_platform'; */ export const useKibanaInjectedVar = (name: string, defaultValue?: unknown) => { const injectedMetadata = npSetup.core.injectedMetadata; - return injectedMetadata.getInjectedVar(name, defaultValue); };