[Logs UX] Classify logs-data-access and logs-shared as platform plugins (#201263)

This classifies `@kbn/logs-data-access-plugin`,
`@kbn/logs-shared-plugin` and `@kbn/observability-logs-overview` as
"platform/shared".

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Felix Stürmer 2024-12-17 11:48:35 +01:00 committed by GitHub
parent c4cf9fe8a5
commit d5e0d3a2f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 228 additions and 128 deletions

View file

@ -1997,9 +1997,6 @@ module.exports = {
},
{
files: [
// logsShared depends on o11y/private plugins, but platform plugins depend on it
'x-pack/plugins/observability_solution/logs_shared/**',
// TODO @kibana/operations
'scripts/create_observability_rules.js', // is importing "@kbn/observability-alerting-test-data" (observability/private)
'src/cli_setup/**', // is importing "@kbn/interactive-setup-plugin" (platform/private)

View file

@ -4,6 +4,6 @@
"owner": [
"@elastic/obs-ux-logs-team"
],
"group": "observability",
"visibility": "private"
"group": "platform",
"visibility": "shared"
}

View file

@ -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
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { SerializableRecord } from '@kbn/utility-types';
export const TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR = 'TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR';
export interface TransactionDetailsByTraceIdLocatorParams extends SerializableRecord {
rangeFrom?: string;
rangeTo?: string;
traceId: string;
}

View file

@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export * from './apm';
export * from './dataset_quality';
export * from './dataset_quality_details';
export * from './logs_explorer';

View file

@ -4,6 +4,6 @@
"owner": [
"@elastic/obs-ux-logs-team"
],
"group": "observability",
"visibility": "private"
"group": "platform",
"visibility": "shared"
}

View file

@ -19,7 +19,6 @@ import { i18n } from '@kbn/i18n';
import {
METRICS_EXPLORER_LOCATOR_ID,
MetricsExplorerLocatorParams,
ObservabilityTriggerId,
} from '@kbn/observability-shared-plugin/common';
import {
BehaviorSubject,
@ -101,10 +100,6 @@ export class Plugin implements InfraClientPluginClass {
registerFeatures(pluginsSetup.home);
}
pluginsSetup.uiActions.registerTrigger({
id: ObservabilityTriggerId.LogEntryContextMenu,
});
const assetDetailsLocator =
pluginsSetup.share.url.locators.get<AssetDetailsLocatorParams>(ASSET_DETAILS_LOCATOR_ID);
const inventoryLocator =

View file

@ -1,13 +1,17 @@
{
"type": "plugin",
"id": "@kbn/logs-data-access-plugin",
"owner": ["@elastic/obs-ux-logs-team"],
"owner": [
"@elastic/obs-ux-logs-team"
],
"group": "platform",
"visibility": "shared",
"plugin": {
"id": "logsDataAccess",
"server": true,
"browser": true,
"requiredPlugins": [
"data",
"data",
"dataViews"
],
"optionalPlugins": [],

View file

@ -2,12 +2,17 @@
"type": "plugin",
"id": "@kbn/logs-shared-plugin",
"owner": "@elastic/obs-ux-logs-team",
"group": "platform",
"visibility": "shared",
"description": "Exposes the shared components and APIs to access and visualize logs.",
"plugin": {
"id": "logsShared",
"server": true,
"browser": true,
"configPath": ["xpack", "logs_shared"],
"configPath": [
"xpack",
"logs_shared"
],
"requiredPlugins": [
"charts",
"data",
@ -15,16 +20,21 @@
"dataViews",
"discoverShared",
"logsDataAccess",
"observabilityShared",
"share",
"spaces",
"uiActions",
"usageCollection",
"embeddable",
],
"optionalPlugins": [
"observabilityAIAssistant",
],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"extraPublicDirs": ["common"]
"requiredBundles": [
"kibanaUtils",
"kibanaReact"
],
"extraPublicDirs": [
"common"
]
}
}

View file

@ -10,19 +10,19 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { buildEsQuery, Filter, Query } from '@kbn/es-query';
import { euiStyled } from '@kbn/kibana-react-plugin/common';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import { JsonValue } from '@kbn/utility-types';
import { noop } from 'lodash';
import React, { useCallback, useEffect, useMemo } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public';
import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public';
import { LogEntryCursor } from '../../../common/log_entry';
import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views';
import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream';
import { useLogView } from '../../hooks/use_log_view';
import { LogViewsClient } from '../../services/log_views';
import { LogColumnRenderConfiguration } from '../../utils/log_column_render_configuration';
import { useKibanaQuerySettings } from '../../utils/use_kibana_query_settings';
import { useLogEntryFlyout } from '../logging/log_entry_flyout';
import { ScrollableLogTextStreamView, VisibleInterval } from '../logging/log_text_stream';
import { LogStreamErrorBoundary } from './log_stream_error_boundary';

View file

@ -7,6 +7,8 @@
import { coreMock } from '@kbn/core/public/mocks';
import {
TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR,
TransactionDetailsByTraceIdLocatorParams,
uptimeOverviewLocatorID,
UptimeOverviewLocatorInfraParams,
UptimeOverviewLocatorParams,
@ -26,10 +28,10 @@ coreStartMock.application.getUrlForApp.mockImplementation((app, options) => {
});
const emptyUrlService = new MockUrlService();
const urlServiceWithUptimeLocator = new MockUrlService();
const urlServiceWithMockLocators = new MockUrlService();
// we can't use the actual locator here because its import would create a
// forbidden ts project reference cycle
urlServiceWithUptimeLocator.locators.create<
urlServiceWithMockLocators.locators.create<
UptimeOverviewLocatorInfraParams | UptimeOverviewLocatorParams
>({
id: uptimeOverviewLocatorID,
@ -37,6 +39,12 @@ urlServiceWithUptimeLocator.locators.create<
return { app: 'uptime', path: '/overview', state: {} };
},
});
urlServiceWithMockLocators.locators.create<TransactionDetailsByTraceIdLocatorParams>({
id: TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR,
getLocation: async (params) => {
return { app: 'apm', path: '/trace-id', state: {} };
},
});
const ProviderWrapper: FC<React.PropsWithChildren<{ urlService?: UrlService }>> = ({
children,
@ -90,7 +98,7 @@ describe('LogEntryActionsMenu component', () => {
describe('uptime link with legacy uptime enabled', () => {
it('renders as enabled when a host ip is present in the log entry', () => {
const elementWrapper = mount(
<ProviderWrapper urlService={urlServiceWithUptimeLocator}>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [{ field: 'host.ip', value: ['HOST_IP'] }],
@ -120,7 +128,7 @@ describe('LogEntryActionsMenu component', () => {
it('renders as enabled when a container id is present in the log entry', () => {
const elementWrapper = mount(
<ProviderWrapper urlService={urlServiceWithUptimeLocator}>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [{ field: 'container.id', value: ['CONTAINER_ID'] }],
@ -150,7 +158,7 @@ describe('LogEntryActionsMenu component', () => {
it('renders as enabled when a pod uid is present in the log entry', () => {
const elementWrapper = mount(
<ProviderWrapper urlService={urlServiceWithUptimeLocator}>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [{ field: 'kubernetes.pod.uid', value: ['POD_UID'] }],
@ -180,7 +188,7 @@ describe('LogEntryActionsMenu component', () => {
it('renders as disabled when no supported field is present in the log entry', () => {
const elementWrapper = mount(
<ProviderWrapper urlService={urlServiceWithUptimeLocator}>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [],
@ -215,7 +223,7 @@ describe('LogEntryActionsMenu component', () => {
describe('apm link', () => {
it('renders with a trace id filter when present in log entry', () => {
const elementWrapper = mount(
<ProviderWrapper>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [{ field: 'trace.id', value: ['1234567'] }],
@ -246,7 +254,7 @@ describe('LogEntryActionsMenu component', () => {
it('renders with a trace id filter and timestamp when present in log entry', () => {
const timestamp = '2019-06-27T17:44:08.693Z';
const elementWrapper = mount(
<ProviderWrapper>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [
@ -279,7 +287,7 @@ describe('LogEntryActionsMenu component', () => {
it('renders as disabled when no supported field is present in log entry', () => {
const elementWrapper = mount(
<ProviderWrapper>
<ProviderWrapper urlService={urlServiceWithMockLocators}>
<LogEntryActionsMenu
logEntry={{
fields: [],
@ -303,7 +311,10 @@ describe('LogEntryActionsMenu component', () => {
elementWrapper.update();
expect(
elementWrapper.find(`button${testSubject('~apmLogEntryActionsMenuItem')}`).prop('disabled')
elementWrapper
.find(`${testSubject('~apmLogEntryActionsMenuItem')}`)
.first()
.prop('disabled')
).toEqual(true);
});
});

View file

@ -7,13 +7,14 @@
import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
import {
TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR,
uptimeOverviewLocatorID,
type TransactionDetailsByTraceIdLocatorParams,
type UptimeOverviewLocatorInfraParams,
} from '@kbn/deeplinks-observability';
import { FormattedMessage } from '@kbn/i18n-react';
import { LinkDescriptor, useLinkProps } from '@kbn/observability-shared-plugin/public';
import { getRouterLinkProps } from '@kbn/router-utils';
import { ILocatorClient } from '@kbn/share-plugin/common/url_service';
import { BrowserUrlService } from '@kbn/share-plugin/public';
import React, { useMemo } from 'react';
import { LogEntry } from '../../../../common/search_strategies/log_entries/log_entry';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
@ -33,14 +34,11 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => {
} = useKibanaContextForPlugin();
const { hide, isVisible, toggle } = useVisibilityState(false);
const apmLinkDescriptor = useMemo(() => getAPMLink(logEntry), [logEntry]);
const uptimeLinkProps = getUptimeLink({ locators })(logEntry);
const apmLinkProps = useLinkProps({
app: 'apm',
...(apmLinkDescriptor ? apmLinkDescriptor : {}),
});
const apmLinkProps = useMemo(() => getAPMLink({ locators })(logEntry), [locators, logEntry]);
const uptimeLinkProps = useMemo(
() => getUptimeLink({ locators })(logEntry),
[locators, logEntry]
);
const menuItems = useMemo(
() => [
@ -58,7 +56,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => {
</EuiContextMenuItem>,
<EuiContextMenuItem
data-test-subj="logEntryActionsMenuItem apmLogEntryActionsMenuItem"
disabled={!apmLinkDescriptor}
disabled={!apmLinkProps}
icon="apmApp"
key="apmLink"
{...apmLinkProps}
@ -69,7 +67,7 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => {
/>
</EuiContextMenuItem>,
],
[apmLinkDescriptor, apmLinkProps, uptimeLinkProps]
[apmLinkProps, uptimeLinkProps]
);
const hasMenuItems = useMemo(() => menuItems.length > 0, [menuItems]);
@ -101,8 +99,8 @@ export const LogEntryActionsMenu = ({ logEntry }: LogEntryActionsMenuProps) => {
};
const getUptimeLink =
({ locators }: { locators: ILocatorClient }) =>
(logEntry: LogEntry): ContextRouterLinkProps | undefined => {
({ locators }: { locators: BrowserUrlService['locators'] }) =>
(logEntry: LogEntry) => {
const uptimeLocator = locators.get<UptimeOverviewLocatorInfraParams>(uptimeOverviewLocatorID);
if (!uptimeLocator) {
@ -135,47 +133,49 @@ const getUptimeLink =
}) as ContextRouterLinkProps;
};
const getAPMLink = (logEntry: LogEntry): LinkDescriptor | undefined => {
const traceId = logEntry.fields.find(
({ field, value }) => typeof value[0] === 'string' && field === 'trace.id'
)?.value?.[0];
const getAPMLink =
({ locators }: { locators: BrowserUrlService['locators'] }) =>
(logEntry: LogEntry) => {
const traceId = logEntry.fields.find(
({ field, value }) => typeof value[0] === 'string' && field === 'trace.id'
)?.value?.[0];
if (typeof traceId !== 'string') {
return undefined;
}
if (typeof traceId !== 'string') {
return undefined;
}
const timestampField = logEntry.fields.find(({ field }) => field === '@timestamp');
const timestamp = timestampField ? timestampField.value[0] : null;
const { rangeFrom, rangeTo } =
typeof timestamp === 'number'
? (() => {
const from = new Date(timestamp);
const to = new Date(timestamp);
const apmLocator = locators.get<TransactionDetailsByTraceIdLocatorParams>(
TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR
);
from.setMinutes(from.getMinutes() - 10);
to.setMinutes(to.getMinutes() + 10);
if (!apmLocator) {
return undefined;
}
return { rangeFrom: from.toISOString(), rangeTo: to.toISOString() };
})()
: { rangeFrom: 'now-1y', rangeTo: 'now' };
const timestampField = logEntry.fields.find(({ field }) => field === '@timestamp');
const timestamp = timestampField ? timestampField.value[0] : null;
const { rangeFrom, rangeTo } =
typeof timestamp === 'number' || typeof timestamp === 'string'
? (() => {
const from = new Date(timestamp);
const to = new Date(timestamp);
return {
app: 'apm',
pathname: getApmTraceUrl({ traceId, rangeFrom, rangeTo }),
from.setMinutes(from.getMinutes() - 10);
to.setMinutes(to.getMinutes() + 10);
return { rangeFrom: from.toISOString(), rangeTo: to.toISOString() };
})()
: { rangeFrom: 'now-1y', rangeTo: 'now' };
const apmLocatorParams = { traceId, rangeFrom, rangeTo };
// Coercing the return value to ContextRouterLinkProps because
// EuiContextMenuItem defines a too broad type for onClick
return getRouterLinkProps({
href: apmLocator.getRedirectUrl(apmLocatorParams),
onClick: () => apmLocator.navigate(apmLocatorParams),
}) as ContextRouterLinkProps;
};
};
function getApmTraceUrl({
traceId,
rangeFrom,
rangeTo,
}: {
traceId: string;
rangeFrom: string;
rangeTo: string;
}) {
return `/link-to/trace/${traceId}?` + new URLSearchParams({ rangeFrom, rangeTo }).toString();
}
export interface ContextRouterLinkProps {
href: string | undefined;

View file

@ -28,7 +28,6 @@ import { useLogEntry } from '../../../containers/logs/log_entry';
import { CenteredEuiFlyoutBody } from '../../centered_flyout_body';
import { DataSearchErrorCallout } from '../../data_search_error_callout';
import { DataSearchProgress } from '../../data_search_progress';
import LogAIAssistant from '../../log_ai_assistant/log_ai_assistant';
import { LogEntryActionsMenu } from './log_entry_actions_menu';
import { LogEntryFieldsTable } from './log_entry_fields_table';
@ -42,7 +41,7 @@ export interface LogEntryFlyoutProps {
export const useLogEntryFlyout = (logViewReference: LogViewReference) => {
const flyoutRef = useRef<OverlayRef>();
const {
services: { http, data, share, uiSettings, application, observabilityAIAssistant },
services: { http, data, share, uiSettings, application, logsShared },
overlays: { openFlyout },
} = useKibanaContextForPlugin();
@ -58,7 +57,7 @@ export const useLogEntryFlyout = (logViewReference: LogViewReference) => {
share,
uiSettings,
application,
observabilityAIAssistant,
logsShared,
});
flyoutRef.current = openFlyout(
@ -72,12 +71,12 @@ export const useLogEntryFlyout = (logViewReference: LogViewReference) => {
);
},
[
logsShared,
application,
closeLogEntryFlyout,
data,
http,
logViewReference,
observabilityAIAssistant,
openFlyout,
share,
uiSettings,
@ -115,7 +114,11 @@ export const LogEntryFlyout = ({
logEntryId,
});
const { observabilityAIAssistant } = useKibanaContextForPlugin().services;
const {
services: {
logsShared: { LogAIAssistant },
},
} = useKibanaContextForPlugin();
useEffect(() => {
if (logViewReference && logEntryId) {
@ -183,12 +186,9 @@ export const LogEntryFlyout = ({
}
>
<EuiFlexGroup direction="column" gutterSize="m">
{observabilityAIAssistant && (
{LogAIAssistant && (
<EuiFlexItem grow={false}>
<LogAIAssistant
observabilityAIAssistant={observabilityAIAssistant}
doc={logEntry}
/>
<LogAIAssistant doc={logEntry} />
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>

View file

@ -5,12 +5,11 @@
* 2.0.
*/
export type { LogEntryStreamItem } from './item';
export type { LogEntryColumnWidths } from './log_entry_column';
export { LogColumnHeader } from './column_headers';
export { LogColumnHeadersWrapper } from './column_headers_wrapper';
export { iconColumnId, LogEntryColumn, useColumnWidths } from './log_entry_column';
export type { LogEntryStreamItem } from './item';
export { LogEntryColumn, iconColumnId, useColumnWidths } from './log_entry_column';
export type { LogEntryColumnWidths } from './log_entry_column';
export { LogEntryContextMenu } from './log_entry_context_menu';
export { LogEntryFieldColumn } from './log_entry_field_column';
export { LogEntryMessageColumn } from './log_entry_message_column';

View file

@ -6,25 +6,19 @@
*/
import { i18n } from '@kbn/i18n';
import { ObservabilityTriggerId } from '@kbn/observability-shared-plugin/common';
import {
useUiTracker,
getContextMenuItemsFromActions,
} from '@kbn/observability-shared-plugin/public';
import { isEmpty } from 'lodash';
import React, { memo, useCallback, useMemo, useState } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { LogColumn, LogEntry } from '../../../../common/log_entry';
import { TextScale } from '../../../../common/log_text_scale';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import {
LogColumnRenderConfiguration,
isFieldColumnRenderConfiguration,
isMessageColumnRenderConfiguration,
isTimestampColumnRenderConfiguration,
LogColumnRenderConfiguration,
} from '../../../utils/log_column_render_configuration';
import { isTimestampColumn } from '../../../utils/log_entry';
import { iconColumnId, LogEntryColumn, LogEntryColumnWidths } from './log_entry_column';
import { useUiTracker } from '../../../utils/use_ui_tracker';
import { LogEntryColumn, LogEntryColumnWidths, iconColumnId } from './log_entry_column';
import { LogEntryContextMenu } from './log_entry_context_menu';
import { LogEntryFieldColumn } from './log_entry_field_column';
import { LogEntryMessageColumn } from './log_entry_message_column';
@ -74,7 +68,7 @@ export const LogEntryRow = memo(
scale,
wrap,
}: LogEntryRowProps) => {
const trackMetric = useUiTracker({ app: 'infra_logs' });
const trackMetric = useUiTracker();
const [isHovered, setIsHovered] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
@ -99,16 +93,6 @@ export const LogEntryRow = memo(
const hasActionViewLogInContext = hasContext && openViewLogInContext !== undefined;
const hasActionsMenu = hasActionFlyoutWithItem || hasActionViewLogInContext;
const uiActions = useKibanaContextForPlugin().services.uiActions;
const externalContextMenuItems = useAsync(() => {
return getContextMenuItemsFromActions({
uiActions,
triggerId: ObservabilityTriggerId.LogEntryContextMenu,
context: logEntry,
});
}, [uiActions, logEntry]);
const menuItems = useMemo(() => {
const items = [];
if (hasActionFlyoutWithItem) {
@ -251,7 +235,6 @@ export const LogEntryRow = memo(
onOpen={openMenu}
onClose={closeMenu}
items={menuItems}
externalItems={externalContextMenuItems.value}
/>
) : null}
</LogEntryColumn>

View file

@ -20,8 +20,7 @@ import {
} from '../types';
export type PluginKibanaContextValue = CoreStart &
LogsSharedClientStartDeps &
LogsSharedClientStartExports;
LogsSharedClientStartDeps & { logsShared: LogsSharedClientStartExports };
export const createKibanaContextForPlugin = (
core: CoreStart,
@ -31,7 +30,7 @@ export const createKibanaContextForPlugin = (
createKibanaReactContext<PluginKibanaContextValue>({
...core,
...plugins,
...pluginStart,
logsShared: pluginStart,
});
export const useKibanaContextForPlugin =

View file

@ -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 type { EsQueryConfig } from '@kbn/es-query';
import { SerializableRecord } from '@kbn/utility-types';
import { useMemo } from 'react';
import { UI_SETTINGS } from '@kbn/data-plugin/public';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
export const useKibanaQuerySettings = (): EsQueryConfig => {
const [allowLeadingWildcards] = useUiSetting$<boolean>(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS);
const [queryStringOptions] = useUiSetting$<SerializableRecord>(UI_SETTINGS.QUERY_STRING_OPTIONS);
const [dateFormatTZ] = useUiSetting$<string>(UI_SETTINGS.DATEFORMAT_TZ);
const [ignoreFilterIfFieldNotInIndex] = useUiSetting$<boolean>(
UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX
);
return useMemo(
() => ({
allowLeadingWildcards,
queryStringOptions,
dateFormatTZ,
ignoreFilterIfFieldNotInIndex,
}),
[allowLeadingWildcards, dateFormatTZ, ignoreFilterIfFieldNotInIndex, queryStringOptions]
);
};

View file

@ -0,0 +1,44 @@
/*
* 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 { useMemo } from 'react';
import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { useKibana } from '@kbn/kibana-react-plugin/public';
/**
* Note: The usage_collection plugin will take care of sending this data to the telemetry server.
* You can find the metrics that are collected by these hooks in Stack Telemetry.
* Search the index `kibana-ui-counter`. You can filter for `eventName` and/or `appName`.
*/
interface TrackOptions {
metricType?: UiCounterMetricType;
delay?: number; // in ms
}
interface ServiceDeps {
usageCollection: UsageCollectionSetup; // TODO: This should really be start. Looking into it.
}
export type TrackMetricOptions = TrackOptions & { metric: string };
export type UiTracker = ReturnType<typeof useUiTracker>;
export type TrackEvent = (options: TrackMetricOptions) => void;
export { METRIC_TYPE };
export function useUiTracker<Services extends ServiceDeps>(): TrackEvent {
const reportUiCounter = useKibana<Services>().services?.usageCollection?.reportUiCounter;
const trackEvent = useMemo(() => {
return ({ metric, metricType = METRIC_TYPE.COUNT }: TrackMetricOptions) => {
if (reportUiCounter) {
reportUiCounter('infra_logs', metricType, metric);
}
};
}, [reportUiCounter]);
return trackEvent;
}

View file

@ -11,7 +11,9 @@
"types/**/*",
"emotion.d.ts"
],
"exclude": ["target/**/*"],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/core",
"@kbn/i18n",
@ -29,7 +31,6 @@
"@kbn/logging-mocks",
"@kbn/kibana-react-plugin",
"@kbn/test-subj-selector",
"@kbn/observability-shared-plugin",
"@kbn/datemath",
"@kbn/core-http-browser",
"@kbn/ui-actions-plugin",
@ -53,5 +54,7 @@
"@kbn/embeddable-plugin",
"@kbn/saved-search-plugin",
"@kbn/spaces-plugin",
"@kbn/analytics",
"@kbn/usage-collection-plugin",
]
}

View file

@ -4,14 +4,14 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import qs from 'query-string';
import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public';
import type { SerializableRecord } from '@kbn/utility-types';
import {
TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR,
type TransactionDetailsByTraceIdLocatorParams,
} from '@kbn/deeplinks-observability';
export const TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR = 'TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR';
export interface TransactionDetailsByTraceIdLocatorParams extends SerializableRecord {
traceId: string;
}
export { TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR, type TransactionDetailsByTraceIdLocatorParams };
export type TransactionDetailsByTraceIdLocator =
LocatorPublic<TransactionDetailsByTraceIdLocatorParams>;
@ -21,10 +21,15 @@ export class TransactionDetailsByTraceIdLocatorDefinition
{
public readonly id = TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR;
public readonly getLocation = async ({ traceId }: TransactionDetailsByTraceIdLocatorParams) => {
public readonly getLocation = async ({
rangeFrom,
rangeTo,
traceId,
}: TransactionDetailsByTraceIdLocatorParams) => {
const params = { rangeFrom, rangeTo };
return {
app: 'apm',
path: `/link-to/trace/${encodeURIComponent(traceId)}`,
path: `/link-to/trace/${encodeURIComponent(traceId)}?${qs.stringify(params)}`,
state: {},
};
};

View file

@ -6,7 +6,6 @@
*/
export enum ObservabilityTriggerId {
LogEntryContextMenu = 'logEntryContextMenu',
ApmTransactionContextMenu = 'apmTransactionContextMenu',
ApmErrorContextMenu = 'apmErrorContextMenu',
}

View file

@ -46,6 +46,7 @@
"@kbn/es-query",
"@kbn/serverless",
"@kbn/data-views-plugin",
"@kbn/deeplinks-observability",
],
"exclude": ["target/**/*", ".storybook/**/*.js"]
}