File: //home/arjun/.pm2/modules/pm2-logrotate/node_modules/deep-metrics/probes/redis-probe.js
/*******************************************************************************
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
'use strict';
var Probe = require('../lib/probe.js');
var aspect = require('../lib/aspect.js');
var request = require('../lib/request.js');
var am = require('../');
var util = require('util');
/**
* Probe to instrument the redis npm.
*
* Events are fired for calls to the basic commands either in upper or lower case
* but are always reported as lower case to keep the two sets of events consistent.
*
* Calls to batch.exec() and multi.exec() are also instrumented. Their events are
* batch.exec and multi.exec to ensure they can be separately tracked.
*/
function RedisProbe() {
Probe.call(this, 'redis');
}
util.inherits(RedisProbe, Probe);
RedisProbe.prototype.attach = function(name, target) {
var that = this;
if (name != 'redis') return;
if (target.__probeAttached__) return target;
target.__probeAttached__ = true;
var methods = [];
[
'APPEND',
'AUTH',
'BGREWRITEAOF',
'BGSAVE',
'BITCOUNT',
'BITOP',
'BITPOS',
'BLPOP',
'BRPOP',
'BRPOPLPUSH',
'CLIENT',
'CLUSTER',
'COMMAND',
'CONFIG',
'DBSIZE',
'DEBUG',
'DECR',
'DECRBY',
'DEL',
'DISCARD',
'DUMP',
'ECHO',
'EVAL',
'EVALSHA',
'EXISTS',
'EXPIRE',
'EXPIREAT',
'FLUSHALL',
'FLUSHDB',
'GEOADD',
'GEOHASH',
'GEOPOS',
'GEODIST',
'GEORADIUS',
'GEORADIUSBYMEMBER',
'GET',
'GETBIT',
'GETRANGE',
'GETSET',
'HDEL',
'HEXISTS',
'HGET',
'HGETALL',
'HINCRBY',
'HINCRBYFLOAT',
'HKEYS',
'HLEN',
'HMGET',
'HMSET',
'HSET',
'HSETNX',
'HSTRLEN',
'HVALS',
'INCR',
'INCRBY',
'INCRBYFLOAT',
'INFO',
'KEYS',
'LASTSAVE',
'LINDEX',
'LINSERT',
'LLEN',
'LPOP',
'LPUSH',
'LPUSHX',
'LRANGE',
'LREM',
'LSET',
'LTRIM',
'MGET',
'MIGRATE',
'MONITOR',
'MOVE',
'MSET',
'MSETNX',
'OBJECT',
'PERSIST',
'PEXPIRE',
'PEXPIREAT',
'PFADD',
'PFCOUNT',
'PFMERGE',
'PING',
'PSETEX',
'PSUBSCRIBE',
'PUBSUB',
'PTTL',
'PUBLISH',
'PUNSUBSCRIBE',
'QUIT',
'RANDOMKEY',
'RENAME',
'RENAMENX',
'RESTORE',
'ROLE',
'RPOP',
'RPOPLPUSH',
'RPUSH',
'RPUSHX',
'SADD',
'SAVE',
'SCARD',
'SCRIPT',
'SDIFF',
'SDIFFSTORE',
'SELECT',
'SET',
'SETBIT',
'SETEX',
'SETNX',
'SETRANGE',
'SHUTDOWN',
'SINTER',
'SINTERSTORE',
'SISMEMBER',
'SLAVEOF',
'SLOWLOG',
'SMEMBERS',
'SMOVE',
'SORT',
'SPOP',
'SRANDMEMBER',
'SREM',
'STRLEN',
'SUBSCRIBE',
'SUNION',
'SUNIONSTORE',
'SYNC',
'TIME',
'TTL',
'TYPE',
'UNSUBSCRIBE',
'UNWATCH',
'WAIT',
'WATCH',
'ZADD',
'ZCARD',
'ZCOUNT',
'ZINCRBY',
'ZINTERSTORE',
'ZLEXCOUNT',
'ZRANGE',
'ZRANGEBYLEX',
'ZREVRANGEBYLEX',
'ZRANGEBYSCORE',
'ZRANK',
'ZREM',
'ZREMRANGEBYLEX',
'ZREMRANGEBYRANK',
'ZREMRANGEBYSCORE',
'ZREVRANGE',
'ZREVRANGEBYSCORE',
'ZREVRANK',
'ZSCORE',
'ZUNIONSTORE',
'SCAN',
'SSCAN',
'HSCAN',
'ZSCAN',
].map(function(m) {
methods.push(m);
methods.push(m.toLowerCase());
});
/* Instrument the basic set of asynchronous calls. */
aspect.around(
target.RedisClient.prototype,
methods,
function(target, method, methodArgs, probeData) {
var eventName = method.toLowerCase();
that.metricsProbeStart(probeData, eventName, methodArgs);
that.requestProbeStart(probeData, eventName, methodArgs);
/* REDIS commands don't have to have a callback.
* All redis calls are asynchronous so we need to instrument or add a
* callback to stop the timer.
*/
aspect.aroundCallback(methodArgs, probeData, function(target, args) {
var callbackPosition = aspect.findCallbackArg(methodArgs);
that.metricsProbeEnd(probeData, eventName, methodArgs);
that.requestProbeEnd(probeData, eventName, methodArgs);
});
},
function(target, method, methodArgs, probeData, rc) {
if (aspect.findCallbackArg(methodArgs) == undefined) {
var eventName = method.toLowerCase();
that.metricsProbeEnd(probeData, eventName, methodArgs);
that.requestProbeEnd(probeData, eventName, methodArgs);
}
}
);
/* Monitor all calls made as one batch/multi.exec call as a single event.
* Instrument the exec method on the object returned from client.batch()
* or client.multi()
*/
aspect.after(target.RedisClient.prototype, ['multi', 'batch', 'MULTI', 'BATCH'], {}, function(
target,
mode,
args,
probeData,
client
) {
// Log the event name as batch.exec or multi.exec
var eventName = mode.toLowerCase() + '.exec';
aspect.around(
client,
['exec', 'EXEC'],
function(target, method, methodArgs, probeData) {
that.metricsProbeStart(probeData, eventName, methodArgs);
that.requestProbeStart(probeData, eventName, methodArgs);
/* REDIS commands don't have to have a callback.
* All redis calls are asynchronous so we need to instrument or add a
* callback to stop the timer.
*/
aspect.aroundCallback(methodArgs, probeData, function() {
that.metricsProbeEnd(probeData, eventName, methodArgs);
that.requestProbeEnd(probeData, eventName, methodArgs);
});
},
function(target, method, methodArgs, probeData, rc) {
if (aspect.findCallbackArg(methodArgs) == undefined) {
that.metricsProbeEnd(probeData, eventName, methodArgs);
that.requestProbeEnd(probeData, eventName, methodArgs);
}
}
);
return client;
});
return target;
};
/*
* Lightweight metrics probe for REDIS requests
*
* These provide:
* time: time event started
* cmd: REDIS method, eg. GET, SET, INCR, etc
* duration: the time for the request to respond
*/
RedisProbe.prototype.metricsEnd = function(probeData, cmd, methodArgs) {
if (probeData && probeData.timer) {
probeData.timer.stop();
am.emit('redis', {
time: probeData.timer.startTimeMillis,
cmd: cmd,
duration: probeData.timer.timeDelta,
});
}
};
/*
* Heavyweight request probes for redis requests
*
*/
RedisProbe.prototype.requestStart = function(probeData, cmd, methodArgs) {
probeData.req = request.startRequest('redis', cmd, false, probeData.timer);
};
RedisProbe.prototype.requestEnd = function(probeData, cmd, methodArgs) {
if (probeData && probeData.req) {
var context = {};
context.cmd = cmd;
probeData.req.stop(context);
}
};
module.exports = RedisProbe;