mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Logs] Update logs stream links to logs explorer (#190835)
## Summary Implements https://github.com/elastic/kibana/issues/190782, as part of the deprecate Logs UI project: https://github.com/elastic/observability-dev/issues/3242. ## Overview - Previously the top level logs locators would redirect to the locators registered in Infra first (for the stream), and logs explorer second if the stream wasn't available. This fork no longer exists, with the top level locators navigating to the logs explorer only. - Infra no longer registers locators for the logs stream. - Consumer / solution uses have been updated. ## Semi-breaking change - Previously we had `link-to` routes that would translate into an internal link. These no longer accept a log view (as this is a stream concept, and is also soon to be deprecated itself). This means bookmarked link-to routes may now ultimately point to slightly different data (depending on the users log sources advanced setting). I don't really see a full proof way around this if we are to truly deprecate all the pieces we need. Also, with the `link-to` routes always being a translation layer this might be absolutely fine — we are just translating things differently now. ## Reviewer notes - The ES Deprecation logs page can be found at: `/app/management/stack/upgrade_assistant/es_deprecation_logs`. ## Change points (Not fully exhaustive)      --------- Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
c772d898d5
commit
773f3b3fd2
41 changed files with 226 additions and 728 deletions
|
@ -29,7 +29,7 @@ jest.mock('@kbn/logs-shared-plugin/public', () => {
|
|||
jest.mock('@kbn/logs-shared-plugin/common', () => {
|
||||
return {
|
||||
getLogsLocatorsFromUrlService: jest.fn().mockReturnValue({
|
||||
logsLocator: { getRedirectUrl: jest.fn(() => 'https://discover-redirect-url') },
|
||||
logsLocator: { getRedirectUrl: jest.fn(() => 'https://logs-explorer-redirect-url') },
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
@ -130,7 +130,7 @@ describe('AgentLogsUI', () => {
|
|||
const result = renderComponent();
|
||||
expect(result.getByTestId('viewInLogsBtn')).toHaveAttribute(
|
||||
'href',
|
||||
`https://discover-redirect-url`
|
||||
`https://logs-explorer-redirect-url`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,14 +6,16 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
import { useDiscoverLocator, useStartServices, useAuthz } from '../../../../../hooks';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useStartServices, useAuthz } from '../../../../../hooks';
|
||||
|
||||
interface ViewLogsProps {
|
||||
logStreamQuery: string;
|
||||
|
@ -32,8 +34,6 @@ export const ViewLogsButton: React.FunctionComponent<ViewLogsProps> = ({
|
|||
startTime,
|
||||
endTime,
|
||||
}) => {
|
||||
const discoverLocator = useDiscoverLocator();
|
||||
|
||||
const { share } = useStartServices();
|
||||
const { logsLocator } = getLogsLocatorsFromUrlService(share.url);
|
||||
const authz = useAuthz();
|
||||
|
@ -54,11 +54,11 @@ export const ViewLogsButton: React.FunctionComponent<ViewLogsProps> = ({
|
|||
});
|
||||
}, [endTime, logStreamQuery, logsLocator, startTime]);
|
||||
|
||||
return authz.fleet.readAgents && (logsLocator || discoverLocator) ? (
|
||||
return authz.fleet.readAgents && logsLocator ? (
|
||||
<EuiButton href={logsUrl} iconType="popout" data-test-subj="viewInLogsBtn">
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.agentLogs.openInLogsUiLinkText"
|
||||
defaultMessage="Open in Logs"
|
||||
defaultMessage="Open in Logs Explorer"
|
||||
/>
|
||||
</EuiButton>
|
||||
) : null;
|
||||
|
|
|
@ -8,26 +8,25 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
|
||||
import { CustomAssetsAccordion } from './components/custom_assets_accordion';
|
||||
import type { CustomAssetsAccordionProps } from './components/custom_assets_accordion';
|
||||
import { useStartServices } from './hooks';
|
||||
import type { PackageAssetsComponent } from './types';
|
||||
|
||||
export const CustomLogsAssetsExtension: PackageAssetsComponent = () => {
|
||||
const { http, cloud } = useStartServices();
|
||||
const isLogsUIAvailable = !cloud?.isServerlessEnabled;
|
||||
// if logs ui is not available, link to discover
|
||||
// TODO: move away from hardcoded link and use locators instead
|
||||
const logStreamUrl = isLogsUIAvailable
|
||||
? http.basePath.prepend('/app/logs/stream')
|
||||
: http.basePath.prepend('/app/discover');
|
||||
const {
|
||||
share: { url },
|
||||
} = useStartServices();
|
||||
const { logsLocator } = getLogsLocatorsFromUrlService(url);
|
||||
|
||||
const views: CustomAssetsAccordionProps['views'] = [
|
||||
{
|
||||
name: i18n.translate('xpack.fleet.assets.customLogs.name', { defaultMessage: 'Logs' }),
|
||||
url: logStreamUrl,
|
||||
url: logsLocator.getRedirectUrl({}),
|
||||
description: i18n.translate('xpack.fleet.assets.customLogs.description', {
|
||||
defaultMessage: 'View Custom logs data in Logs app',
|
||||
defaultMessage: 'View Custom logs data in Logs Explorer',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,113 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { DiscoverAppLocatorParams } from '@kbn/discover-plugin/common';
|
||||
import type { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import {
|
||||
DEFAULT_LOG_VIEW,
|
||||
LogViewColumnConfiguration,
|
||||
LogViewReference,
|
||||
ResolvedLogView,
|
||||
LogsLocatorParams,
|
||||
} from '@kbn/logs-shared-plugin/common';
|
||||
import { flowRight } from 'lodash';
|
||||
import type { InfraClientCoreSetup } from '../../public/types';
|
||||
import { MESSAGE_FIELD, TIMESTAMP_FIELD } from '../constants';
|
||||
import type { TimeRange } from '../time';
|
||||
import {
|
||||
replaceLogFilterInQueryString,
|
||||
replaceLogPositionInQueryString,
|
||||
replaceLogViewInQueryString,
|
||||
} from '../url_state_storage_service';
|
||||
|
||||
interface LocationToDiscoverParams {
|
||||
core: InfraClientCoreSetup;
|
||||
timeRange?: TimeRange;
|
||||
filter?: string;
|
||||
logView?: LogViewReference;
|
||||
}
|
||||
|
||||
export const createSearchString = ({
|
||||
time,
|
||||
timeRange,
|
||||
filter = '',
|
||||
logView = DEFAULT_LOG_VIEW,
|
||||
}: LogsLocatorParams) => {
|
||||
return flowRight(
|
||||
replaceLogFilterInQueryString({ language: 'kuery', query: filter }, time, timeRange),
|
||||
replaceLogPositionInQueryString(time),
|
||||
replaceLogViewInQueryString(logView)
|
||||
)('');
|
||||
};
|
||||
|
||||
export const getLocationToDiscover = async ({
|
||||
core,
|
||||
timeRange,
|
||||
filter,
|
||||
logView = DEFAULT_LOG_VIEW,
|
||||
}: LocationToDiscoverParams) => {
|
||||
const [, plugins] = await core.getStartServices();
|
||||
const { discover, logsShared } = plugins;
|
||||
const { logViews } = logsShared;
|
||||
const resolvedLogView = await logViews.client.getResolvedLogView(logView);
|
||||
|
||||
const discoverParams: DiscoverAppLocatorParams = {
|
||||
...(timeRange ? { from: timeRange.startTime, to: timeRange.endTime } : {}),
|
||||
...(filter
|
||||
? {
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: filter,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
const discoverLocation = await constructDiscoverLocation(
|
||||
discover,
|
||||
discoverParams,
|
||||
resolvedLogView
|
||||
);
|
||||
|
||||
if (!discoverLocation) {
|
||||
throw new Error('Discover location not found');
|
||||
}
|
||||
|
||||
return discoverLocation;
|
||||
};
|
||||
|
||||
const constructDiscoverLocation = async (
|
||||
discover: DiscoverStart,
|
||||
discoverParams: DiscoverAppLocatorParams,
|
||||
resolvedLogView?: ResolvedLogView
|
||||
) => {
|
||||
if (!resolvedLogView) {
|
||||
return await discover.locator?.getLocation(discoverParams);
|
||||
}
|
||||
|
||||
const columns = parseColumns(resolvedLogView.columns);
|
||||
const dataViewSpec = resolvedLogView.dataViewReference.toSpec();
|
||||
|
||||
return await discover.locator?.getLocation({
|
||||
...discoverParams,
|
||||
columns,
|
||||
dataViewId: dataViewSpec.id,
|
||||
dataViewSpec,
|
||||
});
|
||||
};
|
||||
|
||||
const parseColumns = (columns: ResolvedLogView['columns']) => {
|
||||
return columns.map(getColumnValue).filter(Boolean) as string[];
|
||||
};
|
||||
|
||||
const getColumnValue = (column: LogViewColumnConfiguration) => {
|
||||
if ('messageColumn' in column) return MESSAGE_FIELD;
|
||||
if ('timestampColumn' in column) return TIMESTAMP_FIELD;
|
||||
if ('fieldColumn' in column) return column.fieldColumn.field;
|
||||
|
||||
return null;
|
||||
};
|
|
@ -1,17 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { InfraLogsLocator } from './logs_locator';
|
||||
import type { InfraNodeLogsLocator } from './node_logs_locator';
|
||||
|
||||
export * from './logs_locator';
|
||||
export * from './node_logs_locator';
|
||||
|
||||
export interface InfraLocators {
|
||||
logsLocator?: InfraLogsLocator;
|
||||
nodeLogsLocator?: InfraNodeLogsLocator;
|
||||
}
|
|
@ -1,14 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { sharePluginMock } from '@kbn/share-plugin/public/mocks';
|
||||
import type { InfraLocators } from '.';
|
||||
|
||||
export const createLocatorMock = (): jest.Mocked<InfraLocators> => ({
|
||||
logsLocator: sharePluginMock.createLocator(),
|
||||
nodeLogsLocator: sharePluginMock.createLocator(),
|
||||
});
|
|
@ -1,273 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { InfraLogsLocatorDefinition, InfraLogsLocatorDependencies } from './logs_locator';
|
||||
import { InfraNodeLogsLocatorDefinition } from './node_logs_locator';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
DEFAULT_LOG_VIEW,
|
||||
LogViewReference,
|
||||
LogsLocatorParams,
|
||||
NodeLogsLocatorParams,
|
||||
} from '@kbn/logs-shared-plugin/common';
|
||||
|
||||
const setupLogsLocator = async () => {
|
||||
const deps: InfraLogsLocatorDependencies = {
|
||||
core: coreMock.createSetup(),
|
||||
};
|
||||
const logsLocator = new InfraLogsLocatorDefinition(deps);
|
||||
const nodeLogsLocator = new InfraNodeLogsLocatorDefinition(deps);
|
||||
|
||||
return {
|
||||
logsLocator,
|
||||
nodeLogsLocator,
|
||||
};
|
||||
};
|
||||
|
||||
describe('Infra Locators', () => {
|
||||
const APP_ID = 'logs';
|
||||
const FILTER_QUERY = 'trace.id:1234';
|
||||
const nodeType = 'host';
|
||||
const nodeField = findInventoryFields(nodeType).id;
|
||||
const nodeId = uuidv4();
|
||||
const time = 1550671089404;
|
||||
const from = 1676815089000;
|
||||
const to = 1682351734323;
|
||||
|
||||
describe('Logs Locator', () => {
|
||||
it('should create a link to Logs with no state', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
time,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { app, path, state } = await logsLocator.getLocation(params);
|
||||
|
||||
expect(app).toBe(APP_ID);
|
||||
expect(path).toBe(constructUrlSearchString(params));
|
||||
expect(state).toBeDefined();
|
||||
expect(Object.keys(state)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should allow specifying specific logPosition', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
time,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { path } = await logsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific filter', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
time,
|
||||
filter: FILTER_QUERY,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { path } = await logsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific view id', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
time,
|
||||
logView: DEFAULT_LOG_VIEW,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { path } = await logsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific time range', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
time,
|
||||
from,
|
||||
to,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { path } = await logsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return correct structured url', async () => {
|
||||
const params: LogsLocatorParams = {
|
||||
logView: DEFAULT_LOG_VIEW,
|
||||
filter: FILTER_QUERY,
|
||||
time,
|
||||
};
|
||||
const { logsLocator } = await setupLogsLocator();
|
||||
const { app, path, state } = await logsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
|
||||
expect(app).toBe(APP_ID);
|
||||
expect(path).toBe(expected);
|
||||
expect(state).toBeDefined();
|
||||
expect(Object.keys(state)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Node Logs Locator', () => {
|
||||
it('should create a link to Node Logs with no state', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { app, path, state } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
expect(app).toBe(APP_ID);
|
||||
expect(path).toBe(constructUrlSearchString(params));
|
||||
expect(state).toBeDefined();
|
||||
expect(Object.keys(state)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should allow specifying specific logPosition', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { path } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific filter', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
filter: FILTER_QUERY,
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { path } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific view id', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
logView: { ...DEFAULT_LOG_VIEW, logViewId: 'test' },
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { path } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specifying specific time range', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
from,
|
||||
to,
|
||||
logView: DEFAULT_LOG_VIEW,
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { path } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(path).toBe(expected);
|
||||
});
|
||||
|
||||
it('should return correct structured url', async () => {
|
||||
const params: NodeLogsLocatorParams = {
|
||||
nodeId,
|
||||
nodeField,
|
||||
time,
|
||||
logView: DEFAULT_LOG_VIEW,
|
||||
filter: FILTER_QUERY,
|
||||
};
|
||||
const { nodeLogsLocator } = await setupLogsLocator();
|
||||
const { app, path, state } = await nodeLogsLocator.getLocation(params);
|
||||
|
||||
const expected = constructUrlSearchString(params);
|
||||
expect(app).toBe(APP_ID);
|
||||
expect(path).toBe(expected);
|
||||
expect(state).toBeDefined();
|
||||
expect(Object.keys(state)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
export const constructUrlSearchString = (params: Partial<NodeLogsLocatorParams>) => {
|
||||
const { time = 1550671089404, logView } = params;
|
||||
|
||||
return `/stream?logView=${constructLogView(logView)}&logPosition=${constructLogPosition(
|
||||
time
|
||||
)}&logFilter=${constructLogFilter(params)}`;
|
||||
};
|
||||
|
||||
const constructLogView = (logView?: LogViewReference) => {
|
||||
const logViewId =
|
||||
logView && 'logViewId' in logView ? logView.logViewId : DEFAULT_LOG_VIEW.logViewId;
|
||||
|
||||
return `(logViewId:${logViewId},type:log-view-reference)`;
|
||||
};
|
||||
|
||||
const constructLogPosition = (time: number = 1550671089404) => {
|
||||
return `(position:(tiebreaker:0,time:'${moment(time).toISOString()}'))`;
|
||||
};
|
||||
|
||||
const constructLogFilter = ({
|
||||
nodeField,
|
||||
nodeId,
|
||||
filter,
|
||||
timeRange,
|
||||
time,
|
||||
}: Partial<NodeLogsLocatorParams>) => {
|
||||
let finalFilter = filter || '';
|
||||
|
||||
if (nodeId) {
|
||||
const nodeFilter = `${nodeField}: ${nodeId}`;
|
||||
finalFilter = filter ? `(${nodeFilter}) and (${filter})` : nodeFilter;
|
||||
}
|
||||
|
||||
const query = encodeURI(
|
||||
`(query:(language:kuery,query:'${finalFilter}'),refreshInterval:(pause:!t,value:5000)`
|
||||
);
|
||||
|
||||
if (!time) return `${query})`;
|
||||
|
||||
const fromDate = timeRange?.startTime
|
||||
? addHoursToTimestamp(timeRange.startTime, 0)
|
||||
: addHoursToTimestamp(time, -1);
|
||||
|
||||
const toDate = timeRange?.endTime
|
||||
? addHoursToTimestamp(timeRange.endTime, 0)
|
||||
: addHoursToTimestamp(time, 1);
|
||||
|
||||
return `${query},timeRange:(from:'${fromDate}',to:'${toDate}'))`;
|
||||
};
|
||||
|
||||
const addHoursToTimestamp = (timestamp: number, hours: number): string => {
|
||||
return moment(timestamp).add({ hours }).toISOString();
|
||||
};
|
|
@ -1,34 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public';
|
||||
import { INFRA_LOGS_LOCATOR_ID, LogsLocatorParams } from '@kbn/logs-shared-plugin/common';
|
||||
import type { InfraClientCoreSetup } from '../../public/types';
|
||||
|
||||
export type InfraLogsLocator = LocatorPublic<LogsLocatorParams>;
|
||||
|
||||
export interface InfraLogsLocatorDependencies {
|
||||
core: InfraClientCoreSetup;
|
||||
}
|
||||
|
||||
export class InfraLogsLocatorDefinition implements LocatorDefinition<LogsLocatorParams> {
|
||||
public readonly id = INFRA_LOGS_LOCATOR_ID;
|
||||
|
||||
constructor(protected readonly deps: InfraLogsLocatorDependencies) {}
|
||||
|
||||
public readonly getLocation = async (params: LogsLocatorParams) => {
|
||||
const { createSearchString } = await import('./helpers');
|
||||
|
||||
const searchString = createSearchString(params);
|
||||
|
||||
return {
|
||||
app: 'logs',
|
||||
path: `/stream?${searchString}`,
|
||||
state: {},
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,38 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public';
|
||||
import {
|
||||
INFRA_NODE_LOGS_LOCATOR_ID,
|
||||
NodeLogsLocatorParams,
|
||||
createNodeLogsQuery,
|
||||
} from '@kbn/logs-shared-plugin/common';
|
||||
import type { InfraLogsLocatorDependencies } from './logs_locator';
|
||||
|
||||
export type InfraNodeLogsLocator = LocatorPublic<NodeLogsLocatorParams>;
|
||||
|
||||
export type InfraNodeLogsLocatorDependencies = InfraLogsLocatorDependencies;
|
||||
|
||||
export class InfraNodeLogsLocatorDefinition implements LocatorDefinition<NodeLogsLocatorParams> {
|
||||
public readonly id = INFRA_NODE_LOGS_LOCATOR_ID;
|
||||
|
||||
constructor(protected readonly deps: InfraNodeLogsLocatorDependencies) {}
|
||||
|
||||
public readonly getLocation = async (params: NodeLogsLocatorParams) => {
|
||||
const { createSearchString } = await import('./helpers');
|
||||
|
||||
const query = createNodeLogsQuery(params);
|
||||
|
||||
const searchString = createSearchString({ ...params, filter: query });
|
||||
|
||||
return {
|
||||
app: 'logs',
|
||||
path: `/stream?${searchString}`,
|
||||
state: {},
|
||||
};
|
||||
};
|
||||
}
|
|
@ -87,13 +87,7 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => {
|
|||
dataViews: {
|
||||
create: () => Promise.resolve(mockDataView),
|
||||
},
|
||||
locators: {
|
||||
nodeLogsLocator: {
|
||||
getRedirectUrl: () => {
|
||||
return '';
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
uiActions: {
|
||||
getTriggerCompatibleActions: () => {
|
||||
return Promise.resolve([]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
|
||||
import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { LogStream } from '@kbn/logs-shared-plugin/public';
|
||||
import {
|
||||
DEFAULT_LOG_VIEW,
|
||||
|
@ -17,6 +17,7 @@ import {
|
|||
LogViewReference,
|
||||
} from '@kbn/logs-shared-plugin/common';
|
||||
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
|
||||
import { OpenInLogsExplorerButton } from '@kbn/logs-shared-plugin/public';
|
||||
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
|
||||
import { InfraLoadingPanel } from '../../../loading';
|
||||
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
|
||||
|
@ -106,18 +107,12 @@ export const Logs = () => {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="infraAssetDetailsLogsTabOpenInLogsButton"
|
||||
<OpenInLogsExplorerButton
|
||||
href={logsUrl}
|
||||
testSubject={'infraAssetDetailsLogsTabOpenInLogsButton'}
|
||||
size="xs"
|
||||
flush="both"
|
||||
iconType="popout"
|
||||
href={logsUrl}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.nodeDetails.logs.openLogsLink"
|
||||
defaultMessage="Open in Logs"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createLocatorMock } from '../common/locators/locators.mock';
|
||||
import { createInventoryViewsServiceStartMock } from './services/inventory_views/inventory_views_service.mock';
|
||||
import { createMetricsExplorerViewsServiceStartMock } from './services/metrics_explorer_views/metrics_explorer_views_service.mock';
|
||||
import { createTelemetryServiceMock } from './services/telemetry/telemetry_service.mock';
|
||||
|
@ -15,7 +14,6 @@ export const createInfraPluginStartMock = () => ({
|
|||
inventoryViews: createInventoryViewsServiceStartMock(),
|
||||
metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(),
|
||||
telemetry: createTelemetryServiceMock(),
|
||||
locators: createLocatorMock(),
|
||||
});
|
||||
|
||||
export const _ensureTypeCompatibility = (): InfraClientStartExports => createInfraPluginStartMock();
|
||||
|
|
|
@ -6,13 +6,12 @@
|
|||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { DEFAULT_LOG_VIEW, getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { getFilterFromLocation, getTimeFromLocation } from './query_params';
|
||||
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
|
||||
|
||||
export const RedirectToLogs = () => {
|
||||
const { logViewId } = useParams<{ logViewId?: string }>();
|
||||
const location = useLocation();
|
||||
|
||||
const {
|
||||
|
@ -28,11 +27,10 @@ export const RedirectToLogs = () => {
|
|||
{
|
||||
time,
|
||||
filter,
|
||||
logView: { ...DEFAULT_LOG_VIEW, logViewId: logViewId || DEFAULT_LOG_VIEW.logViewId },
|
||||
},
|
||||
{ replace: true }
|
||||
);
|
||||
}, [filter, logsLocator, logViewId, time]);
|
||||
}, [filter, logsLocator, time]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { useEffect } from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { DEFAULT_LOG_VIEW, getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { findInventoryFields, InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
||||
|
||||
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
|
||||
|
@ -21,7 +21,7 @@ type RedirectToNodeLogsType = RouteComponentProps<{
|
|||
|
||||
export const RedirectToNodeLogs = ({
|
||||
match: {
|
||||
params: { nodeId, nodeType, logViewId = DEFAULT_LOG_VIEW.logViewId },
|
||||
params: { nodeId, nodeType },
|
||||
},
|
||||
location,
|
||||
}: RedirectToNodeLogsType) => {
|
||||
|
@ -40,11 +40,10 @@ export const RedirectToNodeLogs = ({
|
|||
nodeId,
|
||||
time,
|
||||
filter,
|
||||
logView: { type: 'log-view-reference', logViewId },
|
||||
},
|
||||
{ replace: true }
|
||||
);
|
||||
}, [filter, nodeLogsLocator, logViewId, nodeId, nodeType, time]);
|
||||
}, [filter, nodeLogsLocator, nodeId, nodeType, time]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { getLogsLocatorsFromUrlService, LogViewReference } from '@kbn/logs-shared-plugin/common';
|
||||
import { OpenInLogsExplorerButton } from '@kbn/logs-shared-plugin/public';
|
||||
import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana';
|
||||
|
||||
interface LogsLinkToStreamProps {
|
||||
|
@ -23,7 +22,7 @@ export const LogsLinkToStream = ({ startTime, endTime, query, logView }: LogsLin
|
|||
const { logsLocator } = getLogsLocatorsFromUrlService(share.url);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
<OpenInLogsExplorerButton
|
||||
href={logsLocator.getRedirectUrl({
|
||||
time: endTime,
|
||||
timeRange: {
|
||||
|
@ -33,14 +32,8 @@ export const LogsLinkToStream = ({ startTime, endTime, query, logView }: LogsLin
|
|||
filter: query,
|
||||
logView,
|
||||
})}
|
||||
data-test-subj="hostsView-logs-link-to-stream-button"
|
||||
iconType="popout"
|
||||
testSubject="hostsView-logs-link-to-stream-button"
|
||||
flush="both"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.hostsViewPage.tabs.logs.openInLogsUiLinkText"
|
||||
defaultMessage="Open in Logs"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -38,11 +38,6 @@ import { createInventoryMetricRuleType } from './alerting/inventory';
|
|||
import { createLogThresholdRuleType } from './alerting/log_threshold';
|
||||
import { createMetricThresholdRuleType } from './alerting/metric_threshold';
|
||||
import { ADD_LOG_STREAM_ACTION_ID, LOG_STREAM_EMBEDDABLE } from './components/log_stream/constants';
|
||||
import {
|
||||
type InfraLocators,
|
||||
InfraLogsLocatorDefinition,
|
||||
InfraNodeLogsLocatorDefinition,
|
||||
} from '../common/locators';
|
||||
import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers';
|
||||
import { registerFeatures } from './register_feature';
|
||||
import { InventoryViewsService } from './services/inventory_views';
|
||||
|
@ -64,7 +59,6 @@ export class Plugin implements InfraClientPluginClass {
|
|||
private inventoryViews: InventoryViewsService;
|
||||
private metricsExplorerViews?: MetricsExplorerViewsService;
|
||||
private telemetry: TelemetryService;
|
||||
private locators?: InfraLocators;
|
||||
private kibanaVersion: string;
|
||||
private isServerlessEnv: boolean;
|
||||
private readonly appUpdater$ = new BehaviorSubject<AppUpdater>(() => ({}));
|
||||
|
@ -108,7 +102,7 @@ export class Plugin implements InfraClientPluginClass {
|
|||
);
|
||||
|
||||
if (this.config.featureFlags.logsUIEnabled) {
|
||||
// fetchData `appLink` redirects to logs/stream
|
||||
// fetchData `appLink` redirects to logs explorer
|
||||
pluginsSetup.observability.dashboard.register({
|
||||
appName: 'infra_logs',
|
||||
hasData: getLogsHasDataFetcher(core.getStartServices),
|
||||
|
@ -207,14 +201,6 @@ export class Plugin implements InfraClientPluginClass {
|
|||
});
|
||||
});
|
||||
|
||||
// Register Locators
|
||||
const logsLocator = this.config.featureFlags.logsUIEnabled
|
||||
? pluginsSetup.share.url.locators.create(new InfraLogsLocatorDefinition({ core }))
|
||||
: undefined;
|
||||
const nodeLogsLocator = this.config.featureFlags.logsUIEnabled
|
||||
? pluginsSetup.share.url.locators.create(new InfraNodeLogsLocatorDefinition({ core }))
|
||||
: undefined;
|
||||
|
||||
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
|
||||
createLogThresholdRuleType(core, pluginsSetup.share.url)
|
||||
);
|
||||
|
@ -395,15 +381,7 @@ export class Plugin implements InfraClientPluginClass {
|
|||
|
||||
// Setup telemetry events
|
||||
this.telemetry.setup({ analytics: core.analytics });
|
||||
|
||||
this.locators = {
|
||||
logsLocator,
|
||||
nodeLogsLocator,
|
||||
};
|
||||
|
||||
return {
|
||||
locators: this.locators,
|
||||
};
|
||||
return {};
|
||||
}
|
||||
|
||||
start(core: InfraClientCoreStart, plugins: InfraClientStartDeps) {
|
||||
|
@ -455,7 +433,6 @@ export class Plugin implements InfraClientPluginClass {
|
|||
inventoryViews,
|
||||
metricsExplorerViews,
|
||||
telemetry,
|
||||
locators: this.locators!,
|
||||
};
|
||||
|
||||
return startContract;
|
||||
|
|
|
@ -54,18 +54,14 @@ import type { UnwrapPromise } from '../common/utility_types';
|
|||
import { InventoryViewsServiceStart } from './services/inventory_views';
|
||||
import { MetricsExplorerViewsServiceStart } from './services/metrics_explorer_views';
|
||||
import { ITelemetryClient } from './services/telemetry';
|
||||
import type { InfraLocators } from '../common/locators';
|
||||
|
||||
// Our own setup and start contract values
|
||||
export interface InfraClientSetupExports {
|
||||
locators: InfraLocators;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface InfraClientSetupExports {}
|
||||
|
||||
export interface InfraClientStartExports {
|
||||
inventoryViews: InventoryViewsServiceStart;
|
||||
metricsExplorerViews?: MetricsExplorerViewsServiceStart;
|
||||
telemetry: ITelemetryClient;
|
||||
locators: InfraLocators;
|
||||
}
|
||||
|
||||
export interface InfraClientSetupDeps {
|
||||
|
|
|
@ -6,13 +6,12 @@
|
|||
*/
|
||||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { encode } from '@kbn/rison';
|
||||
import {
|
||||
FetchData,
|
||||
FetchDataParams,
|
||||
LogsFetchDataResponse,
|
||||
} from '@kbn/observability-plugin/public';
|
||||
import { DEFAULT_LOG_VIEW } from '@kbn/logs-shared-plugin/common';
|
||||
import { DEFAULT_LOG_VIEW, getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { TIMESTAMP_FIELD } from '../../common/constants';
|
||||
import { InfraClientStartDeps, InfraClientStartServicesAccessor } from '../types';
|
||||
|
||||
|
@ -58,7 +57,7 @@ export function getLogsOverviewDataFetcher(
|
|||
getStartServices: InfraClientStartServicesAccessor
|
||||
): FetchData<LogsFetchDataResponse> {
|
||||
return async (params) => {
|
||||
const [, { data, logsShared }] = await getStartServices();
|
||||
const [, { data, logsShared, share }] = await getStartServices();
|
||||
const resolvedLogView = await logsShared.logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW);
|
||||
|
||||
const { stats, series } = await fetchLogsOverview(
|
||||
|
@ -68,13 +67,18 @@ export function getLogsOverviewDataFetcher(
|
|||
params,
|
||||
data
|
||||
);
|
||||
|
||||
const { logsLocator } = getLogsLocatorsFromUrlService(share.url);
|
||||
const timeSpanInMinutes = (params.absoluteTime.end - params.absoluteTime.start) / (1000 * 60);
|
||||
|
||||
const appLink = logsLocator.getRedirectUrl({
|
||||
timeRange: {
|
||||
startTime: params.absoluteTime.start,
|
||||
endTime: params.absoluteTime.end,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
appLink: `/app/logs/stream?logPosition=(end:${encode(params.relativeTime.end)},start:${encode(
|
||||
params.relativeTime.start
|
||||
)})`,
|
||||
appLink,
|
||||
stats: normalizeStats(stats, timeSpanInMinutes),
|
||||
series: normalizeSeries(series),
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ import { of } from 'rxjs';
|
|||
import { createInfraPluginStartMock } from '../mocks';
|
||||
import { InfraClientStartDeps, InfraClientStartExports } from '../types';
|
||||
import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './logs_overview_fetchers';
|
||||
import { sharePluginMock } from '@kbn/share-plugin/public/mocks';
|
||||
|
||||
const DEFAULT_PARAMS = {
|
||||
absoluteTime: { start: 1593430680000, end: 1593430800000 },
|
||||
|
@ -27,7 +28,14 @@ function setup() {
|
|||
const data = dataPluginMock.createStartContract();
|
||||
const logsShared = createLogsSharedPluginStartMock();
|
||||
const pluginStart = createInfraPluginStartMock();
|
||||
const pluginDeps = { data, logsShared } as unknown as InfraClientStartDeps;
|
||||
const share = {
|
||||
url: {
|
||||
locators: {
|
||||
get: jest.fn(() => sharePluginMock.createLocator()),
|
||||
},
|
||||
},
|
||||
};
|
||||
const pluginDeps = { data, logsShared, share } as unknown as InfraClientStartDeps;
|
||||
|
||||
const dataSearch = data.search.search as jest.MockedFunction<typeof data.search.search>;
|
||||
const getResolvedLogView = logsShared.logViews.client.getResolvedLogView as jest.MockedFunction<
|
||||
|
|
|
@ -66,8 +66,6 @@ export {
|
|||
LOGS_LOCATOR_ID,
|
||||
TRACE_LOGS_LOCATOR_ID,
|
||||
NODE_LOGS_LOCATOR_ID,
|
||||
INFRA_LOGS_LOCATOR_ID,
|
||||
INFRA_NODE_LOGS_LOCATOR_ID,
|
||||
getLogsLocatorsFromUrlService,
|
||||
} from './locators';
|
||||
export type { LogsLocatorParams, NodeLogsLocatorParams, TraceLogsLocatorParams } from './locators';
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
export * from './logs_locator';
|
||||
export * from './trace_logs_locator';
|
||||
export * from './node_logs_locator';
|
||||
export * from './infra';
|
||||
export * from './get_logs_locators';
|
||||
|
||||
export type {
|
||||
|
|
|
@ -1,10 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const INFRA_LOGS_LOCATOR_ID = 'INFRA_LOGS_LOCATOR';
|
||||
|
||||
export const INFRA_NODE_LOGS_LOCATOR_ID = 'INFRA_NODE_LOGS_LOCATOR';
|
|
@ -9,7 +9,6 @@ import { ALL_DATASETS_LOCATOR_ID, AllDatasetsLocatorParams } from '@kbn/deeplink
|
|||
import { LocatorDefinition } from '@kbn/share-plugin/common';
|
||||
import { LocatorClient } from '@kbn/share-plugin/common/url_service';
|
||||
|
||||
import { INFRA_LOGS_LOCATOR_ID } from './infra';
|
||||
import { LogsLocatorParams } from './types';
|
||||
import { getLogsQuery, getTimeRangeEndFromTime, getTimeRangeStartFromTime } from './helpers';
|
||||
|
||||
|
@ -21,11 +20,6 @@ export class LogsLocatorDefinition implements LocatorDefinition<LogsLocatorParam
|
|||
constructor(private readonly locators: LocatorClient) {}
|
||||
|
||||
public readonly getLocation = async (params: LogsLocatorParams) => {
|
||||
const infraLogsLocator = this.locators.get<LogsLocatorParams>(INFRA_LOGS_LOCATOR_ID);
|
||||
if (infraLogsLocator) {
|
||||
return infraLogsLocator.getLocation(params);
|
||||
}
|
||||
|
||||
const allDatasetsLocator =
|
||||
this.locators.get<AllDatasetsLocatorParams>(ALL_DATASETS_LOCATOR_ID)!;
|
||||
const { time } = params;
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
import { LocatorClient, LocatorDefinition } from '@kbn/share-plugin/common/url_service';
|
||||
|
||||
import { NodeLogsLocatorParams } from './types';
|
||||
import { INFRA_NODE_LOGS_LOCATOR_ID } from './infra';
|
||||
import { getNodeQuery, getTimeRangeStartFromTime, getTimeRangeEndFromTime } from './helpers';
|
||||
|
||||
export const NODE_LOGS_LOCATOR_ID = 'NODE_LOGS_LOCATOR';
|
||||
|
@ -23,14 +22,6 @@ export class NodeLogsLocatorDefinition implements LocatorDefinition<NodeLogsLoca
|
|||
constructor(private readonly locators: LocatorClient) {}
|
||||
|
||||
public readonly getLocation = async (params: NodeLogsLocatorParams) => {
|
||||
const infraNodeLogsLocator = this.locators.get<NodeLogsLocatorParams>(
|
||||
INFRA_NODE_LOGS_LOCATOR_ID
|
||||
);
|
||||
|
||||
if (infraNodeLogsLocator) {
|
||||
return infraNodeLogsLocator.getLocation(params);
|
||||
}
|
||||
|
||||
const allDatasetsLocator =
|
||||
this.locators.get<AllDatasetsLocatorParams>(ALL_DATASETS_LOCATOR_ID)!;
|
||||
const { time } = params;
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
import { ALL_DATASETS_LOCATOR_ID, AllDatasetsLocatorParams } from '@kbn/deeplinks-observability';
|
||||
import { LocatorDefinition } from '@kbn/share-plugin/common';
|
||||
import { LocatorClient } from '@kbn/share-plugin/common/url_service';
|
||||
import { INFRA_LOGS_LOCATOR_ID } from './infra';
|
||||
import { LogsLocatorParams, TraceLogsLocatorParams } from './types';
|
||||
import { TraceLogsLocatorParams } from './types';
|
||||
|
||||
import { getTraceQuery, getTimeRangeEndFromTime, getTimeRangeStartFromTime } from './helpers';
|
||||
|
||||
|
@ -21,14 +20,6 @@ export class TraceLogsLocatorDefinition implements LocatorDefinition<TraceLogsLo
|
|||
constructor(private readonly locators: LocatorClient) {}
|
||||
|
||||
public readonly getLocation = async (params: TraceLogsLocatorParams) => {
|
||||
const infraLogsLocator = this.locators.get<LogsLocatorParams>(INFRA_LOGS_LOCATOR_ID);
|
||||
if (infraLogsLocator) {
|
||||
return infraLogsLocator.getLocation({
|
||||
...params,
|
||||
filter: getTraceQuery(params).query,
|
||||
});
|
||||
}
|
||||
|
||||
const { time } = params;
|
||||
const allDatasetsLocator =
|
||||
this.locators.get<AllDatasetsLocatorParams>(ALL_DATASETS_LOCATOR_ID)!;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButtonEmpty, EuiButtonEmptyProps } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
|
||||
type OpenInLogsExplorerButtonProps = Pick<EuiButtonEmptyProps, 'href' | 'flush' | 'size'> & {
|
||||
testSubject: string;
|
||||
};
|
||||
|
||||
const OpenInLogsExplorerButton = ({ testSubject, ...rest }: OpenInLogsExplorerButtonProps) => {
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
iconType="popout"
|
||||
data-test-subj={testSubject ?? 'logsSharedOpenInLogsExplorerButton'}
|
||||
{...rest}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.logsShared.openInLogsExplorerButtonText"
|
||||
defaultMessage="Open in Logs Explorer"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default OpenInLogsExplorerButton;
|
|
@ -86,6 +86,9 @@ export const LogEntryTimestampColumn = dynamic(
|
|||
export const ScrollableLogTextStreamView = dynamic(
|
||||
() => import('./components/logging/log_text_stream/scrollable_log_text_stream_view')
|
||||
);
|
||||
export const OpenInLogsExplorerButton = dynamic(
|
||||
() => import('./components/open_in_logs_explorer_button')
|
||||
);
|
||||
|
||||
// State machine utils
|
||||
export {
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
"dashboard",
|
||||
"expressions",
|
||||
"logsExplorer",
|
||||
"logsShared",
|
||||
"licensing",
|
||||
"navigation"
|
||||
],
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { HttpSetup, DocLinksStart } from '@kbn/core/public';
|
||||
import { BrowserUrlService } from '@kbn/share-plugin/public';
|
||||
import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common';
|
||||
import { ObservabilityFetchDataPlugins } from '../../../../typings/fetch_overview_data';
|
||||
import { paths } from '../../../../../common/locators/paths';
|
||||
|
||||
|
@ -23,8 +25,10 @@ export interface ObservabilityStatusContent {
|
|||
|
||||
export const getContent = (
|
||||
http: HttpSetup,
|
||||
docLinks: DocLinksStart
|
||||
docLinks: DocLinksStart,
|
||||
urlService: BrowserUrlService
|
||||
): ObservabilityStatusContent[] => {
|
||||
const { logsLocator } = getLogsLocatorsFromUrlService(urlService);
|
||||
return [
|
||||
{
|
||||
id: 'infra_logs',
|
||||
|
@ -41,9 +45,9 @@ export const getContent = (
|
|||
addLink: http.basePath.prepend('/app/integrations/browse?q=logs'),
|
||||
learnMoreLink: docLinks.links.observability.monitorLogs,
|
||||
goToAppTitle: i18n.translate('xpack.observability.statusVisualization.logs.goToAppTitle', {
|
||||
defaultMessage: 'Show log stream',
|
||||
defaultMessage: 'Show Logs Explorer',
|
||||
}),
|
||||
goToAppLink: http.basePath.prepend('/app/logs/stream'),
|
||||
goToAppLink: logsLocator.getRedirectUrl({}),
|
||||
weight: 1,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,10 +12,14 @@ import { ObservabilityStatusBoxes } from './observability_status_boxes';
|
|||
import { getContent } from './content';
|
||||
|
||||
export function ObservabilityStatus() {
|
||||
const { http, docLinks } = useKibana().services;
|
||||
const {
|
||||
http,
|
||||
docLinks,
|
||||
share: { url },
|
||||
} = useKibana().services;
|
||||
const { hasDataMap } = useHasData();
|
||||
|
||||
const content = getContent(http, docLinks);
|
||||
const content = getContent(http, docLinks, url);
|
||||
|
||||
const boxes = content.map((app) => {
|
||||
return {
|
||||
|
|
|
@ -101,8 +101,9 @@ export function LogsSection({ bucketSize }: Props) {
|
|||
appLink={{
|
||||
href: appLink,
|
||||
label: i18n.translate('xpack.observability.overview.logs.appLink', {
|
||||
defaultMessage: 'Show log stream',
|
||||
defaultMessage: 'Show Logs Explorer',
|
||||
}),
|
||||
prependBasePath: false,
|
||||
}}
|
||||
hasError={status === FETCH_STATUS.FAILURE}
|
||||
>
|
||||
|
|
|
@ -23,6 +23,7 @@ import { ExperimentalBadge } from '../../../../components/experimental_badge';
|
|||
interface AppLink {
|
||||
label: string;
|
||||
href?: string;
|
||||
prependBasePath?: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
@ -44,6 +45,7 @@ export function SectionContainer({
|
|||
}: Props) {
|
||||
const { http } = useKibana().services;
|
||||
const euiAccordionId = useGeneratedHtmlId({ prefix: 'euiAccordion' });
|
||||
const prependBasePath = appLink?.prependBasePath !== undefined ? appLink?.prependBasePath : true;
|
||||
|
||||
return (
|
||||
<EuiPanel color="subdued">
|
||||
|
@ -76,7 +78,7 @@ export function SectionContainer({
|
|||
iconType={'sortRight'}
|
||||
size="xs"
|
||||
color="text"
|
||||
href={http.basePath.prepend(appLink.href)}
|
||||
href={prependBasePath ? http.basePath.prepend(appLink.href) : appLink.href}
|
||||
>
|
||||
{appLink.label}
|
||||
</EuiButtonEmpty>
|
||||
|
|
|
@ -114,7 +114,8 @@
|
|||
"@kbn/investigation-shared",
|
||||
"@kbn/grouping",
|
||||
"@kbn/alerts-grouping",
|
||||
"@kbn/core-http-browser"
|
||||
"@kbn/core-http-browser",
|
||||
"@kbn/logs-shared-plugin",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
|
@ -19410,7 +19410,6 @@
|
|||
"xpack.fleet.agentLogs.logDisabledCallOutTitle": "La collecte de logs est désactivée",
|
||||
"xpack.fleet.agentLogs.logLevelSelectText": "Niveau du log",
|
||||
"xpack.fleet.agentLogs.oldAgentWarningTitle": "La vue Logs requiert Elastic Agent 7.11 ou une version ultérieure. Pour mettre à niveau un agent, accédez au menu Actions ou {downloadLink} une version plus récente.",
|
||||
"xpack.fleet.agentLogs.openInLogsUiLinkText": "Ouvrir dans Logs",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.errorTitleText": "Erreur lors de la réinitialisation du niveau de logging de l'agent",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.successText": "Réinitialiser le niveau de logging de l'agent pour la politique",
|
||||
"xpack.fleet.agentLogs.resetLogLevelLabelText": "Réinitialiser pour rétablir la politique",
|
||||
|
@ -23236,7 +23235,6 @@
|
|||
"xpack.infra.hostsViewPage.tabs.logs.assetLogsWidgetName": "Logs de {type} \"{name}\"",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.loadingEntriesLabel": "Chargement des entrées",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.LogsByHostWidgetName": "Logs par hôte",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.openInLogsUiLinkText": "Ouvrir dans Logs",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "Rechercher les entrées de logs...",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.title": "Logs",
|
||||
"xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "Ouvrir dans Lens",
|
||||
|
@ -23912,7 +23910,6 @@
|
|||
"xpack.infra.nodeDetails.labels.operatinSystem": "Système d'exploitation",
|
||||
"xpack.infra.nodeDetails.labels.projectId": "ID projet",
|
||||
"xpack.infra.nodeDetails.labels.showMoreDetails": "Afficher plus de détails",
|
||||
"xpack.infra.nodeDetails.logs.openLogsLink": "Ouvrir dans Logs",
|
||||
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "Rechercher les entrées de logs...",
|
||||
"xpack.infra.nodeDetails.no": "Non",
|
||||
"xpack.infra.nodeDetails.tabs.anomalies": "Anomalies",
|
||||
|
|
|
@ -19400,7 +19400,6 @@
|
|||
"xpack.fleet.agentLogs.logDisabledCallOutTitle": "ログ収集は無効です",
|
||||
"xpack.fleet.agentLogs.logLevelSelectText": "ログレベル",
|
||||
"xpack.fleet.agentLogs.oldAgentWarningTitle": "ログの表示には、Elastic Agent 7.11以降が必要です。エージェントをアップグレードするには、[アクション]メニューに移動するか、新しいバージョンを{downloadLink}。",
|
||||
"xpack.fleet.agentLogs.openInLogsUiLinkText": "ログで開く",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.errorTitleText": "エージェントログレベルのリセットエラー",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.successText": "エージェントログレベルをポリシーにリセット",
|
||||
"xpack.fleet.agentLogs.resetLogLevelLabelText": "ポリシーにリセット",
|
||||
|
@ -23227,7 +23226,6 @@
|
|||
"xpack.infra.hostsViewPage.tabs.logs.assetLogsWidgetName": "{type} \"{name}\"からのログ",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.loadingEntriesLabel": "エントリーを読み込み中",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.LogsByHostWidgetName": "ホスト別ログ",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.openInLogsUiLinkText": "ログで開く",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "ログエントリーを検索...",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.title": "ログ",
|
||||
"xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "Lensで開く",
|
||||
|
@ -23901,7 +23899,6 @@
|
|||
"xpack.infra.nodeDetails.labels.operatinSystem": "オペレーティングシステム",
|
||||
"xpack.infra.nodeDetails.labels.projectId": "プロジェクト ID",
|
||||
"xpack.infra.nodeDetails.labels.showMoreDetails": "他の詳細を表示",
|
||||
"xpack.infra.nodeDetails.logs.openLogsLink": "ログで開く",
|
||||
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "ログエントリーを検索...",
|
||||
"xpack.infra.nodeDetails.no": "いいえ",
|
||||
"xpack.infra.nodeDetails.tabs.anomalies": "異常",
|
||||
|
|
|
@ -19426,7 +19426,6 @@
|
|||
"xpack.fleet.agentLogs.logDisabledCallOutTitle": "日志收集已禁用",
|
||||
"xpack.fleet.agentLogs.logLevelSelectText": "日志级别",
|
||||
"xpack.fleet.agentLogs.oldAgentWarningTitle": "“日志”视图需要 Elastic Agent 7.11 或更高版本。要升级代理,请前往“操作”菜单或{downloadLink}更新的版本。",
|
||||
"xpack.fleet.agentLogs.openInLogsUiLinkText": "在日志中打开",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.errorTitleText": "重置代理日志记录级别时出错",
|
||||
"xpack.fleet.agentLogs.resetLogLevel.successText": "将代理日志记录级别重置为策略",
|
||||
"xpack.fleet.agentLogs.resetLogLevelLabelText": "重置为策略",
|
||||
|
@ -23255,7 +23254,6 @@
|
|||
"xpack.infra.hostsViewPage.tabs.logs.assetLogsWidgetName": "来自 {type}“{name}”的日志",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.loadingEntriesLabel": "正在加载条目",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.LogsByHostWidgetName": "日志(按主机)",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.openInLogsUiLinkText": "在日志中打开",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.textFieldPlaceholder": "搜索日志条目......",
|
||||
"xpack.infra.hostsViewPage.tabs.logs.title": "日志",
|
||||
"xpack.infra.hostsViewPage.tabs.metricsCharts.actions.openInLines": "在 Lens 中打开",
|
||||
|
@ -23931,7 +23929,6 @@
|
|||
"xpack.infra.nodeDetails.labels.operatinSystem": "操作系统",
|
||||
"xpack.infra.nodeDetails.labels.projectId": "项目 ID",
|
||||
"xpack.infra.nodeDetails.labels.showMoreDetails": "显示更多详情",
|
||||
"xpack.infra.nodeDetails.logs.openLogsLink": "在日志中打开",
|
||||
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "搜索日志条目......",
|
||||
"xpack.infra.nodeDetails.no": "否",
|
||||
"xpack.infra.nodeDetails.tabs.anomalies": "异常",
|
||||
|
|
|
@ -14,11 +14,11 @@ import { setupEnvironment, advanceTime } from '../helpers';
|
|||
import { DeprecationLoggingStatus } from '../../../common/types';
|
||||
import {
|
||||
DEPRECATION_LOGS_INDEX,
|
||||
DEPRECATION_LOGS_SOURCE_ID,
|
||||
DEPRECATION_LOGS_COUNT_POLL_INTERVAL_MS,
|
||||
APPS_WITH_DEPRECATION_LOGS,
|
||||
DEPRECATION_LOGS_ORIGIN_FIELD,
|
||||
} from '../../../common/constants';
|
||||
import { stringifySearchParams } from '../helpers/app_context.mock';
|
||||
|
||||
// Once the logs team register the kibana locators in their app, we should be able
|
||||
// to remove this mock and follow a similar approach to how discover link is tested.
|
||||
|
@ -176,23 +176,22 @@ describe('ES deprecation logs', () => {
|
|||
component.update();
|
||||
|
||||
expect(exists('viewObserveLogs')).toBe(true);
|
||||
const sourceId = DEPRECATION_LOGS_SOURCE_ID;
|
||||
const logPosition = `(end:now,start:'${MOCKED_TIME}')`;
|
||||
const logFilter = encodeURI(
|
||||
`(language:kuery,query:'not ${DEPRECATION_LOGS_ORIGIN_FIELD} : (${APPS_WITH_DEPRECATION_LOGS.join(
|
||||
' or '
|
||||
)})')`
|
||||
);
|
||||
const queryParams = `sourceId=${sourceId}&logPosition=${logPosition}&logFilter=${logFilter}`;
|
||||
expect(find('viewObserveLogs').props().href).toBe(`/app/logs/stream?${queryParams}`);
|
||||
});
|
||||
|
||||
test(`Doesn't show observability app link if infra app is not available`, async () => {
|
||||
const { component, exists } = testBed;
|
||||
|
||||
component.update();
|
||||
|
||||
expect(exists('viewObserveLogs')).toBe(false);
|
||||
const locatorParams = stringifySearchParams({
|
||||
id: DEPRECATION_LOGS_INDEX,
|
||||
timeRange: {
|
||||
from: MOCKED_TIME,
|
||||
to: 'now',
|
||||
},
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: `not ${DEPRECATION_LOGS_ORIGIN_FIELD} : (${APPS_WITH_DEPRECATION_LOGS.join(
|
||||
' or '
|
||||
)})`,
|
||||
},
|
||||
});
|
||||
const href = find('viewObserveLogs').props().href;
|
||||
expect(href).toContain('logsExplorerUrl');
|
||||
expect(href).toContain(locatorParams);
|
||||
});
|
||||
|
||||
test('Has a link to see logs in discover app', async () => {
|
||||
|
|
|
@ -21,6 +21,7 @@ import { apiService } from '../../../public/application/lib/api';
|
|||
import { breadcrumbService } from '../../../public/application/lib/breadcrumbs';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
|
||||
import { OBS_LOGS_EXPLORER_DATA_VIEW_LOCATOR_ID } from '@kbn/deeplinks-observability';
|
||||
|
||||
const data = dataPluginMock.createStartContract();
|
||||
const dataViews = { ...data.dataViews };
|
||||
|
@ -51,10 +52,11 @@ const servicesMock = {
|
|||
const idToUrlMap = {
|
||||
SNAPSHOT_RESTORE_LOCATOR: 'snapshotAndRestoreUrl',
|
||||
DISCOVER_APP_LOCATOR: 'discoverUrl',
|
||||
[OBS_LOGS_EXPLORER_DATA_VIEW_LOCATOR_ID]: 'logsExplorerUrl',
|
||||
};
|
||||
type IdKey = keyof typeof idToUrlMap;
|
||||
|
||||
const stringifySearchParams = (params: Record<string, any>) => {
|
||||
export const stringifySearchParams = (params: Record<string, any>) => {
|
||||
const stringifiedParams = Object.keys(params).reduce((list, key) => {
|
||||
const value = typeof params[key] === 'object' ? JSON.stringify(params[key]) : params[key];
|
||||
|
||||
|
@ -70,6 +72,8 @@ shareMock.url.locators.get = (id: IdKey) => ({
|
|||
useUrl: (): string | undefined => idToUrlMap[id],
|
||||
getUrl: (params: Record<string, any>): string | undefined =>
|
||||
`${idToUrlMap[id]}?${stringifySearchParams(params)}`,
|
||||
getRedirectUrl: (params: Record<string, any>): string | undefined =>
|
||||
`${idToUrlMap[id]}?${stringifySearchParams(params)}`,
|
||||
});
|
||||
|
||||
export const getAppContextMock = (kibanaVersion: SemVer) => ({
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { encode } from '@kbn/rison';
|
||||
import React, { FunctionComponent, useState, useEffect } from 'react';
|
||||
import { buildPhrasesFilter, PhrasesFilter } from '@kbn/es-query';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, EuiText } from '@elastic/eui';
|
||||
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import {
|
||||
OBS_LOGS_EXPLORER_DATA_VIEW_LOCATOR_ID,
|
||||
ObsLogsExplorerDataViewLocatorParams,
|
||||
} from '@kbn/deeplinks-observability';
|
||||
import {
|
||||
APPS_WITH_DEPRECATION_LOGS,
|
||||
DEPRECATION_LOGS_ORIGIN_FIELD,
|
||||
|
@ -25,13 +28,11 @@ import {
|
|||
UIM_DISCOVER_CLICK,
|
||||
} from '../../../lib/ui_metric';
|
||||
|
||||
import {
|
||||
DEPRECATION_LOGS_INDEX_PATTERN,
|
||||
DEPRECATION_LOGS_SOURCE_ID,
|
||||
} from '../../../../../common/constants';
|
||||
import { DEPRECATION_LOGS_INDEX_PATTERN } from '../../../../../common/constants';
|
||||
|
||||
interface Props {
|
||||
checkpoint: string;
|
||||
deprecationDataView: DataView;
|
||||
}
|
||||
|
||||
export const getDeprecationDataView = async (dataService: DataPublicPluginStart) => {
|
||||
|
@ -63,7 +64,7 @@ export const getDeprecationDataView = async (dataService: DataPublicPluginStart)
|
|||
}
|
||||
};
|
||||
|
||||
const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
||||
const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint, deprecationDataView }) => {
|
||||
const {
|
||||
services: { data: dataService },
|
||||
plugins: { share },
|
||||
|
@ -78,19 +79,22 @@ const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const dataView = await getDeprecationDataView(dataService);
|
||||
const field = dataView.getFieldByName(DEPRECATION_LOGS_ORIGIN_FIELD);
|
||||
const field = deprecationDataView.getFieldByName(DEPRECATION_LOGS_ORIGIN_FIELD);
|
||||
|
||||
let filters: PhrasesFilter[] = [];
|
||||
|
||||
if (field !== undefined) {
|
||||
const filter = buildPhrasesFilter(field!, [...APPS_WITH_DEPRECATION_LOGS], dataView);
|
||||
const filter = buildPhrasesFilter(
|
||||
field!,
|
||||
[...APPS_WITH_DEPRECATION_LOGS],
|
||||
deprecationDataView
|
||||
);
|
||||
filter.meta.negate = true;
|
||||
filters = [filter];
|
||||
}
|
||||
|
||||
const url = await locator?.getUrl({
|
||||
indexPatternId: dataView.id,
|
||||
indexPatternId: deprecationDataView.id,
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: `@timestamp > "${checkpoint}"`,
|
||||
|
@ -102,7 +106,7 @@ const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
|||
};
|
||||
|
||||
getDiscoveryUrl();
|
||||
}, [dataService, checkpoint, share.url.locators]);
|
||||
}, [dataService, checkpoint, share.url.locators, deprecationDataView]);
|
||||
|
||||
if (discoveryUrl === undefined) {
|
||||
return null;
|
||||
|
@ -125,32 +129,35 @@ const DiscoverAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const ObservabilityAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
||||
const ObservabilityAppLink: FunctionComponent<Props> = ({ checkpoint, deprecationDataView }) => {
|
||||
const {
|
||||
services: {
|
||||
core: { http },
|
||||
plugins: {
|
||||
share: { url },
|
||||
},
|
||||
} = useAppContext();
|
||||
|
||||
// Ideally we don't want to hardcode the path to the Log Stream app and use the UrlService.locator instead.
|
||||
// Issue opened: https://github.com/elastic/kibana/issues/104855
|
||||
const streamAppPath = '/app/logs/stream';
|
||||
const logsLocator = url.locators.get<ObsLogsExplorerDataViewLocatorParams>(
|
||||
OBS_LOGS_EXPLORER_DATA_VIEW_LOCATOR_ID
|
||||
)!;
|
||||
|
||||
const sourceId = DEPRECATION_LOGS_SOURCE_ID;
|
||||
const logPosition = `(end:now,start:${encode(checkpoint)})`;
|
||||
const logFilter = encodeURI(
|
||||
`(language:kuery,query:'not ${DEPRECATION_LOGS_ORIGIN_FIELD} : (${APPS_WITH_DEPRECATION_LOGS.join(
|
||||
' or '
|
||||
)})')`
|
||||
);
|
||||
const queryParams = `sourceId=${sourceId}&logPosition=${logPosition}&logFilter=${logFilter}`;
|
||||
if (!deprecationDataView.id) return null;
|
||||
|
||||
const logStreamUrl = http?.basePath?.prepend(`${streamAppPath}?${queryParams}`);
|
||||
const logsUrl = logsLocator.getRedirectUrl({
|
||||
id: deprecationDataView.id,
|
||||
timeRange: {
|
||||
from: checkpoint,
|
||||
to: 'now',
|
||||
},
|
||||
query: {
|
||||
language: 'kuery',
|
||||
query: `not ${DEPRECATION_LOGS_ORIGIN_FIELD} : (${APPS_WITH_DEPRECATION_LOGS.join(' or ')})`,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line @elastic/eui/href-or-on-click
|
||||
<EuiLink
|
||||
href={logStreamUrl}
|
||||
href={logsUrl}
|
||||
onClick={() => {
|
||||
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_OBSERVABILITY_CLICK);
|
||||
}}
|
||||
|
@ -158,33 +165,51 @@ const ObservabilityAppLink: FunctionComponent<Props> = ({ checkpoint }) => {
|
|||
>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.overview.viewObservabilityResultsAction"
|
||||
defaultMessage="View deprecation logs in Observability"
|
||||
defaultMessage="View deprecation logs in Logs Explorer"
|
||||
/>
|
||||
</EuiLink>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExternalLinks: FunctionComponent<Props> = ({ checkpoint }) => {
|
||||
const { infra: hasInfraPlugin } = useAppContext().plugins;
|
||||
export const ExternalLinks: FunctionComponent<Omit<Props, 'deprecationDataView'>> = ({
|
||||
checkpoint,
|
||||
}) => {
|
||||
const {
|
||||
services: { data: dataService },
|
||||
plugins: { share },
|
||||
} = useAppContext();
|
||||
|
||||
const [deprecationDataView, setDeprecationDataView] = useState<DataView | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const getDataView = async () => {
|
||||
const dataView = await getDeprecationDataView(dataService);
|
||||
setDeprecationDataView(dataView);
|
||||
};
|
||||
getDataView();
|
||||
}, [dataService, checkpoint, share.url.locators]);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
{hasInfraPlugin && (
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.overview.observe.observabilityDescription"
|
||||
defaultMessage="Get insight into which deprecated APIs are being used and what applications you need to update."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<ObservabilityAppLink checkpoint={checkpoint} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.upgradeAssistant.overview.observe.observabilityDescription"
|
||||
defaultMessage="Get insight into which deprecated APIs are being used and what applications you need to update."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
{deprecationDataView ? (
|
||||
<ObservabilityAppLink
|
||||
checkpoint={checkpoint}
|
||||
deprecationDataView={deprecationDataView}
|
||||
/>
|
||||
) : null}
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiText size="s">
|
||||
|
@ -196,7 +221,9 @@ export const ExternalLinks: FunctionComponent<Props> = ({ checkpoint }) => {
|
|||
</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<DiscoverAppLink checkpoint={checkpoint} />
|
||||
{deprecationDataView ? (
|
||||
<DiscoverAppLink checkpoint={checkpoint} deprecationDataView={deprecationDataView} />
|
||||
) : null}
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/i18n-react",
|
||||
"@kbn/analytics",
|
||||
"@kbn/rison",
|
||||
"@kbn/es-query",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/security-plugin",
|
||||
|
@ -38,7 +37,9 @@
|
|||
"@kbn/shared-ux-router",
|
||||
"@kbn/logs-shared-plugin",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/react-kibana-context-render"
|
||||
"@kbn/react-kibana-context-render",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/deeplinks-observability"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -11,7 +11,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
|
||||
const ONE_HOUR = 60 * 60 * 1000;
|
||||
const LOG_VIEW_ID = 'testView';
|
||||
const LOG_VIEW_REFERENCE = `(logViewId:${LOG_VIEW_ID},type:log-view-reference)`;
|
||||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const pageObjects = getPageObjects(['common']);
|
||||
|
@ -30,7 +29,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
it('redirects to the logs app and parses URL search params correctly', async () => {
|
||||
const location = {
|
||||
hash: '',
|
||||
pathname: `/link-to/${LOG_VIEW_ID}`,
|
||||
pathname: `/link-to`,
|
||||
search: `time=${timestamp}&filter=trace.id:${traceId}`,
|
||||
state: undefined,
|
||||
};
|
||||
|
@ -43,20 +42,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
ensureCurrentUrl: false,
|
||||
}
|
||||
);
|
||||
|
||||
return await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const documentTitle = await browser.getTitle();
|
||||
|
||||
expect(parsedUrl.pathname).to.be('/app/logs/stream');
|
||||
expect(parsedUrl.searchParams.get('logFilter')).to.be(
|
||||
`(query:(language:kuery,query:\'trace.id:${traceId}'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'${startDate}',to:'${endDate}'))`
|
||||
expect(parsedUrl.pathname).to.be('/app/observability-logs-explorer/');
|
||||
expect(parsedUrl.searchParams.get('pageState')).to.contain(
|
||||
`query:(language:kuery,query:\'trace.id:${traceId}')`
|
||||
);
|
||||
expect(parsedUrl.searchParams.get('logPosition')).to.be(
|
||||
`(position:(tiebreaker:0,time:'${date.toISOString()}'))`
|
||||
expect(parsedUrl.searchParams.get('pageState')).to.contain(
|
||||
`time:(from:'${startDate}',to:'${endDate}')`
|
||||
);
|
||||
expect(parsedUrl.searchParams.get('logView')).to.be(LOG_VIEW_REFERENCE);
|
||||
expect(documentTitle).to.contain('Stream - Logs - Observability - Elastic');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -81,17 +78,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const documentTitle = await browser.getTitle();
|
||||
|
||||
expect(parsedUrl.pathname).to.be('/app/logs/stream');
|
||||
expect(parsedUrl.searchParams.get('logFilter')).to.be(
|
||||
`(query:(language:kuery,query:\'(kubernetes.pod.uid: 1234) and (trace.id:${traceId})\'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'${startDate}',to:'${endDate}'))`
|
||||
expect(parsedUrl.pathname).to.be('/app/observability-logs-explorer/');
|
||||
expect(parsedUrl.searchParams.get('pageState')).to.contain(
|
||||
`query:(language:kuery,query:\'(kubernetes.pod.uid: 1234) and (trace.id:${traceId})\')`
|
||||
);
|
||||
expect(parsedUrl.searchParams.get('logPosition')).to.be(
|
||||
`(position:(tiebreaker:0,time:'${date.toISOString()}'))`
|
||||
expect(parsedUrl.searchParams.get('pageState')).to.contain(
|
||||
`time:(from:'${startDate}',to:'${endDate}')`
|
||||
);
|
||||
expect(parsedUrl.searchParams.get('logView')).to.be(LOG_VIEW_REFERENCE);
|
||||
expect(documentTitle).to.contain('Stream - Logs - Observability - Elastic');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue