diff --git a/plugins/node/opentelemetry-koa-instrumentation/src/koa.ts b/plugins/node/opentelemetry-koa-instrumentation/src/koa.ts index a90c3dfa84..a83dc12237 100644 --- a/plugins/node/opentelemetry-koa-instrumentation/src/koa.ts +++ b/plugins/node/opentelemetry-koa-instrumentation/src/koa.ts @@ -23,6 +23,9 @@ import { KoaContext, KoaComponentName, kLayerPatched, + KoaLayerType, + AttributeNames, + KoaPluginSpan, } from './types'; import { VERSION } from './version'; import { getMiddlewareMetadata } from './utils'; @@ -41,7 +44,7 @@ export class KoaInstrumentation extends BasePlugin { */ protected patch(): typeof koa { this._logger.debug('Patching Koa'); - if (this._moduleExports == null) { + if (this._moduleExports === null) { return this._moduleExports; } this._logger.debug('Patching Koa.use'); @@ -118,7 +121,8 @@ export class KoaInstrumentation extends BasePlugin { middlewareLayer[kLayerPatched] = true; this._logger.debug('patching Koa middleware layer'); return async (context: KoaContext, next: koa.Next) => { - if (api.getSpan(api.context.active()) === undefined) { + const parent = api.getSpan(api.context.active()) as KoaPluginSpan; + if (parent === undefined) { return middlewareLayer(context, next); } const metadata = getMiddlewareMetadata( @@ -131,6 +135,26 @@ export class KoaInstrumentation extends BasePlugin { attributes: metadata.attributes, }); + if (!parent?.parentSpanId) { + context.request.ctx.parentSpan = parent; + } + + if ( + metadata.attributes[AttributeNames.KOA_TYPE] === KoaLayerType.ROUTER + ) { + if (context.request.ctx.parentSpan.name) { + const parentRoute = context.request.ctx.parentSpan.name.split(' ')[1]; + if ( + context._matchedRoute && + !context._matchedRoute.toString().includes(parentRoute) + ) { + context.request.ctx.parentSpan.updateName( + `${context.method} ${context._matchedRoute}` + ); + } + } + } + return api.context.with( api.setSpan(api.context.active(), span), async () => { diff --git a/plugins/node/opentelemetry-koa-instrumentation/src/types.ts b/plugins/node/opentelemetry-koa-instrumentation/src/types.ts index 4e3db9307c..9e9aaad17b 100644 --- a/plugins/node/opentelemetry-koa-instrumentation/src/types.ts +++ b/plugins/node/opentelemetry-koa-instrumentation/src/types.ts @@ -16,6 +16,7 @@ import type { Middleware, ParameterizedContext, DefaultState } from 'koa'; import type { RouterParamContext } from '@koa/router'; import type * as Router from '@koa/router'; +import type { Span } from '@opentelemetry/api'; /** * This symbol is used to mark a Koa layer as being already instrumented @@ -41,3 +42,10 @@ export enum KoaLayerType { } export const KoaComponentName = 'koa'; + +/** + * extends opentelemetry/api Span object to instrument the root span name of http plugin by getting parent span id + */ +export interface KoaPluginSpan extends Span { + parentSpanId?: string; +} diff --git a/plugins/node/opentelemetry-koa-instrumentation/test/koa-router.test.ts b/plugins/node/opentelemetry-koa-instrumentation/test/koa-router.test.ts index 3fa43aeef3..315cd66b07 100644 --- a/plugins/node/opentelemetry-koa-instrumentation/test/koa-router.test.ts +++ b/plugins/node/opentelemetry-koa-instrumentation/test/koa-router.test.ts @@ -118,7 +118,7 @@ describe('Koa Instrumentation - Router Tests', () => { const exportedRootSpan = memoryExporter .getFinishedSpans() - .find(span => span.name === 'rootSpan'); + .find(span => span.name === 'GET /post/:id'); assert.notStrictEqual(exportedRootSpan, undefined); }); }); @@ -160,7 +160,7 @@ describe('Koa Instrumentation - Router Tests', () => { const exportedRootSpan = memoryExporter .getFinishedSpans() - .find(span => span.name === 'rootSpan'); + .find(span => span.name === 'GET /:first/post/:id'); assert.notStrictEqual(exportedRootSpan, undefined); }); }); @@ -200,7 +200,7 @@ describe('Koa Instrumentation - Router Tests', () => { const exportedRootSpan = memoryExporter .getFinishedSpans() - .find(span => span.name === 'rootSpan'); + .find(span => span.name === 'GET /:first/post/:id'); assert.notStrictEqual(exportedRootSpan, undefined); }); });