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
JavaScript
/**
* 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);