From 8383b14d9d04304455928dc85d57303351d44c09 Mon Sep 17 00:00:00 2001 From: Yongseok Date: Wed, 8 Nov 2023 21:55:19 +0900 Subject: [PATCH] [#150] Configuration location and filename and linenumber * express without CallSite location and filename and linenumber * koa without CallSite location and filename and linenumber * mysql without CallSite location and filename and linenumber --- lib/config.js | 10 +- lib/instrumentation/instrument-method.js | 9 +- .../module/express/patch-layer.js | 12 +- .../module/koa/koa-register-interceptor.js | 10 +- .../mysql-create-connection-interceptor.js | 2 +- lib/pinpoint-config-default.json | 3 +- test/config.test.js | 28 ++- test/context/callstack.test.js | 68 ++++++- test/instrumentation/module/express.test.js | 187 +++++++++++++++++- test/instrumentation/module/koa.test.js | 51 ++++- test/instrumentation/module/mysql.test.js | 8 +- test/instrumentation/module/mysql2.test.js | 8 +- test/support/agent-singleton-mock.js | 5 + 13 files changed, 373 insertions(+), 28 deletions(-) diff --git a/lib/config.js b/lib/config.js index 0cfdd3d1..88044516 100644 --- a/lib/config.js +++ b/lib/config.js @@ -56,6 +56,7 @@ const ENV_MAP = { container: valueOfBoolean('PINPOINT_CONTAINER'), traceExclusionUrlPatterns: valueOfString('PINPOINT_TRACE_EXCLUSION_URL_PATTERN'), traceExclusionUrlCacheSize: valueOfNumber('PINPOINT_TRACE_EXCLUSION_URL_CACHE_SIZE'), + traceLocationAndFileNameOfCallSite: valueOfBoolean('PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'), } const CONFIG_FILE_MAP = { @@ -81,7 +82,8 @@ const CONFIG_FILE_MAP = { container: 'container', traceExclusionUrlPatterns: 'trace-exclusion-url.pattern', traceExclusionUrlCacheSize: 'trace-exclusion-url.cache-size', - streamDeadlineMinutesClientSide: 'stream-deadline-minutes.client-side' + streamDeadlineMinutesClientSide: 'stream-deadline-minutes.client-side', + traceLocationAndFileNameOfCallSite: 'trace-location-and-filename-of-call-site' } let agentConfig = null @@ -206,11 +208,11 @@ const readRootConfigFile = () => { return require(fileFullPath) } -const getMainModulePath = (requireFuction) => { - if (!requireFuction || !requireFuction.main || !requireFuction.main.filename) { +const getMainModulePath = (requireFunction) => { + if (!requireFunction || !requireFunction.main || !requireFunction.main.filename) { return } - return path.dirname(requireFuction.main.filename) + return path.dirname(requireFunction.main.filename) } const getValue = (key, configFile) => { diff --git a/lib/instrumentation/instrument-method.js b/lib/instrumentation/instrument-method.js index 9a723ef0..9034e3cb 100644 --- a/lib/instrumentation/instrument-method.js +++ b/lib/instrumentation/instrument-method.js @@ -11,6 +11,7 @@ const apiMetaService = require('../context/api-meta-service') const IdGenerator = require('../context/id-generator') const { callSite } = require('./call-stack') const InstrumentMethodContext = require('./instrument-method-context') +const config = require('../config') // ref: SpanEventSimpleAroundInterceptorForPlugin.java class InstrumentMethod { @@ -37,7 +38,13 @@ class InstrumentMethod { const target = this.target shimmer.wrap(this.target, this.method, function (original) { return function () { - const builder = callSite(interceptor.methodDescriptorBuilder) + let builder + const conf = config.getConfig() + if (conf && conf.traceLocationAndFileNameOfCallSite) { + builder = callSite(interceptor.methodDescriptorBuilder) + } else { + builder = interceptor.methodDescriptorBuilder + } const context = new InstrumentMethodContext(builder, traceContext) if (typeof interceptor.prepareBeforeTrace === 'function') { diff --git a/lib/instrumentation/module/express/patch-layer.js b/lib/instrumentation/module/express/patch-layer.js index 2789bf50..121cb0af 100644 --- a/lib/instrumentation/module/express/patch-layer.js +++ b/lib/instrumentation/module/express/patch-layer.js @@ -25,7 +25,11 @@ function patchLayer(layer, context) { const spanEventRecorder = trace.traceBlockBegin() spanEventRecorder.recordServiceType(serviceType) - const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder()) + let builder = context.getMethodDescriptorBuilder() + if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && req && typeof req.method === 'string') { + builder = builder.makeCloneOf(req.method.toLowerCase()) + } + const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(builder) spanEventRecorder.recordApi(apiMethodDescriptor) // https://github.com/elastic/apm-agent-nodejs/issues/443#issuecomment-455352070 @@ -48,7 +52,11 @@ function patchLayer(layer, context) { spanEventRecorder.recordServiceType(serviceType) spanEventRecorder.recordException(err, true) - const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder()) + let builder = context.getMethodDescriptorBuilder() + if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && req && typeof req.method === 'string') { + builder = builder.makeCloneOf(req.method.toLowerCase()) + } + const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(builder) spanEventRecorder.recordApi(apiMethodDescriptor) } const result = original.apply(this, arguments) diff --git a/lib/instrumentation/module/koa/koa-register-interceptor.js b/lib/instrumentation/module/koa/koa-register-interceptor.js index 945048f6..643bee17 100644 --- a/lib/instrumentation/module/koa/koa-register-interceptor.js +++ b/lib/instrumentation/module/koa/koa-register-interceptor.js @@ -13,7 +13,7 @@ const serviceType = require('./koa-service-type') class KoaRegisterInterceptor { constructor() { this.methodDescriptorBuilder = MethodDescriptorBuilder.makeRuntimeDetectionMethodDescriptorBuilder() - .setClassName('koa') + .setClassName('Router') .setFileNameIndex(3) } @@ -38,7 +38,13 @@ class KoaRegisterInterceptor { } layer.stack[handlerIndex] = async function (ctx, next) { - const methodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder()) + const method = ctx.method + + let builder = context.getMethodDescriptorBuilder() + if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && typeof method === 'string') { + builder = builder.makeCloneOf(method.toLowerCase()) + } + const methodDescriptor = apiMetaService.cacheApiWithBuilder(builder) const trace = context.currentTraceObject() let spanEventRecorder = null let result diff --git a/lib/instrumentation/module/mysql/mysql-create-connection-interceptor.js b/lib/instrumentation/module/mysql/mysql-create-connection-interceptor.js index cd186e9c..6bac21fc 100644 --- a/lib/instrumentation/module/mysql/mysql-create-connection-interceptor.js +++ b/lib/instrumentation/module/mysql/mysql-create-connection-interceptor.js @@ -21,7 +21,7 @@ class MySQLCreateConnectionInterceptor { this.methodDescriptorBuilder.setParameterVariableNames('connectionUri') } - doInBeforeTrace(recorder, target, args) { + doInBeforeTrace(recorder) { recorder.recordServiceType(serviceType) } diff --git a/lib/pinpoint-config-default.json b/lib/pinpoint-config-default.json index d00205d1..e3e3f566 100644 --- a/lib/pinpoint-config-default.json +++ b/lib/pinpoint-config-default.json @@ -33,5 +33,6 @@ }, "stream-deadline-minutes": { "client-side": 10 - } + }, + "trace-location-and-filename-of-call-site": false } \ No newline at end of file diff --git a/test/config.test.js b/test/config.test.js index 524084e7..c32e48b9 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -65,8 +65,6 @@ test('main moudle path', (t) => { const conf = config.readRootConfigFile() t.deepEqual(conf, {}, 'configuration is null object') let actual = config.getMainModulePath(require) - let actualParsedPath = path.parse(actual) - t.deepEqual(actualParsedPath.base, 'bin', 'main module path') actual = config.getMainModulePath({}) t.true(actual === undefined, 'config.getMainModulePath({}) return value is undefined') @@ -167,5 +165,31 @@ test('Agent ID length check', (t) => { delete process.env.PINPOINT_AGENT_ID delete process.env.PINPOINT_APPLICATION_NAME + t.end() +}) + +test('callSite config', (t) => { + config.clear() + + let given = config.getConfig() + t.false(given.traceLocationAndFileNameOfCallSite, 'default value is false') + + config.clear() + process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = '' + given = config.getConfig() + t.false(given.traceLocationAndFileNameOfCallSite, 'default value is true validation') + + config.clear() + process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = 'false' + given = config.getConfig() + t.false(given.traceLocationAndFileNameOfCallSite, 'false value is false') + + config.clear() + process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = 'true' + given = config.getConfig() + t.true(given.traceLocationAndFileNameOfCallSite, 'true value is true') + + delete process.env.PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE + config.clear() t.end() }) \ No newline at end of file diff --git a/test/context/callstack.test.js b/test/context/callstack.test.js index 11c14f92..a8bc0b01 100644 --- a/test/context/callstack.test.js +++ b/test/context/callstack.test.js @@ -44,7 +44,7 @@ const TEST_ENV = { } const getServerUrl = (path) => `http://${TEST_ENV.host}:${TEST_ENV.port}${path}` test(`fix express call stack depth`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const app = new express() const path = `/` @@ -108,6 +108,72 @@ test(`fix express call stack depth`, async (t) => { t.equal(actualSpanChunk.localAsyncId.asyncId, 1, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.asyncId') t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.sequence') + t.end() + server.close() + }) +}) + +test('fix express call stack depth without callSite', async (t) => { + agent.bindHttp() + + const app = new express() + const path = `/` + + app.use(express.json()) + app.use(express.urlencoded({ + extended: false + })) + + const router1 = express.Router() + router1.get(path, async (req, res) => { + const result = await axios.get(`https://www.naver.com`) + t.equal(result.status, 200) + res.send('ok router1') + }) + app.use('/router1', router1) + + const server = app.listen(TEST_ENV.port, async function () { + const result1 = await axios.get(getServerUrl(`/router1${path}`)) + t.ok(result1.status, 200) + t.ok(result1.data, 'ok router1') + + t.equal(agent.dataSender.mockSpan.spanEventList.length, 4, `span has 6 span events`) + t.equal(agent.dataSender.mockSpan.apiId, defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor.getApiId(), 'nodeServerMethodDescriptor apiId') + + let actualBuilder = new MethodDescriptorBuilder('use') + .setClassName('Router') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = agent.dataSender.mockSpan.spanEventList[0] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'use(jsonParser) apiId') + t.equal(actualSpanEvent.sequence, 0, 'use(jsonParser) sequence') + t.equal(actualSpanEvent.depth, 1, 'use(jsonParser) depth') + t.equal(actualSpanEvent.serviceType, ServiceTypeCode.express, 'use(jsonParser) serviceType') + + actualBuilder = new MethodDescriptorBuilder('use') + .setClassName('Router') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = agent.dataSender.mockSpan.spanEventList[1] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'use(urlencodedParser) apiId') + t.equal(actualSpanEvent.sequence, 1, 'use(urlencodedParser) sequence') + t.equal(actualSpanEvent.depth, 1, 'use(urlencodedParser) depth') + t.equal(actualSpanEvent.serviceType, ServiceTypeCode.express, 'use(urlencodedParser) serviceType') + + actualSpanEvent = agent.dataSender.mockSpan.spanEventList[2] + t.equal(actualSpanEvent.apiId, 0, 'await axios.get(`https://naver.com`) apiId') + t.equal(actualSpanEvent.sequence, 3, 'await axios.get(`https://naver.com`) sequence') + t.equal(actualSpanEvent.depth, 2, 'await axios.get(`https://naver.com`) depth') + t.equal(actualSpanEvent.serviceType, ServiceTypeCode.ASYNC_HTTP_CLIENT_INTERNAL, 'await axios.get(`https://naver.com`) serviceType') + t.equal(actualSpanEvent.nextAsyncId, 2, 'await axios.get(`https://naver.com`) nextAsyncId') + let actualAnnotation = actualSpanEvent.annotations[0] + t.equal(actualAnnotation.key, 12, 'await axios.get(`https://naver.com`) spanevent annotation key') + t.equal(actualAnnotation.value, 'http.request', 'await axios.get(`https://naver.com`) spanevent annotation value') + + t.equal(agent.dataSender.mockSpanChunks.length, 1, 'await axios.get(`https://naver.com`) spanchunk is 1') + let actualSpanChunk = agent.dataSender.mockSpanChunks[0] + t.equal(actualSpanChunk.agentId, agent.dataSender.mockSpan.agentId, 'await axios.get(`https://naver.com`) spanchunk agentId') + t.equal(actualSpanChunk.localAsyncId.asyncId, 2, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.asyncId') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.sequence') + t.end() server.close() }) diff --git a/test/instrumentation/module/express.test.js b/test/instrumentation/module/express.test.js index 59b2b3d4..47b1e784 100644 --- a/test/instrumentation/module/express.test.js +++ b/test/instrumentation/module/express.test.js @@ -24,7 +24,7 @@ const getServerUrl = (path) => `http://${TEST_ENV.host}:${TEST_ENV.port}${path}` const testName1 = 'express1' test(`${testName1} Should record request in basic route`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName1 @@ -233,7 +233,7 @@ function nextErrorHandleTest(trace, t) { const testName2 = 'express2' test(`[${testName2}] Should record request in express.Router`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName2 @@ -273,7 +273,7 @@ test(`[${testName2}] Should record request in express.Router`, function (t) { const testName3 = 'express3' test(`${testName3} Should record request taking more than 2 sec`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName3 @@ -343,7 +343,7 @@ test(`${testName4} Should record internal error in express.test.js`, function (t const testName5 = 'express5' test(`${testName5} Should record middleware`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName5 @@ -427,7 +427,7 @@ test(`${testName5} Should record middleware`, function (t) { const testName6 = 'express6' test(`${testName6} Should record each http method`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName6 @@ -492,3 +492,180 @@ test('express version check', (t) => { t.equal(actual.name, 'module', 'express version 5.0.0 test') t.end() }) + +test('express without callSite', (t) => { + agent.bindHttp() + + const app = new express() + + app.get('/express1', async (req, res) => { + res.send('ok get') + + agent.callbackTraceClose((trace) => { + t.equal(trace.span.annotations[0].key, annotationKey.HTTP_PARAM.code, 'HTTP param key match') + t.equal(trace.span.annotations[0].value, 'api=test&test1=test', 'HTTP param value match') + t.equal(trace.span.annotations[1].key, annotationKey.HTTP_STATUS_CODE.code, 'HTTP status code') + t.equal(trace.span.annotations[1].value, 200, 'response status is 200') + + let actualBuilder = new MethodDescriptorBuilder('get') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'Router.get', 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') + }) + }) + + + app.post('/express1', (req, res) => { + res.send('ok post') + + agent.callbackTraceClose((trace) => { + t.equal(trace.span.annotations[0].key, annotationKey.HTTP_STATUS_CODE.code, '/express1 HTTP STATUS CODE in annotation zero') + t.equal(trace.span.annotations[0].value, 200, '/express1 HTTP STATUS CODE value in annotation zero') + + let actualBuilder = new MethodDescriptorBuilder('post') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'Router.post', 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'post', 'methodName') + }) + }) + + app.get('/express2', async (req, res) => { + res.send('ok get') + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('get') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'Router.get', 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') + }) + }) + + let errorOrder = 0 + let pathSymbol + + const express3Symbol = Symbol('express3') + app.get('/express3', (req, res, next) => { + errorOrder++ + pathSymbol = express3Symbol + throw new Error('error case') + }) + + const express4Symbol = Symbol('express4') + app.get('/express4', (req, res, next) => { + errorOrder++ + pathSymbol = express4Symbol + next(new Error('error case')) + }) + + app.use(function (err, req, res, next) { + if (pathSymbol == express3Symbol) { + t.equal(errorOrder, 1, 'express3 error order') + } + if (pathSymbol === express4Symbol) { + t.equal(errorOrder, 2, 'express4 error order') + } + + agent.callbackTraceClose((trace) => { + if (errorOrder == 1) { + throwHandleTestWithoutCallSite(trace, t) + } else if (errorOrder == 2) { + nextErrorHandleTestWithoutCallSite(trace, t) + } + }) + + res.status(500).send('Something broke!') + }) + + const server = app.listen(TEST_ENV.port, async function () { + const result1 = await axios.get(getServerUrl('/express1') + '?api=test&test1=test') + t.equal(result1.status, 200) + + const result2 = await axios.post(getServerUrl('/express1')) + t.equal(result2.status, 200) + + const result3 = await axios.get(getServerUrl('/express2')) + t.equal(result3.status, 200) + + try { + await axios.get(getServerUrl('/express3')) + } catch (error) { + t.equal(error.response.status, 500) + } + + try { + await axios.get(getServerUrl('/express4')) + } catch (error) { + t.equal(error.response.status, 500, 'axios.get(getServerUrl(/express4))') + } + + t.end() + server.close() + }) +}) + +function throwHandleTestWithoutCallSite(trace, t) { + t.equal(trace.span.annotations[0].key, annotationKey.HTTP_STATUS_CODE.getCode(), '/express3 HTTP_STATUS_CODE annotationKey matching') + t.equal(trace.span.annotations[0].value, 500, '/express3 HTTP_STATUS_CODE value matching') + + let actualBuilder = new MethodDescriptorBuilder('get') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[1] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'Router.get', 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') + t.equal(spanEvent.sequence, 0, 'sequence') + t.equal(spanEvent.depth, 1, 'spanEvent.depth') + + actualBuilder = new MethodDescriptorBuilder('use') + .setClassName('Router') + const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + spanEvent = trace.span.spanEventList[0] + t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualErrorMethodDescriptor.apiDescriptor, 'Router.use', 'apiDescriptor') + t.equal(actualErrorMethodDescriptor.className, 'Router', 'className') + t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName') + t.equal(spanEvent.sequence, 1, 'sequence') + t.equal(spanEvent.depth, 2, 'spanEvent.depth') + t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:562:11'), 'error case') +} + +function nextErrorHandleTestWithoutCallSite(trace, t) { + let actualBuilder = new MethodDescriptorBuilder('get') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[1] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'Router.get', 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') + t.equal(spanEvent.sequence, 0, 'sequence') + t.equal(spanEvent.depth, 1, 'spanEvent.depth') + + actualBuilder = new MethodDescriptorBuilder('use') + .setClassName('Router') + const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + spanEvent = trace.span.spanEventList[0] + t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(actualErrorMethodDescriptor.apiDescriptor, 'Router.use', 'apiDescriptor') + t.equal(actualErrorMethodDescriptor.className, 'Router', 'className') + t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName') + t.equal(spanEvent.sequence, 1, 'sequence') + t.equal(spanEvent.depth, 2, 'spanEvent.depth') + t.equal(spanEvent.exceptionInfo.intValue, 1, 'error value') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:569:10'), 'error case') +} diff --git a/test/instrumentation/module/koa.test.js b/test/instrumentation/module/koa.test.js index 0f22d0e6..25d19381 100644 --- a/test/instrumentation/module/koa.test.js +++ b/test/instrumentation/module/koa.test.js @@ -23,7 +23,7 @@ const getServerUrl = (path) => `http://${TEST_ENV.host}:${TEST_ENV.port}${path}` const testName1 = 'koa-router1' test(`${testName1} Should record request in basic route koa.test.js`, function (t) { - agent.bindHttp() + agent.bindHttpWithCallSite() const testName = testName1 const PATH = `/${testName}` const app = new Koa() @@ -73,3 +73,52 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( server.close() }) }) + +test(`${testName1} Should record request in basic route koa.test.js`, function (t) { + agent.bindHttp() + const testName = testName1 + const PATH = `/${testName}` + const app = new Koa() + const router = new Router() + + router.get(PATH, async (ctx, next) => { + ctx.body = 'ok. get' + + agent.callbackTraceClose((trace) => { + t.equal(trace.span.annotations[0].key, annotationKey.HTTP_STATUS_CODE.getCode(), 'HTTP status code') + t.equal(trace.span.annotations[0].value, 200, 'response status is 200') + + let actualBuilder = new MethodDescriptorBuilder('get') + .setClassName('Router') + const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let spanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId') + t.equal(spanEvent.annotations[0].key, -1, 'parameter') + t.equal(spanEvent.annotations[0].value, '/koa-router1', 'parameter value matching') + t.true(actualMethodDescriptor.apiDescriptor.startsWith('Router.get'), 'apiDescriptor') + t.equal(actualMethodDescriptor.className, 'Router', 'className') + t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') + }) + }) + router.post(PATH, async (ctx, next) => { + ctx.body = 'ok. post' + }) + + app.use(router.routes()).use(router.allowedMethods()) + + const server = app.listen(TEST_ENV.port, async () => { + const result1 = await axios.get(getServerUrl(PATH)) + t.ok(result1.status, 200) + + const result2 = await axios.post(getServerUrl(PATH)) + t.ok(result2.status, 200) + + const traceMap = agent.traceContext.getAllTraceObject() + log.debug(traceMap.size) + t.ok(traceMap.size > 0) + + t.end() + server.close() + }) +}) + diff --git a/test/instrumentation/module/mysql.test.js b/test/instrumentation/module/mysql.test.js index ef199379..252af5ce 100644 --- a/test/instrumentation/module/mysql.test.js +++ b/test/instrumentation/module/mysql.test.js @@ -20,7 +20,7 @@ const ServiceType = require('../../../lib/context/service-type') const fixtures = path.resolve(__dirname, '..', '..', 'fixtures', 'db') test(`getConnection query hooking`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand(['--default-authentication-plugin=mysql_native_password']) @@ -95,7 +95,7 @@ test(`getConnection query hooking`, async (t) => { }) test(`connection with query`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand(['--default-authentication-plugin=mysql_native_password']) @@ -248,7 +248,7 @@ test(`connection with query`, async (t) => { }) test(`Connection Pool with query`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand(['--default-authentication-plugin=mysql_native_password']) @@ -411,7 +411,7 @@ test(`Connection Pool with query`, async (t) => { }) test(`Cluster with query`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand(['--default-authentication-plugin=mysql_native_password']) diff --git a/test/instrumentation/module/mysql2.test.js b/test/instrumentation/module/mysql2.test.js index 6ccfffa5..f43d5f93 100644 --- a/test/instrumentation/module/mysql2.test.js +++ b/test/instrumentation/module/mysql2.test.js @@ -21,7 +21,7 @@ const ServiceType = require('../../../lib/context/service-type') const fixtures = path.resolve(__dirname, '..', '..', 'fixtures', 'db') test(`getConnection query hooking`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand('--default-authentication-plugin=mysql_native_password') @@ -157,7 +157,7 @@ test(`getConnection query hooking`, async (t) => { }) test(`getConnection promise query hooking`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand('--default-authentication-plugin=mysql_native_password') @@ -222,7 +222,7 @@ test(`getConnection promise query hooking`, async (t) => { }) test(`Connection Pool with query hooking`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand('--default-authentication-plugin=mysql_native_password') @@ -297,7 +297,7 @@ test(`Connection Pool with query hooking`, async (t) => { }) test(`Cluster with query`, async (t) => { - agent.bindHttp() + agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand('--default-authentication-plugin=mysql_native_password') diff --git a/test/support/agent-singleton-mock.js b/test/support/agent-singleton-mock.js index 4f7aeced..957c6883 100644 --- a/test/support/agent-singleton-mock.js +++ b/test/support/agent-singleton-mock.js @@ -37,6 +37,8 @@ class MockAgent extends Agent { if (!json) { json = require('../pinpoint-config-test') + } else { + json = Object.assign({}, json, require('../pinpoint-config-test')) } require('../../lib/config').clear() const config = require('../../lib/config').getConfig(json) @@ -91,6 +93,9 @@ class MockAgent extends Agent { } } + bindHttpWithCallSite() { + this.bindHttp({ 'trace-location-and-filename-of-call-site': true }) + } } const agent = new MockAgent(fixture.config)