Skip to content

Commit

Permalink
[#150] Configuration location and filename and linenumber
Browse files Browse the repository at this point in the history
* express without CallSite  location and filename and linenumber
* koa without CallSite  location and filename and linenumber
* mysql without CallSite  location and filename and linenumber
  • Loading branch information
feelform committed Nov 8, 2023
1 parent 1ad772f commit 8383b14
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 28 deletions.
10 changes: 6 additions & 4 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const ENV_MAP = {
container: valueOfBoolean('PINPOINT_CONTAINER'),
traceExclusionUrlPatterns: valueOfString('PINPOINT_TRACE_EXCLUSION_URL_PATTERN'),
traceExclusionUrlCacheSize: valueOfNumber('PINPOINT_TRACE_EXCLUSION_URL_CACHE_SIZE'),
traceLocationAndFileNameOfCallSite: valueOfBoolean('PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'),
}

const CONFIG_FILE_MAP = {
Expand All @@ -81,7 +82,8 @@ const CONFIG_FILE_MAP = {
container: 'container',
traceExclusionUrlPatterns: 'trace-exclusion-url.pattern',
traceExclusionUrlCacheSize: 'trace-exclusion-url.cache-size',
streamDeadlineMinutesClientSide: 'stream-deadline-minutes.client-side'
streamDeadlineMinutesClientSide: 'stream-deadline-minutes.client-side',
traceLocationAndFileNameOfCallSite: 'trace-location-and-filename-of-call-site'
}

let agentConfig = null
Expand Down Expand Up @@ -206,11 +208,11 @@ const readRootConfigFile = () => {
return require(fileFullPath)
}

const getMainModulePath = (requireFuction) => {
if (!requireFuction || !requireFuction.main || !requireFuction.main.filename) {
const getMainModulePath = (requireFunction) => {
if (!requireFunction || !requireFunction.main || !requireFunction.main.filename) {
return
}
return path.dirname(requireFuction.main.filename)
return path.dirname(requireFunction.main.filename)
}

const getValue = (key, configFile) => {
Expand Down
9 changes: 8 additions & 1 deletion lib/instrumentation/instrument-method.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const apiMetaService = require('../context/api-meta-service')
const IdGenerator = require('../context/id-generator')
const { callSite } = require('./call-stack')
const InstrumentMethodContext = require('./instrument-method-context')
const config = require('../config')

// ref: SpanEventSimpleAroundInterceptorForPlugin.java
class InstrumentMethod {
Expand All @@ -37,7 +38,13 @@ class InstrumentMethod {
const target = this.target
shimmer.wrap(this.target, this.method, function (original) {
return function () {
const builder = callSite(interceptor.methodDescriptorBuilder)
let builder
const conf = config.getConfig()
if (conf && conf.traceLocationAndFileNameOfCallSite) {
builder = callSite(interceptor.methodDescriptorBuilder)
} else {
builder = interceptor.methodDescriptorBuilder
}
const context = new InstrumentMethodContext(builder, traceContext)

if (typeof interceptor.prepareBeforeTrace === 'function') {
Expand Down
12 changes: 10 additions & 2 deletions lib/instrumentation/module/express/patch-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ function patchLayer(layer, context) {
const spanEventRecorder = trace.traceBlockBegin()
spanEventRecorder.recordServiceType(serviceType)

const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder())
let builder = context.getMethodDescriptorBuilder()
if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && req && typeof req.method === 'string') {
builder = builder.makeCloneOf(req.method.toLowerCase())
}
const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(builder)
spanEventRecorder.recordApi(apiMethodDescriptor)

// https://github.com/elastic/apm-agent-nodejs/issues/443#issuecomment-455352070
Expand All @@ -48,7 +52,11 @@ function patchLayer(layer, context) {
spanEventRecorder.recordServiceType(serviceType)
spanEventRecorder.recordException(err, true)

const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder())
let builder = context.getMethodDescriptorBuilder()
if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && req && typeof req.method === 'string') {
builder = builder.makeCloneOf(req.method.toLowerCase())
}
const apiMethodDescriptor = apiMetaService.cacheApiWithBuilder(builder)
spanEventRecorder.recordApi(apiMethodDescriptor)
}
const result = original.apply(this, arguments)
Expand Down
10 changes: 8 additions & 2 deletions lib/instrumentation/module/koa/koa-register-interceptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const serviceType = require('./koa-service-type')
class KoaRegisterInterceptor {
constructor() {
this.methodDescriptorBuilder = MethodDescriptorBuilder.makeRuntimeDetectionMethodDescriptorBuilder()
.setClassName('koa')
.setClassName('Router')
.setFileNameIndex(3)
}

Expand All @@ -38,7 +38,13 @@ class KoaRegisterInterceptor {
}

layer.stack[handlerIndex] = async function (ctx, next) {

Check failure on line 40 in lib/instrumentation/module/koa/koa-register-interceptor.js

View workflow job for this annotation

GitHub Actions / Checkstyle

eslint.rules.no-unused-vars

'next' is defined but never used. (no-unused-vars)

Check failure on line 40 in lib/instrumentation/module/koa/koa-register-interceptor.js

View workflow job for this annotation

GitHub Actions / Checkstyle

eslint.rules.no-unused-vars

'next' is defined but never used. (no-unused-vars)

Check failure on line 40 in lib/instrumentation/module/koa/koa-register-interceptor.js

View workflow job for this annotation

GitHub Actions / Checkstyle

eslint.rules.no-unused-vars

'next' is defined but never used. (no-unused-vars)
const methodDescriptor = apiMetaService.cacheApiWithBuilder(context.getMethodDescriptorBuilder())
const method = ctx.method

let builder = context.getMethodDescriptorBuilder()
if (builder && typeof builder.isRuntimeDetection === 'function' && builder.isRuntimeDetection() && typeof method === 'string') {
builder = builder.makeCloneOf(method.toLowerCase())
}
const methodDescriptor = apiMetaService.cacheApiWithBuilder(builder)
const trace = context.currentTraceObject()
let spanEventRecorder = null
let result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MySQLCreateConnectionInterceptor {
this.methodDescriptorBuilder.setParameterVariableNames('connectionUri')
}

doInBeforeTrace(recorder, target, args) {
doInBeforeTrace(recorder) {
recorder.recordServiceType(serviceType)
}

Expand Down
3 changes: 2 additions & 1 deletion lib/pinpoint-config-default.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@
},
"stream-deadline-minutes": {
"client-side": 10
}
},
"trace-location-and-filename-of-call-site": false
}
28 changes: 26 additions & 2 deletions test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ test('main moudle path', (t) => {
const conf = config.readRootConfigFile()
t.deepEqual(conf, {}, 'configuration is null object')
let actual = config.getMainModulePath(require)
let actualParsedPath = path.parse(actual)
t.deepEqual(actualParsedPath.base, 'bin', 'main module path')

actual = config.getMainModulePath({})
t.true(actual === undefined, 'config.getMainModulePath({}) return value is undefined')
Expand Down Expand Up @@ -167,5 +165,31 @@ test('Agent ID length check', (t) => {
delete process.env.PINPOINT_AGENT_ID
delete process.env.PINPOINT_APPLICATION_NAME

t.end()
})

test('callSite config', (t) => {
config.clear()

let given = config.getConfig()
t.false(given.traceLocationAndFileNameOfCallSite, 'default value is false')

config.clear()
process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = ''
given = config.getConfig()
t.false(given.traceLocationAndFileNameOfCallSite, 'default value is true validation')

config.clear()
process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = 'false'
given = config.getConfig()
t.false(given.traceLocationAndFileNameOfCallSite, 'false value is false')

config.clear()
process.env['PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE'] = 'true'
given = config.getConfig()
t.true(given.traceLocationAndFileNameOfCallSite, 'true value is true')

delete process.env.PINPOINT_TRACE_LOCATION_AND_FILENAME_OF_CALL_SITE
config.clear()
t.end()
})
68 changes: 67 additions & 1 deletion test/context/callstack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const TEST_ENV = {
}
const getServerUrl = (path) => `http://${TEST_ENV.host}:${TEST_ENV.port}${path}`
test(`fix express call stack depth`, async (t) => {
agent.bindHttp()
agent.bindHttpWithCallSite()

const app = new express()
const path = `/`
Expand Down Expand Up @@ -108,6 +108,72 @@ test(`fix express call stack depth`, async (t) => {
t.equal(actualSpanChunk.localAsyncId.asyncId, 1, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.asyncId')
t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.sequence')

t.end()
server.close()
})
})

test('fix express call stack depth without callSite', async (t) => {
agent.bindHttp()

const app = new express()
const path = `/`

app.use(express.json())
app.use(express.urlencoded({
extended: false
}))

const router1 = express.Router()
router1.get(path, async (req, res) => {
const result = await axios.get(`https://www.naver.com`)
t.equal(result.status, 200)
res.send('ok router1')
})
app.use('/router1', router1)

const server = app.listen(TEST_ENV.port, async function () {
const result1 = await axios.get(getServerUrl(`/router1${path}`))
t.ok(result1.status, 200)
t.ok(result1.data, 'ok router1')

t.equal(agent.dataSender.mockSpan.spanEventList.length, 4, `span has 6 span events`)
t.equal(agent.dataSender.mockSpan.apiId, defaultPredefinedMethodDescriptorRegistry.nodeServerMethodDescriptor.getApiId(), 'nodeServerMethodDescriptor apiId')

let actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
let actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
let actualSpanEvent = agent.dataSender.mockSpan.spanEventList[0]
t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'use(jsonParser) apiId')
t.equal(actualSpanEvent.sequence, 0, 'use(jsonParser) sequence')
t.equal(actualSpanEvent.depth, 1, 'use(jsonParser) depth')
t.equal(actualSpanEvent.serviceType, ServiceTypeCode.express, 'use(jsonParser) serviceType')

actualBuilder = new MethodDescriptorBuilder('use')
.setClassName('Router')
actualMethodDescriptor = apiMetaService.cacheApiWithBuilder(actualBuilder)
actualSpanEvent = agent.dataSender.mockSpan.spanEventList[1]
t.equal(actualSpanEvent.apiId, actualMethodDescriptor.apiId, 'use(urlencodedParser) apiId')
t.equal(actualSpanEvent.sequence, 1, 'use(urlencodedParser) sequence')
t.equal(actualSpanEvent.depth, 1, 'use(urlencodedParser) depth')
t.equal(actualSpanEvent.serviceType, ServiceTypeCode.express, 'use(urlencodedParser) serviceType')

actualSpanEvent = agent.dataSender.mockSpan.spanEventList[2]
t.equal(actualSpanEvent.apiId, 0, 'await axios.get(`https://naver.com`) apiId')
t.equal(actualSpanEvent.sequence, 3, 'await axios.get(`https://naver.com`) sequence')
t.equal(actualSpanEvent.depth, 2, 'await axios.get(`https://naver.com`) depth')
t.equal(actualSpanEvent.serviceType, ServiceTypeCode.ASYNC_HTTP_CLIENT_INTERNAL, 'await axios.get(`https://naver.com`) serviceType')
t.equal(actualSpanEvent.nextAsyncId, 2, 'await axios.get(`https://naver.com`) nextAsyncId')
let actualAnnotation = actualSpanEvent.annotations[0]
t.equal(actualAnnotation.key, 12, 'await axios.get(`https://naver.com`) spanevent annotation key')
t.equal(actualAnnotation.value, 'http.request', 'await axios.get(`https://naver.com`) spanevent annotation value')

t.equal(agent.dataSender.mockSpanChunks.length, 1, 'await axios.get(`https://naver.com`) spanchunk is 1')
let actualSpanChunk = agent.dataSender.mockSpanChunks[0]
t.equal(actualSpanChunk.agentId, agent.dataSender.mockSpan.agentId, 'await axios.get(`https://naver.com`) spanchunk agentId')
t.equal(actualSpanChunk.localAsyncId.asyncId, 2, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.asyncId')
t.equal(actualSpanChunk.localAsyncId.sequence, 0, 'await axios.get(`https://naver.com`) spanchunk localAsyncId.sequence')

t.end()
server.close()
})
Expand Down
Loading

0 comments on commit 8383b14

Please sign in to comment.