From 5db95582f0cc5de13bb5096c6ec5244268a2f713 Mon Sep 17 00:00:00 2001 From: Yongseok Date: Tue, 21 Nov 2023 10:27:22 +0900 Subject: [PATCH] [#140] Support AsyncLocalStorage node@16.4 * Rename refactoring MethodDescriptor2 to MethodDescriptor * AsyncLocalStorage above node@16.4 The AsyncHook has been deprecated. Node Document recommend AsyncLocalStorage. --- lib/context/async-trace.js | 13 +- lib/context/context-manager.js | 6 +- lib/context/disable-async-trace.js | 21 +- lib/context/disable-trace.js | 18 +- lib/context/method-descriptor-builder.js | 4 +- lib/context/method-descriptor.js | 83 -- lib/context/trace-context.js | 21 +- lib/context/trace.js | 27 - lib/instrumentation/call-stack.js | 2 +- .../context/async-context-local-storage.js | 29 + .../context/async-hooks-local-storage.js | 30 + .../context/async-service-type.js | 11 + lib/instrumentation/context/local-storage.js | 38 + lib/instrumentation/context/trace-builder.js | 61 ++ lib/instrumentation/http-shared.js | 86 +- lib/instrumentation/instrument-method.js | 6 +- ...od-descriptor2.js => method-descriptor.js} | 4 +- lib/instrumentation/module/ioredis.js | 4 +- lib/instrumentation/module/redis.js | 4 +- test/client/data-sender.test.js | 17 +- test/context/context-manager.test.js | 75 +- test/context/trace-context.test.js | 59 +- .../fix-async-call-stack.test.js | 35 +- test/instrumentation/module/express.test.js | 41 +- test/instrumentation/module/fix-redis.test.js | 183 ++-- test/instrumentation/module/http.test.js | 29 +- test/instrumentation/module/koa.test.js | 8 - test/instrumentation/module/mysql.test.js | 869 +++++++++--------- test/instrumentation/module/mysql2.test.js | 661 ++++++------- test/instrumentation/module/redis.test.js | 34 +- .../request-header-utils.test.js | 13 +- test/support/agent-singleton-mock.js | 19 +- 32 files changed, 1257 insertions(+), 1254 deletions(-) delete mode 100644 lib/context/method-descriptor.js create mode 100644 lib/instrumentation/context/async-context-local-storage.js create mode 100644 lib/instrumentation/context/async-hooks-local-storage.js create mode 100644 lib/instrumentation/context/async-service-type.js create mode 100644 lib/instrumentation/context/local-storage.js create mode 100644 lib/instrumentation/context/trace-builder.js rename lib/instrumentation/{method-descriptor2.js => method-descriptor.js} (93%) diff --git a/lib/context/async-trace.js b/lib/context/async-trace.js index f32652bc..4cdba0ac 100644 --- a/lib/context/async-trace.js +++ b/lib/context/async-trace.js @@ -10,22 +10,15 @@ const SpanEventRecorder = require('./span-event-recorder') const SpanEvent = require('./span-event') const SpanChunk = require('./span-chunk') const BufferedStorage = require('./buffered-storage') +const Trace = require('./trace') -class AsyncTrace { +class AsyncTrace extends Trace { constructor(span, asyncId, traceId, agentInfo, dataSender, sampling) { + super(traceId, agentInfo, dataSender, sampling) this.span = span - this.traceId = traceId - this.agentInfo = agentInfo this.asyncId = asyncId - - this.spanEventRecorder = null - const createAsyncSpanChunk = SpanChunk.getAsyncFactoryMethod(traceId, agentInfo, asyncId) this.storage = new BufferedStorage(dataSender, createAsyncSpanChunk) - - this.callStack = [] - this.sequence = 0 - this.sampling = sampling } traceAsyncBegin() { diff --git a/lib/context/context-manager.js b/lib/context/context-manager.js index 2fb60104..dd944f1f 100644 --- a/lib/context/context-manager.js +++ b/lib/context/context-manager.js @@ -41,13 +41,13 @@ const setObject = (value) => { traceObjectMap.set(asyncId, value) } -const getAllObject = () => { - return traceObjectMap +const disable = () => { + traceObjectMap.clear() } module.exports = { getObject, setObject, - getAllObject, start, + disable } diff --git a/lib/context/disable-async-trace.js b/lib/context/disable-async-trace.js index 9ca35151..9efeb3f2 100644 --- a/lib/context/disable-async-trace.js +++ b/lib/context/disable-async-trace.js @@ -9,20 +9,19 @@ const DisableSpanEventRecorder = require('./disable-span-event-recorder') class DisableAsyncTrace { - traceAsyncBegin () { - return new DisableSpanEventRecorder() - } + traceAsyncBegin () { + return new DisableSpanEventRecorder() + } - traceAsyncEnd (spanEventRecorder) { - - } + traceAsyncEnd () { + } - canSampled () { - return false - } + canSampled () { + return false + } - close () { - } + close () { + } } module.exports = DisableAsyncTrace \ No newline at end of file diff --git a/lib/context/disable-trace.js b/lib/context/disable-trace.js index 7540e630..ad62e423 100644 --- a/lib/context/disable-trace.js +++ b/lib/context/disable-trace.js @@ -6,7 +6,6 @@ 'use strict' -const SpanRecorder = require('./span-recorder') const Span = require('./span') const DisableSpanEventRecorder = require('./disable-span-event-recorder') const DisableAsyncTrace = require('./disable-async-trace') @@ -24,22 +23,9 @@ class DisableTrace { return new DisableSpanEventRecorder() } - traceBlockEnd(spanEventRecorder) {} + traceBlockEnd() {} - completeSpanEvent(spanEvent) { - } - - newAsyncTrace(spanEventRecorder) { - if (spanEventRecorder) { - const asyncId = spanEventRecorder.recordNextAsyncId() - return new DisableAsyncTrace() - } - } - - newAsyncTraceWithId(asyncId) { - if (asyncId) { - return new DisableAsyncTrace() - } + completeSpanEvent() { } canSampled() { diff --git a/lib/context/method-descriptor-builder.js b/lib/context/method-descriptor-builder.js index 8abcce6a..82458272 100644 --- a/lib/context/method-descriptor-builder.js +++ b/lib/context/method-descriptor-builder.js @@ -6,7 +6,7 @@ 'use strict' -const MethodDescriptor2 = require('../instrumentation/method-descriptor2') +const MethodDescriptor = require('../instrumentation/method-descriptor') const MethodType = require('../constant/method-type') // https://v8.dev/docs/stack-trace-api#appendix%3A-stack-trace-format @@ -191,7 +191,7 @@ class MethodDescriptorBuilder { } build() { - const value = new MethodDescriptor2(this.functionName) + const value = new MethodDescriptor(this.functionName) if (this.type !== 'Function' && typeof this.methodName === 'undefined') { value.methodName = this.functionName } else { diff --git a/lib/context/method-descriptor.js b/lib/context/method-descriptor.js deleted file mode 100644 index ca1320f7..00000000 --- a/lib/context/method-descriptor.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Pinpoint Node.js Agent - * Copyright 2020-present NAVER Corp. - * Apache License v2.0 - */ - -'use strict' - -const MethodType = require('../constant/method-type') - -// https://github.com/pinpoint-apm/pinpoint/blob/master/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/DefaultMethodDescriptor.java -class MethodDescriptor { - constructor(moduleName, objectPath, methodName, type, apiDescriptor, apiId, fullName, className, lineNumber, location) { - this.moduleName = moduleName - this.objectPath = objectPath - this.methodName = methodName - this.type = type || MethodType.DEFAULT - this.apiDescriptor = apiDescriptor || this.getDescriptor() - - if (typeof apiId === 'number') { - this.apiId = apiId - } else { - this.apiId = 0 - } - - this.fullName = fullName - this.className = className - this.lineNumber = lineNumber - this.location = location - } - - static create(moduleName, objectPath, methodName) { - return new MethodDescriptor(moduleName, objectPath, methodName) - } - - getDescriptor() { - return [this.moduleName, this.objectPath, this.methodName] - .filter(v => v) - .join('.') - } - - getFullName() { - return this.fullName - } - - getApiId() { - return this.apiId - } - - getModuleName() { - return this.moduleName - } - - getFunctionName() { - return this.objectPath - } - - getMethodName() { - return this.methodName - } - - getApiDescriptor() { - return this.apiDescriptor - } - - getClassName() { - return this.className - } - - getLineNumber() { - return this.lineNumber - } - - getType() { - return this.type - } - - getLocation() { - return this.location - } -} - -module.exports = MethodDescriptor diff --git a/lib/context/trace-context.js b/lib/context/trace-context.js index 129fa87d..3d9daa18 100644 --- a/lib/context/trace-context.js +++ b/lib/context/trace-context.js @@ -6,7 +6,6 @@ 'use strict' -const contextManager = require('./context-manager') const Trace = require('./trace') const TransactionId = require('./transaction-id') const TraceId = require('./trace-id') @@ -15,6 +14,7 @@ const activeTrace = require('../metric/active-trace') const log = require('../utils/logger') const sampler = require('../sampler/sampler') const DisableTrace = require('./disable-trace') +const localStorage = require('../instrumentation/context/local-storage') class TraceContext { constructor() { @@ -35,8 +35,6 @@ class TraceContext { throw new Error('Fail to initialize pinpoint context') } - contextManager.start() - const instance = new TraceContext() instance.agentInfo = { agentId: options.agentId, @@ -79,13 +77,11 @@ class TraceContext { if (false == sampling) { const disableTrace = new DisableTrace(traceId, this.agentInfo, requestData) - this.setCurrentTraceObject(disableTrace) return disableTrace } try { const trace = new Trace(traceId, this.agentInfo, this.dataSender, sampling, requestData) - this.setCurrentTraceObject(trace) activeTrace.register(trace) return trace } catch (e) { @@ -107,11 +103,7 @@ class TraceContext { } currentTraceObject() { - return contextManager.getObject() - } - - setCurrentTraceObject(traceObject) { - contextManager.setObject(traceObject) + return localStorage.getStore() } makeTrace(requestData) { @@ -125,19 +117,12 @@ class TraceContext { requestData.spanId, requestData.parentSpanId, requestData.flag) - const disableTrace = new DisableTrace(traceId, this.agentInfo, requestData) - this.setCurrentTraceObject(disableTrace) - return disableTrace + return new DisableTrace(traceId, this.agentInfo, requestData) } } else { return this.newTraceObject(this.isSampling()) } } - - // only test - getAllTraceObject() { - return contextManager.getAllObject() - } } module.exports = TraceContext diff --git a/lib/context/trace.js b/lib/context/trace.js index 7cae3bbb..0cb3a660 100644 --- a/lib/context/trace.js +++ b/lib/context/trace.js @@ -10,13 +10,9 @@ const SpanRecorder = require('./span-recorder') const SpanEventRecorder = require('./span-event-recorder') const Span = require('./span') const SpanEvent = require('./span-event') -const AsyncTrace = require('./async-trace') const BufferedStorage = require('./buffered-storage') const SpanChunk = require('./span-chunk') const log = require('../utils/logger') -const defaultPredefinedMethodDescriptorRegistry = require('../constant/default-predefined-method-descriptor-registry') -const DisableAsyncTrace = require('./disable-async-trace') -const ServiceType = require('../context/service-type') const calledTraceBlockEnd = Symbol('called-traceBlockEnd') class Trace { @@ -89,29 +85,6 @@ class Trace { } } - newAsyncTrace(spanEventRecorder) { - if (!spanEventRecorder || typeof spanEventRecorder.recordNextAsyncId !== 'function') { - return new DisableAsyncTrace() - } - const asyncId = spanEventRecorder.recordNextAsyncId() - return this.newAsyncTraceWithId(asyncId) - } - - newAsyncTraceWithId(asyncId) { - if (!asyncId) { - return new DisableAsyncTrace() - } - - const asyncTrace = new AsyncTrace(this.span, asyncId, this.traceId, this.agentInfo, this.dataSender, this.sampling) - const spanEventRecorder = asyncTrace.traceAsyncBegin() - spanEventRecorder.recordServiceType(ServiceType.async) - spanEventRecorder.recordApiId(defaultPredefinedMethodDescriptorRegistry.asyncInvocationMethodDescriptor.getApiId()) - spanEventRecorder.spanEvent.endPoint = null - spanEventRecorder.spanEvent.destinationId = null - asyncTrace.storage.storeSpanEvent(spanEventRecorder.spanEvent) - return asyncTrace - } - canSampled() { return this.sampling } diff --git a/lib/instrumentation/call-stack.js b/lib/instrumentation/call-stack.js index 29613445..99b96bbb 100644 --- a/lib/instrumentation/call-stack.js +++ b/lib/instrumentation/call-stack.js @@ -85,7 +85,7 @@ const captureLocationFileNameNamedGroup = (stack) => { } const locationFileNameLineNumber = (stack) => { - return stack.match(/ \((?.+\/)(?[^:/]+):(?[0-9]+):(?[0-9]+)\)$/) + return stack.match(/ \(?(?[^\s]+\/)(?[^:/]+):(?[0-9]+):(?[0-9]+)\)?$/) } const fileNameOfStack = (stack) => { diff --git a/lib/instrumentation/context/async-context-local-storage.js b/lib/instrumentation/context/async-context-local-storage.js new file mode 100644 index 00000000..e5dff043 --- /dev/null +++ b/lib/instrumentation/context/async-context-local-storage.js @@ -0,0 +1,29 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2023-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const { AsyncLocalStorage } = require('node:async_hooks') + +class AsyncContextLocalStorage { + constructor() { + this.storage = new AsyncLocalStorage() + } + + run(store, callback) { + this.storage.run(store, callback) + } + + getStore() { + return this.storage.getStore() + } + + disable() { + this.storage.disable() + } +} + +module.exports = AsyncContextLocalStorage \ No newline at end of file diff --git a/lib/instrumentation/context/async-hooks-local-storage.js b/lib/instrumentation/context/async-hooks-local-storage.js new file mode 100644 index 00000000..28379b12 --- /dev/null +++ b/lib/instrumentation/context/async-hooks-local-storage.js @@ -0,0 +1,30 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2023-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const contextManager = require('../../context/context-manager') + +class AsyncHooksLocalStorage { + constructor() { + contextManager.start() + } + + run(store, callback) { + contextManager.setObject(store) + callback() + } + + getStore() { + return contextManager.getObject() + } + + disable() { + contextManager.disable() + } +} + +module.exports = AsyncHooksLocalStorage \ No newline at end of file diff --git a/lib/instrumentation/context/async-service-type.js b/lib/instrumentation/context/async-service-type.js new file mode 100644 index 00000000..405246b0 --- /dev/null +++ b/lib/instrumentation/context/async-service-type.js @@ -0,0 +1,11 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2023-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const ServiceType = require('../../context/service-type') + +module.exports = new ServiceType(100, 'ASYNC') \ No newline at end of file diff --git a/lib/instrumentation/context/local-storage.js b/lib/instrumentation/context/local-storage.js new file mode 100644 index 00000000..2ce7ca5f --- /dev/null +++ b/lib/instrumentation/context/local-storage.js @@ -0,0 +1,38 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2023-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const semver = require('semver') +const AsyncContextLocalStorage = require('./async-context-local-storage') +const AsyncHooksLocalStorage = require('./async-hooks-local-storage') + +class LocalStorage { + constructor() { + this.storage = this.createLocalStorage() + } + + createLocalStorage() { + if (!semver.satisfies(process.version, '>=16.4.0')) { + return new AsyncHooksLocalStorage() + } + return new AsyncContextLocalStorage() + } + + run(store, callback) { + return this.storage.run(store, callback) + } + + getStore() { + return this.storage.getStore() + } + + disable() { + this.storage.disable() + } +} + +module.exports = new LocalStorage() \ No newline at end of file diff --git a/lib/instrumentation/context/trace-builder.js b/lib/instrumentation/context/trace-builder.js new file mode 100644 index 00000000..77b3cf35 --- /dev/null +++ b/lib/instrumentation/context/trace-builder.js @@ -0,0 +1,61 @@ +/** + * Pinpoint Node.js Agent + * Copyright 2023-present NAVER Corp. + * Apache License v2.0 + */ + +'use strict' + +const Span = require('../../context/span') +const SpanRecorder = require('../../context/span-recorder') +const SpanChunk = require('../../context/span-chunk') +const BufferedStorage = require('../../context/buffered-storage') +const AsyncTrace = require('../../context/async-trace') +const DisableAsyncTrace = require('../../context/disable-async-trace') +const serviceType = require('./async-service-type') +const defaultPredefinedMethodDescriptorRegistry = require('../../constant/default-predefined-method-descriptor-registry') + +class TraceBuilder { + constructor(traceId, agentInfo, dataSender, sampling) { + this.traceId = traceId + this.agentInfo = agentInfo + this.dataSender = dataSender + this.sampling = sampling + + this.span = new Span(traceId, agentInfo) + this.spanRecorder = new SpanRecorder(this.span) + + this.callStack = [] + this.sequence = 0 + + const createSpanChunk = SpanChunk.getFactoryMethod(traceId, agentInfo) + this.storage = new BufferedStorage(dataSender, createSpanChunk) + } + + static valueOfTrace(trace) { + const value = new TraceBuilder(trace.traceId, trace.agentInfo, trace.dataSender, trace.sampling) + value.span = trace.span + value.spanRecorder = trace.spanRecorder + value.callStack = trace.callStack + value.sequence = trace.sequence + value.storage = trace.storage + return value + } + + buildAsyncTrace(asyncId) { + if (!asyncId) { + return new DisableAsyncTrace() + } + + const value = new AsyncTrace(this.span, asyncId, this.traceId, this.agentInfo, this.dataSender, this.sampling) + const spanEventRecorder = value.traceAsyncBegin() + spanEventRecorder.recordServiceType(serviceType) + spanEventRecorder.recordApiId(defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.getApiId()) + spanEventRecorder.spanEvent.endPoint = null + spanEventRecorder.spanEvent.destinationId = null + value.storage.storeSpanEvent(spanEventRecorder.spanEvent) + return value + } +} + +module.exports = TraceBuilder \ No newline at end of file diff --git a/lib/instrumentation/http-shared.js b/lib/instrumentation/http-shared.js index a3a19709..d9393c48 100644 --- a/lib/instrumentation/http-shared.js +++ b/lib/instrumentation/http-shared.js @@ -15,6 +15,8 @@ const RequestHeaderUtils = require('../instrumentation/request-header-utils') const AntPathMatcher = require('../utils/ant-path-matcher') const defaultPredefinedMethodDescriptorRegistry = require('../constant/default-predefined-method-descriptor-registry') const ServiceType = require('../context/service-type') +const localStorage = require('./context/local-storage') +const TraceBuilder = require('./context/trace-builder') let pathMatcher const getPathMatcher = () => { @@ -31,50 +33,56 @@ exports.clearPathMatcher = function () { exports.instrumentRequest = function (agent, moduleName) { return function (original) { return function (event, req, res) { - if (event === 'request') { - if (log.isInfo()) { - log.info('instrumentRequest in http-shared.js ' + JSON.stringify(req)) - } + if (event !== 'request') { + return original.apply(this, arguments) + } - const requestData = RequestHeaderUtils.read(req) - if (!getPathMatcher().matchPath(requestData.rpcName)) { - const trace = agent.createTraceObject(requestData) - if (trace && trace.canSampled()) { - recordRequest(trace.spanRecorder, requestData) - trace.spanRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) - if (requestData && typeof requestData.searchParams === 'string') { - trace.spanRecorder.recordAttribute(annotationKey.HTTP_PARAM, requestData.searchParams) - } - } + if (log.isInfo()) { + log.info('instrumentRequest in http-shared.js ' + JSON.stringify(req)) + } - endOfStream(res, function (err) { - if (log.isInfo()) { - log.info('endOfStream in http-shared.js ' + JSON.stringify(res)) + const requestData = RequestHeaderUtils.read(req) + if (getPathMatcher().matchPath(requestData.rpcName)) { + return original.apply(this, arguments) + } + + const trace = agent.createTraceObject(requestData) + return localStorage.run(trace, () => { + if (trace && trace.canSampled()) { + recordRequest(trace.spanRecorder, requestData) + trace.spanRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) + if (requestData && typeof requestData.searchParams === 'string') { + trace.spanRecorder.recordAttribute(annotationKey.HTTP_PARAM, requestData.searchParams) + } + } + + endOfStream(res, function (err) { + if (log.isInfo()) { + log.info('endOfStream in http-shared.js ' + JSON.stringify(res)) + } + if (!err) { + if (trace && trace.canSampled()) { + trace.spanRecorder.recordAttribute(annotationKey.HTTP_STATUS_CODE, this.statusCode) + return agent.completeTraceObject(trace) } - if (!err) { - if (trace && trace.canSampled()) { + } + + // Handle case where res.end is called after an error occurred on the + // stream (e.g. if the underlying socket was prematurely closed) + const end = res.end + res.end = function () { + const result = end.apply(this, arguments) + if (trace && trace.canSampled()) { + if (this.statusCode) { trace.spanRecorder.recordAttribute(annotationKey.HTTP_STATUS_CODE, this.statusCode) - return agent.completeTraceObject(trace) - } - } - - // Handle case where res.end is called after an error occurred on the - // stream (e.g. if the underlying socket was prematurely closed) - const end = res.end - res.end = function () { - const result = end.apply(this, arguments) - if (trace && trace.canSampled()) { - if (this.statusCode) { - trace.spanRecorder.recordAttribute(annotationKey.HTTP_STATUS_CODE, this.statusCode) - } - return agent.completeTraceObject(trace) } - return result + return agent.completeTraceObject(trace) } - }) - } - } - return original.apply(this, arguments) + return result + } + }) + return original.apply(this, arguments) + }) } } } @@ -105,7 +113,7 @@ exports.traceOutgoingRequest = function (agent, moduleName) { const asyncId = spanEventRecorder.recordNextAsyncId() trace.traceBlockEnd(spanEventRecorder) - const asyncTrace = trace.newAsyncTraceWithId(asyncId) + const asyncTrace = TraceBuilder.valueOfTrace(trace).buildAsyncTrace(asyncId) const asyncEventRecorder = asyncTrace.traceAsyncBegin() asyncEventRecorder.recordServiceType(ServiceType.asyncHttpClient) asyncEventRecorder.recordApiDesc(req.method) diff --git a/lib/instrumentation/instrument-method.js b/lib/instrumentation/instrument-method.js index 9034e3cb..b9c956f7 100644 --- a/lib/instrumentation/instrument-method.js +++ b/lib/instrumentation/instrument-method.js @@ -12,6 +12,7 @@ const IdGenerator = require('../context/id-generator') const { callSite } = require('./call-stack') const InstrumentMethodContext = require('./instrument-method-context') const config = require('../config') +const TraceBuilder = require('./context/trace-builder') // ref: SpanEventSimpleAroundInterceptorForPlugin.java class InstrumentMethod { @@ -79,8 +80,9 @@ class InstrumentMethod { let asyncTrace let asyncEventRecorder - if (trace) { - asyncTrace = trace.newAsyncTrace(recorder) + if (trace && recorder && typeof recorder.recordNextAsyncId === 'function') { + const asyncId = recorder.recordNextAsyncId() + asyncTrace = TraceBuilder.valueOfTrace(trace).buildAsyncTrace(asyncId) asyncEventRecorder = asyncTrace.traceAsyncBegin() const nextSpanId = IdGenerator.next asyncEventRecorder.recordNextSpanId(nextSpanId) diff --git a/lib/instrumentation/method-descriptor2.js b/lib/instrumentation/method-descriptor.js similarity index 93% rename from lib/instrumentation/method-descriptor2.js rename to lib/instrumentation/method-descriptor.js index 1a0c6168..efc5de95 100644 --- a/lib/instrumentation/method-descriptor2.js +++ b/lib/instrumentation/method-descriptor.js @@ -5,7 +5,7 @@ */ // https://v8.dev/docs/stack-trace-api#appendix%3A-stack-trace-format -class MethodDescriptor2 { +class MethodDescriptor { constructor(functionName) { this.functionName = functionName } @@ -51,4 +51,4 @@ class MethodDescriptor2 { } } -module.exports = MethodDescriptor2 \ No newline at end of file +module.exports = MethodDescriptor \ No newline at end of file diff --git a/lib/instrumentation/module/ioredis.js b/lib/instrumentation/module/ioredis.js index 14d5a84d..e7a6565d 100644 --- a/lib/instrumentation/module/ioredis.js +++ b/lib/instrumentation/module/ioredis.js @@ -12,6 +12,7 @@ const ServiceType = require('../../context/service-type') const log = require('../../utils/logger') const IdGenerator = require('../../context/id-generator') const { addressStringOf } = require('../../utils/convert-utils') +const TraceBuilder = require('../context/trace-builder') const spanSym = Symbol('ioredis') const asyncSym = Symbol('ioredis-async') @@ -69,7 +70,8 @@ module.exports = function (agent, version, ioredis) { trace.traceBlockEnd(spanEventRecorder) command[asyncSym] = asyncTrace - asyncTrace = trace.newAsyncTrace(spanEventRecorder) + const asyncId = spanEventRecorder.recordNextAsyncId() + asyncTrace = TraceBuilder.valueOfTrace(trace).buildAsyncTrace(asyncId) const nextSpanId = IdGenerator.next const asyncEventRecorder = asyncTrace.traceAsyncBegin() asyncEventRecorder.recordServiceType(ServiceType.redis) diff --git a/lib/instrumentation/module/redis.js b/lib/instrumentation/module/redis.js index e3cb0a75..b42911af 100644 --- a/lib/instrumentation/module/redis.js +++ b/lib/instrumentation/module/redis.js @@ -12,6 +12,7 @@ const ServiceType = require('../../context/service-type') const log = require('../../utils/logger') const IdGenerator = require('../../context/id-generator') const { addressStringOf } = require('../../utils/convert-utils') +const TraceBuilder = require('../context/trace-builder') module.exports = function (agent, version, redis) { if (!semver.satisfies(version, '>2.5.3')) { @@ -55,7 +56,8 @@ module.exports = function (agent, version, redis) { } trace.traceBlockEnd(spanEventRecorder) - const asyncTrace = trace.newAsyncTrace(spanEventRecorder) + const asyncId = spanEventRecorder.recordNextAsyncId() + const asyncTrace = TraceBuilder.valueOfTrace(trace).buildAsyncTrace(asyncId) const nextSpanId = IdGenerator.next const asyncEventRecorder = asyncTrace.traceAsyncBegin() asyncEventRecorder.recordServiceType(ServiceType.redis) diff --git a/test/client/data-sender.test.js b/test/client/data-sender.test.js index e97cd646..ae2cbc97 100644 --- a/test/client/data-sender.test.js +++ b/test/client/data-sender.test.js @@ -5,8 +5,6 @@ */ const test = require('tape') -const axios = require('axios') - const { fixture } = require('../test-helper') const Trace = require('../../lib/context/trace') @@ -14,20 +12,9 @@ const dataSenderMock = require('../support/data-sender-mock') const dataSender = dataSenderMock() const AgentInfo = require('../../lib/data/dto/agent-info') const agentInfo = AgentInfo.create(fixture.config, Date.now()) -const MethodDescriptor = require('../../lib/context/method-descriptor') -const MethodType = require('../../lib/constant/method-type') const ApiMetaInfo = require('../../lib/data/dto/api-meta-info') const StringMetaInfo = require('../../lib/data/dto/string-meta-info') - -// const { log, fixture, util, enableDataSending } = require('../test-helper') -// enableDataSending() - -// const Trace = require('../../lib/context/trace') -// const dataSenderFactory = require('../../lib/client/data-sender-factory') - -// const GRPC_ENABLE = false -// fixture.config['grpcEnable'] = GRPC_ENABLE -// const dataSender = dataSenderFactory.create(fixture.config, agentInfo) +const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/default-predefined-method-descriptor-registry') test('Should send agent info', function (t) { t.plan(1) @@ -40,7 +27,7 @@ test('Should send agent info', function (t) { test('Should send api meta info', function (t) { t.plan(1) - const methodDescriptor = new MethodDescriptor('http', 'Server', 'request', MethodType.WEB_REQUEST, 'Node Server Process') + const methodDescriptor = defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor const apiMetaInfo = ApiMetaInfo.create(methodDescriptor) dataSender.send(apiMetaInfo) diff --git a/test/context/context-manager.test.js b/test/context/context-manager.test.js index dc4ce701..6e703cb3 100644 --- a/test/context/context-manager.test.js +++ b/test/context/context-manager.test.js @@ -5,33 +5,31 @@ */ const test = require('tape') -const { log, fixture, util } = require('../test-helper') - -const contextManger = require('../../lib/context/context-manager') +const localStorage = require('../../lib/instrumentation/context/local-storage') test('Should trace context in setTimeout', async function (t) { t.plan(1) const initVal = 'value-for-test' - contextManger.setObject(initVal) - - setTimeout(() => { - const currentVal = contextManger.getObject() - t.equal(currentVal, initVal) - }, 300) + localStorage.run(initVal, () => { + setTimeout(() => { + const currentVal = localStorage.getStore() + t.equal(currentVal, initVal) + }, 300) + }) }) test('Should trace context in setInterval', async function (t) { t.plan(1) const initVal = 'value-for-test' - contextManger.setObject(initVal) - - const interval = setInterval(() => { - const currentVal = contextManger.getObject() - t.equal(currentVal, initVal) - clearInterval(interval) - }, 300) + localStorage.run(initVal, () => { + const interval = setInterval(() => { + const currentVal = localStorage.getStore() + t.equal(currentVal, initVal) + clearInterval(interval) + }, 300) + }) }) @@ -39,19 +37,19 @@ test('Should trace context in Promise', function (t) { t.plan(2) const initVal = 'value-for-test' - contextManger.setObject(initVal) - - const promise = new Promise((resolve, reject) => { - setTimeout(() => { - const currentVal = contextManger.getObject() - t.equal(currentVal, initVal) - resolve() - }, 300) - }) - - promise.then(() => { - const resolvedVal = contextManger.getObject() - t.equal(resolvedVal, initVal) + localStorage.run(initVal, () => { + const promise = new Promise((resolve, reject) => { + setTimeout(() => { + const currentVal = localStorage.getStore() + t.equal(currentVal, initVal) + resolve() + }, 300) + }) + + promise.then(() => { + const resolvedVal = localStorage.getStore() + t.equal(resolvedVal, initVal) + }) }) }) @@ -59,16 +57,17 @@ test('Should trace context in setTimeout multiple times', async function (t) { t.plan(2) let initVal = 1 - contextManger.setObject(initVal) - - setTimeout(() => { - const currentVal = contextManger.getObject() - t.equal(currentVal, initVal) - contextManger.setObject(++initVal) - + localStorage.run(initVal, () => { setTimeout(() => { - const currentVal = contextManger.getObject() + const currentVal = localStorage.getStore() t.equal(currentVal, initVal) + + localStorage.run(++initVal, () => { + setTimeout(() => { + const currentVal = localStorage.getStore() + t.equal(currentVal, initVal) + }, 300) + }) }, 300) - }, 300) + }) }) diff --git a/test/context/trace-context.test.js b/test/context/trace-context.test.js index e450fdb2..30b81da6 100644 --- a/test/context/trace-context.test.js +++ b/test/context/trace-context.test.js @@ -12,6 +12,7 @@ const TraceContext = require('../../lib/context/trace-context') const dataSenderMock = require('../support/data-sender-mock') const RequestHeaderUtils = require('../../lib/instrumentation/request-header-utils') const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/default-predefined-method-descriptor-registry') +const localStorage = require('../../lib/instrumentation/context/local-storage') test('Should create continued trace and add span info', function (t) { t.plan(2) @@ -21,14 +22,15 @@ test('Should create continued trace and add span info', function (t) { const traceContext = TraceContext.init(fixture.getAgentInfo(), dataSenderMock()) const trace = traceContext.continueTraceObject(traceId) - - t.equal(traceContext.currentTraceObject().traceId.transactionId.toString(), transactionId.toString()) - - trace.spanRecorder.recordServiceType(ServiceType.express) - trace.spanRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) - - t.equal(traceContext.currentTraceObject().span.serviceType, ServiceType.express) - traceContext.completeTraceObject(trace) + localStorage.run(trace, () => { + t.equal(traceContext.currentTraceObject().traceId.transactionId.toString(), transactionId.toString()) + + trace.spanRecorder.recordServiceType(ServiceType.express) + trace.spanRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) + + t.equal(traceContext.currentTraceObject().span.serviceType, ServiceType.express) + traceContext.completeTraceObject(trace) + }) }) test('Should begin/end trace block asynchronously', async function (t) { @@ -37,26 +39,29 @@ test('Should begin/end trace block asynchronously', async function (t) { // start trace and write span info const traceContext = TraceContext.init(fixture.getAgentInfo(), dataSenderMock()) const startedTrace = traceContext.newTraceObject(true) - const spanRecorder = startedTrace.spanRecorder - spanRecorder.recordServiceType(ServiceType.express) - - const currentTrace = traceContext.currentTraceObject() - const spanEventRecorder = currentTrace.traceBlockBegin() - spanEventRecorder.recordServiceType(ServiceType.express) - spanEventRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) - - t.equal(traceContext.currentTraceObject().callStack.length, 1) - - const anotherContext = traceContext.currentTraceObject() - t.equal(anotherContext.traceId, currentTrace.traceId) - - const spanEventRecorder2 = anotherContext.traceBlockBegin() - t.equal(traceContext.currentTraceObject().callStack.length, 2) - - anotherContext.traceBlockEnd(spanEventRecorder2) - currentTrace.traceBlockEnd(spanEventRecorder) - t.equal(traceContext.currentTraceObject().callStack.length, 0, "traceBolckEnd callstack length is zero") + localStorage.run(startedTrace, () => { + const spanRecorder = startedTrace.spanRecorder + spanRecorder.recordServiceType(ServiceType.express) + + const currentTrace = traceContext.currentTraceObject() + const spanEventRecorder = currentTrace.traceBlockBegin() + spanEventRecorder.recordServiceType(ServiceType.express) + spanEventRecorder.recordApi(defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor) + + t.equal(traceContext.currentTraceObject().callStack.length, 1) + + const anotherContext = traceContext.currentTraceObject() + t.equal(anotherContext.traceId, currentTrace.traceId) + + const spanEventRecorder2 = anotherContext.traceBlockBegin() + t.equal(traceContext.currentTraceObject().callStack.length, 2) + + anotherContext.traceBlockEnd(spanEventRecorder2) + + currentTrace.traceBlockEnd(spanEventRecorder) + t.equal(traceContext.currentTraceObject().callStack.length, 0, "traceBolckEnd callstack length is zero") + }) }) test('Should complete trace ', async function (t) { diff --git a/test/instrumentation/fix-async-call-stack.test.js b/test/instrumentation/fix-async-call-stack.test.js index f226b6e1..31d98956 100644 --- a/test/instrumentation/fix-async-call-stack.test.js +++ b/test/instrumentation/fix-async-call-stack.test.js @@ -7,6 +7,7 @@ const test = require('tape') const agent = require('../support/agent-singleton-mock') const { GenericContainer } = require('testcontainers') +const localStorage = require('../../lib/instrumentation/context/local-storage') test(`fix redis call stack depth`, async (t) => { const container = await new GenericContainer('redis') @@ -17,21 +18,23 @@ test(`fix redis call stack depth`, async (t) => { t.plan(2) - agent.createTraceObject() - const redis = require('redis') - const client = redis.createClient( - container.getMappedPort(6379), - container.getHost(), - ) - - client.set('key', 'value', async function (error) { - t.true(error == null, 'error is null') - - const trace = agent.traceContext.currentTraceObject() - t.equal(trace.callStack.length, 0, 'callStack is 0') - - client.quit() - agent.completeTraceObject(trace) - await container.stop() + const trace = agent.createTraceObject() + localStorage.run(trace, () => { + const redis = require('redis') + const client = redis.createClient( + container.getMappedPort(6379), + container.getHost(), + ) + + client.set('key', 'value', async function (error) { + t.true(error == null, 'error is null') + + const trace = agent.traceContext.currentTraceObject() + t.equal(trace.callStack.length, 0, 'callStack is 0') + + client.quit() + agent.completeTraceObject(trace) + await container.stop() + }) }) }) \ No newline at end of file diff --git a/test/instrumentation/module/express.test.js b/test/instrumentation/module/express.test.js index 47b1e784..bb9b5707 100644 --- a/test/instrumentation/module/express.test.js +++ b/test/instrumentation/module/express.test.js @@ -237,7 +237,7 @@ test(`[${testName2}] Should record request in express.Router`, function (t) { const testName = testName2 - t.plan(3) + t.plan(2) const PATH = '/' + testName const app = new express() @@ -263,10 +263,6 @@ test(`[${testName2}] Should record request in express.Router`, function (t) { const result2 = await axios.get(getServerUrl(`/express.router2${PATH}`)) t.ok(result2.status, 200) - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -277,7 +273,7 @@ test(`${testName3} Should record request taking more than 2 sec`, function (t) { const testName = testName3 - t.plan(2) + t.plan(1) const PATH = '/' + testName const app = new express() @@ -295,11 +291,6 @@ test(`${testName3} Should record request taking more than 2 sec`, function (t) { const server = app.listen(TEST_ENV.port, async function () { const result = await axios.get(getServerUrl(PATH)) t.ok(result.status, 200) - - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -310,7 +301,7 @@ test(`${testName4} Should record internal error in express.test.js`, function (t const testName = testName4 - t.plan(2) + t.plan(1) const PATH = '/' + testName const app = new express() @@ -333,10 +324,6 @@ test(`${testName4} Should record internal error in express.test.js`, function (t const server = app.listen(TEST_ENV.port, async function () { const result = await axios.get(getServerUrl(PATH)) t.ok(result.status, 500) - - const traceMap = agent.traceContext.getAllTraceObject() - t.ok(traceMap.size > 0) - server.close() }) }) @@ -369,14 +356,14 @@ test(`${testName5} Should record middleware`, function (t) { agent.callbackTraceClose((trace) =>{ let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(368) + .setLineNumber(355) .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, 368, 'lineNumber') + t.equal(actualMethodDescriptor.lineNumber, 355, 'lineNumber') t.equal(actualMethodDescriptor.methodName, 'get', 'methodName') t.true(actualMethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 2, 'sequence') @@ -384,14 +371,14 @@ test(`${testName5} Should record middleware`, function (t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(358) + .setLineNumber(345) .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, 358, 'lineNumber') + t.equal(actualMiddleware1MethodDescriptor.lineNumber, 345, 'lineNumber') t.equal(actualMiddleware1MethodDescriptor.functionName, 'use', 'functionName') t.true(actualMiddleware1MethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 1, 'sequence') @@ -399,14 +386,14 @@ test(`${testName5} Should record middleware`, function (t) { actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') - .setLineNumber(353) + .setLineNumber(340) .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, 353, 'lineNumber') + t.equal(actualMiddleware2MethodDescriptor.lineNumber, 340, 'lineNumber') t.equal(actualMiddleware2MethodDescriptor.methodName, 'use', 'methodName') t.true(actualMiddleware2MethodDescriptor.location.endsWith('express.test.js'), 'location') t.equal(spanEvent.sequence, 0, 'sequence') @@ -431,7 +418,7 @@ test(`${testName6} Should record each http method`, function (t) { const testName = testName6 - t.plan(6) + t.plan(5) const PATH = '/' + testName const app = new express() @@ -469,10 +456,6 @@ test(`${testName6} Should record each http method`, function (t) { const result5 = await axios.delete(getServerUrl(PATH)) t.ok(result5.status, 200) - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -641,7 +624,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:562:11'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:545:11'), 'error case') } function nextErrorHandleTestWithoutCallSite(trace, t) { @@ -667,5 +650,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:569:10'), 'error case') + t.true(spanEvent.exceptionInfo.stringValue.endsWith('express.test.js:552:10'), 'error case') } diff --git a/test/instrumentation/module/fix-redis.test.js b/test/instrumentation/module/fix-redis.test.js index 5a9eeae9..b22cb187 100644 --- a/test/instrumentation/module/fix-redis.test.js +++ b/test/instrumentation/module/fix-redis.test.js @@ -8,6 +8,7 @@ const test = require('tape') const agent = require('../../support/agent-singleton-mock') const { GenericContainer } = require('testcontainers') const { addressStringOf } = require('../../../lib/utils/convert-utils') +const localStorage = require('../../../lib/instrumentation/context/local-storage') test(`redis destination id`, async (t) => { const container = await new GenericContainer('redis') @@ -18,37 +19,39 @@ test(`redis destination id`, async (t) => { t.plan(6) - agent.createTraceObject() - const redis = require('redis') - - const client = redis.createClient( - container.getMappedPort(6379), - container.getHost(), - ) - - client.on("error", function (error) { - console.error(error) - }) - - client.set("key", "value", async function (error) { - t.true(error == null, "error is null") - - const trace = agent.traceContext.currentTraceObject() - t.equal(trace.callStack.length, 0, "callStack is 0") - }) - t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "set spanevent callstack") - - client.get("key", async function (error, data) { - t.equal(data, "value", "redis value validation") - - const trace = agent.traceContext.currentTraceObject() - t.equal(trace.callStack.length, 0, "callStack is 0") - - client.quit() - agent.completeTraceObject(trace) - await container.stop() + const trace = agent.createTraceObject() + localStorage.run(trace, () => { + const redis = require('redis') + + const client = redis.createClient( + container.getMappedPort(6379), + container.getHost(), + ) + + client.on("error", function (error) { + console.error(error) + }) + + client.set("key", "value", async function (error) { + t.true(error == null, "error is null") + + const trace = agent.traceContext.currentTraceObject() + t.equal(trace.callStack.length, 0, "callStack is 0") + }) + t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "set spanevent callstack") + + client.get("key", async function (error, data) { + t.equal(data, "value", "redis value validation") + + const trace = agent.traceContext.currentTraceObject() + t.equal(trace.callStack.length, 0, "callStack is 0") + + client.quit() + agent.completeTraceObject(trace) + await container.stop() + }) + t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "get spanevent callstack") }) - t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "get spanevent callstack") }) test("ioredis destination id", async function (t) { @@ -61,31 +64,33 @@ test("ioredis destination id", async function (t) { t.plan(5) const trace = agent.createTraceObject() - const Redis = require('ioredis') - const port = container.getMappedPort(6379) - const redis = new Redis( - port, - container.getHost(), - ) - redis.on("error", function (error) { - console.error(error) - }) - - const result = await redis.set("key", "value") - t.equal(result, "OK", "Success set data") - - redis.get("key", async function (error, data) { - t.equal(data, "value", "redis value validation") - - t.true(agent.dataSender.mockSpanChunks[0].spanEventList.length > 0, "a spanEventList should has one chunk") - - const spanevent = agent.dataSender.mockSpanChunks[0].spanEventList[1] - t.equal(spanevent.destinationId, "Redis", "Redis destionation ID check") - t.true(spanevent.endPoint.endsWith(`:${port}`), `localhost:${port}`) - - redis.quit() - agent.completeTraceObject(trace) - await container.stop() + localStorage.run(trace, async () => { + const Redis = require('ioredis') + const port = container.getMappedPort(6379) + const redis = new Redis( + port, + container.getHost(), + ) + redis.on("error", function (error) { + console.error(error) + }) + + const result = await redis.set("key", "value") + t.equal(result, "OK", "Success set data") + + redis.get("key", async function (error, data) { + t.equal(data, "value", "redis value validation") + + t.true(agent.dataSender.mockSpanChunks[0].spanEventList.length > 0, "a spanEventList should has one chunk") + + const spanevent = agent.dataSender.mockSpanChunks[0].spanEventList[1] + t.equal(spanevent.destinationId, "Redis", "Redis destionation ID check") + t.true(spanevent.endPoint.endsWith(`:${port}`), `localhost:${port}`) + + redis.quit() + agent.completeTraceObject(trace) + await container.stop() + }) }) }) @@ -115,38 +120,40 @@ test(`Fix app crash without callback function https://github.com/pinpoint-apm/pi t.plan(6) - agent.createTraceObject() - const redis = require('redis') - - const client = redis.createClient({ - host: container.getHost(), - port: container.getMappedPort(6379), - db: 3, - }) - - client.select(2) - - client.on("error", function (error) { - console.error(error) - }) - - client.set("key", "value", async function (error) { - t.true(error == null, "error is null") - - const trace = agent.traceContext.currentTraceObject() - t.equal(trace.callStack.length, 0, "callStack is 0") - }) - t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "set spanevent callstack") - - client.get("key", async function (error, data) { - t.equal(data, "value", "redis value validation") - - const trace = agent.traceContext.currentTraceObject() - t.equal(trace.callStack.length, 0, "callStack is 0") - - client.quit() - agent.completeTraceObject(trace) - await container.stop() + const trace = agent.createTraceObject() + localStorage.run(trace, () => { + const redis = require('redis') + + const client = redis.createClient({ + host: container.getHost(), + port: container.getMappedPort(6379), + db: 3, + }) + + client.select(2) + + client.on("error", function (error) { + console.error(error) + }) + + client.set("key", "value", async function (error) { + t.true(error == null, "error is null") + + const trace = agent.traceContext.currentTraceObject() + t.equal(trace.callStack.length, 0, "callStack is 0") + }) + t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "set spanevent callstack") + + client.get("key", async function (error, data) { + t.equal(data, "value", "redis value validation") + + const trace = agent.traceContext.currentTraceObject() + t.equal(trace.callStack.length, 0, "callStack is 0") + + client.quit() + agent.completeTraceObject(trace) + await container.stop() + }) + t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "get spanevent callstack") }) - t.equal(agent.traceContext.currentTraceObject().callStack.length, 0, "get spanevent callstack") }) diff --git a/test/instrumentation/module/http.test.js b/test/instrumentation/module/http.test.js index d2ad6633..b181fa3b 100644 --- a/test/instrumentation/module/http.test.js +++ b/test/instrumentation/module/http.test.js @@ -7,6 +7,7 @@ const test = require('tape') const agent = require('../../support/agent-singleton-mock') const axios = require('axios') +const localStorage = require('../../../lib/instrumentation/context/local-storage') test(`outgoing request URL escape a bug`, async (t) => { agent.bindHttp() @@ -14,17 +15,19 @@ test(`outgoing request URL escape a bug`, async (t) => { t.plan(5) const trace = agent.createTraceObject() - t.true(trace) - - axios.get(`https://www.naver.com`) - .then(function (response) { - t.true(response.status == 200) - - t.true(agent.dataSender.mockSpanChunks[0].spanEventList.length == 2, `spanEventList`) - - const spanEvent = agent.dataSender.mockSpanChunks[0].spanEventList[1] - t.equal(spanEvent.annotations[0].value, "GET", "URL") - t.equal(spanEvent.annotations[1].value, "www.naver.com/", "URL") - agent.completeTraceObject(trace) - }) + localStorage.run(trace, () => { + t.true(trace) + + axios.get(`https://www.naver.com`) + .then(function (response) { + t.true(response.status == 200) + + t.true(agent.dataSender.mockSpanChunks[0].spanEventList.length == 2, `spanEventList`) + + const spanEvent = agent.dataSender.mockSpanChunks[0].spanEventList[1] + t.equal(spanEvent.annotations[0].value, "GET", "URL") + t.equal(spanEvent.annotations[1].value, "www.naver.com/", "URL") + agent.completeTraceObject(trace) + }) + }) }) \ No newline at end of file diff --git a/test/instrumentation/module/koa.test.js b/test/instrumentation/module/koa.test.js index 25d19381..fa2352fe 100644 --- a/test/instrumentation/module/koa.test.js +++ b/test/instrumentation/module/koa.test.js @@ -65,10 +65,6 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( 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() }) @@ -113,10 +109,6 @@ test(`${testName1} Should record request in basic route koa.test.js`, function ( 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 252af5ce..54283cdb 100644 --- a/test/instrumentation/module/mysql.test.js +++ b/test/instrumentation/module/mysql.test.js @@ -17,8 +17,9 @@ const defaultPredefinedMethodDescriptorRegistry = require('../../../lib/constant const mysqlExecuteQueryServiceType = require('../../../lib/instrumentation/module/mysql/mysql-execute-query-service-type') const mysqlServiceType = require('../../../lib/instrumentation/module/mysql/mysql-service-type') const ServiceType = require('../../../lib/context/service-type') - +const localStorage = require('../../../lib/instrumentation/context/local-storage') const fixtures = path.resolve(__dirname, '..', '..', 'fixtures', 'db') + test(`getConnection query hooking`, async (t) => { agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') @@ -34,63 +35,65 @@ test(`getConnection query hooking`, async (t) => { }]) .start() - agent.createTraceObject() - const connection = mysql.createConnection({ - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 1000000, - timezone: '+09:00' - }) - connection.connect(function (err) { - if (err) { - console.error('error connecting: ' + err.stack) - return - } - }) - connection.query(`SELECT * FROM member`, async function (error, results) { - if (error) throw error - t.equal(results[0].id, 'a', 'SELECT member id') - t.equal(results[0].name, 'name1', 'SELECT member name') - t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') - - connection.end() - await container.stop() - const trace = agent.currentTraceObject() - trace.close() - - let actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(38) - .setFileName('mysql.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const createConnectionSpanEvent = trace.span.spanEventList[0] - t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(createConnectionSpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, createConnectionSpanEvent.apiId, 'apiId') - - actualBuilder = new MethodDescriptorBuilder('connect') + const trace = agent.createTraceObject() + localStorage.run(trace, () => { + const connection = mysql.createConnection({ + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 1000000, + timezone: '+09:00' + }) + connection.connect(function (err) { + if (err) { + console.error('error connecting: ' + err.stack) + return + } + }) + connection.query(`SELECT * FROM member`, async function (error, results) { + if (error) throw error + t.equal(results[0].id, 'a', 'SELECT member id') + t.equal(results[0].name, 'name1', 'SELECT member name') + t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') + + connection.end() + await container.stop() + const trace = agent.currentTraceObject() + trace.close() + + let actualBuilder = new MethodDescriptorBuilder('createConnection') + .setLineNumber(40) + .setFileName('mysql.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const createConnectionSpanEvent = trace.span.spanEventList[0] + t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(createConnectionSpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, createConnectionSpanEvent.apiId, 'apiId') + + actualBuilder = new MethodDescriptorBuilder('connect') + .setClassName('Connection') + .setLineNumber(49) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const connectionSpanEvent = trace.span.spanEventList[1] + t.equal(connectionSpanEvent.depth, 1, 'connection spanEvent depth') + t.equal(connectionSpanEvent.sequence, 1, 'connection spanEvent sequence') + t.equal(actualMethodDescriptor.apiId, connectionSpanEvent.apiId, 'apiId') + + actualBuilder = new MethodDescriptorBuilder('query') .setClassName('Connection') - .setLineNumber(47) + .setLineNumber(55) .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const connectionSpanEvent = trace.span.spanEventList[1] - t.equal(connectionSpanEvent.depth, 1, 'connection spanEvent depth') - t.equal(connectionSpanEvent.sequence, 1, 'connection spanEvent sequence') - t.equal(actualMethodDescriptor.apiId, connectionSpanEvent.apiId, 'apiId') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(53) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const querySpanEvent = trace.span.spanEventList[2] - t.equal(querySpanEvent.depth, 1, 'query spanEvent depth') - t.equal(querySpanEvent.sequence, 2, 'query spanEvent sequence') - t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') - - t.end() + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const querySpanEvent = trace.span.spanEventList[2] + t.equal(querySpanEvent.depth, 1, 'query spanEvent depth') + t.equal(querySpanEvent.sequence, 2, 'query spanEvent sequence') + t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + + t.end() + }) }) }) @@ -110,140 +113,142 @@ test(`connection with query`, async (t) => { .start() const trace = agent.createTraceObject() - const connection = mysql.createConnection({ - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 1000000, - timezone: '+09:00' - }) - connection.connect(function (err) { - if (err) { - console.error('error connecting: ' + err.stack) - return - } - }) - - connection.query('SELECT DATABASE() as res', async function (error, results) { - if (error) throw error - t.equal(results[0].res, 'test', 'SELECT DATABASE() as res') - }) - connection.query(`SHOW TABLES`, async function (error, results) { - if (error) throw error - t.equal(results[0].Tables_in_test, 'member', 'SHOW TABLES') - }) - connection.query('SELECT * FROM `member` WHERE id = ?', 'a', async function (error) { - if (error) throw error - }) - connection.query('INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)', ['c', 'cname', '2023-08-18'], async function (error, results) { - if (error) throw error - t.equal(results.affectedRows, 1, 'INSERT INTO `member` (id, name) VALUES (?, ?)') - }) - connection.query('SELECT * FROM `member` WHERE id = ?', 'c', async function (error, results) { - if (error) throw error - - setImmediate(() => { - trace.close() - connection.end() - container.stop() + localStorage.run(trace, () => { + const connection = mysql.createConnection({ + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 1000000, + timezone: '+09:00' + }) + connection.connect(function (err) { + if (err) { + console.error('error connecting: ' + err.stack) + return + } }) - - t.equal(results[0].id, 'c', 'SELECT member id') - t.equal(results[0].name, 'cname', 'SELECT member name') - t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') - }) - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(113) - .setFileName('mysql.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const createConnectionSpanEvent = trace.span.spanEventList[0] - t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(createConnectionSpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, createConnectionSpanEvent.apiId, 'apiId') - - actualBuilder = new MethodDescriptorBuilder('connect') - .setClassName('Connection') - .setLineNumber(122) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const connectionSpanEvent = trace.span.spanEventList[1] - t.equal(connectionSpanEvent.depth, 1, 'connection spanEvent depth') - t.equal(connectionSpanEvent.sequence, 1, 'connection spanEvent sequence') - t.equal(actualMethodDescriptor.apiId, connectionSpanEvent.apiId, 'apiId') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(129) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let querySpanEvent = trace.span.spanEventList[2] - t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') - - let actualParsingResult = sqlMetadataService.cacheSql('SELECT DATABASE() as res') - let actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') - t.equal(actualParsingResult.sql.normalizedSql, 'SELECT DATABASE() as res', 'the query annotation squl normalizedSql') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(133) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[3] - t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') - - actualParsingResult = sqlMetadataService.cacheSql('SHOW TABLES') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') - t.equal(actualParsingResult.sql.normalizedSql, 'SHOW TABLES', 'the query annotation squl normalizedSql') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(137) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[4] - t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') - - actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM `member` WHERE id = ?') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'the query annotation value stringValue1 is sql normalizedSql parsedParameters') - t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'the query annotation value stringValue2 is bind value') - t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM `member` WHERE id = ?', 'the query annotation sql normalizedSql') - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(140) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[5] - t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') - t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') - t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + connection.query('SELECT DATABASE() as res', async function (error, results) { + if (error) throw error + t.equal(results[0].res, 'test', 'SELECT DATABASE() as res') + }) + connection.query(`SHOW TABLES`, async function (error, results) { + if (error) throw error + t.equal(results[0].Tables_in_test, 'member', 'SHOW TABLES') + }) + connection.query('SELECT * FROM `member` WHERE id = ?', 'a', async function (error) { + if (error) throw error + }) + connection.query('INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)', ['c', 'cname', '2023-08-18'], async function (error, results) { + if (error) throw error + t.equal(results.affectedRows, 1, 'INSERT INTO `member` (id, name) VALUES (?, ?)') + }) + connection.query('SELECT * FROM `member` WHERE id = ?', 'c', async function (error, results) { + if (error) throw error - actualParsingResult = sqlMetadataService.cacheSql('INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'the query annotation value stringValue1 is sql normalizedSql parsedParameters') - t.equal(actualQueryAnnotation.value.stringValue2, 'c,cname,2023-08-18', 'the query annotation value stringValue2 is bind value') - t.equal(actualParsingResult.sql.normalizedSql, 'INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)', 'the query annotation sql normalizedSql') + setImmediate(() => { + trace.close() + connection.end() + container.stop() + }) - t.end() + t.equal(results[0].id, 'c', 'SELECT member id') + t.equal(results[0].name, 'cname', 'SELECT member name') + t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') + }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createConnection') + .setLineNumber(117) + .setFileName('mysql.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const createConnectionSpanEvent = trace.span.spanEventList[0] + t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(createConnectionSpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, createConnectionSpanEvent.apiId, 'apiId') + + actualBuilder = new MethodDescriptorBuilder('connect') + .setClassName('Connection') + .setLineNumber(126) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const connectionSpanEvent = trace.span.spanEventList[1] + t.equal(connectionSpanEvent.depth, 1, 'connection spanEvent depth') + t.equal(connectionSpanEvent.sequence, 1, 'connection spanEvent sequence') + t.equal(actualMethodDescriptor.apiId, connectionSpanEvent.apiId, 'apiId') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(133) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let querySpanEvent = trace.span.spanEventList[2] + t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + + let actualParsingResult = sqlMetadataService.cacheSql('SELECT DATABASE() as res') + let actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') + t.equal(actualParsingResult.sql.normalizedSql, 'SELECT DATABASE() as res', 'the query annotation squl normalizedSql') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(137) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[3] + t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + + actualParsingResult = sqlMetadataService.cacheSql('SHOW TABLES') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') + t.equal(actualParsingResult.sql.normalizedSql, 'SHOW TABLES', 'the query annotation squl normalizedSql') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(141) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[4] + t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + + actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM `member` WHERE id = ?') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'the query annotation value stringValue1 is sql normalizedSql parsedParameters') + t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'the query annotation value stringValue2 is bind value') + t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM `member` WHERE id = ?', 'the query annotation sql normalizedSql') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(144) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[5] + t.equal(querySpanEvent.endPoint, 'localhost', 'the createConnection SpanEvent endPoint') + t.equal(querySpanEvent.destinationId, 'test', 'the createConnection SpanEvent destinationId') + t.equal(actualMethodDescriptor.apiId, querySpanEvent.apiId, 'apiId') + + actualParsingResult = sqlMetadataService.cacheSql('INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'the query annotation key') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'the query annotation value') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'the query annotation value stringValue1 is sql normalizedSql parsedParameters') + t.equal(actualQueryAnnotation.value.stringValue2, 'c,cname,2023-08-18', 'the query annotation value stringValue2 is bind value') + t.equal(actualParsingResult.sql.normalizedSql, 'INSERT INTO `member` (id, name, joined) VALUES (?, ?, ?)', 'the query annotation sql normalizedSql') + + t.end() + }) }) }) @@ -263,124 +268,98 @@ test(`Connection Pool with query`, async (t) => { .start() const trace = agent.createTraceObject() - const pool = mysql.createPool({ - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 10000000, - timezone: '+09:00' - }) - - let queryIndex = 0 - pool.getConnection(function (err, connection) { - if (err) throw err - connection.query('SELECT * FROM `member` where id = ?', 'a', async function (error) { - connection.release() - if (error) throw error + localStorage.run(trace, () => { + const pool = mysql.createPool({ + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 10000000, + timezone: '+09:00' }) - queryIndex++ - if (queryIndex == 2) { - setImmediate(async () => { - trace.close() - pool.end() - await container.stop() - }) - } - }) - pool.query('SELECT * FROM `member` where id = ?', 'b', async function (error, results) { - if (error) throw error - t.equal(results[0].id, 'b', 'SELECT member id') - t.equal(results[0].name, 'name2', 'SELECT member name') - t.equal(results[0].joined.getDate(), new Date('2022-07-27T00:00:00+09:00').getDate(), 'SELECT member joined') - queryIndex++ - if (queryIndex == 2) { - setImmediate(async () => { - trace.close() - pool.end() - await container.stop() + let queryIndex = 0 + pool.getConnection(function (err, connection) { + if (err) throw err + connection.query('SELECT * FROM `member` where id = ?', 'a', async function (error) { + connection.release() + if (error) throw error }) - } - }) - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createPool') - .setLineNumber(266) - .setFileName('mysql.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let actualSpanEvent = trace.span.spanEventList[0] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId') - t.equal(actualSpanEvent.endPoint, 'localhost', 'createPool SpanEvent endPoint') - t.equal(actualSpanEvent.destinationId, 'test', 'createPool SpanEvent destinationId') - t.equal(actualSpanEvent.sequence, 0, 'createPool spanEvent sequence') - t.equal(actualSpanEvent.depth, 1, 'createPool spanEvent depth') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'createPool spanEvent serviceType') - - actualBuilder = new MethodDescriptorBuilder('getConnection') - .setClassName('Pool') - .setLineNumber(277) - .setFileName('mysql.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[1] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId') - t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth') - t.equal(actualSpanEvent.sequence, 1, 'Pool.getConnection spanEvent sequence') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType') - - let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null') - - actualBuilder = new MethodDescriptorBuilder('getConnection') - .setClassName('Pool') - .setLineNumber(202) - .setFileName('Pool.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[2] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId on pool.query') - t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth on pool.query') - t.equal(actualSpanEvent.sequence, 2, 'Pool.getConnection spanEvent sequence on pool.query') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType on pool.query') - - actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId on pool.query') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject on pool.query') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId on pool.query') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 on pool.query') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId on pool.query') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 on pool.query') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 on pool.query') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async on pool.query') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId on pool.query') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 on pool.query') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 on pool.query') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null on pool.query') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('PoolConnection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[3] - 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.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query') - - const asyncSpanChunkMatcher = (actualSpanEvent) => { - if (!actualSpanEvent.nextAsyncId) { - return + queryIndex++ + if (queryIndex == 2) { + setImmediate(async () => { + trace.close() + pool.end() + await container.stop() + }) + } + }) + + pool.query('SELECT * FROM `member` where id = ?', 'b', async function (error, results) { + if (error) throw error + t.equal(results[0].id, 'b', 'SELECT member id') + t.equal(results[0].name, 'name2', 'SELECT member name') + t.equal(results[0].joined.getDate(), new Date('2022-07-27T00:00:00+09:00').getDate(), 'SELECT member joined') + queryIndex++ + if (queryIndex == 2) { + setImmediate(async () => { + trace.close() + pool.end() + await container.stop() + }) } + }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createPool') + .setLineNumber(272) + .setFileName('mysql.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId') + t.equal(actualSpanEvent.endPoint, 'localhost', 'createPool SpanEvent endPoint') + t.equal(actualSpanEvent.destinationId, 'test', 'createPool SpanEvent destinationId') + t.equal(actualSpanEvent.sequence, 0, 'createPool spanEvent sequence') + t.equal(actualSpanEvent.depth, 1, 'createPool spanEvent depth') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'createPool spanEvent serviceType') + + actualBuilder = new MethodDescriptorBuilder('getConnection') + .setClassName('Pool') + .setLineNumber(283) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[1] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId') + t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth') + t.equal(actualSpanEvent.sequence, 1, 'Pool.getConnection spanEvent sequence') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType') + + let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null') + + actualBuilder = new MethodDescriptorBuilder('getConnection') + .setClassName('Pool') + .setLineNumber(202) + .setFileName('Pool.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[2] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId on pool.query') + t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth on pool.query') + t.equal(actualSpanEvent.sequence, 2, 'Pool.getConnection spanEvent sequence on pool.query') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType on pool.query') + actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId on pool.query') t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject on pool.query') @@ -393,20 +372,52 @@ test(`Connection Pool with query`, async (t) => { t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId on pool.query') t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 on pool.query') t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 on pool.query') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null on pool.query') - } - asyncSpanChunkMatcher(actualSpanEvent) - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('PoolConnection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[4] - 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.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query') - asyncSpanChunkMatcher(actualSpanEvent) - t.end() + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null on pool.query') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('PoolConnection') + .setLineNumber(285) + .setFileName('mysql.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[3] + 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.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query') + + const asyncSpanChunkMatcher = (actualSpanEvent) => { + if (!actualSpanEvent.nextAsyncId) { + return + } + actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId on pool.query') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject on pool.query') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId on pool.query') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 on pool.query') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId on pool.query') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 on pool.query') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 on pool.query') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async on pool.query') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId on pool.query') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 on pool.query') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 on pool.query') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null on pool.query') + } + asyncSpanChunkMatcher(actualSpanEvent) + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('PoolConnection') + .setLineNumber(214) + .setFileName('Pool.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[4] + 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.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType on pool.query') + asyncSpanChunkMatcher(actualSpanEvent) + t.end() + }) }) }) @@ -426,122 +437,126 @@ test(`Cluster with query`, async (t) => { .start() const trace = agent.createTraceObject() - const poolCluster = mysql.createPoolCluster() - poolCluster.add('MASTER', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 1000000, - timezone: '+09:00' - }) - poolCluster.add('SLAVE1', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 1000000, - timezone: '+09:00' - }) - poolCluster.add('SLAVE2', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - acquireTimeout: 1000000, - timezone: '+09:00' - }) - - poolCluster.getConnection('MASTER', function (err, connection) { - if (err) throw err - connection.query('SELECT * FROM `member` where id = ?', 'a', async function (error, results) { - connection.release() - if (error) throw error - - t.equal(results[0].id, 'a', 'SELECT member id') - t.equal(results[0].name, 'name1', 'SELECT member name') - t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') - - setImmediate(async () => { - trace.close() - poolCluster.end() - await container.stop() + localStorage.run(trace, () => { + const poolCluster = mysql.createPoolCluster() + poolCluster.add('MASTER', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 1000000, + timezone: '+09:00' + }) + poolCluster.add('SLAVE1', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 1000000, + timezone: '+09:00' + }) + poolCluster.add('SLAVE2', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + acquireTimeout: 1000000, + timezone: '+09:00' + }) + + poolCluster.getConnection('MASTER', function (err, connection) { + if (err) throw err + connection.query('SELECT * FROM `member` where id = ?', 'a', async function (error, results) { + connection.release() + if (error) throw error + + t.equal(results[0].id, 'a', 'SELECT member id') + t.equal(results[0].name, 'name1', 'SELECT member name') + t.equal(results[0].joined.getDate(), new Date('2023-01-18T00:00:00+09:00').getDate(), 'SELECT member joined') + + setImmediate(async () => { + trace.close() + poolCluster.end() + await container.stop() + }) }) }) - }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createPoolCluster') + .setLineNumber(441) + .setFileName('mysql.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = trace.span.spanEventList[0] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId') + t.equal(actualSpanEvent.sequence, 0, 'createPoolCluster spanEvent sequence') + t.equal(actualSpanEvent.depth, 1, 'createPoolCluster spanEvent depth') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'createPoolCluster spanEvent serviceType') + + actualBuilder = new MethodDescriptorBuilder('of') + .setLineNumber(142) + .setFileName('PoolCluster.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[1] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolCluster.of spanEvent apiId in poolCluster.getConnection') + t.equal(actualSpanEvent.depth, 1, 'PoolCluster.of spanEvent depth in poolCluster.getConnection') + t.equal(actualSpanEvent.sequence, 1, 'PoolCluster.of spanEvent sequence in poolCluster.getConnection') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'PoolCluster.of spanEvent serviceType in poolCluster.getConnection') + + actualBuilder = new MethodDescriptorBuilder('getConnection') + .setClassName('Pool') + .setLineNumber(145) + .setFileName('PoolCluster.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[2] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId in poolCluster.getConnection') + t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth in poolCluster.getConnection') + t.equal(actualSpanEvent.sequence, 2, 'Pool.getConnection spanEvent sequence in poolCluster.getConnection') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType in poolCluster.getConnection') + + let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId in poolCluster.getConnection') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject in poolCluster.getConnection') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId in poolCluster.getConnection') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null in poolCluster.getConnection') - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createPoolCluster') - .setLineNumber(429) + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('PoolConnection') + .setLineNumber(472) .setFileName('mysql.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let actualSpanEvent = trace.span.spanEventList[0] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'apiId') - t.equal(actualSpanEvent.sequence, 0, 'createPoolCluster spanEvent sequence') - t.equal(actualSpanEvent.depth, 1, 'createPoolCluster spanEvent depth') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'createPoolCluster spanEvent serviceType') - - actualBuilder = new MethodDescriptorBuilder('of') - .setLineNumber(142) - .setFileName('PoolCluster.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[1] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolCluster.of spanEvent apiId in poolCluster.getConnection') - t.equal(actualSpanEvent.depth, 1, 'PoolCluster.of spanEvent depth in poolCluster.getConnection') - t.equal(actualSpanEvent.sequence, 1, 'PoolCluster.of spanEvent sequence in poolCluster.getConnection') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'PoolCluster.of spanEvent serviceType in poolCluster.getConnection') - - actualBuilder = new MethodDescriptorBuilder('getConnection') - .setClassName('Pool') - .setLineNumber(145) - .setFileName('PoolCluster.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[2] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'Pool.getConnection spanEvent apiId in poolCluster.getConnection') - t.equal(actualSpanEvent.depth, 1, 'Pool.getConnection spanEvent depth in poolCluster.getConnection') - t.equal(actualSpanEvent.sequence, 2, 'Pool.getConnection spanEvent sequence in poolCluster.getConnection') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'Pool.getConnection spanEvent serviceType in poolCluster.getConnection') - - let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId in poolCluster.getConnection') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject in poolCluster.getConnection') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId in poolCluster.getConnection') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null in poolCluster.getConnection') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('PoolConnection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[3] - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolConnection.query spanEvent apiId in poolCluster.getConnection') - t.equal(actualSpanEvent.depth, 1, 'PoolConnection.query spanEvent depth in poolCluster.getConnection') - t.equal(actualSpanEvent.sequence, 3, 'PoolConnection.query spanEvent sequence in poolCluster.getConnection') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType in poolCluster.getConnection') - - actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId in poolCluster.getConnection') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject in poolCluster.getConnection') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId in poolCluster.getConnection') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 in poolCluster.getConnection') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null in poolCluster.getConnection') - - t.end() + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[3] + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'PoolConnection.query spanEvent apiId in poolCluster.getConnection') + t.equal(actualSpanEvent.depth, 1, 'PoolConnection.query spanEvent depth in poolCluster.getConnection') + t.equal(actualSpanEvent.sequence, 3, 'PoolConnection.query spanEvent sequence in poolCluster.getConnection') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'PoolConnection.query spanEvent serviceType in poolCluster.getConnection') + + actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanChunk spanId in poolCluster.getConnection') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'spanChunk transactionIdObject in poolCluster.getConnection') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'spanChunk localAsyncId.asyncId is spanEvent nextAsyncId in poolCluster.getConnection') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'spanChunk localAsyncId.sequence is spanEvent 0 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'spanChunk spanEventList[0].apiId must be asyncInvocationDescriptor.apiId in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'spanChunk spanEventList[0].depth is 1 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'spanChunk spanEventList[0].sequence is 0 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'spanChunk spanEventList[0].serviceType is ServiceTypeCode.async in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'spanChunk spanEventList[1].apiId must be actualSpanEvent.apiId in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'spanChunk spanEventList[1].depth is 2 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'spanChunk spanEventList[1].sequence is 1 in poolCluster.getConnection') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'spanChunk spanEventList[1].serviceType is null in poolCluster.getConnection') + + t.end() + }) }) }) \ No newline at end of file diff --git a/test/instrumentation/module/mysql2.test.js b/test/instrumentation/module/mysql2.test.js index f43d5f93..1ebef73b 100644 --- a/test/instrumentation/module/mysql2.test.js +++ b/test/instrumentation/module/mysql2.test.js @@ -18,8 +18,9 @@ const mysqlServiceType = require('../../../lib/instrumentation/module/mysql/mysq const mysqlExecuteQueryServiceType = require('../../../lib/instrumentation/module/mysql/mysql-execute-query-service-type') const defaultPredefinedMethodDescriptorRegistry = require('../../../lib/constant/default-predefined-method-descriptor-registry') const ServiceType = require('../../../lib/context/service-type') - +const localStorage = require('../../../lib/instrumentation/context/local-storage') const fixtures = path.resolve(__dirname, '..', '..', 'fixtures', 'db') + test(`getConnection query hooking`, async (t) => { agent.bindHttpWithCallSite() const source = path.resolve(fixtures, 'mysql.sql') @@ -36,123 +37,125 @@ test(`getConnection query hooking`, async (t) => { .start() const trace = agent.createTraceObject() - const connection = mysql.createConnection({ - host: container.getHost(), - port: container.getPort(3306), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) + localStorage.run(trace, () => { + const connection = mysql.createConnection({ + host: container.getHost(), + port: container.getPort(3306), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + + connection.query(`SELECT * FROM member WHERE id = ?`, 'a', function(err, results) { + if (err) throw err - connection.query(`SELECT * FROM member WHERE id = ?`, 'a', function(err, results) { - if (err) throw err - - t.equal(results[0].id, 'a', 'id in SELECT query hooking') - t.equal(results[0].name, 'name1', 'name in SELECT query hooking') - t.equal(results[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - }) - - connection.query(`INSERT INTO member (id, name, joined) VALUES (?, ?, ?)`, ['c', 'cname', '2023-08-18'], function(err) { - if (err) throw err - }) - - connection.query(`UPDATE member SET name = ? WHERE id = ?`, ['cname2', 'c'], function(err) { - if (err) throw err - }) - - connection.query(`DELETE FROM member WHERE id = ?`, 'c', function(err) { - if (err) throw err - - setImmediate(() => { - trace.close() - connection.end() - container.stop() + t.equal(results[0].id, 'a', 'id in SELECT query hooking') + t.equal(results[0].name, 'name1', 'name in SELECT query hooking') + t.equal(results[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') + }) + + connection.query(`INSERT INTO member (id, name, joined) VALUES (?, ?, ?)`, ['c', 'cname', '2023-08-18'], function(err) { + if (err) throw err + }) + + connection.query(`UPDATE member SET name = ? WHERE id = ?`, ['cname2', 'c'], function(err) { + if (err) throw err + }) + + connection.query(`DELETE FROM member WHERE id = ?`, 'c', function(err) { + if (err) throw err + + setImmediate(() => { + trace.close() + connection.end() + container.stop() + }) + }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createConnection') + .setLineNumber(41) + .setFileName('mysql2.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const createConnectionSpanEvent = trace.span.spanEventList[0] + t.equal(createConnectionSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createConnection spanEvent') + t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'endPoint in createConnection spanEvent') + t.equal(createConnectionSpanEvent.destinationId, 'test', 'destinationId in createConnection spanEvent') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(50) + .setFileName('mysql2.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let querySpanEvent = trace.span.spanEventList[1] + t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') + t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') + t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') + + let actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM member WHERE id = ?') + let actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') + t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'stringValue2 in query annotation') + t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM member WHERE id = ?', 'normalizedSql in query annotation') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(58) + .setFileName('mysql2.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[2] + t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') + t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') + t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') + + actualParsingResult = sqlMetadataService.cacheSql('INSERT INTO member (id, name, joined) VALUES (?, ?, ?)') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') + t.equal(actualQueryAnnotation.value.stringValue2, 'c,cname,2023-08-18', 'stringValue2 in query annotation') + t.equal(actualParsingResult.sql.normalizedSql, 'INSERT INTO member (id, name, joined) VALUES (?, ?, ?)', 'normalizedSql in query annotation') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(62) + .setFileName('mysql2.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[3] + t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') + t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') + t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') + + actualParsingResult = sqlMetadataService.cacheSql('UPDATE member SET name = ? WHERE id = ?') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') + t.equal(actualQueryAnnotation.value.stringValue2, 'cname2,c', 'stringValue2 in query annotation') + t.equal(actualParsingResult.sql.normalizedSql, 'UPDATE member SET name = ? WHERE id = ?', 'normalizedSql in query annotation') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(66) + .setFileName('mysql2.test.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + querySpanEvent = trace.span.spanEventList[4] + t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') + t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') + t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') + + actualParsingResult = sqlMetadataService.cacheSql('DELETE FROM member WHERE id = ?') + actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') + t.equal(actualQueryAnnotation.value.stringValue2, 'c', 'stringValue2 in query annotation') + t.equal(actualParsingResult.sql.normalizedSql, 'DELETE FROM member WHERE id = ?', 'normalizedSql in query annotation') + t.end() }) - }) - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(39) - .setFileName('mysql2.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const createConnectionSpanEvent = trace.span.spanEventList[0] - t.equal(createConnectionSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createConnection spanEvent') - t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'endPoint in createConnection spanEvent') - t.equal(createConnectionSpanEvent.destinationId, 'test', 'destinationId in createConnection spanEvent') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(48) - .setFileName('mysql2.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let querySpanEvent = trace.span.spanEventList[1] - t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') - t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') - t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') - - let actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM member WHERE id = ?') - let actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') - t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'stringValue2 in query annotation') - t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM member WHERE id = ?', 'normalizedSql in query annotation') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(56) - .setFileName('mysql2.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[2] - t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') - t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') - t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') - - actualParsingResult = sqlMetadataService.cacheSql('INSERT INTO member (id, name, joined) VALUES (?, ?, ?)') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') - t.equal(actualQueryAnnotation.value.stringValue2, 'c,cname,2023-08-18', 'stringValue2 in query annotation') - t.equal(actualParsingResult.sql.normalizedSql, 'INSERT INTO member (id, name, joined) VALUES (?, ?, ?)', 'normalizedSql in query annotation') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(60) - .setFileName('mysql2.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[3] - t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') - t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') - t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') - - actualParsingResult = sqlMetadataService.cacheSql('UPDATE member SET name = ? WHERE id = ?') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') - t.equal(actualQueryAnnotation.value.stringValue2, 'cname2,c', 'stringValue2 in query annotation') - t.equal(actualParsingResult.sql.normalizedSql, 'UPDATE member SET name = ? WHERE id = ?', 'normalizedSql in query annotation') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - .setLineNumber(64) - .setFileName('mysql2.test.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - querySpanEvent = trace.span.spanEventList[4] - t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') - t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') - t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') - - actualParsingResult = sqlMetadataService.cacheSql('DELETE FROM member WHERE id = ?') - actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') - t.equal(actualQueryAnnotation.value.stringValue2, 'c', 'stringValue2 in query annotation') - t.equal(actualParsingResult.sql.normalizedSql, 'DELETE FROM member WHERE id = ?', 'normalizedSql in query annotation') - t.end() }) }) @@ -172,52 +175,56 @@ test(`getConnection promise query hooking`, async (t) => { .start() const trace = agent.createTraceObject() - const connection = mysql.createConnection({ - host: container.getHost(), - port: container.getPort(3306), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) - - const [rows] = await connection.promise().query(`SELECT * FROM member WHERE id = ?`, 'a') - t.equal(rows[0].id, 'a', 'id in SELECT query hooking') - t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') - t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - - setImmediate(() => { - trace.close() - connection.end() - container.stop() - t.end() - }) - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(175) - .setFileName('mysql2.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - const createConnectionSpanEvent = trace.span.spanEventList[0] - t.equal(createConnectionSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createConnection spanEvent') - t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'endPoint in createConnection spanEvent') - t.equal(createConnectionSpanEvent.destinationId, 'test', 'destinationId in createConnection spanEvent') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('Connection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let querySpanEvent = trace.span.spanEventList[1] - t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') - t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') - t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') - - let actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM member WHERE id = ?') - let actualQueryAnnotation = querySpanEvent.annotations[0] - t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') - t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') - t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') - t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'stringValue2 in query annotation') - t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM member WHERE id = ?', 'normalizedSql in query annotation') + localStorage.run(trace, async () => { + const connection = mysql.createConnection({ + host: container.getHost(), + port: container.getPort(3306), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + + const [rows] = await connection.promise().query(`SELECT * FROM member WHERE id = ?`, 'a') + t.equal(rows[0].id, 'a', 'id in SELECT query hooking') + t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') + t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') + + setImmediate(() => { + trace.close() + connection.end() + container.stop() + t.end() + }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createConnection') + .setLineNumber(179) + .setFileName('mysql2.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + const createConnectionSpanEvent = trace.span.spanEventList[0] + t.equal(createConnectionSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createConnection spanEvent') + t.equal(createConnectionSpanEvent.endPoint, 'localhost', 'endPoint in createConnection spanEvent') + t.equal(createConnectionSpanEvent.destinationId, 'test', 'destinationId in createConnection spanEvent') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('Connection') + .setLineNumber(103) + .setFileName('promise.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let querySpanEvent = trace.span.spanEventList[1] + t.equal(querySpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in query spanEvent') + t.equal(querySpanEvent.endPoint, 'localhost', 'endPoint in query spanEvent') + t.equal(querySpanEvent.destinationId, 'test', 'destinationId in query spanEvent') + + let actualParsingResult = sqlMetadataService.cacheSql('SELECT * FROM member WHERE id = ?') + let actualQueryAnnotation = querySpanEvent.annotations[0] + t.equal(actualQueryAnnotation.key, annotationKey.SQL_ID.getCode(), 'key in query annotation') + t.equal(actualQueryAnnotation.value.intValue, actualParsingResult.sqlId, 'intValue in query annotation') + t.equal(actualQueryAnnotation.value.stringValue1, '', 'stringValue1 in query annotation') + t.equal(actualQueryAnnotation.value.stringValue2, 'a', 'stringValue2 in query annotation') + t.equal(actualParsingResult.sql.normalizedSql, 'SELECT * FROM member WHERE id = ?', 'normalizedSql in query annotation') + }) }) }) @@ -237,62 +244,66 @@ test(`Connection Pool with query hooking`, async (t) => { .start() const trace = agent.createTraceObject() - const pool = mysql.createPool({ - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) - const promisePool = pool.promise() + localStorage.run(trace, async () => { + const pool = mysql.createPool({ + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + const promisePool = pool.promise() + + const [rows] = await promisePool.query(`SELECT * FROM member WHERE id = ?`, 'a') + t.equal(rows[0].id, 'a', 'id in SELECT query hooking') + t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') + t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - const [rows] = await promisePool.query(`SELECT * FROM member WHERE id = ?`, 'a') - t.equal(rows[0].id, 'a', 'id in SELECT query hooking') - t.equal(rows[0].name, 'name1', 'name in SELECT query hooking') - t.equal(rows[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - - setImmediate(() => { - trace.close() - pool.end() - container.stop() - t.end() - }) - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createPool') - .setLineNumber(240) - .setFileName('mysql2.test.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let actualSpanEvent = trace.span.spanEventList[0] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createPool spanEvent') - t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint in createPool spanEvent') - t.equal(actualSpanEvent.destinationId, 'test', 'destinationId in createPool spanEvent') - t.equal(actualSpanEvent.sequence, 0, 'sequence in createPool spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in createPool spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in createPool spanEvent') - - actualBuilder = new MethodDescriptorBuilder('getConnection') - .setClassName('Pool') - .setLineNumber(143) - .setFileName('pool.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[1] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.destinationId, 'test', 'destinationId in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.sequence, 1, 'sequence in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in Pool.getConnection() spanEvent') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('PoolConnection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[2] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.sequence, 2, 'sequence in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in PoolConnection.query() spanEvent') + setImmediate(() => { + trace.close() + pool.end() + container.stop() + t.end() + }) + + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createPool') + .setLineNumber(248) + .setFileName('mysql2.test.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = trace.span.spanEventList[0] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createPool spanEvent') + t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint in createPool spanEvent') + t.equal(actualSpanEvent.destinationId, 'test', 'destinationId in createPool spanEvent') + t.equal(actualSpanEvent.sequence, 0, 'sequence in createPool spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in createPool spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in createPool spanEvent') + + actualBuilder = new MethodDescriptorBuilder('getConnection') + .setClassName('Pool') + .setLineNumber(143) + .setFileName('pool.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[1] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.endPoint, 'localhost', 'endPoint in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.destinationId, 'test', 'destinationId in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.sequence, 1, 'sequence in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in Pool.getConnection() spanEvent') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('PoolConnection') + .setLineNumber(153) + .setFileName('pool.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[2] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.sequence, 2, 'sequence in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in PoolConnection.query() spanEvent') + }) }) }) @@ -312,112 +323,116 @@ test(`Cluster with query`, async (t) => { .start() const trace = agent.createTraceObject() - const poolCluster = mysqlp.createPoolCluster() - poolCluster.add('MASTER', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) - poolCluster.add('SLAVE1', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) - poolCluster.add('SLAVE2', { - host: container.getHost(), - port: container.getPort(), - database: 'test', - user: container.getUsername(), - password: container.getUserPassword(), - timezone: '+09:00' - }) - const connection = await poolCluster.getConnection() + localStorage.run(trace, async () => { + const poolCluster = mysqlp.createPoolCluster() + poolCluster.add('MASTER', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + poolCluster.add('SLAVE1', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + poolCluster.add('SLAVE2', { + host: container.getHost(), + port: container.getPort(), + database: 'test', + user: container.getUsername(), + password: container.getUserPassword(), + timezone: '+09:00' + }) + const connection = await poolCluster.getConnection() + + const [results] = await connection.query(`SELECT * FROM member WHERE id = ?`, 'a') + t.equal(results[0].id, 'a', 'id in SELECT query hooking') + t.equal(results[0].name, 'name1', 'name in SELECT query hooking') + t.equal(results[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - const [results] = await connection.query(`SELECT * FROM member WHERE id = ?`, 'a') - t.equal(results[0].id, 'a', 'id in SELECT query hooking') - t.equal(results[0].name, 'name1', 'name in SELECT query hooking') - t.equal(results[0].joined.toISOString().slice(0, 10), '2023-01-17', 'joined in SELECT query hooking') - - agent.callbackTraceClose((trace) => { - let actualBuilder = new MethodDescriptorBuilder('createPoolCluster') - .setLineNumber(545) - .setFileName('promise.js') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - let actualSpanEvent = trace.span.spanEventList[0] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createPoolCluster spanEvent') - t.equal(actualSpanEvent.sequence, 0, 'sequence in createPoolCluster spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in createPoolCluster spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in createPoolCluster spanEvent') - - actualBuilder = new MethodDescriptorBuilder('of') - .setLineNumber(169) - .setFileName('pool_cluster.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[1] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolCluster.of() spanEvent') - t.equal(actualSpanEvent.sequence, 1, 'sequence in PoolCluster.of() spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in PoolCluster.of() spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in PoolCluster.of() spanEvent') - - actualBuilder = new MethodDescriptorBuilder('getConnection') - .setClassName('Pool') - .setLineNumber(177) - .setFileName('pool_cluster.js') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[2] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.sequence, 2, 'sequence in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in Pool.getConnection() spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in Pool.getConnection() spanEvent') - - let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'transactionIdObject in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'localAsyncId asyncId in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'localAsyncId sequence in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationMethodDescriptor.apiId, 'apiId in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'apiId in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'sequence in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'depth in spanChunk in PoolCluster.getConnection()') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'serviceType in spanChunk in PoolCluster.getConnection()') - - actualBuilder = new MethodDescriptorBuilder('query') - .setClassName('PoolConnection') - actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - actualSpanEvent = trace.span.spanEventList[3] - t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.sequence, 3, 'sequence in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.depth, 1, 'depth in PoolConnection.query() spanEvent') - t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in PoolConnection.query() spanEvent') - - actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) - t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'transactionIdObject in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'localAsyncId asyncId in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'localAsyncId sequence in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationMethodDescriptor.apiId, 'apiId in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'apiId in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'sequence in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'depth in spanChunk in PoolConnection.query()') - t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in spanChunk in PoolConnection.query()') - }) - - setImmediate(() => { - trace.close() - connection.release() - container.stop() - t.end() + agent.callbackTraceClose((trace) => { + let actualBuilder = new MethodDescriptorBuilder('createPoolCluster') + .setLineNumber(545) + .setFileName('promise.js') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + let actualSpanEvent = trace.span.spanEventList[0] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in createPoolCluster spanEvent') + t.equal(actualSpanEvent.sequence, 0, 'sequence in createPoolCluster spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in createPoolCluster spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in createPoolCluster spanEvent') + + actualBuilder = new MethodDescriptorBuilder('of') + .setLineNumber(169) + .setFileName('pool_cluster.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[1] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolCluster.of() spanEvent') + t.equal(actualSpanEvent.sequence, 1, 'sequence in PoolCluster.of() spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in PoolCluster.of() spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in PoolCluster.of() spanEvent') + + actualBuilder = new MethodDescriptorBuilder('getConnection') + .setClassName('Pool') + .setLineNumber(177) + .setFileName('pool_cluster.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[2] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.sequence, 2, 'sequence in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in Pool.getConnection() spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlServiceType.getCode(), 'serviceType in Pool.getConnection() spanEvent') + + let actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'transactionIdObject in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'localAsyncId asyncId in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'localAsyncId sequence in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationMethodDescriptor.apiId, 'apiId in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'apiId in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'sequence in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'depth in spanChunk in PoolCluster.getConnection()') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlServiceType.getCode(), 'serviceType in spanChunk in PoolCluster.getConnection()') + + actualBuilder = new MethodDescriptorBuilder('query') + .setClassName('PoolConnection') + .setLineNumber(103) + .setFileName('promise.js') + actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + actualSpanEvent = trace.span.spanEventList[3] + t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'apiId in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.sequence, 3, 'sequence in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.depth, 1, 'depth in PoolConnection.query() spanEvent') + t.equal(actualSpanEvent.serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in PoolConnection.query() spanEvent') + + actualSpanChunk = trace.storage.dataSender.findSpanChunk(actualSpanEvent.nextAsyncId) + t.equal(actualSpanChunk.spanId, actualSpanEvent.spanId, 'spanId in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.transactionIdObject, trace.traceId.transactionId, 'transactionIdObject in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.localAsyncId.asyncId, actualSpanEvent.nextAsyncId, 'localAsyncId asyncId in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'localAsyncId sequence in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[0].apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationMethodDescriptor.apiId, 'apiId in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[0].sequence, 0, 'sequence in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[0].depth, 1, 'depth in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[0].serviceType, ServiceType.async.getCode(), 'serviceType in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[1].apiId, actualSpanEvent.apiId, 'apiId in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[1].sequence, 1, 'sequence in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[1].depth, 2, 'depth in spanChunk in PoolConnection.query()') + t.equal(actualSpanChunk.spanEventList[1].serviceType, mysqlExecuteQueryServiceType.getCode(), 'serviceType in spanChunk in PoolConnection.query()') + }) + + setImmediate(() => { + trace.close() + connection.release() + container.stop() + t.end() + }) }) }) \ No newline at end of file diff --git a/test/instrumentation/module/redis.test.js b/test/instrumentation/module/redis.test.js index bc5973d6..7a5395e1 100644 --- a/test/instrumentation/module/redis.test.js +++ b/test/instrumentation/module/redis.test.js @@ -8,16 +8,11 @@ const test = require('tape') const axios = require('axios') const { log } = require('../../test-helper') - const agent = require('../../support/agent-singleton-mock') - const express = require('express') const Koa = require('koa') const Router = require('koa-router') const koaBodyParser = require('koa-bodyparser') - -const { GenericContainer } = require('testcontainers') - const ioRedis = require('ioredis-mock') const Redis = require('redis-mock') @@ -39,7 +34,7 @@ test(`${testName1} should Record the connections between express and redis.`, fu const testName = testName1 - t.plan(3) + t.plan(2) const app = new express() const client = Redis.createClient() @@ -87,11 +82,6 @@ test(`${testName1} should Record the connections between express and redis.`, fu const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) t.ok(rstGet.status, 200) - - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -102,7 +92,7 @@ test(`${testName2} should Record the connections between express and ioredis.`, const testName = testName2 - t.plan(2) + t.plan(1) const app = new express() const redis = new ioRedis() @@ -148,13 +138,6 @@ test(`${testName2} should Record the connections between express and ioredis.`, const rstPush = await axios.post(getServerUrl(PATH), redisData) t.ok(rstPush.status, 200) - // const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) - // t.ok(rstGet.status, 200) - - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -165,7 +148,7 @@ test(`${testName3} should Record the connections between koa and redis.`, functi const testName = testName3 - t.plan(2) + t.plan(1) const app = new Koa() const router = new Router() @@ -207,10 +190,6 @@ test(`${testName3} should Record the connections between koa and redis.`, functi const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) t.ok(rstGet.status, 200) - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) @@ -221,7 +200,7 @@ test(`${testName4} should Record the connections between koa and ioredis.`, func const testName = testName4 - t.plan(2) + t.plan(1) const app = new Koa() const router = new Router() @@ -262,11 +241,6 @@ test(`${testName4} should Record the connections between koa and ioredis.`, func console.log('Step2.') const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) t.ok(rstGet.status, 200) - - const traceMap = agent.traceContext.getAllTraceObject() - log.debug(traceMap.size) - t.ok(traceMap.size > 0) - server.close() }) }) \ No newline at end of file diff --git a/test/instrumentation/request-header-utils.test.js b/test/instrumentation/request-header-utils.test.js index 80de7204..ce64a6d9 100644 --- a/test/instrumentation/request-header-utils.test.js +++ b/test/instrumentation/request-header-utils.test.js @@ -7,12 +7,11 @@ const test = require('tape') const axios = require('axios') const http = require('http') - -const { log, fixture, util } = require('../test-helper') - +const { fixture } = require('../test-helper') const RequestHeaderUtils = require('../../lib/instrumentation/request-header-utils') const agent = require('../support/agent-singleton-mock') const PinpointHeader = require('../../lib/constant/http-header').PinpointHeader +const localStorage = require('../../lib/instrumentation/context/local-storage') const headers = { 'Pinpoint-TraceID': fixture.getTraceId().transactionId.toString(), @@ -50,10 +49,10 @@ test('Should write pinpoint header', async function (t) { }) .on('request', (req, res) => { const trace = agent.createTraceObject() - - const writtenReq = RequestHeaderUtils.write(req, agent) - - t.equal(writtenReq.headers[PinpointHeader.HTTP_TRACE_ID], trace.traceId.transactionId.toString(), "trace ID new ID was added in Header") + localStorage.run(trace, () => { + const writtenReq = RequestHeaderUtils.write(req, agent) + t.equal(writtenReq.headers[PinpointHeader.HTTP_TRACE_ID], trace.traceId.transactionId.toString(), "trace ID new ID was added in Header") + }) }) .listen(5005, async function() { await axios.get(`http://${endPoint}${rpcName}?q=1`) diff --git a/test/support/agent-singleton-mock.js b/test/support/agent-singleton-mock.js index 957c6883..43219d5b 100644 --- a/test/support/agent-singleton-mock.js +++ b/test/support/agent-singleton-mock.js @@ -7,7 +7,6 @@ 'use strict' const { fixture, log } = require('../test-helper') - const enableDataSending = require('../test-helper').enableDataSending enableDataSending() const Agent = require('../../lib/agent') @@ -15,10 +14,10 @@ const dataSenderMock = require('./data-sender-mock') const shimmer = require('@pinpoint-apm/shimmer') const httpShared = require('../../lib/instrumentation/http-shared') const traceContext = require('../../lib/context/trace-context') -const contextManager = require('../../lib/context/context-manager') const activeTrace = require('../../lib/metric/active-trace') const apiMetaService = require('../../lib/context/api-meta-service') const { setDataSender } = require('../../lib/client/data-sender-factory') +const localStorage = require('../../lib/instrumentation/context/local-storage') class MockAgent extends Agent { startSchedule(agentId, agentStartTime) { @@ -52,21 +51,7 @@ class MockAgent extends Agent { log.debug('shimming http.request function') shimmer.wrap(http, 'request', httpShared.traceOutgoingRequest(agent, 'http')) - // on node v10.15.0 init call before destory - const traces = this.traceContext.getAllTraceObject() - let traceSet = new Set() - for (const [key, trace] of traces) { - traceSet.add(trace) - } - for (const trace of traceSet) { - if (typeof trace.completed === 'function' && trace.completed()) { - continue - } - this.traceContext.completeTraceObject(trace) - } - - const traceObjectMap = contextManager.getAllObject() - traceObjectMap.clear() + localStorage.disable() const activeTraces = activeTrace.getAllTraces() activeTraces.forEach((value) => {