UNPKG

appdynamics

Version:

Performance Profiler and Monitor

184 lines (156 loc) 5.74 kB
'use strict'; exports.RabbitMQEntryProbe = RabbitMQEntryProbe; const CHANNEL_MESSAGES = Symbol('appdynamics.channel_messages'); const ACTIVE_BT = Symbol('appdynamics.active_bt'); function RabbitMQEntryProbe(agent) { this.agent = agent; } RabbitMQEntryProbe.prototype.init = function () { }; RabbitMQEntryProbe.prototype.attach = function (obj, moduleName) { var self = this; if(moduleName == 'amqplib/lib/callback_model') { self.wrapMethods(obj); } else if(moduleName == 'amqplib/lib/channel_model') { self.wrapMethods(obj); } }; RabbitMQEntryProbe.prototype.wrapMethods = function(obj) { if(!obj || !obj.Channel) return; var self = this; var proxy = self.agent.proxy; proxy.before(obj.Channel.prototype, 'consume', function(obj, args) { self.handleConsume(obj, args); }, false, false, self.agent.thread.current()); proxy.after(obj.Channel.prototype, 'ack', function(obj, args, ret) { self.handleAck(false, "ACK", obj, args, ret); }); proxy.after(obj.Channel.prototype, 'nack', function(obj, args, ret) { self.handleAck(true, "NACK", obj, args, ret); }); proxy.after(obj.Channel.prototype, 'reject', function(obj, args, ret) { self.handleAck(true, "REJECT", obj, args, ret); }); proxy.after(obj.Channel.prototype, 'ackAll', function(obj) { self.handleAckAll(obj); }); proxy.after(obj.Channel.prototype, 'emit', function(obj, args, ret) { self.handleEmit(obj, args, ret); }); }; RabbitMQEntryProbe.prototype.endTransaction = function(message, isRejected, operation) { var self = this; const transaction = message[ACTIVE_BT]; if (!transaction) return; if(isRejected != false) { var errorObject = (operation != "CHANNEL_ERROR" && operation != "CHANNEL_CLOSED") ? `${operation} called on message` : `${operation} called on channel`; transaction.error = errorObject; } self.agent.profiler.endTransaction(transaction.time, transaction); delete message[ACTIVE_BT]; }; RabbitMQEntryProbe.prototype.handleEmit = function(channel, args) { var self = this; var eventName = args[0]; if(eventName === 'close') { self.endAllTransactionsOnChannel(channel, true, "CHANNEL_CLOSED"); } else if (eventName === 'error') { self.endAllTransactionsOnChannel(channel, true, "CHANNEL_ERROR"); } }; RabbitMQEntryProbe.prototype.handleAckAll = function(channel) { var self = this; self.endAllTransactionsOnChannel(channel, false, "ACK"); }; RabbitMQEntryProbe.prototype.endAllTransactionsOnChannel = function(channel, isRejected, operation) { var self = this; const channelMessages = channel[CHANNEL_MESSAGES] ? channel[CHANNEL_MESSAGES] : []; channelMessages.forEach((message) => { self.endTransaction(message, false, operation); }); Object.defineProperty(channel, CHANNEL_MESSAGES, { value: [], enumerable: false, configurable: true, }); }; RabbitMQEntryProbe.prototype.handleAck = function(isRejected, operation, channel, args) { var self = this; if (!args || args.length < 1) return; const message = args[0]; const allUpTo = args[1] || false; const channelMessages = channel[CHANNEL_MESSAGES] ? channel[CHANNEL_MESSAGES] : []; const msgIndex = channelMessages.findIndex((msgDetails) => msgDetails === message); if (msgIndex < 0) { // could be user double ack the same message? do nothing. } else if (operation != "REJECT" && allUpTo) { for (let i = 0; i <= msgIndex; i++) { self.endTransaction(channelMessages[i], isRejected, operation); } channelMessages.splice(0, msgIndex + 1); } else { self.endTransaction(message, isRejected, operation); channelMessages.splice(msgIndex, 1); } }; RabbitMQEntryProbe.prototype.handleConsume = function(obj, args) { var self = this; var profiler = self.agent.profiler; if(!args || args.length < 2) { return; } // args = queue, callback, options args[1] = createWrapperFunc(obj, args[1]); var options = undefined; if(args.length >= 3) { options = args[2]; } function createWrapperFunc(channel, original) { return function() { var args = arguments; self.agent.context.runGeneric(handlerFunc, args); function handlerFunc() { var msg = undefined; if(arguments && arguments.length >= 1) { msg = arguments[0]; } if (!msg) return original.apply(this, arguments); var exchange = "default"; var routingKey = "default"; var headers = {}; if(msg && msg.properties && msg.properties.headers) { headers = msg.properties.headers; } if(msg && msg.fields && msg.fields.exchange) { exchange = msg.fields.exchange; } if(msg && msg.fields && msg.fields.routingKey) { routingKey = msg.fields.routingKey; } var req = {url: `${exchange}/${routingKey}`, method: null, headers: headers}; var time = profiler.time(true); var transaction = profiler.startTransaction(time, req, 'NODEJS_WEB'); self.agent.context.set('threadId', transaction.threadId); if (options.noAck) { profiler.endTransaction(time, transaction); } else { if (!channel.hasOwnProperty(CHANNEL_MESSAGES)) { Object.defineProperty(channel, CHANNEL_MESSAGES, { value: [], enumerable: false, configurable: true, }); } Object.defineProperty(msg, ACTIVE_BT, { value: transaction, enumerable: false, configurable: true, }); channel[CHANNEL_MESSAGES].push(msg); } return original.apply(this, arguments); } }; } };