From e3139386d87dec70bc5f3f689ffe2271dba58942 Mon Sep 17 00:00:00 2001 From: Matthew Turner Date: Sun, 12 Nov 2023 20:13:15 +1100 Subject: [PATCH] fix(instrumentation-koa): handle koa routes being of type RegExp (#1754) * chore: replace deprecated SpanAttributes type with Attributes * fix: handle layer path being a string or regex * chore: add test for RegExp koa route * fix: single quote string due to linting --------- Co-authored-by: Amir Blum --- .../package.json | 2 +- .../src/instrumentation.ts | 2 +- .../src/utils.ts | 10 ++-- .../test/koa.test.ts | 57 ++++++++++++++++++- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/plugins/node/opentelemetry-instrumentation-koa/package.json b/plugins/node/opentelemetry-instrumentation-koa/package.json index 6a1b9653ed..ac7e0e77ca 100644 --- a/plugins/node/opentelemetry-instrumentation-koa/package.json +++ b/plugins/node/opentelemetry-instrumentation-koa/package.json @@ -71,7 +71,7 @@ "@opentelemetry/instrumentation": "^0.45.1", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/koa": "2.13.9", - "@types/koa__router": "12.0.1" + "@types/koa__router": "12.0.3" }, "homepage": "https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-koa#readme" } diff --git a/plugins/node/opentelemetry-instrumentation-koa/src/instrumentation.ts b/plugins/node/opentelemetry-instrumentation-koa/src/instrumentation.ts index 9e38ea2618..843639241e 100644 --- a/plugins/node/opentelemetry-instrumentation-koa/src/instrumentation.ts +++ b/plugins/node/opentelemetry-instrumentation-koa/src/instrumentation.ts @@ -134,7 +134,7 @@ export class KoaInstrumentation extends InstrumentationBase { private _patchLayer( middlewareLayer: KoaPatchedMiddleware, isRouter: boolean, - layerPath?: string + layerPath?: string | RegExp ): KoaMiddleware { const layerType = isRouter ? KoaLayerType.ROUTER : KoaLayerType.MIDDLEWARE; // Skip patching layer if its ignored in the config diff --git a/plugins/node/opentelemetry-instrumentation-koa/src/utils.ts b/plugins/node/opentelemetry-instrumentation-koa/src/utils.ts index 6de3e1f3e2..a50cad0b15 100644 --- a/plugins/node/opentelemetry-instrumentation-koa/src/utils.ts +++ b/plugins/node/opentelemetry-instrumentation-koa/src/utils.ts @@ -16,24 +16,24 @@ import { KoaContext, KoaLayerType, KoaInstrumentationConfig } from './types'; import { KoaMiddleware } from './internal-types'; import { AttributeNames } from './enums/AttributeNames'; -import { SpanAttributes } from '@opentelemetry/api'; +import { Attributes } from '@opentelemetry/api'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; export const getMiddlewareMetadata = ( context: KoaContext, layer: KoaMiddleware, isRouter: boolean, - layerPath?: string + layerPath?: string | RegExp ): { - attributes: SpanAttributes; + attributes: Attributes; name: string; } => { if (isRouter) { return { attributes: { - [AttributeNames.KOA_NAME]: layerPath, + [AttributeNames.KOA_NAME]: layerPath?.toString(), [AttributeNames.KOA_TYPE]: KoaLayerType.ROUTER, - [SemanticAttributes.HTTP_ROUTE]: layerPath, + [SemanticAttributes.HTTP_ROUTE]: layerPath?.toString(), }, name: context._matchedRouteName || `router - ${layerPath}`, }; diff --git a/plugins/node/opentelemetry-instrumentation-koa/test/koa.test.ts b/plugins/node/opentelemetry-instrumentation-koa/test/koa.test.ts index 4d11b7d069..eed6667372 100644 --- a/plugins/node/opentelemetry-instrumentation-koa/test/koa.test.ts +++ b/plugins/node/opentelemetry-instrumentation-koa/test/koa.test.ts @@ -132,7 +132,7 @@ describe('Koa Instrumentation', () => { }; describe('Instrumenting @koa/router calls', () => { - it('should create a child span for middlewares', async () => { + it('should create a child span for middlewares (string route)', async () => { const rootSpan = tracer.startSpan('rootSpan'); const rpcMetadata: RPCMetadata = { type: RPCType.HTTP, span: rootSpan }; app.use((ctx, next) => @@ -168,6 +168,10 @@ describe('Koa Instrumentation', () => { requestHandlerSpan?.attributes[AttributeNames.KOA_TYPE], KoaLayerType.ROUTER ); + assert.strictEqual( + requestHandlerSpan?.attributes[AttributeNames.KOA_NAME], + '/post/:id' + ); assert.strictEqual( requestHandlerSpan?.attributes[SemanticAttributes.HTTP_ROUTE], @@ -179,6 +183,57 @@ describe('Koa Instrumentation', () => { ); }); + it('should create a child span for middlewares (RegExp route)', async () => { + const rootSpan = tracer.startSpan('rootSpan'); + const rpcMetadata: RPCMetadata = { type: RPCType.HTTP, span: rootSpan }; + app.use((ctx, next) => + context.with( + setRPCMetadata( + trace.setSpan(context.active(), rootSpan), + rpcMetadata + ), + next + ) + ); + + const router = new KoaRouter(); + router.get(/^\/post/, ctx => { + ctx.body = 'Post'; + }); + + app.use(router.routes()); + + await context.with( + trace.setSpan(context.active(), rootSpan), + async () => { + await httpRequest.get(`http://localhost:${port}/post/0`); + rootSpan.end(); + + assert.deepStrictEqual(memoryExporter.getFinishedSpans().length, 2); + const requestHandlerSpan = memoryExporter + .getFinishedSpans() + .find(span => span.name.includes('router - /^\\/post/')); + assert.notStrictEqual(requestHandlerSpan, undefined); + + assert.strictEqual( + requestHandlerSpan?.attributes[AttributeNames.KOA_TYPE], + KoaLayerType.ROUTER + ); + assert.strictEqual( + requestHandlerSpan?.attributes[AttributeNames.KOA_NAME], + '/^\\/post/' + ); + + assert.strictEqual( + requestHandlerSpan?.attributes[SemanticAttributes.HTTP_ROUTE], + '/^\\/post/' + ); + + assert.strictEqual(rpcMetadata.route, '/^\\/post/'); + } + ); + }); + it('should create a named child span for middlewares', async () => { const rootSpan = tracer.startSpan('rootSpan'); const rpcMetadata: RPCMetadata = { type: RPCType.HTTP, span: rootSpan };