UNPKG

ca-apm-probe

Version:

CA APM Node.js Agent monitors real-time health and performance of Node.js applications

231 lines (191 loc) 7.1 kB
/** * Copyright (c) 2015 CA. All rights reserved. * * This software and all information contained therein is confidential and proprietary and * shall not be duplicated, used, disclosed or disseminated in any way except as authorized * by the applicable license agreement, without the express written permission of CA. All * authorized reproductions must be marked with this language. * * EXCEPT AS SET FORTH IN THE APPLICABLE LICENSE AGREEMENT, TO THE EXTENT * PERMITTED BY APPLICABLE LAW, CA PROVIDES THIS SOFTWARE WITHOUT WARRANTY * OF ANY KIND, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL CA BE * LIABLE TO THE END USER OR ANY THIRD PARTY FOR ANY LOSS OR DAMAGE, DIRECT OR * INDIRECT, FROM THE USE OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, LOST * PROFITS, BUSINESS INTERRUPTION, GOODWILL, OR LOST DATA, EVEN IF CA IS * EXPRESSLY ADVISED OF SUCH LOSS OR DAMAGE. */ 'use strict' var path = require('path'); var agent = require('../agent'); var proxy = require('../proxy'); var bagent = require('../browser-agent'); var util = require('util'); var logger = require("../logger.js"); var targetModule = new Object; var eventNameFormatted = 'route.handler'; var { baSnippet, isResponseDecEnabled, isAutoInjectionEnabled, isCorGuidEnabled, maxCookieExpInMs, allowedResContentType } = bagent.initializeBrowserAgentParameters(); module.exports = function (fastify) { targetModule.fastify = fastify; targetModule.reply = findReplyjs(targetModule.fastify, require); targetModule.kReplyHeaders = findjs(targetModule.fastify, require, 'symbols.js') proxy.before(targetModule.reply.prototype, 'send', function (obj, args, storage) { if (isResponseDecEnabled && isAutoInjectionEnabled && obj.raw._headerSent === false) { var contentType = obj[targetModule.kReplyHeaders.kReplyHeaders]['content-type']; if(!contentType){ contentType = 'text/html; charset=utf-8'; obj[targetModule.kReplyHeaders.kReplyHeaders]['content-type'] = contentType; } if (args[0] && contentType.includes('text/html')) { var originalHeaders = args[0]; if(typeof originalHeaders !== 'string'){ var actualBody = obj.serialize(originalHeaders); if(!bagent.validateRespIfJson(actualBody)){ originalHeaders = actualBody; } } args[0] = bagent.prepareScript(storage.get('ctx')) + originalHeaders; } } }); const wrappedFastify = wrapConstructor(fastify, proxyFastifyFunction); wrappedFastify.fastify = fastify.fastify if (fastify.fastify) { wrappedFastify.fastify = wrappedFastify } if (fastify.default) { wrappedFastify.default = wrappedFastify } return wrappedFastify; }; function proxyFastifyFunction() { return function proxyFastifyFunction() { const wrappedFastify = targetModule.fastify.apply(this, arguments) proxy.before(wrappedFastify, 'addHook', handleHook); wrappedFastify.addHook('onRoute', (routeOptions) => { if (!routeOptions.handler) { return } proxy.before(routeOptions, 'handler', routeOptionHandler); }); proxy.around(wrappedFastify, 'decorate', decorateHook, decorateHook); return wrappedFastify; } } function wrapConstructor(module, wrappedConstructor, args) { if (!module) { return module } if (typeof wrappedConstructor === 'function') { wrappedConstructor = { wrapper: wrappedConstructor } } const name = String(!module || module === true ? module : module.name || 'undefined'); const wrappedConstructorArgs = [this, module, name]; if (args && args.length) { wrappedConstructorArgs.push.apply(wrappedConstructorArgs, args) } let wrappedModule = wrappedConstructor.wrapper.apply(null, wrappedConstructorArgs) if (wrappedModule && wrappedModule !== module) { return wrappedModule; } return module; } function handleHook(obj, args, storage) { // var path = obj.path; var req = args[0]; var res = args[1]; } function routeOptionHandler(obj, args, storage) { if (agent.paused) return; var request = args[0]; var response = args[1]; if (request && request.raw) { request = request.raw; } if (response && response.raw) { response = response.raw; } var version = obj.version; var ctx = storage.get('ctx') || request.__CA_ctx; if (logger.isDebug()) { if (ctx) { logger.debug('%s[%d %d %d]: path - %s', eventNameFormatted, ctx.txid, ctx.lane, ctx.evtid, path); } else { logger.debug('%s - no context', eventNameFormatted); logger.debug((new Error('No context')).stack); } } ctx = agent.asynchEventStart(ctx, eventNameFormatted, { route: request.url, http_method: request.method, version: version }); storage.set('ctx', ctx); var errorObject = new Object(); var end_save = response.end; response.end = function () { if (response.finished === false && ctx) { ctx = agent.asynchEventDone(ctx, eventNameFormatted, null, errorObject); if (ctx) { agent.asynchEventFinish(ctx); } } end_save.apply(response, arguments); }; } function decorateHook(obj, args, storage) { var path = obj.path; var req = args[0]; var res = args[1]; } function findReplyjs(express, require) { var cache = require.cache; for (var key in cache) { var candidate = cache[key]; if (candidate.exports !== express) { continue; } var dirname = path.dirname(candidate.filename); var filename; filename = path.join(dirname, 'lib', 'reply.js'); try { return require(filename); } catch (e) { return null; } } } function findjs(express, require, javascript) { var cache = require.cache; for (var key in cache) { var candidate = cache[key]; if (candidate.exports !== express) { continue; } var dirname = path.dirname(candidate.filename); var filename; filename = path.join(dirname, 'lib', javascript); try { return require(filename); } catch (e) { return null; } } } function getMethodsWithProbes() { if (!targetModule.methodMap) { var mt = new Object; mt[0] = 'route#handler'; targetModule.methodMap = mt; } return targetModule.methodMap; } function instrument(methodMap) { targetModule.methodMap = methodMap; } module.exports.getMethodsWithProbes = getMethodsWithProbes; module.exports.instrument = instrument.bind(module);