UNPKG

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