ca-apm-probe
Version:
CA APM Node.js Agent monitors real-time health and performance of Node.js applications
235 lines (216 loc) • 8.25 kB
JavaScript
/**
* Copyright (c) 2021 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('ca-apm-probe/lib/agent');
var proxy = require('ca-apm-probe/lib/proxy');
var util = require('util');
var logger = require("ca-apm-probe/lib/logger");
var traceUtil = require("ca-apm-probe/lib/utils/trace-util");
const { prototype } = require('stream');
var targetModule = new Object;
var eventNameFormatted = 'oracledb.execute';
var Connection;
module.exports = function (oracledb) {
targetModule.oracledb = oracledb;
targetModule.OracledbConnection = findOracledb(targetModule.oracledb, require);
targetModule.Connection = findConnection(targetModule.oracledb, require);
targetModule.methodMap = getMethodsWithProbes();
var methodMap = targetModule.methodMap;
var OracledbConnection = targetModule.OracledbConnection;
var Connection = targetModule.Connection;
var connectionStringURL="localhost:1521/PDB";
var storageContext;
var ctx;
if (methodMap[0] == "skip_instrument") {
return;
}
if (!Connection) {
logger.error('failed to instrument oracledb, cannot find Connection prototype');
return;
}
logger.info('Loading oracledb probe');
let queryInstrumentMethods = ['execute', 'executeMany']
let connectionInstrumentMethods = ['getConnection', 'createPool']
proxy.before(OracledbConnection, connectionInstrumentMethods, connectionQueryHook);
proxy.before(Connection.prototype, queryInstrumentMethods, queryHook);
proxy.after(Connection.prototype, queryInstrumentMethods, queryAfterHook);
};
function connectionQueryHook(obj, args, storage) {
if (agent.paused) {
return;
}
var req = args[0];
if (args[0].connectString) {
connectionStringURL = args[0].connectString;
}
storageContext = storage.get('ctx');
}
function queryHook(obj, args, storage) {
if (agent.paused) {
return;
}
var req = args[0];
var dburl;
var config;
var command = args.length > 0 ? args[0] : undefined;
if (connectionStringURL) {
dburl = "oracle:thin:@"+connectionStringURL;
}
var ctx = storage.get('ctx') || storageContext;
if (ctx != null) {
logger.debug('oracledb[%d %d %d]: %s', ctx.txid, ctx.lane, ctx.evtid, command);
}
else {
logger.debug('oracledb.execute - no context: %s', command);
logger.debug((new Error('No context')).stack);
}
eventNameFormatted = geteventNameFormattedText(args);
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, 'oracledbError');
if (ctx != null) {
logger.debug('oracledb[%d %d %d]: callback', ctx.txid, ctx.lane, ctx.evtid);
eventNameFormatted = geteventNameFormattedText(args);
ctx = agent.asynchEventDone(ctx, eventNameFormatted, null, errorObject);
storage.set('ctx', ctx);
}
else {
logger.debug('oracledb - no context: callback');
logger.debug((new Error('No context')).stack);
}
}, function(obj, args) {
if (ctx) {
logger.debug('oracledb[%d %d %d]: finish', ctx.txid, ctx.lane, ctx.evtid);
agent.asynchEventFinish(ctx);
}
else {
logger.debug('oracledb - no context: finish');
logger.debug((new Error('No context')).stack);
}
});
}
function queryAfterHook(obj, args, promise, storage) {
if (agent.paused) {
return;
}
var ctx = storage.get('ctx');
//For async calls with Promise have to be handled after function execution using Promise.then in after API
if (ctx) {
logger.debug('oracledb[%d %d %d]', ctx.txid, ctx.lane, ctx.evtid);
//Two handlers are used for resolve and reject of Promise
if (promise) {
promise.then(getPromiseSuccessHandler(storage, ctx, obj, args), getPromiseRejectionHandler(storage, ctx, obj, args));
}
}
else {
logger.debug('oracledb - no context: callback');
logger.debug((new Error('No context')).stack);
}
}
function getPromiseSuccessHandler(storage, ctx, obj, args) {
return function oracleDbPromiseSuccessHandler(val) {
eventNameFormatted = geteventNameFormattedText(args);
logger.debug('event name: %s , current context: [%d %d %d]', eventNameFormatted, ctx.txid, ctx.lane, ctx.evtid);
var eventArgs = {};
if (connectionStringURL) {
eventArgs.dbServer = "oracle:thin:@"+connectionStringURL;
}
ctx = agent.asynchEventDone(ctx, eventNameFormatted, eventArgs, null);
storage.set('ctx', ctx);
agent.asynchEventFinish(ctx);
};
}
function getPromiseRejectionHandler(storage, ctx, obj, args) {
return function oracleDbPromiseRejectionHandler(val) {
eventNameFormatted = geteventNameFormattedText(args);
logger.debug('event name: %s , current context: [%d %d %d]', eventNameFormatted, ctx.txid, ctx.lane, ctx.evtid);
var eventArgs = {};
var errorObject = traceUtil.getFormattedErrorObject(val) || traceUtil.createErrorObject('oracledbError', val);
if (connectionStringURL) {
eventArgs.dbServer = "oracle:thin:@"+connectionStringURL;
}
ctx = agent.asynchEventDone(ctx, eventNameFormatted, eventArgs, errorObject);
storage.set('ctx', ctx);
agent.asynchEventFinish(ctx);
};
}
function geteventNameFormattedText(args) {
var eventNameFormattedText = 'oracledb.execute';
try {
if (Array.isArray(args[1])) {
var queryArgs = args[1];
if (queryArgs.filter(Array.isArray).length > 1 ) {
eventNameFormattedText = 'oracledb.executeMany';
}
}
} catch (e) {
return eventNameFormattedText;
}
return eventNameFormattedText;
}
function findConnection(oracledb, require) {
var cache = require.cache;
for (var key in cache) {
var candidate = cache[key];
if (candidate.exports !== oracledb) {
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;
}
function findOracledb(oracledb, require) {
var cache = require.cache;
for (var key in cache) {
var candidate = cache[key];
if (candidate.exports !== oracledb) {
continue;
}
var dirname = path.dirname(candidate.filename);
var filename = path.join(dirname, 'lib', 'oracledb.js');
try {
return require(filename);
} catch (e) {
return null;
}
}
return null;
}
function getMethodsWithProbes() {
if ( !targetModule.methodMap) {
var mt = new Object;
mt[0]='oracledb#getConnection';
mt[1]='oracledb#execute';
mt[2]='oracledb#executeMany';
mt[3]='oracledb#createPool';
targetModule.methodMap = mt;
}
return targetModule.methodMap;
}
function instrument(methodMap) {
targetModule.methodMap = methodMap;
}
module.exports.getMethodsWithProbes = getMethodsWithProbes;
module.exports.instrument = instrument.bind(module);