From 067caa90b9df6131a828c71be9d77494669c8d16 Mon Sep 17 00:00:00 2001 From: Ty Bekiares Date: Mon, 21 Nov 2022 06:28:19 -0600 Subject: [PATCH] feat(instrumentation-grpc): set net.peer.name,port on client spans --- CHANGELOG.md | 1 + .../src/grpc-js/index.ts | 8 +++++++- .../opentelemetry-instrumentation-grpc/src/grpc/index.ts | 8 +++++++- .../opentelemetry-instrumentation-grpc/src/utils.ts | 3 +++ .../opentelemetry-instrumentation-grpc/test/helper.ts | 8 ++++++++ .../test/utils/assertionUtils.ts | 8 +++++++- 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab76cfe6178..8b8648cbf10 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 peer.service 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 4a1c8e9f3aa..03345458af9 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 } from '../utils'; +import { _extractMethodAndService, URI_REGEX } from '../utils'; import { AttributeValues } from '../enums/AttributeValues'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; @@ -298,6 +298,12 @@ export class GrpcJsInstrumentation extends InstrumentationBase { [SemanticAttributes.RPC_METHOD]: method, [SemanticAttributes.RPC_SERVICE]: service, }); + // set peer.service 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'])); + } return context.with(trace.setSpan(context.active(), span), () => makeGrpcClientRemoteCall(original, args, metadata, this)(span) ); diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts index b31d9ef55c3..4c5718eacb1 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/grpc/index.ts @@ -41,7 +41,7 @@ import { serverStreamAndBidiHandler, } from './serverUtils'; import { makeGrpcClientRemoteCall, getMetadata } from './clientUtils'; -import { _extractMethodAndService, _methodIsIgnored } from '../utils'; +import { _extractMethodAndService, _methodIsIgnored, URI_REGEX } from '../utils'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import {AttributeValues} from '../enums/AttributeValues'; @@ -306,6 +306,12 @@ export class GrpcNativeInstrumentation extends InstrumentationBase< [SemanticAttributes.RPC_METHOD]: method, [SemanticAttributes.RPC_SERVICE]: service, }); + // set peer.service 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'])); + } return context.with(trace.setSpan(context.active(), span), () => makeGrpcClientRemoteCall( grpcClient, diff --git a/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts b/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts index 16bf81a7286..fea901deccd 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/src/utils.ts @@ -19,6 +19,9 @@ 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 "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 5564c7c964f..271ac75a39d 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/helper.ts @@ -481,6 +481,8 @@ export const runTests = ( const validations = { name: `grpc.pkg_test.GrpcTester/${method.methodName}`, status: grpc.status.OK, + netPeerName: 'localhost', + netPeerPort: grpcPort }; assert.strictEqual(spans.length, 2); @@ -530,6 +532,8 @@ export const runTests = ( const validations = { name: `grpc.pkg_test.GrpcTester/${method.methodName}`, status: grpc.status.OK, + netPeerName: 'localhost', + netPeerPort: grpcPort }; assertSpan( moduleName, @@ -590,6 +594,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]; @@ -629,6 +635,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 e399a4a5c52..19eadd7f032 100644 --- a/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts +++ b/experimental/packages/opentelemetry-instrumentation-grpc/test/utils/assertionUtils.ts @@ -23,6 +23,7 @@ import { hrTimeToMilliseconds, hrTimeToMicroseconds, } from '@opentelemetry/core'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const grpcStatusCodeToOpenTelemetryStatusCode = ( status: grpc.status | grpcJs.status @@ -37,7 +38,7 @@ 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); @@ -55,6 +56,11 @@ 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(