diff --git a/packages/kbn-apm-types/src/es_fields/apm.ts b/packages/kbn-apm-types/src/es_fields/apm.ts index 6b0a68379f5d4..6c7276317894c 100644 --- a/packages/kbn-apm-types/src/es_fields/apm.ts +++ b/packages/kbn-apm-types/src/es_fields/apm.ts @@ -8,6 +8,7 @@ */ export const TIMESTAMP = 'timestamp.us'; +export const AT_TIMESTAMP = '@timestamp'; export const AGENT = 'agent'; export const AGENT_NAME = 'agent.name'; export const AGENT_VERSION = 'agent.version'; @@ -20,12 +21,18 @@ export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; export const CLOUD_PROVIDER = 'cloud.provider'; export const CLOUD_REGION = 'cloud.region'; export const CLOUD_MACHINE_TYPE = 'cloud.machine.type'; +export const CLOUD_PROJECT_ID = 'cloud.project.id'; +export const CLOUD_PROJECT_NAME = 'cloud.project.name'; +export const CLOUD_IMAGE_ID = 'cloud.image.id'; export const CLOUD_ACCOUNT_ID = 'cloud.account.id'; +export const CLOUD_ACCOUNT_NAME = 'cloud.account.name'; export const CLOUD_INSTANCE_ID = 'cloud.instance.id'; export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; export const CLOUD_SERVICE_NAME = 'cloud.service.name'; export const EVENT_SUCCESS_COUNT = 'event.success_count'; +export const EVENT_SUCCESS_COUNT_SUM = 'event.success_count.sum'; +export const EVENT_SUCCESS_COUNT_VALUE_COUNT = 'event.success_count.value_count'; export const SERVICE = 'service'; export const SERVICE_NAME = 'service.name'; @@ -39,18 +46,28 @@ export const SERVICE_RUNTIME_VERSION = 'service.runtime.version'; export const SERVICE_NODE_NAME = 'service.node.name'; export const SERVICE_VERSION = 'service.version'; export const SERVICE_TARGET_TYPE = 'service.target.type'; +export const SERVICE_TARGET_NAME = 'service.target.name'; export const SERVICE_OVERFLOW_COUNT = 'service_transaction.aggregation.overflow_count'; export const URL_FULL = 'url.full'; export const HTTP_REQUEST_METHOD = 'http.request.method'; export const HTTP_RESPONSE_STATUS_CODE = 'http.response.status_code'; + export const USER_ID = 'user.id'; export const USER_AGENT_ORIGINAL = 'user_agent.original'; export const USER_AGENT_NAME = 'user_agent.name'; +export const USER_AGENT_DEVICE_NAME = 'user_agent.device.name'; +export const USER_AGENT_VERSION = 'user_agent.version'; export const OBSERVER_HOSTNAME = 'observer.hostname'; +export const OBSERVER_ID = 'observer.id'; +export const OBSERVER_TYPE = 'observer.type'; +export const OBSERVER_NAME = 'observer.name'; +export const OBSERVER_VERSION = 'observer.version'; export const OBSERVER_LISTENING = 'observer.listening'; +export const OBSERVER_VERSION_MAJOR = 'observer.version_major'; export const PROCESSOR_EVENT = 'processor.event'; +export const PROCESSOR_NAME = 'processor.name'; export const TRANSACTION_DURATION = 'transaction.duration.us'; export const TRANSACTION_DURATION_HISTOGRAM = 'transaction.duration.histogram'; @@ -67,6 +84,9 @@ export const TRANSACTION_OVERFLOW_COUNT = 'transaction.aggregation.overflow_coun // for transaction metrics export const TRANSACTION_ROOT = 'transaction.root'; export const TRANSACTION_PROFILER_STACK_TRACE_IDS = 'transaction.profiler_stack_trace_ids'; +export const TRANSACTION_NAME_TEXT = 'transaction.name.text'; +export const TRANSACTION_REPRESENTATIVE_COUNT = 'transaction.representative_count'; +export const TRANSACTION_SPAN_COUNT_STARTED = 'transaction.span_count.started'; export const EVENT_OUTCOME = 'event.outcome'; @@ -95,6 +115,7 @@ export const SPAN_COMPOSITE_SUM = 'span.composite.sum.us'; export const SPAN_COMPOSITE_COMPRESSION_STRATEGY = 'span.composite.compression_strategy'; export const SPAN_SYNC = 'span.sync'; +export const SPAN_REPRESENTATIVE_COUNT = 'span.representative_count'; // Parent ID for a transaction or span export const PARENT_ID = 'parent.id'; @@ -138,6 +159,7 @@ export const LABEL_GC = 'labels.gc'; export const LABEL_TYPE = 'labels.type'; export const LABEL_TELEMETRY_AUTO_VERSION = 'labels.telemetry_auto_version'; export const LABEL_LIFECYCLE_STATE = 'labels.lifecycle_state'; +export const LABEL_SOME_RESOURCE_ATTRIBUTE = 'labels.some_resource_attribute'; export const HOST = 'host'; export const HOST_HOSTNAME = 'host.hostname'; // Do not use. Please use `HOST_NAME` instead. @@ -145,14 +167,19 @@ export const HOST_NAME = 'host.name'; export const HOST_OS_PLATFORM = 'host.os.platform'; export const HOST_ARCHITECTURE = 'host.architecture'; export const HOST_OS_VERSION = 'host.os.version'; +export const HOST_IP = 'host.ip'; export const CONTAINER_ID = 'container.id'; export const CONTAINER = 'container'; export const CONTAINER_IMAGE = 'container.image.name'; +export const CONTAINER_OS = 'container.os'; +export const CONTAINER_TTL_INSTANCES = 'container.totalNumberInstances'; export const KUBERNETES = 'kubernetes'; export const KUBERNETES_POD_NAME = 'kubernetes.pod.name'; export const KUBERNETES_POD_UID = 'kubernetes.pod.uid'; +export const KUBERNETES_NAMESPACE = 'kubernetes.namespace'; +export const KUBERNETES_NODE_NAME = 'kubernetes.node.name'; export const FAAS_ID = 'faas.id'; export const FAAS_NAME = 'faas.name'; @@ -180,6 +207,8 @@ export const VALUE_OTEL_JVM_PROCESS_MEMORY_NON_HEAP = 'non_heap'; export const TIER = '_tier'; export const INDEX = '_index'; export const DATA_STEAM_TYPE = 'data_stream.type'; +export const DATA_STREAM_NAMESPACE = 'data_stream.namespace'; +export const DATA_STREAM_DATASET = 'data_stream.dataset'; // Mobile export const NETWORK_CONNECTION_TYPE = 'network.connection.type'; @@ -194,6 +223,7 @@ export const CLIENT_GEO_REGION_ISO_CODE = 'client.geo.region_iso_code'; export const CLIENT_GEO_COUNTRY_NAME = 'client.geo.country_name'; export const CLIENT_GEO_CITY_NAME = 'client.geo.city_name'; export const CLIENT_GEO_REGION_NAME = 'client.geo.region_name'; +export const CLIENT_IP = 'client.ip'; export const CHILD_ID = 'child.id'; diff --git a/x-pack/plugins/observability_solution/apm/common/es_fields/infra_metrics.ts b/x-pack/plugins/observability_solution/apm/common/es_fields/infra_metrics.ts index fbf09e1887dfb..7d7081a0e1900 100644 --- a/x-pack/plugins/observability_solution/apm/common/es_fields/infra_metrics.ts +++ b/x-pack/plugins/observability_solution/apm/common/es_fields/infra_metrics.ts @@ -7,9 +7,12 @@ // Kubernetes export const KUBERNETES_CONTAINER_NAME = 'kubernetes.container.name'; +export const KUBERNETES_NODE_NAME = 'kubernetes.node.name'; +export const KUBERNETES_CONTAINER_ID = 'kubernetes.container.id'; export const KUBERNETES_DEPLOYMENT = 'kubernetes.deployment'; export const KUBERNETES_DEPLOYMENT_NAME = 'kubernetes.deployment.name'; export const KUBERNETES_NAMESPACE_NAME = 'kubernetes.namespace.name'; export const KUBERNETES_NAMESPACE = 'kubernetes.namespace'; export const KUBERNETES_REPLICASET = 'kubernetes.replicaset'; export const KUBERNETES_REPLICASET_NAME = 'kubernetes.replicaset.name'; +export const KUBERNETES_POD_NAME = 'kubernetes.pod.name'; diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index abebac6512b50..6aafed8b4877d 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -11,7 +11,6 @@ import { createHash } from 'crypto'; import { flatten, merge, pickBy, sortBy, sum, uniq } from 'lodash'; import { SavedObjectsClient } from '@kbn/core/server'; import type { APMIndices } from '@kbn/apm-data-access-plugin/server'; -import { normalizeFields } from '../../../utils/normalize_fields'; import { AGENT_NAMES, RUM_AGENT_NAMES } from '../../../../common/agent_name'; import { AGENT_ACTIVATION_METHOD, @@ -55,10 +54,7 @@ import { SavedServiceGroup, } from '../../../../common/service_groups'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; -import { APMError } from '../../../../typings/es_schemas/ui/apm_error'; import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; -import { Span } from '../../../../typings/es_schemas/ui/span'; -import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { APMDataTelemetry, APMPerService, @@ -75,6 +71,7 @@ import { APM_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, SavedApmCustomDashboard, } from '../../../../common/custom_dashboards'; +import { serviceVersionMapping } from '../../../utils/es_fields_mappings'; type ISavedObjectsClient = Pick; const TIME_RANGES = ['1d', 'all'] as const; @@ -695,10 +692,7 @@ export const tasks: TelemetryTask[] = [ }, }); - const hit = normalizeFields(response.hits.hits[0]?.fields) as Pick< - Transaction | Span | APMError, - 'observer' - >; + const hit = serviceVersionMapping(response.hits.hits[0]?.fields); if (!hit || !hit.observer?.version) { return {}; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_metadata_for_dependency.ts b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_metadata_for_dependency.ts index 9d7e1d7a44e14..d54f422baa136 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_metadata_for_dependency.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_metadata_for_dependency.ts @@ -7,11 +7,10 @@ import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import type { Span } from '@kbn/apm-types/es_schemas_ui'; import { maybe } from '../../../common/utils/maybe'; import { SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../common/es_fields/apm'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { normalizeFields } from '../../utils/normalize_fields'; +import { metadataForDependencyMapping } from '../../utils/es_fields_mappings'; export interface MetadataForDependencyResponse { spanType: string | undefined; @@ -56,7 +55,7 @@ export async function getMetadataForDependency({ }); const sample = maybe(sampleResponse.hits.hits[0])?.fields; - const sampleNorm = sample ? (normalizeFields(sample) as unknown as Span) : null; + const sampleNorm = sample ? metadataForDependencyMapping(sample) : null; return { spanType: sampleNorm?.span.type, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts index 66c1eb015bd0a..143478a8a54b9 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/dependencies/get_top_dependency_spans.ts @@ -8,8 +8,6 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { kqlQuery, rangeQuery, termQuery, termsQuery } from '@kbn/observability-plugin/server'; import { keyBy } from 'lodash'; -import { Span } from '@kbn/apm-types/es_schemas_ui'; -import type { TransactionRaw } from '@kbn/apm-types/es_schemas_raw'; import { AGENT_NAME, EVENT_OUTCOME, @@ -30,7 +28,10 @@ import { environmentQuery } from '../../../common/utils/environment_query'; import { maybe } from '../../../common/utils/maybe'; import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { normalizeFields } from '../../utils/normalize_fields'; +import { + topDependencySpansMapping, + transactionsForDependencySpansMapping, +} from '../../utils/es_fields_mappings'; const MAX_NUM_SPANS = 1000; @@ -115,7 +116,7 @@ export async function getTopDependencySpans({ ], }, }) - ).hits.hits.map((hit) => normalizeFields(hit?.fields) as unknown as Span); + ).hits.hits.map((hit) => topDependencySpansMapping(hit?.fields)); const transactionIds = spans.map((span) => span.transaction!.id); @@ -138,7 +139,7 @@ export async function getTopDependencySpans({ }, }, }) - ).hits.hits.map((hit) => normalizeFields(hit.fields) as unknown as TransactionRaw); + ).hits.hits.map((hit) => transactionsForDependencySpansMapping(hit.fields)); const transactionsById = keyBy(transactions, (transaction) => transaction.transaction.id); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts b/x-pack/plugins/observability_solution/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts index 5857800f6575e..d1f775af2a9ae 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts @@ -6,7 +6,61 @@ */ import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server'; -import { ERROR_ID, SERVICE_NAME } from '../../../../common/es_fields/apm'; +import { OBSERVER_VERSION_MAJOR } from '@kbn/observability-shared-plugin/common'; +import { + ERROR_EXCEPTION_STACKTRACE, + ERROR_LOG_STACKTRACE, + ERROR_STACK_TRACE, +} from '@kbn/discover-utils/src/field_constants'; +import { + AGENT_NAME, + AGENT_VERSION, + CONTAINER_ID, + CONTAINER_IMAGE, + ERROR_ID, + HOST_ARCHITECTURE, + HOST_HOSTNAME, + HOST_NAME, + HTTP_RESPONSE_STATUS_CODE, + KUBERNETES_POD_UID, + OBSERVER_HOSTNAME, + OBSERVER_ID, + OBSERVER_TYPE, + OBSERVER_NAME, + OBSERVER_VERSION, + PARENT_ID, + PROCESSOR_EVENT, + PROCESSOR_NAME, + SERVICE_NAME, + TRACE_ID, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_FRAMEWORK_VERSION, + SERVICE_NODE_NAME, + SERVICE_RUNTIME_NAME, + SERVICE_RUNTIME_VERSION, + SERVICE_LANGUAGE_NAME, + SERVICE_LANGUAGE_VERSION, + SERVICE_VERSION, + HOST_OS_PLATFORM, + TRANSACTION_ID, + TRANSACTION_SAMPLED, + TRANSACTION_TYPE, + ERROR_CULPRIT, + ERROR_PAGE_URL, + ERROR_LOG_MESSAGE, + URL_FULL, + USER_ID, + PROCESS_PID, + PROCESS_ARGS, + PROCESS_TITLE, + AT_TIMESTAMP, + ERROR_EXC_HANDLED, + ERROR_EXC_TYPE, + ERROR_EXC_MESSAGE, + URL_DOMAIN, + URL_ORIGINAL, +} from '../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../common/utils/environment_query'; import { ApmDocumentType } from '../../../../common/document_type'; import { RollupInterval } from '../../../../common/rollup'; @@ -14,6 +68,13 @@ import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm import { getTransaction } from '../../transactions/get_transaction'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { APMError } from '../../../../typings/es_schemas/ui/apm_error'; +import { + KUBERNETES_CONTAINER_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_NAMESPACE, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_CONTAINER_ID, +} from '../../../../common/es_fields/infra_metrics'; export interface ErrorSampleDetailsResponse { transaction: Transaction | undefined; @@ -60,7 +121,73 @@ export async function getErrorSampleDetails({ ], }, }, - fields: ['*'], + fields: [ + AGENT_VERSION, + AGENT_NAME, + PARENT_ID, + TRACE_ID, + // AGENT_EPHEMERAL_ID, + // OBSERVER_EPHEMERAL_ID, + OBSERVER_HOSTNAME, + OBSERVER_ID, + OBSERVER_TYPE, + OBSERVER_NAME, + OBSERVER_VERSION, + OBSERVER_VERSION_MAJOR, + CONTAINER_ID, + CONTAINER_IMAGE, + PROCESSOR_NAME, + PROCESSOR_EVENT, + HOST_ARCHITECTURE, + HOST_HOSTNAME, + HOST_NAME, + 'host.ip', + 'http.request.method', + HTTP_RESPONSE_STATUS_CODE, + 'http.version', + KUBERNETES_POD_UID, + KUBERNETES_NAMESPACE, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_CONTAINER_ID, + KUBERNETES_CONTAINER_NAME, + SERVICE_NAME, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_FRAMEWORK_VERSION, + SERVICE_NODE_NAME, + SERVICE_RUNTIME_NAME, + SERVICE_RUNTIME_VERSION, + SERVICE_LANGUAGE_NAME, + SERVICE_LANGUAGE_VERSION, + SERVICE_VERSION, + PROCESS_ARGS, + PROCESS_PID, + PROCESS_TITLE, + HOST_OS_PLATFORM, + AT_TIMESTAMP, + TRANSACTION_ID, + TRANSACTION_SAMPLED, + TRANSACTION_TYPE, + ERROR_ID, + ERROR_CULPRIT, + 'error.exception.attributes.response', + // ERROR_EXC_ATTRIBUTES_RESPONSE, + // ERROR_EXC_CODE, + ERROR_EXC_MESSAGE, + ERROR_EXC_TYPE, + // ERROR_EXCEPTION_MODULE, + ERROR_EXC_HANDLED, + ERROR_EXCEPTION_STACKTRACE, // todo: fix me + ERROR_PAGE_URL, + ERROR_LOG_MESSAGE, + ERROR_LOG_STACKTRACE, // todo: fixme + ERROR_STACK_TRACE, + URL_DOMAIN, + URL_FULL, + URL_ORIGINAL, + USER_ID, + ], }, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_agent.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_agent.ts index cbfddfe585e77..5302e1dbb6481 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_agent.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_agent.ts @@ -7,6 +7,7 @@ import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { serviceAgentNameMapping } from '../../utils/es_fields_mappings'; import { AGENT_NAME, SERVICE_NAME, @@ -16,7 +17,6 @@ import { } from '../../../common/es_fields/apm'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { getServerlessTypeFromCloudData, ServerlessType } from '../../../common/serverless'; -import { normalizeFields } from '../../utils/normalize_fields'; interface ServiceAgent { agent?: { @@ -102,8 +102,9 @@ export async function getServiceAgent({ return {}; } - // TODO fix in the mapper - const { agent, service, cloud } = normalizeFields(response.hits.hits[0].fields) as ServiceAgent; + const { agent, service, cloud } = serviceAgentNameMapping( + response.hits.hits[0].fields + ) as ServiceAgent; const serverlessType = getServerlessTypeFromCloudData(cloud?.provider, cloud?.service?.name); return { diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_instance_metadata_details.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_instance_metadata_details.ts index 86fdcf1d8d95d..2054c29deae79 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_instance_metadata_details.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_instance_metadata_details.ts @@ -7,6 +7,7 @@ import { merge } from 'lodash'; import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { serviceInstanceMetadataDetailsMapping } from '../../utils/es_fields_mappings'; import { METRICSET_NAME, SERVICE_NAME, SERVICE_NODE_NAME } from '../../../common/es_fields/apm'; import { maybe } from '../../../common/utils/maybe'; import { @@ -20,7 +21,6 @@ import { Container } from '../../../typings/es_schemas/raw/fields/container'; import { Kubernetes } from '../../../typings/es_schemas/raw/fields/kubernetes'; import { Host } from '../../../typings/es_schemas/raw/fields/host'; import { Cloud } from '../../../typings/es_schemas/raw/fields/cloud'; -import { normalizeFields } from '../../utils/normalize_fields'; export interface ServiceInstanceMetadataDetailsResponse { '@timestamp': string; @@ -72,11 +72,7 @@ export async function getServiceInstanceMetadataDetails({ } ); - return maybe( - normalizeFields( - response.hits.hits[0]?.fields - ) as unknown as ServiceInstanceMetadataDetailsResponse - ); + return maybe(serviceInstanceMetadataDetailsMapping(response.hits.hits[0]?.fields)); } async function getTransactionEventSample() { @@ -96,7 +92,7 @@ export async function getServiceInstanceMetadataDetails({ } ); - return maybe(normalizeFields(response.hits.hits[0]?.fields)); + return maybe(serviceInstanceMetadataDetailsMapping(response.hits.hits[0]?.fields)); } async function getTransactionMetricSample() { @@ -119,7 +115,7 @@ export async function getServiceInstanceMetadataDetails({ }, } ); - return maybe(normalizeFields(response.hits.hits[0]?.fields)); + return maybe(serviceInstanceMetadataDetailsMapping(response.hits.hits[0]?.fields)); } // we can expect the most detail of application metrics, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_details.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_details.ts index a52ded5428e59..caec6b724bb06 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_details.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_details.ts @@ -7,32 +7,59 @@ import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { serviceMetadataDetailsMapping } from '../../utils/es_fields_mappings'; import { environmentQuery } from '../../../common/utils/environment_query'; import { - AGENT, - CONTAINER, - CLOUD, + KUBERNETES_CONTAINER_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_NAMESPACE, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_CONTAINER_ID, + KUBERNETES_NODE_NAME, + KUBERNETES_POD_NAME, +} from '../../../common/es_fields/infra_metrics'; +import { + AGENT_NAME, + AGENT_VERSION, + CONTAINER_ID, + CONTAINER_IMAGE, CLOUD_AVAILABILITY_ZONE, + CLOUD_INSTANCE_ID, CLOUD_REGION, + CLOUD_PROJECT_NAME, CLOUD_MACHINE_TYPE, CLOUD_SERVICE_NAME, - CONTAINER_ID, - HOST, - KUBERNETES, - SERVICE, - SERVICE_NAME, + CLOUD_INSTANCE_NAME, + CLOUD_PROVIDER, + CLOUD_ACCOUNT_ID, + CLOUD_ACCOUNT_NAME, + CLOUD_IMAGE_ID, + CLOUD_PROJECT_ID, + HOST_ARCHITECTURE, + HOST_HOSTNAME, + HOST_NAME, + HOST_IP, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_FRAMEWORK_VERSION, SERVICE_NODE_NAME, + SERVICE_RUNTIME_NAME, + SERVICE_RUNTIME_VERSION, + SERVICE_LANGUAGE_NAME, + SERVICE_LANGUAGE_VERSION, SERVICE_VERSION, + SERVICE_NAME, FAAS_ID, FAAS_TRIGGER_TYPE, LABEL_TELEMETRY_AUTO_VERSION, + CONTAINER_TTL_INSTANCES, + HOST_OS_PLATFORM, } from '../../../common/es_fields/apm'; import { ContainerType } from '../../../common/service_metadata'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { should } from './get_service_metadata_icons'; -import { normalizeFields } from '../../utils/normalize_fields'; import { isOpenTelemetryAgentName, hasOpenTelemetryPrefix } from '../../../common/agent_name'; type ServiceMetadataDetailsRaw = Pick< @@ -113,16 +140,7 @@ export async function getServiceMetadataDetails({ body: { track_total_hits: 1, size: 1, - fields: [ - `${SERVICE}*`, - `${AGENT}*`, - `telemetry*`, - HOST, - CONTAINER, - KUBERNETES, - CLOUD, - LABEL_TELEMETRY_AUTO_VERSION, - ], + _source: false, query: { bool: { filter, should }, }, @@ -178,13 +196,56 @@ export async function getServiceMetadataDetails({ }, totalNumberInstances: { cardinality: { field: SERVICE_NODE_NAME } }, }, + fields: [ + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_FRAMEWORK_VERSION, + SERVICE_NODE_NAME, + SERVICE_RUNTIME_NAME, + SERVICE_RUNTIME_VERSION, + SERVICE_LANGUAGE_NAME, + SERVICE_LANGUAGE_VERSION, + SERVICE_VERSION, + SERVICE_NAME, + AGENT_NAME, + AGENT_VERSION, + CONTAINER_ID, + CONTAINER_IMAGE, + HOST_ARCHITECTURE, + HOST_HOSTNAME, + HOST_NAME, + HOST_IP, + HOST_OS_PLATFORM, + CONTAINER_TTL_INSTANCES, + KUBERNETES_NAMESPACE, + KUBERNETES_NODE_NAME, + KUBERNETES_POD_NAME, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_CONTAINER_ID, + KUBERNETES_CONTAINER_NAME, + CLOUD_AVAILABILITY_ZONE, + CLOUD_INSTANCE_NAME, + CLOUD_INSTANCE_ID, + CLOUD_MACHINE_TYPE, + CLOUD_PROJECT_ID, + CLOUD_PROJECT_NAME, + CLOUD_PROVIDER, + CLOUD_REGION, + CLOUD_ACCOUNT_ID, + CLOUD_ACCOUNT_NAME, + CLOUD_IMAGE_ID, + CLOUD_SERVICE_NAME, + LABEL_TELEMETRY_AUTO_VERSION, + `telemetry*`, + ], }, }; const response = await apmEventClient.search('get_service_metadata_details', params); const fields = response.hits.hits[0]?.fields; - const fieldsNorm = (fields ? normalizeFields(fields) : undefined) as + const fieldsNorm = (fields ? serviceMetadataDetailsMapping(fields) : undefined) as | ServiceMetadataDetailsRaw | undefined; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_icons.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_icons.ts index 3e44a313ffdfd..3108b07524ec2 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_icons.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_metadata_icons.ts @@ -7,25 +7,42 @@ import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { TransactionRaw } from '@kbn/apm-types'; +import { serviceMetadataIconsMapping } from '../../utils/es_fields_mappings'; import { AGENT_NAME, CLOUD_PROVIDER, CLOUD_SERVICE_NAME, CONTAINER_ID, - KUBERNETES, SERVICE_NAME, KUBERNETES_POD_NAME, HOST_OS_PLATFORM, LABEL_TELEMETRY_AUTO_VERSION, AGENT_VERSION, SERVICE_FRAMEWORK_NAME, + CLOUD_AVAILABILITY_ZONE, + CLOUD_INSTANCE_NAME, + CLOUD_INSTANCE_ID, + CLOUD_MACHINE_TYPE, + CLOUD_PROJECT_ID, + CLOUD_PROJECT_NAME, + CLOUD_REGION, + CLOUD_ACCOUNT_ID, + CLOUD_ACCOUNT_NAME, + CLOUD_IMAGE_ID, } from '../../../common/es_fields/apm'; import { ContainerType } from '../../../common/service_metadata'; -import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getProcessorEventForTransactions } from '../../lib/helpers/transactions'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { ServerlessType, getServerlessTypeFromCloudData } from '../../../common/serverless'; -import { normalizeFields } from '../../utils/normalize_fields'; +import { + KUBERNETES_CONTAINER_ID, + KUBERNETES_CONTAINER_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_NAMESPACE, + KUBERNETES_NODE_NAME, + KUBERNETES_REPLICASET_NAME, +} from '../../../common/es_fields/infra_metrics'; type ServiceMetadataIconsRaw = Pick; @@ -73,8 +90,32 @@ export async function getServiceMetadataIcons({ body: { track_total_hits: 1, size: 1, + _source: false, query: { bool: { filter, should } }, - fields: [KUBERNETES, CLOUD_PROVIDER, CONTAINER_ID, AGENT_NAME, CLOUD_SERVICE_NAME], + fields: [ + KUBERNETES_NAMESPACE, + KUBERNETES_NODE_NAME, + KUBERNETES_POD_NAME, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_CONTAINER_ID, + KUBERNETES_CONTAINER_NAME, + CLOUD_AVAILABILITY_ZONE, + CLOUD_INSTANCE_NAME, + CLOUD_INSTANCE_ID, + CLOUD_MACHINE_TYPE, + CLOUD_PROJECT_ID, + CLOUD_PROJECT_NAME, + CLOUD_PROVIDER, + CLOUD_REGION, + CLOUD_ACCOUNT_ID, + CLOUD_ACCOUNT_NAME, + CLOUD_IMAGE_ID, + CLOUD_SERVICE_NAME, + CONTAINER_ID, + AGENT_NAME, + CLOUD_SERVICE_NAME, + ], }, }; @@ -89,7 +130,7 @@ export async function getServiceMetadataIcons({ }; } - const { kubernetes, cloud, container, agent } = normalizeFields( + const { kubernetes, cloud, container, agent } = serviceMetadataIconsMapping( response.hits.hits[0].fields ) as ServiceMetadataIconsRaw; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_linked_parents.ts b/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_linked_parents.ts index 63cad913578a0..81e3aa845fd08 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_linked_parents.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_linked_parents.ts @@ -6,6 +6,7 @@ */ import { rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { linkedParentsOfSpanMapping } from '../../utils/es_fields_mappings'; import { SPAN_ID, SPAN_LINKS, @@ -13,10 +14,7 @@ import { TRANSACTION_ID, PROCESSOR_EVENT, } from '../../../common/es_fields/apm'; -import { SpanRaw } from '../../../typings/es_schemas/raw/span_raw'; -import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { normalizeFields } from '../../utils/normalize_fields'; export async function getLinkedParentsOfSpan({ apmEventClient, @@ -58,8 +56,7 @@ export async function getLinkedParentsOfSpan({ }, }); - const fields = response.hits.hits[0]?.fields; - const fieldsNorm = normalizeFields(fields) as unknown as TransactionRaw | SpanRaw | undefined; + const fieldsNorm = linkedParentsOfSpanMapping(response.hits.hits[0]?.fields); return fieldsNorm?.span?.links || []; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_span_links_details.ts b/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_span_links_details.ts index 45efddc7f863f..5e35a12730d5f 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_span_links_details.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/span_links/get_span_links_details.ts @@ -7,6 +7,7 @@ import { kqlQuery, rangeQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { chunk, compact, isEmpty, keyBy } from 'lodash'; +import { spanLinksDetailsMapping } from '../../utils/es_fields_mappings'; import { SERVICE_NAME, SPAN_ID, @@ -29,7 +30,6 @@ import { SpanRaw } from '../../../typings/es_schemas/raw/span_raw'; import { TransactionRaw } from '../../../typings/es_schemas/raw/transaction_raw'; import { getBufferedTimerange } from './utils'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; -import { normalizeFields } from '../../utils/normalize_fields'; async function fetchSpanLinksDetails({ apmEventClient, @@ -126,7 +126,7 @@ async function fetchSpanLinksDetails({ // The above query might return other spans from the same transaction because siblings spans share the same transaction.id // so, if it is a span we need to guarantee that the span.id is the same as the span links ids if (fields['processor.event']?.[0] === ProcessorEvent.span) { - const span = normalizeFields(fields) as unknown as SpanRaw; + const span = spanLinksDetailsMapping(fields); const hasSpanId = spanIdsMap[span.span.id] || false; return hasSpanId; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts index da4ddfde693fb..7ee2c06eb4433 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts @@ -11,6 +11,7 @@ import { QueryDslQueryContainer, Sort } from '@elastic/elasticsearch/lib/api/typ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { rangeQuery } from '@kbn/observability-plugin/server'; import { last } from 'lodash'; +import { errorDocsMapping, traceDocMapping } from '../../utils/es_fields_mappings'; import { APMConfig } from '../..'; import { AGENT_NAME, @@ -54,7 +55,6 @@ import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_ev import { getSpanLinksCountById } from '../span_links/get_linked_children'; import { ApmDocumentType } from '../../../common/document_type'; import { RollupInterval } from '../../../common/rollup'; -import { normalizeFields } from '../../utils/normalize_fields'; export interface TraceItems { exceedsMax: boolean; @@ -135,12 +135,8 @@ export async function getTraceItems({ const traceDocsTotal = traceResponse.total; const exceedsMax = traceDocsTotal > maxTraceItems; - const traceDocs = traceResponse.hits.map( - (hit) => normalizeFields(hit.fields) as unknown as WaterfallTransaction | WaterfallSpan - ); - const errorDocs = errorResponse.hits.hits.map( - (hit) => normalizeFields(hit.fields) as unknown as WaterfallError - ); + const traceDocs = traceResponse.hits.map((hit) => traceDocMapping(hit.fields)); + const errorDocs = errorResponse.hits.hits.map((hit) => errorDocsMapping(hit.fields)); return { exceedsMax, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_span/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_span/index.ts index 62a3462dde76a..145ffbf14f0ea 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_span/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_span/index.ts @@ -7,13 +7,13 @@ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { spanMapping } from '../../../utils/es_fields_mappings'; import { SPAN_ID, TRACE_ID } from '../../../../common/es_fields/apm'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { getTransaction } from '../get_transaction'; import { Span } from '../../../../typings/es_schemas/ui/span'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; -import { normalizeFields } from '../../../utils/normalize_fields'; export async function getSpan({ spanId, @@ -62,8 +62,7 @@ export async function getSpan({ : undefined, ]); - const fields = spanResp.hits.hits[0]?.fields; - const fieldsNorm = normalizeFields(fields) as unknown as Span; + const fieldsNorm = spanMapping(spanResp.hits.hits[0]?.fields); return { span: fieldsNorm, parentTransaction }; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction/index.ts index b807058a4aa43..b3ce38f1df4a3 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction/index.ts @@ -6,9 +6,35 @@ */ import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; -import type { Transaction } from '@kbn/apm-types/es_schemas_ui'; -import { normalizeFields } from '../../../utils/normalize_fields'; -import { TRACE_ID, TRANSACTION_ID } from '../../../../common/es_fields/apm'; +import { transactionMapping } from '../../../utils/es_fields_mappings'; +import { + AGENT_NAME, + AGENT_VERSION, + DATA_STEAM_TYPE, + DATA_STREAM_DATASET, + DATA_STREAM_NAMESPACE, + EVENT_OUTCOME, + EVENT_SUCCESS_COUNT, + LABEL_SOME_RESOURCE_ATTRIBUTE, + OBSERVER_HOSTNAME, + OBSERVER_TYPE, + OBSERVER_VERSION, + PROCESSOR_EVENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_NAME, + SERVICE_NODE_NAME, + TIMESTAMP, + TRACE_ID, + TRANSACTION_DURATION, + TRANSACTION_ID, + TRANSACTION_NAME, + TRANSACTION_RESULT, + TRANSACTION_SAMPLED, + TRANSACTION_TYPE, + SPAN_ID, + AT_TIMESTAMP, + TRANSACTION_REPRESENTATIVE_COUNT, +} from '../../../../common/es_fields/apm'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { ApmDocumentType } from '../../../../common/document_type'; @@ -49,12 +75,36 @@ export async function getTransaction({ ]), }, }, - fields: ['*'], + fields: [ + TRANSACTION_REPRESENTATIVE_COUNT, + TRANSACTION_RESULT, + TRANSACTION_SAMPLED, + TRANSACTION_ID, + TRANSACTION_DURATION, + TRANSACTION_TYPE, + TRANSACTION_NAME, + SERVICE_NODE_NAME, + SERVICE_NAME, + SERVICE_FRAMEWORK_NAME, + TRACE_ID, + AGENT_NAME, + AGENT_VERSION, + EVENT_SUCCESS_COUNT, + EVENT_OUTCOME, + PROCESSOR_EVENT, + DATA_STREAM_NAMESPACE, + DATA_STEAM_TYPE, + DATA_STREAM_DATASET, + SPAN_ID, + OBSERVER_HOSTNAME, + OBSERVER_TYPE, + OBSERVER_VERSION, + TIMESTAMP, + AT_TIMESTAMP, + LABEL_SOME_RESOURCE_ATTRIBUTE, + ], }, }); - const fields = resp.hits.hits[0]?.fields; - const fieldsNorm = normalizeFields(fields) as unknown as Transaction; - - return fieldsNorm; + return transactionMapping(resp.hits.hits[0]?.fields); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_name/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_name/index.ts index f90cad704c049..2de0b2aae04d2 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_name/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_name/index.ts @@ -6,13 +6,12 @@ */ import { rangeQuery } from '@kbn/observability-plugin/server'; -import type { Transaction } from '../../../../typings/es_schemas/ui/transaction'; -import { normalizeFields } from '../../../utils/normalize_fields'; import { ApmDocumentType } from '../../../../common/document_type'; import { SERVICE_NAME, TRANSACTION_NAME } from '../../../../common/es_fields/apm'; import { RollupInterval } from '../../../../common/rollup'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { transactionMapping } from '../../../utils/es_fields_mappings'; export async function getTransactionByName({ transactionName, @@ -53,8 +52,5 @@ export async function getTransactionByName({ }, }); - const fields = resp.hits.hits[0]?.fields; - const fieldsNorm = normalizeFields(fields) as unknown as Transaction; - - return fieldsNorm; + return transactionMapping(resp.hits.hits[0]?.fields); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_trace/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_trace/index.ts index 68891d3d908e3..b9aca516a5470 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_trace/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/transactions/get_transaction_by_trace/index.ts @@ -7,10 +7,9 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { rangeQuery } from '@kbn/observability-plugin/server'; -import type { Transaction } from '@kbn/apm-types/es_schemas_ui'; -import { normalizeFields } from '../../../utils/normalize_fields'; import { TRACE_ID, PARENT_ID } from '../../../../common/es_fields/apm'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { transactionMapping } from '../../../utils/es_fields_mappings'; export async function getRootTransactionByTraceId({ traceId, @@ -52,8 +51,6 @@ export async function getRootTransactionByTraceId({ }; const resp = await apmEventClient.search('get_root_transaction_by_trace_id', params); - const fields = resp.hits.hits[0]?.fields; - const transaction = normalizeFields(fields) as unknown as Transaction; - return { transaction }; + return transactionMapping(resp.hits.hits[0]?.fields); } diff --git a/x-pack/plugins/observability_solution/apm/server/utils/es_fields_mappings.ts b/x-pack/plugins/observability_solution/apm/server/utils/es_fields_mappings.ts new file mode 100644 index 0000000000000..a5335c56320db --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/server/utils/es_fields_mappings.ts @@ -0,0 +1,814 @@ +/* + * 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 { AgentName } from '@kbn/elastic-agent-utils'; +import { + APMError, + Span, + SpanLink, + AGENT_NAME, + AGENT_VERSION, + AT_TIMESTAMP, + CHILD_ID, + DATA_STEAM_TYPE, + DATA_STREAM_DATASET, + DATA_STREAM_NAMESPACE, + ERROR_CULPRIT, + ERROR_EXC_HANDLED, + ERROR_EXC_TYPE, + EVENT_OUTCOME, + EVENT_SUCCESS_COUNT, + FAAS_COLDSTART, + LABEL_SOME_RESOURCE_ATTRIBUTE, + OBSERVER_HOSTNAME, + OBSERVER_TYPE, + OBSERVER_VERSION, + OBSERVER_VERSION_MAJOR, + PARENT_ID, + PROCESSOR_NAME, + PROCESSOR_EVENT, + SERVICE_ENVIRONMENT, + SERVICE_FRAMEWORK_NAME, + SERVICE_NAME, + SERVICE_NODE_NAME, + SPAN_ACTION, + SPAN_COMPOSITE_COMPRESSION_STRATEGY, + SPAN_COMPOSITE_COUNT, + SPAN_COMPOSITE_SUM, + SPAN_DESTINATION_SERVICE_RESOURCE, + SPAN_DURATION, + SPAN_ID, + SPAN_LINKS_SPAN_ID, + SPAN_LINKS_TRACE_ID, + SPAN_NAME, + SPAN_SUBTYPE, + SPAN_SYNC, + SPAN_TYPE, + TIMESTAMP, + TRACE_ID, + TRANSACTION_DURATION, + TRANSACTION_ID, + TRANSACTION_NAME, + TRANSACTION_REPRESENTATIVE_COUNT, + TRANSACTION_RESULT, + TRANSACTION_SAMPLED, + TRANSACTION_TYPE, + ERROR_EXC_MESSAGE, + ERROR_EXCEPTION, + ERROR_GROUP_ID, + ERROR_ID, + ERROR_LOG_MESSAGE, + HOST_NAME, + CONTAINER_ID, + CONTAINER_IMAGE, + CLOUD_PROVIDER, + CLOUD_INSTANCE_NAME, + AGENT_ACTIVATION_METHOD, + HOST_ARCHITECTURE, + HOST_HOSTNAME, + HOST_IP, + HOST_OS_PLATFORM, + HTTP_RESPONSE_STATUS_CODE, + KUBERNETES_NAMESPACE, + KUBERNETES_NODE_NAME, + KUBERNETES_POD_NAME, + KUBERNETES_POD_UID, + SERVICE_FRAMEWORK_VERSION, + SERVICE_LANGUAGE_NAME, + SERVICE_RUNTIME_NAME, + SERVICE_RUNTIME_VERSION, + SERVICE_VERSION, + SERVICE_TARGET_NAME, + SERVICE_TARGET_TYPE, + SPAN_REPRESENTATIVE_COUNT, + SERVICE_LANGUAGE_VERSION, + CLOUD_AVAILABILITY_ZONE, + CLOUD_INSTANCE_ID, + CLOUD_MACHINE_TYPE, + CLOUD_PROJECT_ID, + CLOUD_PROJECT_NAME, + CLOUD_REGION, + CLOUD_ACCOUNT_ID, + CLOUD_ACCOUNT_NAME, + CLOUD_IMAGE_ID, + CLOUD_SERVICE_NAME, +} from '@kbn/apm-types'; +import { + KUBERNETES_CONTAINER_NAME, + KUBERNETES_DEPLOYMENT_NAME, + KUBERNETES_REPLICASET_NAME, + KUBERNETES_CONTAINER_ID, +} from '../../common/es_fields/infra_metrics'; +import { Transaction } from '../../typings/es_schemas/ui/transaction'; +import { TransactionRaw } from '../../typings/es_schemas/raw/transaction_raw'; +import { + WaterfallError, + WaterfallSpan, + WaterfallTransaction, +} from '../../common/waterfall/typings'; +import { EventOutcome } from '../../common/event_outcome'; +import { Exception } from '../../typings/es_schemas/raw/error_raw'; + +type ServiceMetadataIconsRaw = Pick; +type ServiceMetadataDetailsRaw = Pick< + TransactionRaw, + 'service' | 'agent' | 'host' | 'container' | 'kubernetes' | 'cloud' | 'labels' +>; +const normalizeValue = (field: unknown[] | unknown): T => { + return (Array.isArray(field) && field.length > 0 ? field[0] : field) as T; +}; + +export const metadataForDependencyMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + span: { + type: normalizeValue(fields[SPAN_TYPE]), + subtype: normalizeValue(fields[SPAN_SUBTYPE]), + }, + }; +}; + +export const transactionsForDependencySpansMapping = ( + fields: Partial> +) => { + if (!fields) return undefined; + + return { + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + name: normalizeValue(fields[TRANSACTION_NAME]), + type: normalizeValue(fields[TRANSACTION_TYPE]), + }, + }; +}; + +export const topDependencySpansMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + }, + span: { + id: normalizeValue(fields[SPAN_ID]), + name: normalizeValue(fields[SPAN_NAME]), + duration: { + us: normalizeValue(fields[SPAN_DURATION]), + }, + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + service: { + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + }, + event: { + outcome: normalizeValue(fields[EVENT_OUTCOME]), + }, + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + }; +}; + +export const spanMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + parent: { + id: normalizeValue(fields[PARENT_ID]), + }, + observer: { + type: normalizeValue(fields[OBSERVER_TYPE]), + version: normalizeValue(fields[OBSERVER_VERSION]), + version_major: normalizeValue(fields[OBSERVER_VERSION_MAJOR]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: normalizeValue(fields[AGENT_VERSION]), + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + service: { + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + framework: { + name: normalizeValue(fields[SERVICE_FRAMEWORK_NAME]), + version: normalizeValue(fields[SERVICE_FRAMEWORK_VERSION]), + }, + target: { + name: normalizeValue(fields[SERVICE_TARGET_NAME]), + type: normalizeValue(fields[SERVICE_TARGET_TYPE]), + }, + }, + event: { + outcome: normalizeValue(fields[EVENT_OUTCOME]), + success_count: normalizeValue(fields[EVENT_SUCCESS_COUNT]), + }, + processor: { + name: normalizeValue<'transaction'>(fields[PROCESSOR_NAME]), + event: normalizeValue<'span'>(fields[PROCESSOR_EVENT]), + }, + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + }, + span: { + duration: { + us: normalizeValue(fields[SPAN_DURATION]), + }, + subtype: normalizeValue(fields[SPAN_SUBTYPE]), + name: normalizeValue(fields[SPAN_NAME]), + destination: { + service: { + resource: normalizeValue(fields[SPAN_DESTINATION_SERVICE_RESOURCE]), + }, + }, + id: normalizeValue(fields[SPAN_ID]), + type: normalizeValue(fields[SPAN_TYPE]), + representative_count: normalizeValue(fields[SPAN_REPRESENTATIVE_COUNT]), + }, + timestamp: { + us: normalizeValue(fields[TIMESTAMP]), + }, + http: { + response: { + status_code: normalizeValue(fields[HTTP_RESPONSE_STATUS_CODE]), + }, + }, + data_stream: { + namespace: normalizeValue(fields[DATA_STREAM_NAMESPACE]), + type: normalizeValue(fields[DATA_STEAM_TYPE]), + dataset: normalizeValue(fields[DATA_STREAM_DATASET]), + }, + }; +}; + +export const spanLinksDetailsMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + span: { + id: normalizeValue(fields[SPAN_ID]), + name: normalizeValue(fields[SPAN_NAME]), + type: normalizeValue(fields[SPAN_TYPE]), + subtype: normalizeValue(fields[SPAN_SUBTYPE]), + duration: { + us: normalizeValue(fields[SPAN_DURATION]), + }, + }, + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + name: normalizeValue(fields[TRANSACTION_NAME]), + duration: { + us: normalizeValue(fields[TRANSACTION_DURATION]), + }, + }, + service: { + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + }, + processor: { + event: normalizeValue(fields[PROCESSOR_EVENT]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + }, + }; +}; + +// todo: pending #192337 +export const linkedParentsOfSpanMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return (fields[SPAN_LINKS_TRACE_ID] as string[]).map((v, index) => { + return { + trace: { + id: v, + }, + span: { + id: fields[SPAN_LINKS_SPAN_ID]?.[index] ?? '', + }, + }; + }) as SpanLink[]; +}; + +export const transactionMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + transaction: { + representative_count: normalizeValue(fields[TRANSACTION_REPRESENTATIVE_COUNT]), + result: normalizeValue(fields[TRANSACTION_RESULT]), + sampled: normalizeValue(fields[TRANSACTION_SAMPLED]), + id: normalizeValue(fields[TRANSACTION_ID]), + duration: { + us: normalizeValue(fields[TRANSACTION_DURATION]), + }, + type: normalizeValue(fields[TRANSACTION_TYPE]), + name: normalizeValue(fields[TRANSACTION_NAME]), + }, + service: { + node: { + name: normalizeValue(fields[SERVICE_NODE_NAME]), + }, + language: { + name: normalizeValue(fields[SERVICE_NODE_NAME]), + }, + name: normalizeValue(fields[SERVICE_NAME]), + framework: { + name: normalizeValue(fields[SERVICE_FRAMEWORK_NAME]), + }, + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: normalizeValue(fields[AGENT_VERSION]), + }, + event: { + success_count: normalizeValue(fields[EVENT_SUCCESS_COUNT]), + outcome: normalizeValue(fields[EVENT_OUTCOME]), + }, + processor: { + event: normalizeValue<'transaction'>(fields[PROCESSOR_EVENT]), + name: normalizeValue<'transaction'>(fields[PROCESSOR_NAME]), + }, + data_stream: { + namespace: normalizeValue(fields[DATA_STREAM_NAMESPACE]), + type: normalizeValue(fields[DATA_STEAM_TYPE]), + dataset: normalizeValue(fields[DATA_STREAM_DATASET]), + }, + span: { + id: normalizeValue(fields[SPAN_ID]), + links: linkedParentsOfSpanMapping(fields), + }, + observer: { + hostname: normalizeValue(fields[OBSERVER_HOSTNAME]), + type: normalizeValue(fields[OBSERVER_TYPE]), + version: normalizeValue(fields[OBSERVER_VERSION]), + version_major: normalizeValue(fields[OBSERVER_VERSION_MAJOR]), + }, + timestamp: { + us: normalizeValue(fields[TIMESTAMP]), + }, + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + labels: { + some_resource_attribute: normalizeValue(fields[LABEL_SOME_RESOURCE_ATTRIBUTE]), + }, + }; +}; + +export const traceDocMapping = ( + fields: Partial> +): WaterfallTransaction | WaterfallSpan | undefined => { + if (!fields) return undefined; + + return { + timestamp: { + us: normalizeValue(fields[TIMESTAMP]), + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + parent: { + id: normalizeValue(fields[PARENT_ID]), + }, + service: { + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + }, + event: { + outcome: normalizeValue(fields[EVENT_OUTCOME]), + }, + processor: { + event: normalizeValue<'span'>(fields[PROCESSOR_EVENT]), + }, + transaction: { + result: normalizeValue(fields[TRANSACTION_RESULT]), + id: normalizeValue(fields[TRANSACTION_ID]), + duration: { + us: normalizeValue(fields[TRANSACTION_DURATION]), + }, + type: normalizeValue(fields[TRANSACTION_TYPE]), + name: normalizeValue(fields[TRANSACTION_NAME]), + }, + faas: { + coldstart: normalizeValue(fields[FAAS_COLDSTART]), + }, + span: { + id: normalizeValue(fields[SPAN_ID]), + name: normalizeValue(fields[SPAN_NAME]), + type: normalizeValue(fields[SPAN_TYPE]), + subtype: normalizeValue(fields[SPAN_SUBTYPE]), + duration: { + us: normalizeValue(fields[SPAN_DURATION]), + }, + action: normalizeValue(fields[SPAN_ACTION]), + links: linkedParentsOfSpanMapping(fields) as SpanLink[], + composite: { + count: normalizeValue(fields[SPAN_COMPOSITE_COUNT]), + sum: { + us: normalizeValue(fields[SPAN_COMPOSITE_SUM]), + }, + compression_strategy: normalizeValue(fields[SPAN_COMPOSITE_COMPRESSION_STRATEGY]), + }, + sync: normalizeValue(fields[SPAN_SYNC]), + }, + child: { + id: normalizeValue(fields[CHILD_ID]), + }, + }; +}; + +export const errorDocsMapping = ( + fields: Partial> +): WaterfallError | undefined => { + if (!fields) return undefined; + + return { + timestamp: { + us: normalizeValue(fields[TIMESTAMP]), + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + }, + parent: { + id: normalizeValue(fields[PARENT_ID]), + }, + service: { + name: normalizeValue(fields[SERVICE_NAME]), + }, + error: { + id: normalizeValue(fields[ERROR_ID]), + log: { + message: normalizeValue(fields[ERROR_LOG_MESSAGE]), + }, + exception: normalizeValue(fields[ERROR_EXCEPTION]), + grouping_key: normalizeValue(fields[ERROR_GROUP_ID]), + }, + }; +}; + +export const errorGroupMainStatisticsMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + error: { + id: normalizeValue(fields[ERROR_ID]), + log: { + message: normalizeValue(fields[ERROR_LOG_MESSAGE]), + }, + exception: [ + { + message: normalizeValue(fields[ERROR_EXC_MESSAGE]), + handled: normalizeValue(fields[ERROR_EXC_HANDLED]), + type: normalizeValue(fields[ERROR_EXC_TYPE]), + }, + ], + culprit: normalizeValue(fields[ERROR_CULPRIT]), + grouping_key: normalizeValue(fields[ERROR_GROUP_ID]), + }, + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + }; +}; + +export const errorSampleDetailsMapping = ( + fields: Partial> +): APMError | undefined => { + if (!fields) return undefined; + + return { + observer: { + type: normalizeValue(fields[OBSERVER_TYPE]), + version: normalizeValue(fields[OBSERVER_VERSION]), + version_major: normalizeValue(fields[OBSERVER_VERSION_MAJOR]), + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: '', + }, + trace: { + id: normalizeValue(fields[TRACE_ID]), + }, + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + service: { + node: { + name: normalizeValue(fields[SERVICE_NODE_NAME]), + }, + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + }, + host: { + name: normalizeValue(fields[HOST_NAME]), + }, + error: { + id: normalizeValue(fields[ERROR_ID]), + exception: [ + { + message: normalizeValue(fields[ERROR_EXC_MESSAGE]), + type: normalizeValue(fields[ERROR_EXC_TYPE]), + }, + ], + grouping_key: normalizeValue(fields[ERROR_GROUP_ID]), + }, + processor: { + name: normalizeValue<'error'>(fields[PROCESSOR_NAME]), + event: normalizeValue<'error'>(fields[PROCESSOR_EVENT]), + }, + transaction: { + id: normalizeValue(fields[TRANSACTION_ID]), + type: normalizeValue(fields[TRANSACTION_TYPE]), + sampled: normalizeValue(fields[TRANSACTION_SAMPLED]), + }, + timestamp: { + us: normalizeValue(fields[TIMESTAMP]), + }, + }; +}; + +export const serviceMetadataDetailsMapping = ( + fields: Partial> +): ServiceMetadataDetailsRaw | undefined => { + if (!fields) return undefined; + const kubernetesNamespace = normalizeValue(fields[KUBERNETES_NAMESPACE]); + const containerId = normalizeValue(fields[CONTAINER_ID]); + const cloudServiceName = normalizeValue(fields[CLOUD_PROVIDER]); + const serviceRuntimeName = normalizeValue(fields[SERVICE_RUNTIME_NAME]); + return { + service: { + name: normalizeValue(fields[SERVICE_NAME]), + version: normalizeValue(fields[SERVICE_VERSION]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + framework: { + name: normalizeValue(fields[SERVICE_FRAMEWORK_NAME]), + version: normalizeValue(fields[SERVICE_FRAMEWORK_VERSION]), + }, + node: { + name: normalizeValue(fields[SERVICE_NODE_NAME]), + }, + ...(serviceRuntimeName + ? { + runtime: { + name: serviceRuntimeName, + version: normalizeValue(fields[SERVICE_RUNTIME_VERSION]), + }, + } + : undefined), + language: { + name: normalizeValue(fields[SERVICE_LANGUAGE_NAME]), + version: normalizeValue(fields[SERVICE_LANGUAGE_VERSION]), + }, + }, + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: normalizeValue(fields[AGENT_VERSION]), + }, + host: { + architecture: normalizeValue(fields[HOST_ARCHITECTURE]), + hostname: normalizeValue(fields[HOST_HOSTNAME]), + name: normalizeValue(fields[HOST_NAME]), + ip: normalizeValue(fields[HOST_IP]), + os: { + platform: normalizeValue(fields[HOST_OS_PLATFORM]), + }, + }, + ...(containerId + ? { + container: { + id: containerId, + image: normalizeValue(fields[CONTAINER_IMAGE]), + }, + } + : undefined), + ...(kubernetesNamespace + ? { + kubernetes: { + pod: { + name: normalizeValue(fields[KUBERNETES_POD_NAME]), + uid: normalizeValue(fields[KUBERNETES_POD_UID]), + }, + namespace: kubernetesNamespace, + replicaset: { + name: normalizeValue(fields[KUBERNETES_REPLICASET_NAME]), + }, + deployment: { + name: normalizeValue(fields[KUBERNETES_DEPLOYMENT_NAME]), + }, + container: { + id: normalizeValue(fields[KUBERNETES_CONTAINER_ID]), + name: normalizeValue(fields[KUBERNETES_CONTAINER_NAME]), + }, + }, + } + : undefined), + ...(cloudServiceName + ? { + cloud: { + availability_zone: normalizeValue(fields[CLOUD_AVAILABILITY_ZONE]), + instance: { + name: normalizeValue(fields[CLOUD_INSTANCE_NAME]), + id: normalizeValue(fields[CLOUD_INSTANCE_ID]), + }, + machine: { + type: normalizeValue(fields[CLOUD_MACHINE_TYPE]), + }, + project: { + id: normalizeValue(fields[CLOUD_PROJECT_ID]), + name: normalizeValue(fields[CLOUD_PROJECT_NAME]), + }, + provider: normalizeValue(fields[CLOUD_PROVIDER]), + region: normalizeValue(fields[CLOUD_REGION]), + account: { + id: normalizeValue(fields[CLOUD_ACCOUNT_ID]), + name: normalizeValue(fields[CLOUD_ACCOUNT_NAME]), + }, + image: { + id: normalizeValue(fields[CLOUD_IMAGE_ID]), + }, + service: { + name: normalizeValue(fields[CLOUD_SERVICE_NAME]), + }, + }, + } + : undefined), + }; +}; + +export const serviceMetadataIconsMapping = ( + fields: Partial> +): ServiceMetadataIconsRaw | undefined => { + if (!fields) return undefined; + const kubernetesPodName = normalizeValue(fields[KUBERNETES_POD_NAME]); + const containerId = normalizeValue(fields[CONTAINER_ID]); + const cloudProvider = normalizeValue(fields[CLOUD_PROVIDER]); + return { + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: '', + }, + ...(cloudProvider + ? { + cloud: { + availability_zone: normalizeValue(fields[CLOUD_AVAILABILITY_ZONE]), + instance: { + name: normalizeValue(fields[CLOUD_INSTANCE_NAME]), + id: normalizeValue(fields[CLOUD_INSTANCE_ID]), + }, + machine: { + type: normalizeValue(fields[CLOUD_MACHINE_TYPE]), + }, + project: { + id: normalizeValue(fields[CLOUD_PROJECT_ID]), + name: normalizeValue(fields[CLOUD_PROJECT_NAME]), + }, + provider: cloudProvider, + region: normalizeValue(fields[CLOUD_REGION]), + account: { + id: normalizeValue(fields[CLOUD_ACCOUNT_ID]), + name: normalizeValue(fields[CLOUD_ACCOUNT_NAME]), + }, + image: { + id: normalizeValue(fields[CLOUD_IMAGE_ID]), + }, + service: { + name: normalizeValue(fields[CLOUD_SERVICE_NAME]), + }, + }, + } + : undefined), + ...(containerId + ? { + container: { + id: containerId, + image: normalizeValue(fields[CONTAINER_IMAGE]), + }, + } + : undefined), + ...(kubernetesPodName + ? { + kubernetes: { + pod: { + name: normalizeValue(fields[KUBERNETES_POD_NAME]), + uid: normalizeValue(fields[KUBERNETES_POD_UID]), + }, + namespace: normalizeValue(fields[KUBERNETES_NAMESPACE]), + replicaset: { + name: normalizeValue(fields[KUBERNETES_REPLICASET_NAME]), + }, + deployment: { + name: normalizeValue(fields[KUBERNETES_DEPLOYMENT_NAME]), + }, + container: { + id: normalizeValue(fields[KUBERNETES_CONTAINER_ID]), + name: normalizeValue(fields[KUBERNETES_CONTAINER_NAME]), + }, + }, + } + : undefined), + }; +}; + +// todo: check https://github.com/jennypavlova/kibana/pull/6#discussion_r1771611817 +export const serviceVersionMapping = ( + fields: Partial> +): Pick | undefined => { + if (!fields) return undefined; + + return { + observer: { + version: normalizeValue(fields[OBSERVER_VERSION]), + version_major: normalizeValue(fields[OBSERVER_VERSION_MAJOR]), + }, + }; +}; + +// todo: missing `could` mapping +export const serviceInstanceMetadataDetailsMapping = ( + fields: Partial> = {} +) => { + if (!fields) return undefined; + + return { + '@timestamp': normalizeValue(fields[AT_TIMESTAMP]), + agent: { + name: normalizeValue(fields[AGENT_NAME]), + version: normalizeValue(fields[AGENT_VERSION]), + activation_method: normalizeValue(fields[AGENT_ACTIVATION_METHOD]), + }, + host: { + architecture: normalizeValue(fields[HOST_ARCHITECTURE]), + hostname: normalizeValue(fields[HOST_HOSTNAME]), + name: normalizeValue(fields[HOST_NAME]), + ip: normalizeValue(fields[HOST_IP]), + os: { + platform: normalizeValue(fields[HOST_OS_PLATFORM]), + }, + }, + service: { + name: normalizeValue(fields[SERVICE_NAME]), + environment: normalizeValue(fields[SERVICE_ENVIRONMENT]), + framework: { + name: normalizeValue(fields[SERVICE_FRAMEWORK_NAME]), + versions: normalizeValue(fields[SERVICE_FRAMEWORK_VERSION]), + }, + node: { + name: normalizeValue(fields[SERVICE_NODE_NAME]), + }, + runtime: { + name: normalizeValue(fields[SERVICE_RUNTIME_NAME]), + version: normalizeValue(fields[SERVICE_RUNTIME_VERSION]), + }, + language: { + name: normalizeValue(fields[SERVICE_LANGUAGE_NAME]), + version: normalizeValue(fields[SERVICE_LANGUAGE_VERSION]), + }, + version: normalizeValue(fields[SERVICE_VERSION]), + }, + kubernetes: { + namespace: normalizeValue(fields[KUBERNETES_NAMESPACE]), + node: { + name: normalizeValue(fields[KUBERNETES_NODE_NAME]), + }, + pod: { + name: normalizeValue(fields[KUBERNETES_POD_NAME]), + uid: normalizeValue(fields[KUBERNETES_POD_UID]), + }, + }, + container: { + id: normalizeValue(fields[CONTAINER_ID]), + }, + }; +}; + +// todo: missing `cloud` and `service` properties +export const serviceAgentNameMapping = (fields: Partial>) => { + if (!fields) return undefined; + + return { + agent: { + name: normalizeValue(fields[AGENT_NAME]), + }, + }; +};