diff --git a/x-pack/plugins/observability_solution/apm/common/entities/types.ts b/x-pack/plugins/observability_solution/apm/common/entities/types.ts index f953fb5c593ed4..bdca62bc66824f 100644 --- a/x-pack/plugins/observability_solution/apm/common/entities/types.ts +++ b/x-pack/plugins/observability_solution/apm/common/entities/types.ts @@ -27,4 +27,5 @@ export interface EntityServiceListItem { environments: string[]; serviceName: string; agentName: AgentName; + hasLogMetrics: boolean; } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx index c73526a5350c5a..49856327dd7034 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx @@ -16,6 +16,7 @@ import { EuiTitle, EuiButtonEmpty, useEuiTheme, + EuiButtonIcon, } from '@elastic/eui'; import { apmLight } from '@kbn/shared-svg'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -23,7 +24,11 @@ import { useKibana } from '../../../../context/kibana_context/use_kibana'; import { ApmPluginStartDeps, ApmServices } from '../../../../plugin'; import { AddApmData } from '../../../shared/add_data_buttons/buttons'; -export function AddAPMCallOut() { +interface Props { + onClose: () => void; +} + +export function AddAPMCallOut({ onClose }: Props) { const { euiTheme } = useEuiTheme(); const { services } = useKibana(); @@ -35,41 +40,56 @@ export function AddAPMCallOut() { return ( - - - - - - -

- + + + + -

-
+
+ + +

+ +

+
- + - -

- +

+ +

+
+ +
+
+ + + + + -

- - +
+
diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx index d1f08b16eaf15b..c89487b527fbe3 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx @@ -12,17 +12,29 @@ * 2.0. */ +import { + EuiCallOut, + EuiFlexGroup, + EuiFlexGroupProps, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { EuiFlexGroupProps, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { AnnotationsContextProvider } from '../../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../../context/chart_pointer_event/chart_pointer_event_context'; -import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { useTimeRange } from '../../../../hooks/use_time_range'; -import { AddAPMCallOut } from './add_apm_callout'; -import { LogRateChart } from '../charts/log_rate_chart'; import { LogErrorRateChart } from '../charts/log_error_rate_chart'; +import { LogRateChart } from '../charts/log_rate_chart'; +import { AddAPMCallOut } from './add_apm_callout'; +import { useLocalStorage } from '../../../../hooks/use_local_storage'; +import { isPending, useFetcher } from '../../../../hooks/use_fetcher'; /** * The height a chart should be if it's next to a table with 5 rows and a title. * Add the height of the pagination row. @@ -32,6 +44,10 @@ const chartHeight = 400; export function LogsServiceOverview() { const { serviceName } = useApmServiceContext(); + const [isLogsApmCalloutEnabled, setIsLogsApmCalloutEnabled] = useLocalStorage( + 'apm.isLogsApmCalloutEnabled', + true + ); const { query: { environment, rangeFrom, rangeTo }, @@ -39,11 +55,28 @@ export function LogsServiceOverview() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const { data, status } = useFetcher( + (callAPI) => { + return callAPI('GET /internal/apm/entities/services/{serviceName}/summary', { + params: { path: { serviceName }, query: { end, environment, start } }, + }); + }, + [end, environment, serviceName, start] + ); + const { isLarge } = useBreakpoints(); const isSingleColumn = isLarge; const rowDirection: EuiFlexGroupProps['direction'] = isSingleColumn ? 'column' : 'row'; + if (isPending(status)) { + return ( +
+ +
+ ); + } + return ( - - + {isLogsApmCalloutEnabled ? ( + <> + { + setIsLogsApmCalloutEnabled(false); + }} + /> + + + ) : null} + {data?.entity?.hasLogMetrics === false ? ( + <> + + + {i18n.translate('xpack.apm.logsServiceOverview.logLevelLink', { + defaultMessage: 'log.level', + })} + + ), + learnMoreLink: ( + + {i18n.translate('xpack.apm.logsServiceOverview.learnMoreLink', { + defaultMessage: 'Learn more', + })} + + ), + }} + /> + + + + ) : null} diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx index d0a2cfa898b792..45e8a9ca859a09 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx @@ -32,13 +32,14 @@ import { EnvironmentBadge } from '../../../../shared/environment_badge'; import { ServiceLink } from '../../../../shared/links/apm/service_link'; import { ListMetric } from '../../../../shared/list_metric'; import { ITableColumn } from '../../../../shared/managed_table'; -import { NotAvailableApmMetrics } from '../../../../shared/not_available_apm_metrics'; +import { NotAvailableApmMetrics } from '../../../../shared/not_available_popover/not_available_apm_metrics'; import { TruncateWithTooltip } from '../../../../shared/truncate_with_tooltip'; import { ServiceInventoryFieldName } from './multi_signal_services_table'; import { EntityServiceListItem, SignalTypes } from '../../../../../../common/entities/types'; -import { isApmSignal } from '../../../../../utils/get_signal_type'; +import { isApmSignal, isLogsSignal } from '../../../../../utils/get_signal_type'; import { ColumnHeader } from './column_header'; import { APIReturnType } from '../../../../../services/rest/create_call_apm_api'; +import { NotAvailableLogsMetrics } from '../../../../shared/not_available_popover/not_available_log_metrics'; type ServicesDetailedStatisticsAPIResponse = APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; @@ -205,9 +206,12 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, serviceName }) => { - const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); + render: (_, { metrics, serviceName, signalTypes, hasLogMetrics }) => { + if (isLogsSignal(signalTypes) && !hasLogMetrics) { + return ; + } + const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); return ( { + render: (_, { metrics, serviceName, signalTypes, hasLogMetrics }) => { + if (isLogsSignal(signalTypes) && !hasLogMetrics) { + return ; + } + const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_ERROR_RATE); return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx index 135848bae13d4f..8e3752e5ce0bf3 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { ItemsBadge } from '../item_badge'; -import { NotAvailableEnvironment } from '../not_available_environment'; +import { NotAvailableEnvironment } from '../not_available_popover/not_available_environment'; interface Props { environments: string[]; diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx similarity index 81% rename from x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx rename to x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx index c4d2eb8563af6a..66c466502bdd1c 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx @@ -7,10 +7,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { PopoverBadge } from './popover_badge'; -import { useKibana } from '../../context/kibana_context/use_kibana'; -import { ApmPluginStartDeps, ApmServices } from '../../plugin'; -import { AddApmData } from './add_data_buttons/buttons'; +import { PopoverBadge } from '../popover_badge'; +import { useKibana } from '../../../context/kibana_context/use_kibana'; +import { ApmPluginStartDeps, ApmServices } from '../../../plugin'; +import { AddApmData } from '../add_data_buttons/buttons'; export function NotAvailableApmMetrics() { const { services } = useKibana(); diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx similarity index 92% rename from x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx rename to x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx index aecc9f2586e710..af4cdd58ec1f93 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiCode, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { PopoverBadge } from './popover_badge'; +import { PopoverBadge } from '../popover_badge'; -export const NotAvailableEnvironment = () => { +export function NotAvailableEnvironment() { return ( { } /> ); -}; +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx new file mode 100644 index 00000000000000..480795c5339442 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx @@ -0,0 +1,53 @@ +/* + * 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 { EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { PopoverBadge } from '../popover_badge'; + +export function NotAvailableLogsMetrics() { + return ( + + {i18n.translate( + 'xpack.apm.servicesTable.notAvailableLogsMetrics.content.logLevelLink', + { defaultMessage: 'log.level' } + )} + + ), + }} + /> + } + footer={ + + {i18n.translate('xpack.apm.servicesTable.notAvailableLogsMetrics.footer.learnMore', { + defaultMessage: 'Learn more', + })} + + } + /> + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts index a72bb4d782e9c6..e8a3c4c4313dfe 100644 --- a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts +++ b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts @@ -11,5 +11,5 @@ export function isApmSignal(signalTypes: SignalTypes[]) { return signalTypes.includes(SignalTypes.METRICS) || signalTypes.includes(SignalTypes.TRACES); } export function isLogsSignal(signalTypes: SignalTypes[]) { - return signalTypes.includes(SignalTypes.LOGS) && !isApmSignal(signalTypes); + return signalTypes.includes(SignalTypes.LOGS); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts index 40704fd3fed9fd..a69a906454ff74 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts @@ -17,6 +17,7 @@ import { environmentQuery } from '../../../common/utils/environment_query'; import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; import { getServiceEntitiesHistoryMetrics } from './get_service_entities_history_metrics'; import { EntitiesRaw, EntityType, ServiceEntities } from './types'; +import { isFiniteNumber } from '../../../common/utils/is_finite_number'; export function entitiesRangeQuery(start: number, end: number): QueryDslQueryContainer[] { return [ @@ -44,14 +45,16 @@ export async function getEntities({ environment, kuery, size, + serviceName, }: { entitiesESClient: EntitiesESClient; start: number; end: number; environment: string; - kuery: string; + kuery?: string; size: number; -}) { + serviceName?: string; +}): Promise { const entities = ( await entitiesESClient.searchLatest(`get_entities`, { body: { @@ -65,6 +68,7 @@ export async function getEntities({ ...environmentQuery(environment, SERVICE_ENVIRONMENT), ...entitiesRangeQuery(start, end), ...termQuery(ENTITY_TYPE, EntityType.SERVICE), + ...termQuery(SERVICE_NAME, serviceName), ], }, }, @@ -82,7 +86,8 @@ export async function getEntities({ }) : undefined; - return entities.map((entity): ServiceEntities => { + return entities.map((entity) => { + const historyLogRate = serviceEntitiesHistoryMetricsMap?.[entity.entity.id]?.logRate; return { serviceName: entity.service.name, environment: Array.isArray(entity.service?.environment) // TODO fix this in the EEM @@ -92,6 +97,7 @@ export async function getEntities({ signalTypes: entity.data_stream.type, entity: { ...entity.entity, + hasLogMetrics: isFiniteNumber(historyLogRate) ? historyLogRate > 0 : false, // History metrics undefined means that for the selected time range there was no ingestion happening. metrics: serviceEntitiesHistoryMetricsMap?.[entity.entity.id] || { latency: null, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.ts new file mode 100644 index 00000000000000..0ea5d27e689716 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.ts @@ -0,0 +1,40 @@ +/* + * 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 { EntitiesESClient } from '../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; +import { withApmSpan } from '../../utils/with_apm_span'; +import { getEntities } from './get_entities'; +import { ServiceEntities } from './types'; + +interface Params { + entitiesESClient: EntitiesESClient; + serviceName: string; + environment: string; + start: number; + end: number; +} + +export async function getServiceEntitySummary({ + end, + entitiesESClient, + environment, + serviceName, + start, +}: Params): Promise { + return withApmSpan('get_service_entity_summary', async () => { + const entities = await getEntities({ + end, + entitiesESClient, + environment, + size: 1, + start, + serviceName, + }); + + return entities[0]; + }); +} diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts index c3894a38da2c44..0f52a9956af5b4 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts @@ -13,12 +13,43 @@ import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/cr import { createApmServerRoute } from '../../apm_routes/create_apm_server_route'; import { environmentRt, kueryRt, rangeRt } from '../../default_api_types'; import { getServiceEntities } from './get_service_entities'; +import { getServiceEntitySummary } from '../get_service_entity_summary'; +import { ServiceEntities } from '../types'; import { getServiceEntitiesHistoryTimeseries } from '../get_service_entities_history_timeseries'; export interface EntityServicesResponse { services: EntityServiceListItem[]; } +const serviceEntitiesSummaryRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/entities/services/{serviceName}/summary', + params: t.type({ + path: t.type({ serviceName: t.string }), + query: t.intersection([environmentRt, rangeRt]), + }), + options: { tags: ['access:apm'] }, + async handler(resources): Promise { + const { context, params, request } = resources; + const coreContext = await context.core; + + const entitiesESClient = await createEntitiesESClient({ + request, + esClient: coreContext.elasticsearch.client.asCurrentUser, + }); + + const { serviceName } = params.path; + const { start, end, environment } = params.query; + + return getServiceEntitySummary({ + entitiesESClient, + start, + end, + serviceName, + environment, + }); + }, +}); + const servicesEntitiesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/entities/services', params: t.type({ @@ -161,4 +192,5 @@ export const servicesEntitiesRoutesRepository = { ...serviceLogRateTimeseriesRoute, ...serviceLogErrorRateTimeseriesRoute, ...servicesEntitiesDetailedStatisticsRoute, + ...serviceEntitiesSummaryRoute, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts index 35a19a218eea06..f98cef107a3d16 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts @@ -16,6 +16,7 @@ export interface Entity { latestTimestamp: string; identityFields: string[]; metrics: EntityMetrics; + hasLogMetrics: boolean; } export interface TraceMetrics { @@ -52,4 +53,5 @@ export interface MergedServiceEntities { signalTypes: SignalTypes[]; environments: string[]; metrics: EntityMetrics[]; + hasLogMetrics: boolean; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts index a7d18a93bd854e..f73d8cd5f4f8de 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts @@ -34,6 +34,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -57,6 +58,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-2', + hasLogMetrics: true, }, ]; @@ -76,6 +78,7 @@ describe('calculateAverageMetrics', () => { throughput: 7.5, }, serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -90,6 +93,7 @@ describe('calculateAverageMetrics', () => { throughput: 10, }, serviceName: 'service-2', + hasLogMetrics: true, }, ]); }); @@ -117,6 +121,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]; @@ -135,6 +140,7 @@ describe('calculateAverageMetrics', () => { throughput: 7.5, }, serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts index 544513fd501d20..dc52a8369c1cc6 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts @@ -27,6 +27,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -47,6 +48,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -69,6 +71,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:env-service-1', + hasLogMetrics: true, }, }, { @@ -87,6 +90,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'apm-only-1:synthtrace-env-2', + hasLogMetrics: true, }, }, { @@ -105,6 +109,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-2:env-service-3', + hasLogMetrics: true, }, }, { @@ -123,6 +128,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-2:env-service-3', + hasLogMetrics: true, }, }, ]; @@ -151,6 +157,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -174,6 +181,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-2', + hasLogMetrics: true, }, ]); }); @@ -195,6 +203,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -213,6 +222,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -231,6 +241,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:prod', + hasLogMetrics: true, }, }, ]; @@ -265,6 +276,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -286,6 +298,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -306,6 +319,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); @@ -325,6 +339,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -342,6 +357,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -369,6 +385,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -390,6 +407,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -410,6 +428,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); @@ -429,6 +448,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -446,6 +466,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -473,6 +494,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts index 7dd8bfdace7bfe..6f96a25c63a937 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts @@ -36,6 +36,7 @@ function mergeFunc(entity: ServiceEntities, existingEntity?: MergedServiceEntiti environments: compact([entity?.environment]), latestTimestamp: entity.entity.latestTimestamp, metrics: [entity.entity.metrics], + hasLogMetrics: entity.entity.hasLogMetrics, }; } return { @@ -45,5 +46,6 @@ function mergeFunc(entity: ServiceEntities, existingEntity?: MergedServiceEntiti environments: uniq(compact([...existingEntity?.environments, entity?.environment])), latestTimestamp: entity.entity.latestTimestamp, metrics: [...existingEntity?.metrics, entity.entity.metrics], + hasLogMetrics: entity.entity.hasLogMetrics || existingEntity.hasLogMetrics, }; }