newrelic
Version:
New Relic agent
102 lines (85 loc) • 2.68 kB
JavaScript
var logger = require('../../logger').child({component: 'async_hooks'})
var promInit = require('../promise')
module.exports = initialize
/**
* The spec for the native `Promise` class.
*/
var STATIC_PROMISE_METHODS = ['accept', 'all', 'defer', 'race', 'reject', 'resolve']
var NATIVE_PROMISE_SPEC = {
name: 'global',
constructor: 'Promise',
executor: true,
useFinally: false,
$proto: {
then: ['then', 'chain'],
catch: ['catch']
},
$static: {
$copy: STATIC_PROMISE_METHODS,
cast: STATIC_PROMISE_METHODS
}
}
function initialize(agent, shim) {
var enableHooks = agent.config.checkAsyncHookStatus()
if (enableHooks && tryAsyncHooks(agent, shim)) {
logger.debug('Using async_hooks.')
} else {
logger.debug('Using promise instrumentation.')
promInit(agent, global, NATIVE_PROMISE_SPEC)
}
}
function tryAsyncHooks(agent, shim) {
var asyncHooks = null
try {
asyncHooks = require('async_hooks')
} catch (e) {
logger.info(e, 'Not using async_hooks module.')
return false
}
// this map is reused to track the segment that was active when
// the before callback is called to be replaced in the after callback
var segmentMap = new Map()
module.exports.segmentMap = segmentMap
var hook = asyncHooks.createHook({
init: function initHook(id, type, triggerId, promiseWrap) {
var transaction = agent.getTransaction()
var parentSegment = segmentMap.get(triggerId)
if (!parentSegment && !transaction || type !== 'PROMISE') {
return
}
var activeSegment = shim.getActiveSegment() || parentSegment
if (promiseWrap && promiseWrap.promise) {
promiseWrap.promise.__NR_id = id
}
segmentMap.set(id, activeSegment)
},
before: function beforeHook(id) {
var hookSegment = segmentMap.get(id)
if (!hookSegment) {
return
}
segmentMap.set(id, shim.getActiveSegment())
shim.setActiveSegment(hookSegment)
},
after: function afterHook(id) {
var hookSegment = segmentMap.get(id)
// hookSegment is the segment that was active before the promise
// executed. If the promise is executing before a segment has been
// restored, hookSegment will be null and should be restored. Thus
// undefined is the only invalid value here.
if (hookSegment === undefined) {
return
}
segmentMap.set(id, shim.getActiveSegment())
shim.setActiveSegment(hookSegment)
},
destroy: function destHook(id) {
segmentMap.delete(id)
}
}).enable()
agent.on('unload', function disableHook() {
hook.disable()
})
return true
}