UNPKG

ca-apm-probe

Version:

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

201 lines (175 loc) 6.14 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. */ var path = require('path'); var agent = require('../agent'); var proxy = require('../proxy'); var util = require('util'); var logger = require("../logger.js"); var targetModule = new Object; module.exports = function(mysql) { targetModule.mysql = mysql; targetModule.Connection = findConnection(targetModule.mysql, require); targetModule.Protocol = findProtocol(targetModule.Connection, require); targetModule.methodMap = getMethodsWithProbes(); var methodMap = targetModule.methodMap; //}; // //function instrument(methodMap) { var Connection = targetModule.Connection; logger.info('Loading mysql probe'); if (!Connection) { logger.error('failed to instrument mysql, cannot find Connection prototype'); return; } var Protocol = targetModule.Protocol; if (!Protocol) { logger.error('failed to instrument mysql, cannot find Protocol prototype'); return; } proxy.before(Connection.prototype, 'query', function(obj, args, storage) { if (agent.paused) return; // check if we need to instrument if (methodMap[0] == "skip_instrument") { return; } var command = args.length > 0 ? args[0] : undefined; var eventNameFormatted = 'mysql.query'; //console.log(util.inspect(obj, { showHidden: true, depth: 1 })); var dburl = 'jdbc:mysql://unknown'; if (obj.config) { dburl = 'mysql://' + obj.config.host + ':' + obj.config.port + '/' + obj.config.database; } var ctx = storage.get('ctx'); var debug = logger.isDebug(); if (debug) { if (ctx != null) { logger.debug('mysql[%d %d %d]: %s', ctx.txid, ctx.lane, ctx.evtid, command); } else { logger.debug('mysql.query - no context: %s', command); logger.debug((new Error('No context')).stack); } } ctx = agent.asynchEventStart(ctx, eventNameFormatted, { url: dburl, query: command}); storage.set('ctx', ctx); proxy.callback(args, -1, function(obj, args, storage) { var errorObject = agent.checkAndSetErrorObject(args, 'MySQLError'); if (ctx != null) { if (debug) { logger.debug('mysql[%d %d %d]: callback', ctx.txid, ctx.lane, ctx.evtid); } ctx = agent.asynchEventDone(ctx, eventNameFormatted, null, errorObject); storage.set('ctx', ctx); } else { if (debug) { logger.debug('mysql - no context: callback'); logger.debug((new Error('No context')).stack); } } }, function(obj, args) { if (ctx) { if (debug) { logger.debug('mysql[%d %d %d]: finish', ctx.txid, ctx.lane, ctx.evtid); } agent.asynchEventFinish(ctx); } else { if (debug) { logger.debug('mysql - no context: finish'); logger.debug((new Error('No context')).stack); } } }); }); proxy.before(Protocol.prototype, '_enqueue', function(obj, args, storage) { if (agent.paused) return; if (methodMap[0] == "skip_instrument") { return; } var ctx = storage.get('ctx'); var debug = logger.isDebug(); if (ctx != null) { storage.set('ctx', ctx); if (debug) { logger.debug('mysql[%d %d %d]: _enqueue', ctx.txid, ctx.lane, ctx.evtid); } var newArgs = [args[0]._callback]; proxy.callback(newArgs, -1, function(obj, args, storage) { storage.set('ctx', ctx); if (debug) { logger.debug('mysql[%d %d %d]: _enqueue callback', ctx.txid, ctx.lane, ctx.evtid); } }); args[0]._callback = newArgs[0]; } }); } // mysql connection module pattern for ref // var mysqlConnPathPat = /mysql[\/\\]+lib[\/\\]+Connection\.js$/; // given module mysql, search the require cache for the corresponding // Connection prototype function findConnection(mysql, require) { var cache = require.cache; for (var key in cache) { var candidate = cache[key]; if (candidate.exports !== mysql) { continue; } var dirname = path.dirname(candidate.filename); var filename = path.join(dirname, 'lib', 'Connection.js'); try { return require(filename); } catch (e) { return null; } } return null; } // mysql protocol module pattern for ref // var mysqlProtoPathPat = /mysql[\/\\]+lib[\/\\]+protocol[\/\\]+Protocol\.js$/; //given module Connection, search the require cache for the corresponding //Protocol prototype function findProtocol(parent, require) { var cache = require.cache; for (var key in cache) { var candidate = cache[key]; if (candidate.exports !== parent) { continue; } var dirname = path.dirname(candidate.filename); var filename = path.join(dirname, 'protocol', 'Protocol.js'); try { return require(filename); } catch (e) { return null; } } return null; } function getMethodsWithProbes() { if ( !targetModule.methodMap) { var mt =new Object; mt[0]='mysql#query'; targetModule.methodMap = mt; } return targetModule.methodMap; } function instrument(methodMap) { targetModule.methodMap = methodMap; } module.exports.getMethodsWithProbes = getMethodsWithProbes; module.exports.instrument = instrument.bind(module);