Skip to content

Commit

Permalink
[pinpoint-apm#140] Support nested async trace
Browse files Browse the repository at this point in the history
  • Loading branch information
feelform committed Nov 21, 2023
1 parent 5db9558 commit 8ca7210
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 32 deletions.
62 changes: 62 additions & 0 deletions test/instrumentation/context/nested-async-trace.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Pinpoint Node.js Agent
* Copyright 2020-present NAVER Corp.
* Apache License v2.0
*/

const test = require('tape')
const agent = require('../../support/agent-singleton-mock')
const express = require('express')
const axios = require('axios')
const MethodDescriptorBuilder = require('../../../lib/context/method-descriptor-builder')
const apiMetaService = require('../../../lib/context/api-meta-service')
const { expected } = require('../../fixtures/instrument-support')
const mysql = require('mysql')
const path = require('path')
const fixtures = path.resolve(__dirname, '..', '..', 'fixtures', 'db')
const { MySqlContainer } = require('testcontainers')

test(`nested mysql async query with express`, async (t) => {
agent.bindHttpWithCallSite()
const source = path.resolve(fixtures, 'mysql.sql')
const container = await new MySqlContainer()
.withCommand(['--default-authentication-plugin=mysql_native_password'])
.withEnvironment({
'MYSQL_DATABASE': 'test',
'TZ': 'Asia/Seoul',
})
.withCopyFilesToContainer([{
source: source,
target: '/docker-entrypoint-initdb.d/mysql.sql'
}])
.start()

const app = new express()
app.get('/test1', (req, res) => {
res.send('ok get')

agent.callbackTraceClose(async (trace) => {
const actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(35)
.setFileName('nested-async-trace.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
const actualSpanEvent = trace.span.spanEventList[0]
t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId is equal')
t.equal(actualMethodDescriptor.apiDescriptor, expected('app.get', 'Function.app.get'), 'apiDescriptor is equal')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className is equal')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName is equal')
t.equal(actualSpanEvent.sequence, 0, 'sequence is 0')
t.equal(actualSpanEvent.depth, 1, 'depth is 0')
await container.stop()
})
})

const server = app.listen(5006, async () => {
const result = await axios.get('http://localhost:5006/test1')
t.equal(result.status, 200, 'status is 200')

t.end()
server.close()
})
})
49 changes: 24 additions & 25 deletions test/instrumentation/module/express.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

const test = require('tape')
const axios = require('axios')

const { log, util } = require('../../test-helper')
const agent = require('../../support/agent-singleton-mock')
const express = require('express')
Expand Down Expand Up @@ -42,14 +41,14 @@ test(`${testName1} Should record request in basic route`, function (t) {

let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(34)
.setLineNumber(33)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[0]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 34, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 33, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName')
t.true(actualMethodDescriptor.location.length > 0, 'location')
})
Expand All @@ -65,14 +64,14 @@ test(`${testName1} Should record request in basic route`, function (t) {

let actualBuilder = new MethodDescriptorBuilder(expected('post', 'app.post'))
.setClassName(expected('app', 'Function'))
.setLineNumber(59)
.setLineNumber(58)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[0]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.post', 'Function.app.post')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 59, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 58, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'post', 'methodName')
t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location')
})
Expand All @@ -84,14 +83,14 @@ test(`${testName1} Should record request in basic route`, function (t) {
agent.callbackTraceClose((trace) => {
let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(81)
.setLineNumber(80)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[0]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 81, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 80, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName')
t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location')
})
Expand Down Expand Up @@ -166,69 +165,69 @@ function throwHandleTest(trace, t) {

let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(104)
.setLineNumber(103)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[1]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 104, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 103, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName')
t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location')
t.equal(spanEvent.sequence, 0, 'sequence')
t.equal(spanEvent.depth, 1, 'spanEvent.depth')

actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
.setLineNumber(117)
.setLineNumber(116)
.setFileName('express.test.js')
const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
spanEvent = trace.span.spanEventList[0]
t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualErrorMethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor')
t.equal(actualErrorMethodDescriptor.className, 'Router', 'className')
t.equal(actualErrorMethodDescriptor.lineNumber, 117, 'lineNumber')
t.equal(actualErrorMethodDescriptor.lineNumber, 116, 'lineNumber')
t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName')
t.true(actualErrorMethodDescriptor.location.endsWith('express.test.js'), 'location')
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:107:11'), 'error case')
t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:106:11'), 'error case')
}

function nextErrorHandleTest(trace, t) {
let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(111)
.setLineNumber(110)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[1]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 111, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 110, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName')
t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location')
t.equal(spanEvent.sequence, 0, 'sequence')
t.equal(spanEvent.depth, 1, 'spanEvent.depth')

actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
.setLineNumber(117)
.setLineNumber(116)
.setFileName('express.test.js')
const actualErrorMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
spanEvent = trace.span.spanEventList[0]
t.equal(actualErrorMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualErrorMethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor')
t.equal(actualErrorMethodDescriptor.className, 'Router', 'className')
t.equal(actualErrorMethodDescriptor.lineNumber, 117, 'lineNumber')
t.equal(actualErrorMethodDescriptor.lineNumber, 116, 'lineNumber')
t.equal(actualErrorMethodDescriptor.methodName, 'use', 'methodName')
t.true(actualErrorMethodDescriptor.location.endsWith('express.test.js'), 'location')
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:114:10'), 'error case')
t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:113:10'), 'error case')
}

const testName2 = 'express2'
Expand Down Expand Up @@ -356,44 +355,44 @@ test(`${testName5} Should record middleware`, function (t) {
agent.callbackTraceClose((trace) =>{
let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(355)
.setLineNumber(354)
.setFileName('express.test.js')
const actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let spanEvent = trace.span.spanEventList[2]
t.equal(actualMethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMethodDescriptor.apiDescriptor.startsWith(expected('app.get', 'Function.app.get')), 'apiDescriptor')
t.equal(actualMethodDescriptor.className, expected('app', 'Function'), 'className')
t.equal(actualMethodDescriptor.lineNumber, 355, 'lineNumber')
t.equal(actualMethodDescriptor.lineNumber, 354, 'lineNumber')
t.equal(actualMethodDescriptor.methodName, 'get', 'methodName')
t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location')
t.equal(spanEvent.sequence, 2, 'sequence')
t.equal(spanEvent.depth, 1, 'spanEvent.depth')

actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
.setLineNumber(345)
.setLineNumber(344)
.setFileName('express.test.js')
const actualMiddleware1MethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
spanEvent = trace.span.spanEventList[1]
t.equal(actualMiddleware1MethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMiddleware1MethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor')
t.equal(actualMiddleware1MethodDescriptor.className, 'Router', 'className')
t.equal(actualMiddleware1MethodDescriptor.lineNumber, 345, 'lineNumber')
t.equal(actualMiddleware1MethodDescriptor.lineNumber, 344, 'lineNumber')
t.equal(actualMiddleware1MethodDescriptor.functionName, 'use', 'functionName')
t.true(actualMiddleware1MethodDescriptor.location.endsWith('express.test.js'), 'location')
t.equal(spanEvent.sequence, 1, 'sequence')
t.equal(spanEvent.depth, 1, 'spanEvent.depth')

actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
.setLineNumber(340)
.setLineNumber(339)
.setFileName('express.test.js')
const actualMiddleware2MethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
spanEvent = trace.span.spanEventList[0]
t.equal(actualMiddleware2MethodDescriptor.apiId, spanEvent.apiId, 'apiId')
t.true(actualMiddleware2MethodDescriptor.apiDescriptor.startsWith('Router.use'), 'apiDescriptor')
t.equal(actualMiddleware2MethodDescriptor.className, 'Router', 'className')
t.equal(actualMiddleware2MethodDescriptor.lineNumber, 340, 'lineNumber')
t.equal(actualMiddleware2MethodDescriptor.lineNumber, 339, 'lineNumber')
t.equal(actualMiddleware2MethodDescriptor.methodName, 'use', 'methodName')
t.true(actualMiddleware2MethodDescriptor.location.endsWith('express.test.js'), 'location')
t.equal(spanEvent.sequence, 0, 'sequence')
Expand Down Expand Up @@ -624,7 +623,7 @@ function throwHandleTestWithoutCallSite(trace, t) {
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:545:11'), 'error case')
t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:544:11'), 'error case')
}

function nextErrorHandleTestWithoutCallSite(trace, t) {
Expand All @@ -650,5 +649,5 @@ function nextErrorHandleTestWithoutCallSite(trace, t) {
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:552:10'), 'error case')
t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:551:10'), 'error case')
}
14 changes: 7 additions & 7 deletions test/instrumentation/module/mysql.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,10 @@ test(`Connection Pool with query`, async (t) => {
queryIndex++
if (queryIndex == 2) {
setImmediate(async () => {
trace.close()
pool.end()
await container.stop()
})
trace.close()
})
}
})

Expand All @@ -304,9 +304,9 @@ test(`Connection Pool with query`, async (t) => {
queryIndex++
if (queryIndex == 2) {
setImmediate(async () => {
trace.close()
pool.end()
await container.stop()
trace.close()
})
}
})
Expand Down Expand Up @@ -379,10 +379,10 @@ test(`Connection Pool with query`, async (t) => {
.setLineNumber(285)
.setFileName('mysql.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.span.spanEventList[3]
actualSpanEvent = trace.span.spanEventList.find( e => e.apiId === actualMethodDescriptor.apiId )
t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolConnection.query spanEvent apiId on pool.query')
t.equal(actualSpanEvent.depth, 1, 'PoolConnection.query spanEvent depth on pool.query')
t.equal(actualSpanEvent.sequence, 3, 'PoolConnection.query spanEvent sequence on pool.query')
t.equal(actualSpanEvent.sequence, trace.span.spanEventList.findIndex( e => e.apiId === actualMethodDescriptor.apiId ), 'PoolConnection.query spanEvent sequence on pool.query')
t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query')

const asyncSpanChunkMatcher = (actualSpanEvent) => {
Expand Down Expand Up @@ -410,10 +410,10 @@ test(`Connection Pool with query`, async (t) => {
.setLineNumber(214)
.setFileName('Pool.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.span.spanEventList[4]
actualSpanEvent = trace.span.spanEventList.find( e => e.apiId === actualMethodDescriptor.apiId )
t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolConnection.query spanEvent apiId on pool.query')
t.equal(actualSpanEvent.depth, 1, 'PoolConnection.query spanEvent depth on pool.query')
t.equal(actualSpanEvent.sequence, 4, 'PoolConnection.query spanEvent sequence on pool.query')
t.equal(actualSpanEvent.sequence, trace.span.spanEventList.findIndex( e => e.apiId === actualMethodDescriptor.apiId ), 'PoolConnection.query spanEvent sequence on pool.query')
t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query')
asyncSpanChunkMatcher(actualSpanEvent)
t.end()
Expand Down

0 comments on commit 8ca7210

Please sign in to comment.