apminsight
Version:
monitor nodejs applications
350 lines (329 loc) • 11.4 kB
JavaScript
// 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;