Skip to content

Commit

Permalink
[#253] Support async spanEvent tracing after ChildTrace closed
Browse files Browse the repository at this point in the history
  • Loading branch information
feelform committed Dec 26, 2024
1 parent 1afd6c9 commit d025627
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 145 deletions.
3 changes: 2 additions & 1 deletion lib/context/disable-trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ class DisableTrace {
constructor(traceRoot) {
this.traceRoot = traceRoot
this.spanRecorder = new DisableSpanRecorder()
this.spanEventRecorder = new DisableSpanEventRecorder()
}


traceBlockBegin() {
return new DisableSpanEventRecorder()
return this.spanEventRecorder
}

traceBlockEnd() {}
Expand Down
13 changes: 7 additions & 6 deletions lib/context/trace/child-trace-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,20 @@ class ChildTraceBuilder {
}

traceBlockBegin(stackId = StackId.default) {
if (this.closed) {
return SpanEventRecorder.nullObject(this.traceRoot)
}

return this.callStack.makeSpanEventRecorder(stackId)
}

traceBlockEnd(spanEventRecorder) {
if (this.closed) {
return
// If ChildTraceBuilder is closed, aync spanEvent was popped in the previous call.
// So, increase the depth of the next spanEvent.
const spanEventBuilder = spanEventRecorder.getSpanEventBuilder()
spanEventBuilder.setDepth(spanEventBuilder.getDepth() + 1)
}

this.endSpanEventBuilder(spanEventRecorder.getSpanEventBuilder())
if (this.closed) {
this.repository.flush()
}
}

endSpanEventBuilder(builder) {
Expand Down
4 changes: 4 additions & 0 deletions lib/context/trace/disable-child-trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ class DisableChildTrace {

traceBlockEnd() {}

getSpanRecorder() {
return this.spanRecorder
}

getTraceRoot() {
return this.traceRoot
}
Expand Down
4 changes: 4 additions & 0 deletions lib/context/trace/span-event-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ class SpanEventBuilder {
return this
}

getDepth() {
return this.depth
}

getStartTime() {
return this.startTime
}
Expand Down
8 changes: 8 additions & 0 deletions lib/instrumentation/instrument-arrow-function.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
const shimmer = require('@pinpoint-apm/shimmer')
const HookFunctionArguments = require('./hook-function-arguments')
const localStorage = require('./context/local-storage')
// const log = require('../utils/logger')

// ref: SpanEventSimpleAroundInterceptorForPlugin.java
class InstrumentArrowFunction {
Expand Down Expand Up @@ -65,6 +66,13 @@ class InstrumentArrowFunction {
return original.apply(this, arguments)
}

// if (log.isInfo()) {
// if (typeof method !== 'symbol') {
// log.info(`target: ${JSON.stringify(target)}, method: ${method}, original: ${original} InstrumentArrowFunction.addChildTraceInterceptor`)
// } else {
// log.info(`target: ${JSON.stringify(target)}, symbol method, original: ${original} InstrumentArrowFunction.addChildTraceInterceptor`)
// }
// }
const childTraceBuilder = traceContext.continueAsyncContextTraceObject(trace.getTraceRoot(), asyncId.nextLocalAsyncId2())
return localStorage.run(childTraceBuilder, () => {
const result = original.apply(this, arguments)
Expand Down
19 changes: 0 additions & 19 deletions test/fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,10 @@ const captureNamedGroup = (callSite) => {
, namedGroupTypeMethod([callSite], 0))
}

function assertSpanChunk(asyncTrace, callback) {
const origin = asyncTrace.close
asyncTrace.close = function () {
origin.apply(this, arguments)
callback(asyncTrace.storage.dataSender.findSpanChunk(asyncTrace.asyncId))
}
}

function assertTrace(callback) {
const trace = localStorage.getStore()
const origin = trace.close
trace.close = function () {
origin.apply(this, arguments)
callback(trace)
}
}

module.exports = {
config,
getTransactionId,
getTraceId,
getAgentInfo,
captureNamedGroup,
assertSpanChunk,
assertTrace,
}
39 changes: 23 additions & 16 deletions test/instrumentation/context/nested-async-trace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,23 @@ const pResultReturnUnaryService = (call, callback) => {
}
}

let spanOrSpanChunks = []
const resetSpanOrSpanChunks = () => {
spanOrSpanChunks = []
}
const getSpanOrSpanChunks = () => {
return spanOrSpanChunks
}
const getSpanChunk = (asyncId) =>{
return getSpanOrSpanChunks().find(spanOrSpanChunk => spanOrSpanChunk.getLocalasyncid().getAsyncid() === asyncId.getAsyncId() && spanOrSpanChunk.getLocalasyncid().getSequence() === asyncId.getSequence())
}

let spanMessageEndEventCallback
let dataStreamCount = 0
const spanMessageStreamService = (call) => {
call.on('data', (spanMessage) => {
const spanOrSpanChunk = spanMessage.getSpan() ?? spanMessage.getSpanchunk()
const spanOrSpanChunks = agent.getSpanOrSpanChunks()
const spanOrSpanChunks = getSpanOrSpanChunks()
spanOrSpanChunks.push(spanOrSpanChunk)
if (agent.getTraces().length === ++dataStreamCount) {
spanMessageEndEventCallback?.()
Expand Down Expand Up @@ -100,6 +111,7 @@ test(`nested mysql async query with express`, async (t) => {

collectorServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), async (err, port) => {
agent.bindHttpWithCallSite(port)
resetSpanOrSpanChunks()
const source = path.resolve(fixtures, 'mysql.sql')
const container = await new MySqlContainer()
.withCommand(['--default-authentication-plugin=mysql_native_password'])
Expand Down Expand Up @@ -144,7 +156,7 @@ test(`nested mysql async query with express`, async (t) => {
agent.callbackTraceClose(async (trace) => {
let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(117)
.setLineNumber(129)
.setFileName('nested-async-trace.test.js')
let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 0)
Expand All @@ -157,7 +169,7 @@ test(`nested mysql async query with express`, async (t) => {
t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express')

actualBuilder = new MethodDescriptorBuilder('createConnection')
.setLineNumber(118)
.setLineNumber(130)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 1)
Expand All @@ -170,7 +182,7 @@ test(`nested mysql async query with express`, async (t) => {

actualBuilder = new MethodDescriptorBuilder('connect')
.setClassName('Connection')
.setLineNumber(127)
.setLineNumber(139)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 2)
Expand All @@ -192,7 +204,7 @@ test(`nested mysql async query with express`, async (t) => {

actualBuilder = new MethodDescriptorBuilder('query')
.setClassName('Connection')
.setLineNumber(133)
.setLineNumber(145)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 3)
Expand All @@ -214,7 +226,7 @@ test(`nested mysql async query with express`, async (t) => {

actualBuilder = new MethodDescriptorBuilder('query')
.setClassName('Connection')
.setLineNumber(139)
.setLineNumber(151)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = actualSpanChunk.spanEventList[1]
Expand All @@ -241,7 +253,7 @@ test(`nested mysql async query with express`, async (t) => {
spanMessageEndEventCallback = async () => {
const trace = agent.getTrace(0)
const childTrace = agent.getTrace(1)
const actualSpanChunkGrpc = agent.getSpanChunk(childTrace.localAsyncId)
const actualSpanChunkGrpc = getSpanChunk(childTrace.localAsyncId)
t.equal(actualSpanChunkGrpc.getVersion(), 1, 'PSpanChunk.version is 1')

const actualTractionId = actualSpanChunkGrpc.getTransactionid()
Expand Down Expand Up @@ -364,7 +376,7 @@ test(`nested mysql2 async query with express`, async (t) => {
t.true(trace.spanBuilder.remoteAddress === '127.0.0.1' || trace.spanBuilder.remoteAddress === '::1', `remoteAddress is ${trace.spanBuilder.remoteAddress}`)
let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get'))
.setClassName(expected('app', 'Function'))
.setLineNumber(320)
.setLineNumber(332)
.setFileName('nested-async-trace.test.js')
let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 0)
Expand All @@ -377,7 +389,7 @@ test(`nested mysql2 async query with express`, async (t) => {
t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express')

actualBuilder = new MethodDescriptorBuilder('createConnection')
.setLineNumber(321)
.setLineNumber(333)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 1)
Expand All @@ -390,7 +402,7 @@ test(`nested mysql2 async query with express`, async (t) => {

actualBuilder = new MethodDescriptorBuilder('query')
.setClassName('Connection')
.setLineNumber(331)
.setLineNumber(343)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 2)
Expand All @@ -412,7 +424,7 @@ test(`nested mysql2 async query with express`, async (t) => {

actualBuilder = new MethodDescriptorBuilder('query')
.setClassName('Connection')
.setLineNumber(337)
.setLineNumber(349)
.setFileName('nested-async-trace.test.js')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = actualSpanChunk.spanEventList[1]
Expand Down Expand Up @@ -493,11 +505,6 @@ test(`nested mysql2 async query with express`, async (t) => {
}

dataStreamCount = 0
agent.setContinueAsyncContextTraceObjectCallback(async (trace) => {
const callerTrace = agent.getCallerTraceByAsyncId(trace.localAsyncId)
t.true(callerTrace !== undefined, 'parent trace is not undefined')
})

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

0 comments on commit d025627

Please sign in to comment.