diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a13ad70f..6e98fd24db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ### :rocket: (Enhancement) * feat(api): add `getActiveBaggage` API [#3385](https://github.com/open-telemetry/opentelemetry-js/pull/3385) +* feat(instrumentation-grpc): set net.peer.name and net.peer.port on client spans [#3430](https://github.com/open-telemetry/opentelemetry-js/pull/3430) ### :bug: (Bug Fix) diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts index 9473a17994..9147f455a0 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc-js/index.ts @@ -49,7 +49,7 @@ import { getMetadata, } from './clientUtils'; import { EventEmitter } from 'events'; -import { _extractMethodAndService, metadataCapture } from '../utils'; +import { _extractMethodAndService, metadataCapture, URI_REGEX } from '../utils'; import { AttributeValues } from '../enums/AttributeValues'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; @@ -309,6 +309,18 @@ export class GrpcJsInstrumentation extends InstrumentationBase { [SemanticAttributes.RPC_METHOD]: method, [SemanticAttributes.RPC_SERVICE]: service, }); + // set net.peer.* from target (e.g., "dns:otel-productcatalogservice:8080") as a hint to APMs + const parsedUri = URI_REGEX.exec(this.getChannel().getTarget()); + if (parsedUri != null && parsedUri.groups != null) { + span.setAttribute( + SemanticAttributes.NET_PEER_NAME, + parsedUri.groups['name'] + ); + span.setAttribute( + SemanticAttributes.NET_PEER_PORT, + parseInt(parsedUri.groups['port']) + ); + } instrumentation._metadataCapture.client.captureRequestMetadata( span, diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index dee3c9c3e3..436e04ada4 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -45,6 +45,7 @@ import { _extractMethodAndService, _methodIsIgnored, metadataCapture, + URI_REGEX, } from '../utils'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { AttributeValues } from '../enums/AttributeValues'; @@ -321,6 +322,18 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< [SemanticAttributes.RPC_METHOD]: method, [SemanticAttributes.RPC_SERVICE]: service, }); + // set net.peer.* from target (e.g., "dns:otel-productcatalogservice:8080") as a hint to APMs + const parsedUri = URI_REGEX.exec(this.getChannel().getTarget()); + if (parsedUri != null && parsedUri.groups != null) { + span.setAttribute( + SemanticAttributes.NET_PEER_NAME, + parsedUri.groups['name'] + ); + span.setAttribute( + SemanticAttributes.NET_PEER_PORT, + parseInt(parsedUri.groups['port']) + ); + } instrumentation._metadataCapture.client.captureRequestMetadata( span, diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts index 473cc920a1..8d031107dd 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts @@ -19,6 +19,10 @@ import type * as grpcTypes from 'grpc'; import type * as grpcJsTypes from '@grpc/grpc-js'; import { IgnoreMatcher } from './types'; +// e.g., "dns:otel-productcatalogservice:8080" or "otel-productcatalogservice:8080" or "127.0.0.1:8080" +export const URI_REGEX = + /(?:([A-Za-z0-9+.-]+):(?:\/\/)?)?(?[A-Za-z0-9+.-]+):(?[0-9+.-]+)$/; + // Equivalent to lodash _.findIndex export const findIndex: (args: T[], fn: (arg: T) => boolean) => number = ( args, diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts index 5b16525a5d..85b6a1c64e 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts @@ -515,6 +515,8 @@ export const runTests = ( const validations = { name: `grpc.pkg_test.GrpcTester/${methodName}`, status: grpc.status.OK, + netPeerName: 'localhost', + netPeerPort: grpcPort, }; assertSpan(moduleName, serverSpan, SpanKind.SERVER, validations); @@ -699,6 +701,8 @@ export const runTests = ( const validations = { name: `grpc.pkg_test.GrpcTester/${method.methodName}`, status: errorCode, + netPeerName: 'localhost', + netPeerPort: grpcPort, }; const serverRoot = spans[0]; const clientRoot = spans[1]; @@ -738,6 +742,8 @@ export const runTests = ( const validations = { name: `grpc.pkg_test.GrpcTester/${method.methodName}`, status: errorCode, + netPeerName: 'localhost', + netPeerPort: grpcPort, }; assertSpan(moduleName, serverSpan, SpanKind.SERVER, validations); assertSpan(moduleName, clientSpan, SpanKind.CLIENT, validations); diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts b/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts index 64ed00df76..24d4bf7af8 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts @@ -38,7 +38,12 @@ export const assertSpan = ( component: string, span: ReadableSpan, kind: SpanKind, - validations: { name: string; status: grpc.status | grpcJs.status } + validations: { + name: string; + status: grpc.status | grpcJs.status; + netPeerName?: string; + netPeerPort?: number; + } ) => { assert.strictEqual(span.spanContext().traceId.length, 32); assert.strictEqual(span.spanContext().spanId.length, 16); @@ -56,6 +61,21 @@ export const assertSpan = ( assert.ok(span.spanContext()); } + if ( + span.kind === SpanKind.CLIENT && + validations.netPeerName !== undefined && + validations.netPeerPort !== undefined + ) { + assert.strictEqual( + span.attributes[SemanticAttributes.NET_PEER_NAME], + validations.netPeerName + ); + assert.strictEqual( + span.attributes[SemanticAttributes.NET_PEER_PORT], + validations.netPeerPort + ); + } + // validations assert.strictEqual(span.name, validations.name); assert.strictEqual(