Skip to content

Commit

Permalink
[8.x] [APM][OTel] Use `telemetry.sdk` as a fallback for mis…
Browse files Browse the repository at this point in the history
…sing `agent.name` on non-tracing data (elastic#196529) (elastic#196854)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[APM][OTel] Use `telemetry.sdk` as a fallback for missing
`agent.name` on non-tracing data
(elastic#196529)](elastic#196529)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Sergi
Romeu","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-18T11:30:19Z","message":"[APM][OTel]
Use `telemetry.sdk` as a fallback for missing `agent.name` on
non-tracing data (elastic#196529)\n\n## Summary\r\n\r\nRelated to
elastic#195854\r\n\r\nThis PR adds a fallback when we are missing `agent.name`
on APM Service\r\nInventory list.\r\nUsing [OTel semantic
convention\r\nfields](https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk)\r\n`telemetry.sdk.language`
and `telemetry.sdk.name` to maintain\r\n`agent.name` format for OTel
fields like `otlp/${agent}`\r\n`opentelemetry/${agent}`.\r\n\r\n##
Screenshots\r\n| Before | After
|\r\n\r\n|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|\r\n|\r\n![image](https://github.com/user-attachments/assets/792ca256-f01d-4eae-8a2d-af16fa34ea61)\r\n|\r\n![image](https://github.com/user-attachments/assets/2816d1c7-1207-4da8-adb5-ec417b3fd26e)\r\n|\r\n\r\n![image](https://github.com/user-attachments/assets/27df0ffc-8d5f-475c-ad6b-04086521871b)|![image](https://github.com/user-attachments/assets/d088a746-1375-4918-8e55-d3968a80772d)\r\n\r\n\r\n##
How to test it\r\n1. Open otel-apm-e2e-poc repo\r\n2. Add the following
code under `processors -> transform ->\r\nmetric_statements` in
`otelcol.yaml`\r\n```yaml\r\n - context: datapoint\r\n statements:\r\n -
set(attributes[\"processor.event\"], \"metric\")\r\n```\r\n3. Run `make
start-stack` & `make build` & `make run` on otel poc repo \r\n4. Don't
forget to run also `make start` on opentelemetry-demo and\r\n[follow
this\r\nguide](https://github.com/elastic/otel-apm-e2e-poc?tab=readme-ov-file#working-with-opentelemetry-demo)\r\nto
make it work.\r\n5. In Kibana add `elasticsearch.ignoreVersionMismatch:
true` to\r\n`kibana.dev.yml`.\r\n6. If you go to APM Service Inventory
list you will see missing icons.\r\n7. Checkout to my branch\r\n8. Try
again, they should be
fixed","sha":"16098c454751939d9e3587ff458bb652b28e3d79","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","apm:opentelemetry","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services"],"title":"[APM][OTel]
Use `telemetry.sdk` as a fallback for missing `agent.name` on
non-tracing
data","number":196529,"url":"https://github.com/elastic/kibana/pull/196529","mergeCommit":{"message":"[APM][OTel]
Use `telemetry.sdk` as a fallback for missing `agent.name` on
non-tracing data (elastic#196529)\n\n## Summary\r\n\r\nRelated to
elastic#195854\r\n\r\nThis PR adds a fallback when we are missing `agent.name`
on APM Service\r\nInventory list.\r\nUsing [OTel semantic
convention\r\nfields](https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk)\r\n`telemetry.sdk.language`
and `telemetry.sdk.name` to maintain\r\n`agent.name` format for OTel
fields like `otlp/${agent}`\r\n`opentelemetry/${agent}`.\r\n\r\n##
Screenshots\r\n| Before | After
|\r\n\r\n|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|\r\n|\r\n![image](https://github.com/user-attachments/assets/792ca256-f01d-4eae-8a2d-af16fa34ea61)\r\n|\r\n![image](https://github.com/user-attachments/assets/2816d1c7-1207-4da8-adb5-ec417b3fd26e)\r\n|\r\n\r\n![image](https://github.com/user-attachments/assets/27df0ffc-8d5f-475c-ad6b-04086521871b)|![image](https://github.com/user-attachments/assets/d088a746-1375-4918-8e55-d3968a80772d)\r\n\r\n\r\n##
How to test it\r\n1. Open otel-apm-e2e-poc repo\r\n2. Add the following
code under `processors -> transform ->\r\nmetric_statements` in
`otelcol.yaml`\r\n```yaml\r\n - context: datapoint\r\n statements:\r\n -
set(attributes[\"processor.event\"], \"metric\")\r\n```\r\n3. Run `make
start-stack` & `make build` & `make run` on otel poc repo \r\n4. Don't
forget to run also `make start` on opentelemetry-demo and\r\n[follow
this\r\nguide](https://github.com/elastic/otel-apm-e2e-poc?tab=readme-ov-file#working-with-opentelemetry-demo)\r\nto
make it work.\r\n5. In Kibana add `elasticsearch.ignoreVersionMismatch:
true` to\r\n`kibana.dev.yml`.\r\n6. If you go to APM Service Inventory
list you will see missing icons.\r\n7. Checkout to my branch\r\n8. Try
again, they should be
fixed","sha":"16098c454751939d9e3587ff458bb652b28e3d79"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196529","number":196529,"mergeCommit":{"message":"[APM][OTel]
Use `telemetry.sdk` as a fallback for missing `agent.name` on
non-tracing data (elastic#196529)\n\n## Summary\r\n\r\nRelated to
elastic#195854\r\n\r\nThis PR adds a fallback when we are missing `agent.name`
on APM Service\r\nInventory list.\r\nUsing [OTel semantic
convention\r\nfields](https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk)\r\n`telemetry.sdk.language`
and `telemetry.sdk.name` to maintain\r\n`agent.name` format for OTel
fields like `otlp/${agent}`\r\n`opentelemetry/${agent}`.\r\n\r\n##
Screenshots\r\n| Before | After
|\r\n\r\n|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|\r\n|\r\n![image](https://github.com/user-attachments/assets/792ca256-f01d-4eae-8a2d-af16fa34ea61)\r\n|\r\n![image](https://github.com/user-attachments/assets/2816d1c7-1207-4da8-adb5-ec417b3fd26e)\r\n|\r\n\r\n![image](https://github.com/user-attachments/assets/27df0ffc-8d5f-475c-ad6b-04086521871b)|![image](https://github.com/user-attachments/assets/d088a746-1375-4918-8e55-d3968a80772d)\r\n\r\n\r\n##
How to test it\r\n1. Open otel-apm-e2e-poc repo\r\n2. Add the following
code under `processors -> transform ->\r\nmetric_statements` in
`otelcol.yaml`\r\n```yaml\r\n - context: datapoint\r\n statements:\r\n -
set(attributes[\"processor.event\"], \"metric\")\r\n```\r\n3. Run `make
start-stack` & `make build` & `make run` on otel poc repo \r\n4. Don't
forget to run also `make start` on opentelemetry-demo and\r\n[follow
this\r\nguide](https://github.com/elastic/otel-apm-e2e-poc?tab=readme-ov-file#working-with-opentelemetry-demo)\r\nto
make it work.\r\n5. In Kibana add `elasticsearch.ignoreVersionMismatch:
true` to\r\n`kibana.dev.yml`.\r\n6. If you go to APM Service Inventory
list you will see missing icons.\r\n7. Checkout to my branch\r\n8. Try
again, they should be
fixed","sha":"16098c454751939d9e3587ff458bb652b28e3d79"}}]}] BACKPORT-->

Co-authored-by: Sergi Romeu <[email protected]>
  • Loading branch information
kibanamachine and rmyz authored Oct 21, 2024
1 parent db599d1 commit 4c5a369
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 8 deletions.
4 changes: 4 additions & 0 deletions packages/kbn-apm-types/src/es_fields/apm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ export const METRIC_OTEL_JVM_SYSTEM_CPU_PERCENT = 'process.runtime.jvm.system.cp
export const METRIC_OTEL_JVM_GC_DURATION = 'process.runtime.jvm.gc.duration';
export const VALUE_OTEL_JVM_PROCESS_MEMORY_HEAP = 'heap';
export const VALUE_OTEL_JVM_PROCESS_MEMORY_NON_HEAP = 'non_heap';
// OpenTelemetry semconv fields for AgentName https://opentelemetry.io/docs/specs/semconv/resource/#telemetry-sdk
export const TELEMETRY_SDK_NAME = 'telemetry.sdk.name';
export const TELEMETRY_SDK_LANGUAGE = 'telemetry.sdk.language';
export const TELEMETRY_SDK_VERSION = 'telemetry.sdk.version';

// Metadata
export const TIER = '_tier';
Expand Down
8 changes: 8 additions & 0 deletions packages/kbn-elastic-agent-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ A utility package providing functions for working with Elastic Agents. This pack

## Functions

- **`getAgentName`**

```typescript
export function getAgentName(agentName: string | null, telemetryAgentName: string | null, telemetrySdkName: string | null): AgentName;
```

Tries to get the correct agent name, `agentName` is the first option, if it fails checks if `telemetryAgentName` and `telemetrySdkName` are defined and return them as `telemetrySdkName/telemetryAgentName`, if not, `telemetryAgentName` is returned

- **`isOpenTelemetryAgentName`**

```typescript
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-elastic-agent-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

export {
getAgentName,
isOpenTelemetryAgentName,
hasOpenTelemetryPrefix,
isJavaAgentName,
Expand Down
13 changes: 13 additions & 0 deletions packages/kbn-elastic-agent-utils/src/agent_guards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import {
getAgentName,
hasOpenTelemetryPrefix,
isAndroidAgentName,
isAWSLambdaAgentName,
Expand Down Expand Up @@ -119,4 +120,16 @@ describe('Agents guards', () => {
expect(isAzureFunctionsAgentName('azure.functions')).toBe(true);
expect(isAzureFunctionsAgentName('not-an-agent')).toBe(false);
});

it('getAgentName returns agent name by default', () => {
expect(getAgentName('nodejs', 'go', 'otlp')).toBe('nodejs');
});

it('getAgentName returns telemetry sdk name and telemetry agent name if agent name is not defined', () => {
expect(getAgentName(null, 'go', 'otlp')).toBe('otlp/go');
});

it('getAgentName returns telemetry agent name if agent name and telemetry sdk are not defined', () => {
expect(getAgentName(null, 'go', null)).toBe('go');
});
});
16 changes: 16 additions & 0 deletions packages/kbn-elastic-agent-utils/src/agent_guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ import type {
ServerlessType,
} from './agent_names';

export function getAgentName(
agentName: string | null,
telemetryAgentName: string | null,
telemetrySdkName: string | null
) {
if (agentName) {
return agentName;
}

if (telemetrySdkName && telemetryAgentName) {
return `${telemetrySdkName}/${telemetryAgentName}`;
}

return telemetryAgentName;
}

export function hasOpenTelemetryPrefix(agentName?: string, language: string = '') {
if (!agentName) {
return false;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { rangeQuery } from '@kbn/observability-plugin/server';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { FlattenedApmEvent } from '@kbn/apm-data-access-plugin/server/utils/unflatten_known_fields';
import { getAgentName } from '@kbn/elastic-agent-utils';
import { environmentQuery } from '../../../common/utils/environment_query';
import {
CLOUD_AVAILABILITY_ZONE,
Expand All @@ -21,6 +22,11 @@ import {
SERVICE_VERSION,
FAAS_ID,
FAAS_TRIGGER_TYPE,
AGENT_NAME,
TELEMETRY_SDK_LANGUAGE,
TELEMETRY_SDK_NAME,
AGENT_VERSION,
TELEMETRY_SDK_VERSION,
} from '../../../common/es_fields/apm';
import { ContainerType } from '../../../common/service_metadata';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
Expand Down Expand Up @@ -158,7 +164,25 @@ export async function getServiceMetadataDetails({
},
};

const response = await apmEventClient.search('get_service_metadata_details', params);
const data = await apmEventClient.search('get_service_metadata_details', params);

if (data.hits.total.value === 0) {
return {
service: undefined,
container: undefined,
cloud: undefined,
};
}

const response = structuredClone(data);
response.hits.hits[0].fields[AGENT_NAME] = getAgentName(
data.hits.hits[0]?.fields?.[AGENT_NAME] as unknown as string | null,
data.hits.hits[0]?.fields?.[TELEMETRY_SDK_LANGUAGE] as unknown as string | null,
data.hits.hits[0]?.fields?.[TELEMETRY_SDK_NAME] as unknown as string | null
) as unknown as unknown[];
response.hits.hits[0].fields[AGENT_VERSION] =
response.hits.hits[0].fields[AGENT_VERSION] ??
data.hits.hits[0]?.fields?.[TELEMETRY_SDK_VERSION];

const event = unflattenKnownApmEventFields(
maybe(response.hits.hits[0])?.fields as undefined | FlattenedApmEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { rangeQuery } from '@kbn/observability-plugin/server';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import type { FlattenedApmEvent } from '@kbn/apm-data-access-plugin/server/utils/unflatten_known_fields';
import { getAgentName } from '@kbn/elastic-agent-utils';
import { maybe } from '../../../common/utils/maybe';
import { asMutableArray } from '../../../common/utils/as_mutable_array';
import {
Expand All @@ -22,6 +23,8 @@ import {
LABEL_TELEMETRY_AUTO_VERSION,
AGENT_VERSION,
SERVICE_FRAMEWORK_NAME,
TELEMETRY_SDK_NAME,
TELEMETRY_SDK_LANGUAGE,
} from '../../../common/es_fields/apm';
import { ContainerType, SERVICE_METADATA_KUBERNETES_KEYS } from '../../../common/service_metadata';
import { getProcessorEventForTransactions } from '../../lib/helpers/transactions';
Expand Down Expand Up @@ -66,6 +69,8 @@ export async function getServiceMetadataIcons({
CONTAINER_ID,
AGENT_NAME,
CLOUD_SERVICE_NAME,
TELEMETRY_SDK_NAME,
TELEMETRY_SDK_LANGUAGE,
...SERVICE_METADATA_KUBERNETES_KEYS,
] as const);

Expand All @@ -85,9 +90,9 @@ export async function getServiceMetadataIcons({
},
};

const response = await apmEventClient.search('get_service_metadata_icons', params);
const data = await apmEventClient.search('get_service_metadata_icons', params);

if (response.hits.total.value === 0) {
if (data.hits.total.value === 0) {
return {
agentName: undefined,
containerType: undefined,
Expand All @@ -96,6 +101,13 @@ export async function getServiceMetadataIcons({
};
}

const response = structuredClone(data);
response.hits.hits[0].fields[AGENT_NAME] = getAgentName(
data.hits.hits[0]?.fields?.[AGENT_NAME] as unknown as string | null,
data.hits.hits[0]?.fields?.[TELEMETRY_SDK_LANGUAGE] as unknown as string | null,
data.hits.hits[0]?.fields?.[TELEMETRY_SDK_NAME] as unknown as string | null
) as unknown as unknown[];

const event = unflattenKnownApmEventFields(
maybe(response.hits.hits[0])?.fields as undefined | FlattenedApmEvent
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
*/

import { kqlQuery, rangeQuery, wildcardQuery } from '@kbn/observability-plugin/server';
import { getAgentName } from '@kbn/elastic-agent-utils';
import { ApmDocumentType } from '../../../../common/document_type';
import {
AGENT_NAME,
SERVICE_ENVIRONMENT,
SERVICE_NAME,
TRANSACTION_TYPE,
SERVICE_OVERFLOW_COUNT,
TELEMETRY_SDK_NAME,
TELEMETRY_SDK_LANGUAGE,
} from '../../../../common/es_fields/apm';
import { RollupInterval } from '../../../../common/rollup';
import { ServiceGroup } from '../../../../common/service_groups';
Expand Down Expand Up @@ -124,6 +127,16 @@ export async function getServiceTransactionStats({
size: maxNumServices,
},
aggs: {
telemetryAgentName: {
terms: {
field: TELEMETRY_SDK_LANGUAGE,
},
},
telemetrySdkName: {
terms: {
field: TELEMETRY_SDK_NAME,
},
},
transactionType: {
terms: {
field: TRANSACTION_TYPE,
Expand Down Expand Up @@ -169,9 +182,11 @@ export async function getServiceTransactionStats({
topTransactionTypeBucket?.environments.buckets.map(
(environmentBucket) => environmentBucket.key as string
) ?? [],
agentName: topTransactionTypeBucket?.sample.top[0].metrics[AGENT_NAME] as
| AgentName
| undefined,
agentName: getAgentName(
topTransactionTypeBucket?.sample.top[0].metrics[AGENT_NAME] as string | null,
bucket.telemetryAgentName.buckets[0]?.key as string | null,
bucket.telemetrySdkName.buckets[0]?.key as string | null
) as AgentName,
latency: topTransactionTypeBucket?.avg_duration.value,
transactionErrorRate: topTransactionTypeBucket
? calculateFailedTransactionRate(topTransactionTypeBucket)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@

import { kqlQuery, rangeQuery, wildcardQuery } from '@kbn/observability-plugin/server';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { getAgentName } from '@kbn/elastic-agent-utils';
import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent';
import { AGENT_NAME, SERVICE_ENVIRONMENT, SERVICE_NAME } from '../../../../common/es_fields/apm';
import {
AGENT_NAME,
SERVICE_ENVIRONMENT,
SERVICE_NAME,
TELEMETRY_SDK_LANGUAGE,
TELEMETRY_SDK_NAME,
} from '../../../../common/es_fields/apm';
import { environmentQuery } from '../../../../common/utils/environment_query';
import { ServiceGroup } from '../../../../common/service_groups';
import { RandomSampler } from '../../../lib/helpers/get_random_sampler';
Expand Down Expand Up @@ -99,6 +106,16 @@ export async function getServicesWithoutTransactions({
field: SERVICE_ENVIRONMENT,
},
},
telemetryAgentName: {
terms: {
field: TELEMETRY_SDK_LANGUAGE,
},
},
telemetrySdkName: {
terms: {
field: TELEMETRY_SDK_NAME,
},
},
latest: {
top_metrics: {
metrics: [{ field: AGENT_NAME } as const],
Expand All @@ -122,7 +139,11 @@ export async function getServicesWithoutTransactions({
return {
serviceName: bucket.key as string,
environments: bucket.environments.buckets.map((envBucket) => envBucket.key as string),
agentName: bucket.latest.top[0].metrics[AGENT_NAME] as AgentName,
agentName: getAgentName(
bucket.latest.top[0].metrics[AGENT_NAME] as string | null,
bucket.telemetryAgentName.buckets[0]?.key as string | null,
bucket.telemetrySdkName.buckets[0]?.key as string | null
) as AgentName,
};
}) ?? [],
maxCountExceeded,
Expand Down

0 comments on commit 4c5a369

Please sign in to comment.