UNPKG

apminsight

Version:

monitor nodejs applications

350 lines (329 loc) 11.4 kB
// supported from v0.0.1 to v2.8.0 var wrapper = require("./../wrapper"); var utils = require("./../../util/utils"); var logger = require("./../../util/logger"); var url = require("url"); var componentName = "REDIS"; var moduleInfo = { functions: [ { functionName: ["RedisClient.prototype.internal_send_command"], component: componentName, wrapper: wrapCommand }, { functionName: ["RedisClient.prototype.write"], component: componentName, wrapper: wrapWrite }, { functionName: ["RedisClient.prototype.send_command"], component: componentName, wrapper: wrapSendCommand }, { functionName: ["createClient"], component: componentName, wrapper: wrapCreateClient } ] }; function apmRedisCb() {} function wrapCommand(actual) { return function (commandInfo) { var curTxn = apmInsightAgentInstance.getCurTxn(); if ( !curTxn || curTxn.isCompleted() || utils.isEmpty(commandInfo.command) ) { return actual.apply(this, arguments); } var parentTracker = apmInsightAgentInstance.getCurTracker(); var trackerName = componentName + " - " + commandInfo.command.toUpperCase(); var trackerInfo = { trackerName: trackerName, component: componentName, parent: parentTracker }; var curTracker = apmInsightAgentInstance.createTracker(trackerInfo); var cb = commandInfo.callback && commandInfo.callback != apmRedisCb ? commandInfo.callback : apmRedisCb; commandInfo.callback = wrapper.wrapCallBack(cb, curTracker, curTxn); arguments[0] = commandInfo; appendMachineInfo(curTracker, this); try { return actual.apply(this, arguments); } catch (e) { logger.error("redis internal command handler", e); apmInsightAgentInstance.handleErrorTracker(curTracker, e); throw e; } finally { apmInsightAgentInstance.setCurTracker(parentTracker); } }; } function wrapSendCommand(actual) { return function (command) { var curTxn = apmInsightAgentInstance.getCurTxn(); if ( !curTxn || curTxn.isCompleted() || utils.isEmpty(command) || typeof command !== "string" ) { return actual.apply(this, arguments); } var cbIndex = wrapper.getCallBackIndex(arguments); var parentTracker = apmInsightAgentInstance.getCurTracker(); var trackerName = componentName + " - " + command.toUpperCase(); var trackerInfo = { trackerName: trackerName, component: componentName }; var curTracker = apmInsightAgentInstance.createTracker(trackerInfo); appendMachineInfo(curTracker, this); try { if (cbIndex < 0) { var result = actual.apply(this, arguments); result = wrapper.checkAndWrapPromise( result, curTxn, curTracker ); return result; } else { var wrappedCb = wrapper.wrapCallBack( arguments[cbIndex], curTracker, curTxn ); arguments[cbIndex] = wrappedCb; return actual.apply(this, arguments); } } catch (e) { logger.error("redis send command handler", e); apmInsightAgentInstance.handleErrorTracker(curTracker, e); throw e; } finally { apmInsightAgentInstance.setCurTracker(parentTracker); } }; } function wrapCreateClient(actual) { return function () { var curTxn = apmInsightAgentInstance.getCurTxn(); if (!curTxn || curTxn.isCompleted()) { return actual.apply(this, arguments); } var parentTracker = apmInsightAgentInstance.getCurTracker(); var trackerName = componentName + " - createClient"; var trackerInfo = { trackerName: trackerName, component: componentName, parent: parentTracker }; var curTracker = apmInsightAgentInstance.createTracker(trackerInfo); try { var result = actual.apply(this, arguments); appendMachineInfo(curTracker, result, arguments); wrapCommandManually(result, curTracker.getInfo()); return result; } catch (e) { logger.error("redis create client ", e); apmInsightAgentInstance.handleErrorTracker(curTracker, e); throw e; } finally { apmInsightAgentInstance.setCurTracker(parentTracker); } }; } /* eslint-disable no-unused-vars */ var commandsToInstrumentManually = [ "GET", "set", "HGET", "HSET", "HMGET", "HMSET", "HGETALL", "HVALS", "SADD" ]; /* eslint-enable no-unused-vars */ function wrapCommandManually(client, trackerInfo) { try { var redisVersion = parseInt( apmInsightAgentInstance.getDependencyList().redis.split("^")[1] ); if (redisVersion > 3 && client) { if (utils.isFunction(client.GET)) { var actualCapGetFunc = client.GET; client.GET = wrapEachCommand( actualCapGetFunc, "GET", trackerInfo ); } if (utils.isFunction(client.get)) { var actualSmGetFunc = client.get; client.get = wrapEachCommand( actualSmGetFunc, "get", trackerInfo ); } if (utils.isFunction(client.SET)) { var actualCapSetFunc = client.SET; client.SET = wrapEachCommand( actualCapSetFunc, "SET", trackerInfo ); } if (utils.isFunction(client.set)) { var actualSmSetFunc = client.set; client.set = wrapEachCommand( actualSmSetFunc, "set", trackerInfo ); } if (utils.isFunction(client.HGET)) { var actualCapHgetFunc = client.HGET; client.HGET = wrapEachCommand( actualCapHgetFunc, "HGET", trackerInfo ); } if (utils.isFunction(client.hget)) { var actualSmHgetFunc = client.hget; client.hget = wrapEachCommand( actualSmHgetFunc, "hget", trackerInfo ); } if (utils.isFunction(client.HSET)) { var actualCapHsetFunc = client.HSET; client.HSET = wrapEachCommand( actualCapHsetFunc, "HSET", trackerInfo ); } if (utils.isFunction(client.hset)) { var actualSmHsetFunc = client.hset; client.hset = wrapEachCommand( actualSmHsetFunc, "hset", trackerInfo ); } } } catch (err) { logger.error( "Error while finding the redis version and instrumenting." ); } } function wrapEachCommand(actual, command, clientMachineInfo) { return function () { var curTxn = apmInsightAgentInstance.getCurTxn(); if (!curTxn || curTxn.isCompleted()) { return actual.apply(this, arguments); } var parentTracker = apmInsightAgentInstance.getCurTracker(); var trackerName = componentName + " - " + command; var trackerInfo = { trackerName: trackerName, component: componentName }; var curTracker = apmInsightAgentInstance.createTracker(trackerInfo); try { var trackInfo = {}; trackInfo.host = clientMachineInfo.host; trackInfo.port = clientMachineInfo.port; curTracker.updateInfo(trackInfo); return actual.apply(this, arguments); } catch (e) { logger.error("redis command handler - " + command + " : ", e); apmInsightAgentInstance.handleErrorTracker(curTracker, e); throw e; } finally { apmInsightAgentInstance.setCurTracker(parentTracker); } }; } function appendMachineInfo(curTracker, invoker, args) { var machineInfo = {}; //For version < 4.x we can get hostname and port from invoker machineInfo = getMachineInfo(invoker); //For version > 4.x if the arguments are empty the client connects to localhost on port 6379. Or they can pass port as argument. To connect to a different host or port, use a connection string in the format redis[s]://[[username][:password]@][host][:port][/db-number]: //eg : createClient({url: 'redis://alice:foobared@awesome.redis.server:6380' }); if (utils.isEmptyObject(machineInfo) && args) { if (utils.isPositiveNumber(args[0])) { machineInfo.host = "localhost"; machineInfo.port = args[0]; } else if (utils.isObject(args[0]) && args[0].url) { var { hostname, port } = new url.URL(args[0].url); machineInfo.host = hostname; machineInfo.port = port; } else { machineInfo.host = "localhost"; machineInfo.port = 6379; } } if (curTracker) { curTracker.updateInfo(machineInfo); } } function getMachineInfo(invoker) { var hostInfo = {}; if (!invoker) { return hostInfo; } if (invoker.port && invoker.host) { hostInfo.host = invoker.host; hostInfo.port = invoker.port; } else if ( invoker.options && invoker.options.host && invoker.options.port ) { hostInfo.host = invoker.options.host; hostInfo.port = invoker.options.port; } else if ( invoker.connection_options && invoker.connection_options.host && invoker.connection_options.port ) { hostInfo.host = invoker.connection_options.host; hostInfo.port = invoker.connection_options.port; } else if ( invoker.connectionOption && invoker.connectionOption.host && invoker.connectionOption.port ) { hostInfo.host = invoker.connectionOption.host; hostInfo.port = invoker.connectionOption.port; } return hostInfo; } function wrapWrite(actual) { return function () { var parentTracker = apmInsightAgentInstance.getCurTracker(); if (isRedisTracker(parentTracker)) { parentTracker.setActive(); } return actual.apply(this, arguments); }; } function isRedisTracker(tracker) { return tracker && tracker.getComponent() === componentName; } module.exports = moduleInfo;