diff --git a/lib/agent.js b/lib/agent.js index ea2606d7..50bcea7d 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -11,6 +11,7 @@ const TraceContext = require('./context/trace-context') const log = require('./utils/logger') const stringMetaService = require('./context/string-meta-service') const apiMetaService = require('./context/api-meta-service') +const sqlMetadataService = require('./instrumentation/sql/sql-metadata-service') const Scheduler = require('./utils/scheduler') const AgentStatsMonitor = require('./metric/agent-stats-monitor') const { initializeConfig, getConfig } = require('./config') @@ -37,14 +38,13 @@ class Agent { const agentStartTime = Date.now() this.agentInfo = this.createAgentInfo(this.config, agentStartTime) - this.initializeDataSender() - this.initializePinpointClient() + const dataSender = this.makeDataSender() + this.dataSender = dataSender + this.initializeDataSender(dataSender) + this.initializePinpointClient(dataSender) this.traceContext = new TraceContext(this.agentInfo, this.dataSender, this.config) - stringMetaService.init(this.dataSender) - apiMetaService.init(this.dataSender) - this.startSchedule(agentId, agentStartTime) this.initializeSupportModules() @@ -53,17 +53,23 @@ class Agent { log.warn('[Pinpoint Agent][' + agentId + '] Init Completed') } - initializeDataSender() { - this.dataSender = dataSenderFactory.create(this.config, this.agentInfo) - this.dataSender.send(this.agentInfo) + makeDataSender() { + return dataSenderFactory.create(this.config, this.agentInfo) + } + + initializeDataSender(dataSender) { + dataSender.send(this.agentInfo) + stringMetaService.init(dataSender) + apiMetaService.init(dataSender) + sqlMetadataService.setDataSender(dataSender) } initializeSupportModules() { this.moduleHook = new ModuleHook(this) } - initializePinpointClient() { - this.pinpointClient = new PinpointClient(this.config, this.agentInfo, this.dataSender) + initializePinpointClient(dataSender) { + this.pinpointClient = new PinpointClient(this.config, this.agentInfo, dataSender) } createTraceObject(requestData) { diff --git a/lib/client/data-sender-factory.js b/lib/client/data-sender-factory.js index d27a5e11..29df5b21 100644 --- a/lib/client/data-sender-factory.js +++ b/lib/client/data-sender-factory.js @@ -10,7 +10,7 @@ const GrpcDataSender = require('./grpc-data-sender') const DataSender = require('./data-sender') const create = (config, agentInfo) => { - const dataSender = new DataSender(config, new GrpcDataSender( + return new DataSender(config, new GrpcDataSender( config.collectorIp, config.collectorTcpPort, config.collectorStatPort, @@ -18,7 +18,6 @@ const create = (config, agentInfo) => { agentInfo, config )) - return dataSender } module.exports = { diff --git a/lib/client/pinpoint-client.js b/lib/client/pinpoint-client.js index 0d9e27a6..8a59c9b8 100644 --- a/lib/client/pinpoint-client.js +++ b/lib/client/pinpoint-client.js @@ -7,7 +7,6 @@ 'use strict' const CommandType = require('../constant/commaned-type').CommandType -const log = require('../utils/logger') class PinpointClient { constructor(config, agentInfo, dataSender) { diff --git a/lib/context/disable-trace.js b/lib/context/disable-trace.js index dde61730..27032806 100644 --- a/lib/context/disable-trace.js +++ b/lib/context/disable-trace.js @@ -14,11 +14,12 @@ class DisableTrace { constructor(traceRoot) { this.traceRoot = traceRoot this.spanRecorder = new DisableSpanRecorder() + this.spanEventRecorder = new DisableSpanEventRecorder() } traceBlockBegin() { - return new DisableSpanEventRecorder() + return this.spanEventRecorder } traceBlockEnd() {} diff --git a/lib/context/span-event-recorder.js b/lib/context/span-event-recorder.js index 7b3dd00b..f9881621 100644 --- a/lib/context/span-event-recorder.js +++ b/lib/context/span-event-recorder.js @@ -11,7 +11,7 @@ const StringMetaService = require('./string-meta-service') const AsyncId = require('./async-id') const AnnotationKeyUtils = require('./annotation-key-utils') const log = require('../utils/logger') -const sqlMetaDataService = require('../instrumentation/sql/sql-metadata-service') +const sqlMetadataService = require('../instrumentation/sql/sql-metadata-service') const Annotations = require('../instrumentation/context/annotation/annotations') // https://github.com/pinpoint-apm/pinpoint/blob/master/bootstraps/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/context/SpanEventRecorder.java @@ -157,7 +157,7 @@ class SpanEventRecorder { return } - const parsingResult = sqlMetaDataService.cacheSql(sql) + const parsingResult = sqlMetadataService.cacheSql(sql) this.recordSqlParsingResult(parsingResult, bindString) return parsingResult } diff --git a/lib/context/trace/child-trace-builder.js b/lib/context/trace/child-trace-builder.js index 7bb462da..95c607e2 100644 --- a/lib/context/trace/child-trace-builder.js +++ b/lib/context/trace/child-trace-builder.js @@ -29,19 +29,20 @@ class ChildTraceBuilder { } traceBlockBegin(stackId = StackId.default) { - if (this.closed) { - return SpanEventRecorder.nullObject(this.traceRoot) - } - return this.callStack.makeSpanEventRecorder(stackId) } traceBlockEnd(spanEventRecorder) { if (this.closed) { - return + // If ChildTraceBuilder is closed, aync spanEvent was popped in the previous call. + // So, increase the depth of the next spanEvent. + const spanEventBuilder = spanEventRecorder.getSpanEventBuilder() + spanEventBuilder.setDepth(spanEventBuilder.getDepth() + 1) } - this.endSpanEventBuilder(spanEventRecorder.getSpanEventBuilder()) + if (this.closed) { + this.repository.flush() + } } endSpanEventBuilder(builder) { diff --git a/lib/context/trace/disable-child-trace.js b/lib/context/trace/disable-child-trace.js index 10e05610..ac9e9b97 100644 --- a/lib/context/trace/disable-child-trace.js +++ b/lib/context/trace/disable-child-trace.js @@ -22,6 +22,10 @@ class DisableChildTrace { traceBlockEnd() {} + getSpanRecorder() { + return this.spanRecorder + } + getTraceRoot() { return this.traceRoot } diff --git a/lib/context/trace/span-event-builder.js b/lib/context/trace/span-event-builder.js index 3f4e4b2b..74d230af 100644 --- a/lib/context/trace/span-event-builder.js +++ b/lib/context/trace/span-event-builder.js @@ -113,6 +113,10 @@ class SpanEventBuilder { return this } + getDepth() { + return this.depth + } + getStartTime() { return this.startTime } diff --git a/lib/context/trace/span-event-recorder2.js b/lib/context/trace/span-event-recorder2.js index a4653512..a918d2db 100644 --- a/lib/context/trace/span-event-recorder2.js +++ b/lib/context/trace/span-event-recorder2.js @@ -11,7 +11,7 @@ const Annotations = require('../../instrumentation/context/annotation/annotation const SpanEventBuilder = require('./span-event-builder') const log = require('../../utils/logger') const AnnotationKeyUtils = require('../annotation-key-utils') -const sqlMetaDataService = require('../../instrumentation/sql/sql-metadata-service') +const sqlMetadataService = require('../../instrumentation/sql/sql-metadata-service') const stringMetaService = require('../string-meta-service') const AsyncId = require('../async-id') @@ -127,7 +127,7 @@ class SpanEventRecorder { return } - const parsingResult = sqlMetaDataService.cacheSql(sql) + const parsingResult = sqlMetadataService.cacheSql(sql) this.recordSqlParsingResult(parsingResult, bindString) return parsingResult } diff --git a/lib/instrumentation/instrument-arrow-function.js b/lib/instrumentation/instrument-arrow-function.js index 865844b8..faead6e0 100644 --- a/lib/instrumentation/instrument-arrow-function.js +++ b/lib/instrumentation/instrument-arrow-function.js @@ -9,6 +9,7 @@ const shimmer = require('@pinpoint-apm/shimmer') const HookFunctionArguments = require('./hook-function-arguments') const localStorage = require('./context/local-storage') +// const log = require('../utils/logger') // ref: SpanEventSimpleAroundInterceptorForPlugin.java class InstrumentArrowFunction { @@ -65,6 +66,13 @@ class InstrumentArrowFunction { return original.apply(this, arguments) } + // if (log.isInfo()) { + // if (typeof method !== 'symbol') { + // log.info(`target: ${JSON.stringify(target)}, method: ${method}, original: ${original} InstrumentArrowFunction.addChildTraceInterceptor`) + // } else { + // log.info(`target: ${JSON.stringify(target)}, symbol method, original: ${original} InstrumentArrowFunction.addChildTraceInterceptor`) + // } + // } const childTraceBuilder = traceContext.continueAsyncContextTraceObject(trace.getTraceRoot(), asyncId.nextLocalAsyncId2()) return localStorage.run(childTraceBuilder, () => { const result = original.apply(this, arguments) diff --git a/lib/instrumentation/sql/sql-metadata-service.js b/lib/instrumentation/sql/sql-metadata-service.js index 707158b0..eadc67f8 100644 --- a/lib/instrumentation/sql/sql-metadata-service.js +++ b/lib/instrumentation/sql/sql-metadata-service.js @@ -8,17 +8,13 @@ const SimpleCache = require('../../utils/simple-cache') const SqlParser = require('./sql-parser') const ParsingResult = require('./parsing-result') -class SqlMetaDataService { +class SqlMetadataService { constructor() { this.cache = new SimpleCache(1024) } - set dataSender(dataSender) { - this._dataSender = dataSender - } - - get dataSender() { - return this._dataSender + setDataSender(dataSender) { + this.dataSender = dataSender } cacheSql(sql) { @@ -38,10 +34,8 @@ class SqlMetaDataService { } send(sqlMetaData) { - if (this.dataSender && typeof this.dataSender.send === 'function') { - this.dataSender.send(sqlMetaData) - } + this.dataSender?.send?.(sqlMetaData) } } -module.exports = new SqlMetaDataService() \ No newline at end of file +module.exports = new SqlMetadataService() \ No newline at end of file diff --git a/test/client/data-sender.test.js b/test/client/data-sender.test.js index 192dc54f..d0840bb1 100644 --- a/test/client/data-sender.test.js +++ b/test/client/data-sender.test.js @@ -6,7 +6,7 @@ const test = require('tape') -require('../support/agent-singleton-mock') +const agent = require('../support/agent-singleton-mock') const { fixture } = require('../test-helper') const dataSenderMock = require('../support/data-sender-mock') const dataSender = dataSenderMock() @@ -18,6 +18,7 @@ const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/de test('Should send agent info', function (t) { t.plan(1) + agent.bindHttp() dataSender.send(agentInfo) @@ -26,6 +27,7 @@ test('Should send agent info', function (t) { test('Should send api meta info', function (t) { t.plan(1) + agent.bindHttp() const methodDescriptor = defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor const apiMetaInfo = ApiMetaInfo.create(methodDescriptor) @@ -36,6 +38,7 @@ test('Should send api meta info', function (t) { test('Should send string meta info', function (t) { t.plan(1) + agent.bindHttp() const stringMetaInfo = StringMetaInfo.create('1', 'test string') dataSender.send(stringMetaInfo) diff --git a/test/client/grpc-data-sender.test.js b/test/client/grpc-data-sender.test.js index 00725697..33d29b5f 100644 --- a/test/client/grpc-data-sender.test.js +++ b/test/client/grpc-data-sender.test.js @@ -20,10 +20,10 @@ const AsyncSpanChunkBuilder = require('../../lib/context/trace/async-span-chunk- const SpanRepository = require('../../lib/context/trace/span-repository') const ChildTraceBuilder = require('../../lib/context/trace/child-trace-builder') const serviceType = require('../../lib/context/service-type') -const makeMockDataSender = require('../fixtures/mock-data-sender') const SpanChunkBuilder = require('../../lib/context/span-chunk-builder') const Trace = require('../../lib/context/trace/trace2') const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/default-predefined-method-descriptor-registry') +const dataSenderMock = require('../support/data-sender-mock') let sendSpanMethodOnDataCallback function sendSpan(call) { @@ -70,7 +70,7 @@ test('Should send span', function (t) { server.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => { const grpcDataSender = beforeSpecificOne(port, DataSource) const traceRoot = new RemoteTraceRootBuilder(agent.agentInfo, '5').build() - dataSender = makeMockDataSender(agent.config, grpcDataSender) + dataSender = dataSenderMock(agent.config, grpcDataSender) const spanBuilder = new SpanBuilder(traceRoot) spanBuilder.setServiceType(1400) spanBuilder.setEndPoint('localhost:3000') @@ -167,7 +167,7 @@ test('sendSpanChunk redis.SET.end', function (t) { const grpcDataSender = beforeSpecificOne(port, DataSource) const traceRoot = new RemoteTraceRootBuilder(agent.agentInfo, '5').build() const asyncId = AsyncId.make() - dataSender = makeMockDataSender(agent.config, grpcDataSender) + dataSender = dataSenderMock(agent.config, grpcDataSender) const spanChunkBuilder = new AsyncSpanChunkBuilder(traceRoot, asyncId) const repository = new SpanRepository(spanChunkBuilder, dataSender, agent.agentInfo) const childTraceBuilder = new ChildTraceBuilder(traceRoot, repository, asyncId) @@ -245,7 +245,7 @@ test('sendSpanChunk redis.GET.end', (t) => { const grpcDataSender = beforeSpecificOne(port, DataSource) const traceRoot = new RemoteTraceRootBuilder(agent.agentInfo, '5').build() const asyncId = AsyncId.make() - dataSender = makeMockDataSender(agent.config, grpcDataSender) + dataSender = dataSenderMock(agent.config, grpcDataSender) const spanChunkBuilder = new AsyncSpanChunkBuilder(traceRoot, asyncId) const repository = new SpanRepository(spanChunkBuilder, dataSender, agent.agentInfo) const childTraceBuilder = new ChildTraceBuilder(traceRoot, repository, asyncId) @@ -315,7 +315,7 @@ test('sendSpan', (t) => { server.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), (error, port) => { const grpcDataSender = beforeSpecificOne(port, DataSource) const traceRoot = new RemoteTraceRootBuilder(agent.agentInfo, '5').build() - dataSender = makeMockDataSender(agent.config, grpcDataSender) + dataSender = dataSenderMock(agent.config, grpcDataSender) const spanBuilder = new SpanBuilder(traceRoot) const spanChunkBuilder = new SpanChunkBuilder(traceRoot) const repository = new SpanRepository(spanChunkBuilder, dataSender, agent.agentInfo) diff --git a/test/client/mock-grpc-data-sender.js b/test/client/mock-grpc-data-sender.js index 7ad8b86d..bbfe1fce 100644 --- a/test/client/mock-grpc-data-sender.js +++ b/test/client/mock-grpc-data-sender.js @@ -7,12 +7,27 @@ 'use strict' const GrpcDataSender = require('../../lib/client/grpc-data-sender') +class MockGrpcStream { + constructor(stream) { + this.grpcStream = stream + } + + write(data) { + this.grpcStream.write(data) + } + + end() { + } +} + class MockGrpcDataSender extends GrpcDataSender { initializeClients() { let self = this this.agentClient = { requestAgentInfo: function (pAgentInfo) { self.actualAgentInfo = pAgentInfo + }, + close: function () { } } @@ -28,12 +43,14 @@ class MockGrpcDataSender extends GrpcDataSender { }, requestSqlUidMetaData: function (pSqlUidMetaData) { self.actualSqlUidMetaData = pSqlUidMetaData + }, + close: function () { } } this.actualSpans = [] } - get actualSpan () { + get actualSpan() { return this.actualSpans[this.actualSpans.length - 1] } @@ -42,19 +59,19 @@ class MockGrpcDataSender extends GrpcDataSender { initializeSpanStream() { let self = this - this.spanStream = { + this.spanStream = new MockGrpcStream({ write: function (span) { self.actualSpans.push(span) }, end: function () { } - } + }) } initializeProfilerClients() { let self = this - this.commandStream = { + this.commandStream = new MockGrpcStream({ write: function (pmessage) { self.actualPCmdMessage = pmessage }, @@ -64,12 +81,12 @@ class MockGrpcDataSender extends GrpcDataSender { on: function () { } - } + }) } initializeStatStream() { let self = this - this.statStream = { + this.statStream = new MockGrpcStream({ write: function (pmessage) { self.actualPStatMessage = pmessage }, @@ -79,12 +96,12 @@ class MockGrpcDataSender extends GrpcDataSender { on: function () { } - } + }) } initializePingStream() { let self = this - this.pingStream = { + this.pingStream = new MockGrpcStream({ write: function (pmessage) { self.actualPingMessage = pmessage }, @@ -94,7 +111,7 @@ class MockGrpcDataSender extends GrpcDataSender { on: function () { } - } + }) } initializeAgentInfoScheduler() { diff --git a/test/fixture.js b/test/fixture.js index 5c15fe8b..10d1b2a8 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -36,29 +36,10 @@ const captureNamedGroup = (callSite) => { , namedGroupTypeMethod([callSite], 0)) } -function assertSpanChunk(asyncTrace, callback) { - const origin = asyncTrace.close - asyncTrace.close = function () { - origin.apply(this, arguments) - callback(asyncTrace.storage.dataSender.findSpanChunk(asyncTrace.asyncId)) - } -} - -function assertTrace(callback) { - const trace = localStorage.getStore() - const origin = trace.close - trace.close = function () { - origin.apply(this, arguments) - callback(trace) - } -} - module.exports = { config, getTransactionId, getTraceId, getAgentInfo, captureNamedGroup, - assertSpanChunk, - assertTrace, } diff --git a/test/fixtures/mock-data-sender.js b/test/fixtures/mock-data-sender.js deleted file mode 100644 index 89688894..00000000 --- a/test/fixtures/mock-data-sender.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Pinpoint Node.js Agent - * Copyright 2020-present NAVER Corp. - * Apache License v2.0 - */ - -'use strict' - -const DataSender = require('../../lib/client/data-sender') -const AgentInfo = require('../../lib/data/dto/agent-info') -const ApiMetaInfo = require('../../lib/data/dto/api-meta-info') -const StringMetaInfo = require('../../lib/data/dto/string-meta-info') -const Span = require('../../lib/context/span') -const SpanChunk = require('../../lib/context/span-chunk') -const SqlMetaData = require('../../lib/client/sql-meta-data') - -class MockDataSender extends DataSender { - constructor(config, dataSender) { - super(config, dataSender) - this.mockAPIMetaInfos = [] - this.mockSpanChunks = [] - this.mockSpans = [] - } - - send(data) { - if (data instanceof AgentInfo) { - this.mockAgentInfo = data - super.send(data) - } else if (data instanceof ApiMetaInfo) { - this.mockAPIMetaInfos.push(data) - super.send(data) - } else if (data instanceof StringMetaInfo) { - this.mockMetaInfo = data - super.send(data) - } else if (data instanceof Span) { - this.mockSpan = data - this.mockSpans.push(data) - super.send(data) - } else if (data instanceof SpanChunk) { - this.mockSpanChunks.push(data) - super.send(data) - } else if (data instanceof SqlMetaData) { - this.mockSqlMetaData = data - super.send(data) - } else if (data?.isAsyncSpanChunk?.()) { - this.mockSpanChunks.push(data) - super.send(data) - } else if (data?.isSpan?.()) { - this.mockSpan = data - this.mockSpans.push(data) - super.send(data) - } - } - - findSpanChunk(asyncId) { - return this.mockSpanChunks.find(spanChunk => spanChunk.localAsyncId.asyncId === (asyncId.asyncId || asyncId)) - } - findSpanEvent(apiId) { - return this.mockSpan.spanEventList.find(event => event.apiId === apiId) - } -} - -const makeMockDataSender = (config, dataSender) => new MockDataSender(config, dataSender) - -module.exports = makeMockDataSender \ No newline at end of file diff --git a/test/instrumentation/context/nested-async-trace.test.js b/test/instrumentation/context/nested-async-trace.test.js index 97b73d72..ac720641 100644 --- a/test/instrumentation/context/nested-async-trace.test.js +++ b/test/instrumentation/context/nested-async-trace.test.js @@ -26,6 +26,7 @@ const services = require('../../../lib/data/v1/Service_grpc_pb') const spanMessages = require('../../../lib/data/v1/Span_pb') const log = require('../../../lib/utils/logger') const { Empty } = require('google-protobuf/google/protobuf/empty_pb') +const sqlMetadataService = require('../../../lib/instrumentation/sql/sql-metadata-service') let callbackPresultReturnUnaryService const pResultReturnUnaryService = (call, callback) => { @@ -45,20 +46,29 @@ const pResultReturnUnaryService = (call, callback) => { } } +let spanOrSpanChunks = [] +const resetSpanOrSpanChunks = () => { + spanOrSpanChunks = [] +} +const getSpanOrSpanChunks = () => { + return spanOrSpanChunks +} +const getSpanChunk = (asyncId) =>{ + return getSpanOrSpanChunks().find(spanOrSpanChunk => spanOrSpanChunk.getLocalasyncid().getAsyncid() === asyncId.getAsyncId() && spanOrSpanChunk.getLocalasyncid().getSequence() === asyncId.getSequence()) +} + let spanMessageEndEventCallback let dataStreamCount = 0 const spanMessageStreamService = (call) => { call.on('data', (spanMessage) => { const spanOrSpanChunk = spanMessage.getSpan() ?? spanMessage.getSpanchunk() - const spanOrSpanChunks = agent.getSpanOrSpanChunks() + const spanOrSpanChunks = getSpanOrSpanChunks() spanOrSpanChunks.push(spanOrSpanChunk) if (agent.getTraces().length === ++dataStreamCount) { spanMessageEndEventCallback?.() } }) - - call.on('end', () => { - }) + call.on('end', () => {}) } function sendAgentStat(call, callback) { @@ -95,11 +105,13 @@ test(`nested mysql async query with express`, async (t) => { collectorServer.addService(services.MetadataService, { requestApiMetaData: pResultReturnUnaryService, requestSqlUidMetaData: pResultReturnUnaryService, + requestSqlMetaData: pResultReturnUnaryService }) process.env['PINPOINT_PROFILER_SQL_STAT'] = 'true' collectorServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), async (err, port) => { agent.bindHttpWithCallSite(port) + resetSpanOrSpanChunks() const source = path.resolve(fixtures, 'mysql.sql') const container = await new MySqlContainer() .withCommand(['--default-authentication-plugin=mysql_native_password']) @@ -144,7 +156,7 @@ test(`nested mysql async query with express`, async (t) => { agent.callbackTraceClose(async (trace) => { let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(117) + .setLineNumber(129) .setFileName('nested-async-trace.test.js') let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 0) @@ -157,7 +169,7 @@ test(`nested mysql async query with express`, async (t) => { t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express') actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(118) + .setLineNumber(130) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 1) @@ -170,7 +182,7 @@ test(`nested mysql async query with express`, async (t) => { actualBuilder = new MethodDescriptorBuilder('connect') .setClassName('Connection') - .setLineNumber(127) + .setLineNumber(139) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.repository.dataSender.mockSpan.spanEventList.find(spanEvent => spanEvent.sequence === 2) @@ -192,7 +204,7 @@ test(`nested mysql async query with express`, async (t) => { actualBuilder = new MethodDescriptorBuilder('query') .setClassName('Connection') - .setLineNumber(133) + .setLineNumber(145) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 3) @@ -214,7 +226,7 @@ test(`nested mysql async query with express`, async (t) => { actualBuilder = new MethodDescriptorBuilder('query') .setClassName('Connection') - .setLineNumber(139) + .setLineNumber(151) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = actualSpanChunk.spanEventList[1] @@ -241,7 +253,7 @@ test(`nested mysql async query with express`, async (t) => { spanMessageEndEventCallback = async () => { const trace = agent.getTrace(0) const childTrace = agent.getTrace(1) - const actualSpanChunkGrpc = agent.getSpanChunk(childTrace.localAsyncId) + const actualSpanChunkGrpc = getSpanChunk(childTrace.localAsyncId) t.equal(actualSpanChunkGrpc.getVersion(), 1, 'PSpanChunk.version is 1') const actualTractionId = actualSpanChunkGrpc.getTransactionid() @@ -298,6 +310,7 @@ test(`nested mysql2 async query with express`, async (t) => { collectorServer.addService(services.MetadataService, { requestApiMetaData: pResultReturnUnaryService, requestSqlUidMetaData: pResultReturnUnaryService, + requestSqlMetaData: pResultReturnUnaryService }) collectorServer.bindAsync('localhost:0', grpc.ServerCredentials.createInsecure(), async (err, port) => { @@ -363,7 +376,7 @@ test(`nested mysql2 async query with express`, async (t) => { t.true(trace.spanBuilder.remoteAddress === '127.0.0.1' || trace.spanBuilder.remoteAddress === '::1', `remoteAddress is ${trace.spanBuilder.remoteAddress}`) let actualBuilder = new MethodDescriptorBuilder(expected('get', 'app.get')) .setClassName(expected('app', 'Function')) - .setLineNumber(319) + .setLineNumber(332) .setFileName('nested-async-trace.test.js') let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) let actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 0) @@ -376,7 +389,7 @@ test(`nested mysql2 async query with express`, async (t) => { t.equal(actualSpanEvent.serviceType, expressServiceType.getCode(), 'serviceType is express') actualBuilder = new MethodDescriptorBuilder('createConnection') - .setLineNumber(320) + .setLineNumber(333) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 1) @@ -389,7 +402,7 @@ test(`nested mysql2 async query with express`, async (t) => { actualBuilder = new MethodDescriptorBuilder('query') .setClassName('Connection') - .setLineNumber(330) + .setLineNumber(343) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = trace.spanBuilder.spanEventList.find(spanEvent => spanEvent.sequence === 2) @@ -411,7 +424,7 @@ test(`nested mysql2 async query with express`, async (t) => { actualBuilder = new MethodDescriptorBuilder('query') .setClassName('Connection') - .setLineNumber(336) + .setLineNumber(349) .setFileName('nested-async-trace.test.js') actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) actualSpanEvent = actualSpanChunk.spanEventList[1] @@ -459,6 +472,7 @@ test(`nested mysql2 async query with express`, async (t) => { connection.end() t.equal(agent.getSendedApiMetaInfos().length, 0, 'agent.getSendedApiMetaInfos() is empty') + t.equal(agent.getSqlMetadata().length, 0, 'agent.getSqlMetadata() is empty') t.end() trace.repository.dataSender.close() } @@ -466,25 +480,31 @@ test(`nested mysql2 async query with express`, async (t) => { }) callbackPresultReturnUnaryService = async (error, result, call) => { - const apiDescriptor = [...apiMetaService.cache.cache.entries()] - .map(([, value]) => value) - .find(apiDescriptor => apiDescriptor.apiId === call.request.getApiid()) - t.equal(call.request.getApiid(), apiDescriptor.apiId, `apiId is ${call.request.getApiid()}`) - t.equal(call.request.getApiinfo(), apiDescriptor.getApiDescriptor(), `apiInfo is ${call.request.getApiinfo()}`) - t.equal(call.request.getLine(), apiDescriptor.lineNumber, `line is ${call.request.getLine()}`) - t.equal(call.request.getLocation(), apiDescriptor.getLocation(), `location is ${call.request.getLocation()}`) - t.equal(call.request.getType(), apiDescriptor.getType(), `type is ${call.request.getType()}`) - const sendedApiMetaInfos = agent.getSendedApiMetaInfos() - const index = sendedApiMetaInfos.findIndex(item => item === apiDescriptor) - sendedApiMetaInfos.splice(index, 1) + if (typeof call.request.getApiid === 'function') { + const apiDescriptor = [...apiMetaService.cache.cache.entries()] + .map(([, value]) => value) + .find(apiDescriptor => apiDescriptor.apiId === call.request.getApiid()) + t.equal(call.request.getApiid(), apiDescriptor.apiId, `apiId is ${call.request.getApiid()}`) + t.equal(call.request.getApiinfo(), apiDescriptor.getApiDescriptor(), `apiInfo is ${call.request.getApiinfo()}`) + t.equal(call.request.getLine(), apiDescriptor.lineNumber, `line is ${call.request.getLine()}`) + t.equal(call.request.getLocation(), apiDescriptor.getLocation(), `location is ${call.request.getLocation()}`) + t.equal(call.request.getType(), apiDescriptor.getType(), `type is ${call.request.getType()}`) + const sendedApiMetaInfos = agent.getSendedApiMetaInfos() + const index = sendedApiMetaInfos.findIndex(item => item === apiDescriptor) + sendedApiMetaInfos.splice(index, 1) + } else { + const parsingResult = [...sqlMetadataService.cache.cache.entries()] + .map(([, value]) => value) + .find(parsingResult => parsingResult.sqlMetaDataValue().sqlId === call.request.getSqlid()) + t.equal(call.request.getSqlid(), parsingResult.sqlMetaDataValue().sqlId, `sqlId is ${call.request.getSqlid()}`) + t.equal(call.request.getSql(), parsingResult.sqlMetaDataValue().sql, `sql is ${call.request.getSql()}`) + const sendedSqlMetadata = agent.getSqlMetadata() + const index = sendedSqlMetadata.findIndex(item => item === parsingResult) + sendedSqlMetadata.splice(index, 1) + } } dataStreamCount = 0 - agent.setContinueAsyncContextTraceObjectCallback(async (trace) => { - const parentTrace = agent.getTraceByAsyncId(trace.localAsyncId) - t.true(parentTrace !== undefined, 'parent trace is not undefined') - }) - const server = app.listen(5006, async () => { const result = await axios.get('http://localhost:5006/test1') t.equal(result.status, 200, 'status is 200') diff --git a/test/instrumentation/module/redis.test.js b/test/instrumentation/module/redis.test.js index 4c1ffb1c..f803e690 100644 --- a/test/instrumentation/module/redis.test.js +++ b/test/instrumentation/module/redis.test.js @@ -20,8 +20,6 @@ const apiMetaService = require('../../../lib/context/api-meta-service') const defaultPredefinedMethodDescriptorRegistry = require('../../../lib/constant/default-predefined-method-descriptor-registry') const ServiceType = require('../../../lib/context/service-type') const annotationKey = require('../../../lib/constant/annotation-key') -const localStorage = require('../../../lib/instrumentation/context/local-storage') -const { assertTrace } = require('../../fixture') const https = require('https') const TEST_ENV = { @@ -62,54 +60,19 @@ test(`${testName1} should Record the connections between express and redis.`, as var key = req.body.name var value = JSON.stringify(req.body) - let actualNextAsyncId - let actualSpanId req.cache.set(key, value, async function (err, data) { if (err) { console.log(err) res.send("error " + err) return } - const trace = localStorage.getStore() - axios.get(`https://www.naver.com`, { httpsAgent: new https.Agent({ keepAlive: false }) }) - .then(function (response) { - const actualHttpCallbackNextAsyncId = actualNextAsyncId - const actualHttpCallbackSpanChunk = trace.repository.dataSender.findSpanChunk(actualHttpCallbackNextAsyncId) - t.equal(actualHttpCallbackSpanChunk.traceRoot.getTraceId().getSpanId(), trace.traceRoot.getTraceId().getSpanId(), 'HTTP request callback spanId') - t.equal(actualHttpCallbackSpanChunk.getTraceRoot(), trace.getTraceRoot(), 'HTTP request callback transactionId') - t.equal(actualHttpCallbackSpanChunk.localAsyncId.asyncId, actualHttpCallbackNextAsyncId.asyncId, 'HTTP request callback localAsyncId.asyncId') - t.equal(actualHttpCallbackSpanChunk.localAsyncId.sequence, 1, 'HTTP request callback localAsyncId.sequence') - - let actualSpanEvent = actualHttpCallbackSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 0) - t.equal(actualSpanEvent.apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'HTTP request callback asyncInvocationDescriptor.spanEvent.apiId') - t.equal(actualSpanEvent.depth, 1, 'HTTP request callback asyncInvocationDescriptor.spanEvent.depth') - t.equal(actualSpanEvent.sequence, 0, 'HTTP request callback asyncInvocationDescriptor.spanEvent.sequence') - t.equal(actualSpanEvent.serviceType, ServiceType.async.getCode(), 'HTTP request callback asyncInvocationDescriptor.spanEvent.serviceType') - - actualSpanEvent = actualHttpCallbackSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 1) - t.equal(actualSpanEvent.apiId, 0, 'HTTP request callback spanEvent.apiId') - t.equal(actualSpanEvent.depth, 2, 'HTTP request callback spanEvent.depth') - t.equal(actualSpanEvent.sequence, 1, 'HTTP request callback spanEvent.sequence') - t.equal(actualSpanEvent.serviceType, ServiceType.asyncHttpClient.getCode(), 'HTTP request callback spanEvent.serviceType') - - let actualAnnotation = actualSpanEvent.annotations[0] - t.equal(actualAnnotation.key, annotationKey.API.getCode(), 'HTTP request callback spanEvent.annotation[0].key') - t.equal(actualAnnotation.value, 'GET', 'HTTP request callback spanEvent.annotation[0].value') - actualAnnotation = actualSpanEvent.annotations[1] - t.equal(actualAnnotation.key, annotationKey.HTTP_URL.getCode(), 'HTTP request callback spanEvent.annotation[1].key annotationKey.HTTP_URL') - t.equal(actualAnnotation.value, 'www.naver.com/', 'HTTP request callback spanEvent.annotation[1].value annotationKey.HTTP_URL') - actualAnnotation = actualSpanEvent.annotations[2] - t.equal(actualAnnotation.key, annotationKey.HTTP_STATUS_CODE.getCode(), 'HTTP request callback spanEvent.annotation[2].key annotationKey.HTTP_STATUS_CODE') - t.equal(actualAnnotation.value, 200, 'HTTP request callback spanEvent.annotation[2].value annotationKey.HTTP_STATUS_CODE') - }) - req.cache.expire(key, 10) - assertTrace(trace => { - let actualSpanChunk = trace.repository.dataSender.mockSpanChunks[0] - t.equal(actualSpanChunk.traceRoot.getTraceId().getSpanId(), trace.traceRoot.getTraceId().getSpanId(), 'spanId') - t.equal(actualSpanChunk.getTraceRoot(), trace.getTraceRoot(), 'transactionId') - t.equal(actualSpanChunk.localAsyncId.asyncId, actualNextAsyncId.asyncId, 'localAsyncId.asyncId') - t.equal(actualSpanChunk.localAsyncId.sequence, 1, 'localAsyncId.sequence') + agent.callbackTraceClose((childTrace) => { + let actualSpanChunk = childTrace.repository.dataSender.mockSpanChunks[0] + t.equal(actualSpanChunk.traceRoot.getTraceId().getSpanId(), childTrace.traceRoot.getTraceId().getSpanId(), 'spanId') + t.equal(actualSpanChunk.getTraceRoot(), childTrace.getTraceRoot(), 'transactionId') + t.equal(actualSpanChunk.localAsyncId.asyncId, childTrace.localAsyncId.asyncId, 'localAsyncId.asyncId') + t.equal(actualSpanChunk.localAsyncId.sequence, childTrace.localAsyncId.sequence, 'localAsyncId.sequence') let actualSpanEvent = actualSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 0) t.equal(actualSpanEvent.apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'apiId') @@ -125,25 +88,62 @@ test(`${testName1} should Record the connections between express and redis.`, as let actualAnnotation = actualSpanEvent.annotations.find(annotation => annotation.key === annotationKey.API.getCode()) t.equal(actualAnnotation.value, 'http.request', 'HTTP request annotation value') - actualNextAsyncId = actualSpanEvent.asyncId + }) - actualSpanEvent = actualSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 2) - let actualBuilder = new MethodDescriptorBuilder('expire') - .setClassName('RedisClient') - let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) - t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'RedisClient.expire apiId') - t.equal(actualMethodDescriptor.apiDescriptor, 'RedisClient.expire', 'RedisClient.expire apiDescriptor') - t.equal(actualMethodDescriptor.className, 'RedisClient', 'RedisClient.expire className') - t.equal(actualMethodDescriptor.methodName, 'expire', 'RedisClient.expire methodName') - t.equal(actualSpanEvent.sequence, 2, 'RedisClient.expire actualSpanEvent.sequence') - t.equal(actualSpanEvent.depth, 2, 'RedisClient.expire actualSpanEvent.depth') - t.equal(actualSpanEvent.serviceType, 8200, 'RedisClient.expire actualSpanEvent.serviceType') - t.equal(actualSpanEvent.destinationId, 'Redis', 'RedisClient.expire actualSpanEvent.destinationId') - t.equal(actualSpanEvent.endPoint, `localhost:${container.getMappedPort(6379)}`, 'RedisClient.expire actualSpanEvent.endPoint') + await axios.get(`https://www.naver.com`, { httpsAgent: new https.Agent({ keepAlive: false }) }) + req.cache.expire(key, 10, function (err, data) { + agent.callbackTraceClose((childTrace) => { + t.true(err === null, `expire callback err is null`) + let actualSpanEvent = agent.getSpanEventByAsyncId(childTrace.localAsyncId) + let actualBuilder = new MethodDescriptorBuilder('expire') + .setClassName('RedisClient') + let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) + t.equal(actualMethodDescriptor.apiId, actualSpanEvent.apiId, 'RedisClient.expire apiId') + t.equal(actualMethodDescriptor.apiDescriptor, 'RedisClient.expire', 'RedisClient.expire apiDescriptor') + t.equal(actualMethodDescriptor.className, 'RedisClient', 'RedisClient.expire className') + t.equal(actualMethodDescriptor.methodName, 'expire', 'RedisClient.expire methodName') + t.equal(actualSpanEvent.sequence, 2, 'RedisClient.expire actualSpanEvent.sequence') + t.equal(actualSpanEvent.depth, 2, 'RedisClient.expire actualSpanEvent.depth') + t.equal(actualSpanEvent.serviceType, 8200, 'RedisClient.expire actualSpanEvent.serviceType') + t.equal(actualSpanEvent.destinationId, 'Redis', 'RedisClient.expire actualSpanEvent.destinationId') + t.equal(actualSpanEvent.endPoint, `localhost:${container.getMappedPort(6379)}`, 'RedisClient.expire actualSpanEvent.endPoint') + }) }) + + const childTrace = agent.currentTraceObject() + const actualHttpCallbackNextAsyncId = childTrace.repository.dataSender.mockSpanChunks[1].localAsyncId + const actualHttpCallbackSpanChunk = childTrace.repository.dataSender.findSpanChunk(actualHttpCallbackNextAsyncId) + t.equal(actualHttpCallbackSpanChunk.traceRoot.getTraceId().getSpanId(), childTrace.traceRoot.getTraceId().getSpanId(), 'HTTP request callback spanId') + t.equal(actualHttpCallbackSpanChunk.getTraceRoot(), childTrace.getTraceRoot(), 'HTTP request callback transactionId') + t.equal(actualHttpCallbackSpanChunk.localAsyncId.asyncId, actualHttpCallbackNextAsyncId.asyncId, 'HTTP request callback localAsyncId.asyncId') + t.equal(actualHttpCallbackSpanChunk.localAsyncId.sequence, 1, 'HTTP request callback localAsyncId.sequence') + + let actualSpanEvent = actualHttpCallbackSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 0) + t.equal(actualSpanEvent.apiId, defaultPredefinedMethodDescriptorRegistry.asyncInvocationDescriptor.apiId, 'HTTP request callback asyncInvocationDescriptor.spanEvent.apiId') + t.equal(actualSpanEvent.depth, 1, 'HTTP request callback asyncInvocationDescriptor.spanEvent.depth') + t.equal(actualSpanEvent.sequence, 0, 'HTTP request callback asyncInvocationDescriptor.spanEvent.sequence') + t.equal(actualSpanEvent.serviceType, ServiceType.async.getCode(), 'HTTP request callback asyncInvocationDescriptor.spanEvent.serviceType') + + actualSpanEvent = actualHttpCallbackSpanChunk.spanEventList.find(spanEvent => spanEvent.sequence === 1) + t.equal(actualSpanEvent.apiId, 0, 'HTTP request callback spanEvent.apiId') + t.equal(actualSpanEvent.depth, 2, 'HTTP request callback spanEvent.depth') + t.equal(actualSpanEvent.sequence, 1, 'HTTP request callback spanEvent.sequence') + t.equal(actualSpanEvent.serviceType, ServiceType.asyncHttpClient.getCode(), 'HTTP request callback spanEvent.serviceType') + + let actualAnnotation = actualSpanEvent.annotations[0] + t.equal(actualAnnotation.key, annotationKey.API.getCode(), 'HTTP request callback spanEvent.annotation[0].key') + t.equal(actualAnnotation.value, 'GET', 'HTTP request callback spanEvent.annotation[0].value') + actualAnnotation = actualSpanEvent.annotations[1] + t.equal(actualAnnotation.key, annotationKey.HTTP_URL.getCode(), 'HTTP request callback spanEvent.annotation[1].key annotationKey.HTTP_URL') + t.equal(actualAnnotation.value, 'www.naver.com/', 'HTTP request callback spanEvent.annotation[1].value annotationKey.HTTP_URL') + actualAnnotation = actualSpanEvent.annotations[2] + t.equal(actualAnnotation.key, annotationKey.HTTP_STATUS_CODE.getCode(), 'HTTP request callback spanEvent.annotation[2].key annotationKey.HTTP_STATUS_CODE') + t.equal(actualAnnotation.value, 200, 'HTTP request callback spanEvent.annotation[2].value annotationKey.HTTP_STATUS_CODE') + + res.json(value) }) - assertTrace(trace => { + agent.callbackTraceClose(trace => { let actualBuilder = new MethodDescriptorBuilder('use') .setClassName('Router') let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) @@ -193,10 +193,7 @@ test(`${testName1} should Record the connections between express and redis.`, as t.equal(actualSpanEvent.serviceType, 8200, 'serviceType') t.equal(actualSpanEvent.destinationId, 'Redis', 'destinationId') t.equal(actualSpanEvent.endPoint, `localhost:${container.getMappedPort(6379)}`, 'endPoint') - actualNextAsyncId = actualSpanEvent.asyncId - actualSpanId = trace.spanBuilder.traceRoot.getTraceId().getSpanId() }) - res.json(value) }) app.get(`${PATH}/:name`, function (req, res, next) { @@ -223,10 +220,12 @@ test(`${testName1} should Record the connections between express and redis.`, as const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) t.ok(rstGet.status, 200) + t.end() + }) + t.teardown(async () => { client.quit() await container.stop() server.close() - t.end() }) }) @@ -403,7 +402,7 @@ test(`${testName3} should Record the connections between koa and redis.`, async const PATH = `/${testName}` app.use(koaBodyParser()) router.post(PATH, async function (ctx, next) { - console.log(ctx.request.body) + // console.log(ctx.request.body) const key = ctx.request.body.name const value = JSON.stringify(ctx.request.body) @@ -481,7 +480,7 @@ test(`${testName4} should Record the connections between koa and ioredis.`, asyn if (err) { console.error(err) } else { - console.log(result) + // console.log(result) } }) @@ -489,14 +488,14 @@ test(`${testName4} should Record the connections between koa and ioredis.`, asyn if (err) { console.error(err) } else { - console.log(result) + // console.log(result) } }) redis.get(key, (err, result) => { if (err) { console.error(err) } else { - console.log(result) + // console.log(result) } }) @@ -506,11 +505,8 @@ test(`${testName4} should Record the connections between koa and ioredis.`, asyn app.use(router.routes()).use(router.allowedMethods()) const server = app.listen(TEST_ENV.port, async () => { - console.log('Step1.') const rstPush = await axios.post(getServerUrl(PATH), redisData) t.ok(rstPush.status, 200) - - console.log('Step2.') const rstGet = await axios.get(getServerUrl(`${PATH}/jundol`)) t.ok(rstGet.status, 200) @@ -545,7 +541,7 @@ test(`${testName1} await connections between express and redis.`, async function res.send("error " + err) return } - assertTrace(async childTrace => { + agent.callbackTraceClose(async childTrace => { let actualSpanChunk = childTrace.repository.dataSender.mockSpanChunks[0] let actualNextAsyncId = trace.repository.buffer.find(spanEvent => spanEvent.sequence === 1).asyncId t.equal(actualSpanChunk.traceRoot.getTraceId().getSpanId(), traceRoot.getTraceId().getSpanId(), 'spanId') @@ -625,7 +621,7 @@ test(`${testName1} await connections between express and redis.`, async function res.json('ok') }) - assertTrace(trace => { + agent.callbackTraceClose(trace => { let actualBuilder = new MethodDescriptorBuilder('post') .setClassName('Router') let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder) diff --git a/test/instrumentation/request-header-utils.test.js b/test/instrumentation/request-header-utils.test.js index 920c9cab..9a1a0a77 100644 --- a/test/instrumentation/request-header-utils.test.js +++ b/test/instrumentation/request-header-utils.test.js @@ -10,7 +10,6 @@ const http = require('http') const https = require('https') const { fixture } = require('../test-helper') const agent = require('../support/agent-singleton-mock') -agent.bindHttp() const localStorage = require('../../lib/instrumentation/context/local-storage') const express = require('express') const HttpOutgoingRequestHeader = require('../../lib/instrumentation/http/http-outgoing-request-header') @@ -27,6 +26,7 @@ const endPoint = 'localhost:5005' const rpcName = '/tests/123' test('Should read pinpoint header', async function (t) { + agent.bindHttp() const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('hello') @@ -47,6 +47,7 @@ test('Should read pinpoint header', async function (t) { }) test('Should write pinpoint header', async function (t) { + agent.bindHttp() const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('hello') @@ -79,6 +80,7 @@ test('Should write pinpoint header', async function (t) { }) test('nested request HTTP', async function (t) { + agent.bindHttp() const app = new express() let actualAssertsOn5006 diff --git a/test/pinpoint-config-test.json b/test/pinpoint-config-test.json index cf0436c9..b6177bec 100644 --- a/test/pinpoint-config-test.json +++ b/test/pinpoint-config-test.json @@ -15,7 +15,7 @@ "http-status-code-errors": [ "5xx" ], - "log-level": "WARN", + "log-level": "INFO", "enable": true, "enabled-data-sending": true, "enabled-stats-monitor-sending": false, diff --git a/test/support/agent-singleton-mock.js b/test/support/agent-singleton-mock.js index c0988ae9..6be93d5c 100644 --- a/test/support/agent-singleton-mock.js +++ b/test/support/agent-singleton-mock.js @@ -15,7 +15,7 @@ const shimmer = require('@pinpoint-apm/shimmer') const httpShared = require('../../lib/instrumentation/http-shared') const activeTrace = require('../../lib/metric/active-trace') const localStorage = require('../../lib/instrumentation/context/local-storage') -const sqlMetaDataService = require('../../lib/instrumentation/sql/sql-metadata-service') +const sqlMetadataService = require('../../lib/instrumentation/sql/sql-metadata-service') const SimpleCache = require('../../lib/utils/simple-cache') const sampler = require('../../lib/sampler/sampler') const TraceSampler = require('../../lib/context/trace/trace-sampler') @@ -32,14 +32,6 @@ const getTraces = () => { return traces } -let spanOrSpanChunks = [] -const resetSpanOrSpanChunks = () => { - spanOrSpanChunks = [] -} -const getSpanOrSpanChunks = () => { - return spanOrSpanChunks -} - let sendedApiMetaInfos = [] const resetSendedApiMetaInfos = () => { sendedApiMetaInfos = [] @@ -47,16 +39,49 @@ const resetSendedApiMetaInfos = () => { const getSendedApiMetaInfos = () => { return sendedApiMetaInfos } -const getTraceByAsyncId = (asyncId) => { + +let sqlMetadata = [] +const resetSqlMetaData = () => { + sqlMetadata = [] +} +const getSqlMetadata = () => { + return sqlMetadata +} + +const getSpanEvents = () => { + return getTraces().flatMap(trace => { + let spanEvents = trace.callStack.stack.concat(trace.repository.buffer) + if (trace.repository.spanChunkedSpanEvents) { + spanEvents = spanEvents.concat(trace.repository.spanChunkedSpanEvents) + } + return spanEvents + }) +} + +const getSpanEventByAsyncId = (asyncId) => { + return getSpanEvents().find(spanEvent => spanEvent.asyncId?.getAsyncId() === asyncId.getAsyncId()) +} + +const getCallerTraceByAsyncId = (asyncId) => { return getTraces().find(trace => { - const spanChunkSpanEvents = agent.dataSender.mockSpanChunks.flatMap(spanChunk => spanChunk.spanEventList) - const spanEvents = trace.callStack.stack.concat(trace.repository.buffer).concat(spanChunkSpanEvents) + let spanEvents = trace.callStack.stack.concat(trace.repository.buffer) + if (trace.repository.spanChunkedSpanEvents) { + spanEvents = spanEvents.concat(trace.repository.spanChunkedSpanEvents) + } const asyncSpanEvent = spanEvents.find(spanEvent => spanEvent.asyncId?.getAsyncId() === asyncId.getAsyncId()) - return asyncSpanEvent + return asyncSpanEvent ? trace : null }) } class MockAgent extends Agent { + constructor(initOptions) { + initOptions.collector['span-port'] = -1 + initOptions.collector['stat-port'] = -1 + initOptions.collector['tcp-port'] = -1 + initOptions = portProperties(initOptions) + super(initOptions) + } + startSchedule(agentId, agentStartTime) { this.mockAgentId = agentId this.mockAgentStartTime = agentStartTime @@ -71,8 +96,7 @@ class MockAgent extends Agent { this.cleanHttp() this.dataSender.clear() - json = this.portProperties(json) - + json = portProperties(json) if (!json) { json = require('../pinpoint-config-test') } else { @@ -82,7 +106,7 @@ class MockAgent extends Agent { const config = require('../../lib/config').getConfig(json) this.config = config - sqlMetaDataService.cache = new SimpleCache(1024) + sqlMetadataService.cache = new SimpleCache(1024) this.traceContext.isSampling = sampler.getIsSampling(config.sampling, config.sampleRate) if (sampler.getSamplingCountGenerator()) { sampler.getSamplingCountGenerator().reset() @@ -107,37 +131,76 @@ class MockAgent extends Agent { this.traceContext.traceSampler = new TraceSampler(this.agentInfo, config) this.traceContext.config = config - const dataSender = dataSenderMock(this.config, this.agentInfo) + const dataSender = this.makeDataSender() this.traceContext.dataSender = dataSender + this.dataSender.close() this.dataSender = dataSender - + // this.initializeDataSender(dataSender) stringMetaService.init(dataSender) apiMetaService.init(dataSender) - - resetSpanOrSpanChunks() + sqlMetadataService.setDataSender(dataSender) resetTraces() - this.newTraceCallback = null - const getNewTraceCallback = () => { - return this.newTraceCallback + const findSpanEvents = function () { + let spanEvents = this.callStack.stack.concat(this.repository.buffer) + if (this.repository.spanChunkedSpanEvents) { + spanEvents = spanEvents.concat(this.repository.spanChunkedSpanEvents) + } + if (this.spanBuilder?.spanEventList) { + spanEvents = spanEvents.concat(this.spanBuilder.spanEventList) + } + return spanEvents + } + const findSpanEventByAsyncId = function (asyncId) { + return this.findSpanEvents().find(spanEvent => spanEvent.asyncId?.getAsyncId() === asyncId.getAsyncId()) } shimmer.wrap(this.traceContext, 'newTrace', function (origin) { return function () { const returned = origin.apply(this, arguments) - getNewTraceCallback()?.(returned) + returned.findSpanEvents = findSpanEvents + returned.findSpanEventByAsyncId = findSpanEventByAsyncId + const sendSpanChunkFunction = returned.repository.sendSpanChunk + returned.repository.sendSpanChunk = function () { + const result = sendSpanChunkFunction.apply(this, arguments) + if (!returned.repository.spanChunkedSpanEvents) { + returned.repository.spanChunkedSpanEvents = [] + } + returned.repository.spanChunkedSpanEvents.push(arguments[0]) + return result + } + const closeOrigin = returned.close + returned.close = function () { + const result = closeOrigin.apply(this, arguments) + returned.closeCallback?.(returned) + return result + } getTraces().push(returned) return returned } }) - this.continueAsyncContextTraceObjectCallback = null - const getContinueAsyncContextTraceObjectCallback = () => { - return this.continueAsyncContextTraceObjectCallback - } shimmer.wrap(this.traceContext, 'continueAsyncContextTraceObject', function (origin) { return function () { const returned = origin.apply(this, arguments) - getContinueAsyncContextTraceObjectCallback()?.(returned) + returned.findSpanEvents = findSpanEvents + returned.findSpanEventByAsyncId = findSpanEventByAsyncId + if (returned.repository) { + const sendSpanChunkFunction = returned.repository.sendSpanChunk + returned.repository.sendSpanChunk = function () { + const result = sendSpanChunkFunction.apply(this, arguments) + if (!returned.repository.spanChunkedSpanEvents) { + returned.repository.spanChunkedSpanEvents = [] + } + returned.repository.spanChunkedSpanEvents = returned.repository.spanChunkedSpanEvents.concat(arguments[0]) + return result + } + } + const closeOrigin = returned.close + returned.close = function () { + const result = closeOrigin.apply(this, arguments) + returned.closeCallback?.(returned) + return result + } getTraces().push(returned) return returned } @@ -151,6 +214,15 @@ class MockAgent extends Agent { return returned } }) + + resetSqlMetaData() + shimmer.wrap(sqlMetadataService, 'send', function (origin) { + return function () { + const returned = origin.apply(this, arguments) + getSqlMetadata().push(arguments[0]) + return returned + } + }) } cleanHttp() { @@ -170,20 +242,11 @@ class MockAgent extends Agent { } bindHttpWithCallSite(conf) { - conf = this.portProperties(conf) + conf = portProperties(conf) conf = Object.assign({}, { 'trace-location-and-filename-of-call-site': true }, conf) this.bindHttp(conf) } - portProperties(conf) { - if (typeof conf !== 'number') { - return conf - } - const testConf = require('../pinpoint-config-test') - const collectorConf = Object.assign(testConf.collector, { 'span-port': conf, 'stat-port': conf, 'tcp-port': conf }) - return Object.assign({}, { collector: collectorConf }) - } - completeTraceObject(trace) { super.completeTraceObject(trace) } @@ -196,30 +259,48 @@ class MockAgent extends Agent { return getTraces()[index] } - getSpanChunk(asyncId) { - return getSpanOrSpanChunks().find(spanOrSpanChunk => spanOrSpanChunk.getLocalasyncid().getAsyncid() === asyncId.getAsyncId() && spanOrSpanChunk.getLocalasyncid().getSequence() === asyncId.getSequence()) + getTraceByAsyncId(asyncId) { + return getTraces().find(trace => trace.findSpanEventByAsyncId(asyncId)) } - getSpanOrSpanChunks() { - return getSpanOrSpanChunks() + getSpanEventByAsyncId(asyncId) { + return getSpanEventByAsyncId(asyncId) } getSendedApiMetaInfos() { return getSendedApiMetaInfos() } - setNewTraceCallback(callback) { - this.newTraceCallback = callback + getSqlMetadata() { + return getSqlMetadata() } - setContinueAsyncContextTraceObjectCallback(callback) { - this.continueAsyncContextTraceObjectCallback = callback + getCallerTraceByAsyncId(asyncId) { + return getCallerTraceByAsyncId(asyncId) } - getTraceByAsyncId(asyncId) { - return getTraceByAsyncId(asyncId) + makeDataSender() { + return dataSenderMock(this.config, this.agentInfo) + } +} + +function portProperties(conf) { + if (!conf) { + return conf } + + if (typeof conf !== 'number') { + if (conf.collector) { + return conf + } + const collectorConf = Object.assign({ 'ip': '127.0.0.1', 'span-port': -1, 'stat-port': -1, 'tcp-port': -1 }) + return Object.assign(conf, { collector: collectorConf }) + } + const portNumber = conf + const collectorConf = Object.assign(require('../pinpoint-config-test').collector, { 'span-port': portNumber, 'stat-port': portNumber, 'tcp-port': portNumber }) + return Object.assign({ collector: collectorConf }) } + const agent = new MockAgent(require('../pinpoint-config-test')) module.exports = agent \ No newline at end of file diff --git a/test/support/data-sender-mock.js b/test/support/data-sender-mock.js index 7ec0b1c7..7c18a4ba 100644 --- a/test/support/data-sender-mock.js +++ b/test/support/data-sender-mock.js @@ -15,6 +15,7 @@ const DataSender = require('../../lib/client/data-sender') const MockGrpcDataSender = require('../client/mock-grpc-data-sender') const SqlMetaData = require('../../lib/client/sql-meta-data') const GrpcDataSender = require('../../lib/client/grpc-data-sender') +const SqlUidMetaData = require('../../lib/client/sql-uid-meta-data') class MockDataSender extends DataSender { constructor(config, dataSender) { @@ -22,6 +23,8 @@ class MockDataSender extends DataSender { this.mockAPIMetaInfos = [] this.mockSpanChunks = [] this.mockSpans = [] + this.mockSqlMetadata = [] + this.mockSqlUidMetadata = [] } send(data) { @@ -31,31 +34,26 @@ class MockDataSender extends DataSender { if (data instanceof AgentInfo) { this.mockAgentInfo = data - super.send(data) } else if (data instanceof ApiMetaInfo) { this.mockAPIMetaInfos.push(data) - super.send(data) } else if (data instanceof StringMetaInfo) { this.mockMetaInfo = data - super.send(data) } else if (data instanceof Span) { this.mockSpan = data this.mockSpans.push(data) - super.send(data) } else if (data instanceof SpanChunk) { this.mockSpanChunks.push(data) - super.send(data) } else if (data instanceof SqlMetaData) { - this.mockSqlMetaData = data - super.send(data) + this.mockSqlMetadata.push(data) } else if (data?.isAsyncSpanChunk?.()) { this.mockSpanChunks.push(data) - super.send(data) } else if (data?.isSpan?.()) { this.mockSpan = data this.mockSpans.push(data) - super.send(data) + } else if (data instanceof SqlUidMetaData) { + this.mockSqlUidMetadata.push(data) } + super.send(data) } findSpanChunk(asyncId) { @@ -69,6 +67,8 @@ class MockDataSender extends DataSender { this.mockAPIMetaInfos = [] this.mockSpanChunks = [] this.mockSpans = [] + this.mockSqlMetadata = [] + this.mockSqlUidMetadata = [] } close() { @@ -77,11 +77,16 @@ class MockDataSender extends DataSender { } } -const dataSender = (conf, agentInfo) => { - if (typeof conf?.collectorSpanPort === 'number') { +const dataSender = (conf, agentInfoOrGrpcDataSender) => { + if (typeof agentInfoOrGrpcDataSender?.sendAgentInfo === 'function') { return new MockDataSender({ enabledDataSending: true, - }, new GrpcDataSender(conf.collectorIp, conf.collectorSpanPort, conf.collectorStatPort, conf.collectorTcpPort, agentInfo, conf)) + }, agentInfoOrGrpcDataSender) + } + if (conf?.collectorSpanPort > 0) { + return new MockDataSender({ + enabledDataSending: true, + }, new GrpcDataSender(conf.collectorIp, conf.collectorSpanPort, conf.collectorStatPort, conf.collectorTcpPort, agentInfoOrGrpcDataSender, conf)) } return new MockDataSender({ enabledDataSending: true, diff --git a/test/utils/log/logger.test.js b/test/utils/log/logger.test.js index fdfca3a8..bdddecfa 100644 --- a/test/utils/log/logger.test.js +++ b/test/utils/log/logger.test.js @@ -60,16 +60,16 @@ test('Logger.Builder', (t) => { test('Logger by a log level', (t) => { let actual = Logger.makeBuilder('debug', {}).build() - t.equal(actual.type.name, 'DEBUG') + t.equal(actual.type.name, 'DEBUG', `debug name match`) actual = Logger.makeBuilder('info', {}).build() - t.equal(actual.type.name, 'INFO') + t.equal(actual.type.name, 'INFO', `info name match`) actual = Logger.makeBuilder('warn', {}).build() - t.equal(actual.type.name, 'WARN') + t.equal(actual.type.name, 'WARN', `warn name match`) actual = Logger.makeBuilder('error', {}).build() - t.equal(actual.type.name, 'ERROR') + t.equal(actual.type.name, 'ERROR', `error name match`) t.end() }) \ No newline at end of file