├── README.md └── cw-redis-stats.py /README.md: -------------------------------------------------------------------------------- 1 | redis-cloudwatch 2 | ================ 3 | 4 | ## About 5 | Provides custom metrics for [Redis](http://redis.io) in AWS CloudWatch similar to those provided by Redis in Elasticache. 6 | ## Prerequsites 7 | This requires that you have installed [Redis-Py](https://github.com/andymccurdy/redis-py), [Boto](https://github.com/boto/boto) and have created a [Boto configuration file](http://docs.pythonboto.org/en/latest/boto_config_tut.html) with your AWS credentials (~/.boto). You'll want to run this script as a cronjob every minute. 8 | ## Install 9 | sudo pip install boto redis 10 | curl https://raw.githubusercontent.com/e271828-/redis-cloudwatch/master/cw-redis-stats.py | sudo tee /usr/local/bin/cw-redis-stats.py 11 | sudo chmod +x /usr/local/bin/cw-redis-stats.py 12 | (crontab -l ; echo "* * * * * /usr/local/bin/cw-redis-stats.py")| crontab - 13 | 14 | 15 | ## Metrics Captured 16 | | Metric | Description | Unit | 17 | |----------|:-------------|:------| 18 | |CurrConnections | The number of client connections, excluding connections from read replicas. | Count 19 | |Evictions | The number of keys that have been evicted due to the maxmemory limit. | Count 20 | |Reclaimed | The total number of key expiration events. | Count 21 | |BytesUsedForCache | The total number of bytes allocated by Redis. | Bytes 22 | |CacheHits | The number of successful key lookups. | Count 23 | |CacheMisses | The number of unsuccessful key lookups. | Count 24 | |CurrItems | The number of items in the cache. This is derived from the Redis keyspace statistic, summing all of the keys in the entire keyspace. | Count 25 | |UsedMemory | The memory in bytes used by the server. | Count 26 | |IOPS | The number of instantaneous ops per sec. | Count 27 | |InputKbps | The number of instantaneous input kbps. | Count 28 | |OutputKbps | The number of instantaneous output kbps. | Count 29 | 30 | 31 | 32 | 33 | ## Aggregated Command Metrics Captured 34 | | Metric | Description | Unit | 35 | |----------|:-------------|:------| 36 | |GetTypeCmds | The total number of get types of commands. This is derived from the Redis commandstats statistic by summing all of the get types of commands (get, mget, hget, etc.) | Count 37 | |SetTypeCmds | The total number of set types of commands. This is derived from the Redis commandstats statistic by summing all of the set types of commands (set, hset, etc.) | Count 38 | |KeyBasedCmds | The total number of commands that are key-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more keys. | Count 39 | |StringBasedCmds | The total number of commands that are string-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more strings. | Count 40 | |HashBasedCmds | The total number of commands that are hash-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more hashes. | Count 41 | |ListBasedCmds | The total number of commands that are list-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more lists. | Count 42 | |SetBasedCmds | The total number of commands that are set-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more sets. | Count 43 | |SortedSetBasedCmds | The total number of commands that are sorted set-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more sorted sets. | Count 44 | |HyperLogLogBasedCmds | The total number of commands that are hyperloglog-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more hyperloglogs. | Count 45 | |ScriptBasedCmds | The total number of commands that are script-based. This is derived from the Redis commandstats statistic by summing all of the commands that act upon one or more scripts (eval, evalsha, etc). | Count 46 | 47 | -------------------------------------------------------------------------------- /cw-redis-stats.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Send Redis usage metrics to Amazon CloudWatch 4 | 5 | This is intended to run on an Amazon EC2 instance and requires a boto config 6 | (~/.boto) allowing to write CloudWatch metrics. 7 | ''' 8 | 9 | import redis 10 | from boto.ec2 import cloudwatch 11 | from boto.utils import get_instance_metadata 12 | 13 | command_groups = { 14 | 'GetTypeCmds': ['get','getbit','getrange','getset','mget','hget','hgetall','hmget'], 15 | 'SetTypeCmds': ['set','setbit','setex','setnx','setrange','mset','msetnx','psetnx', 16 | 'hmset','hset','hsetnx','lset'], 17 | 'KeyBasedCmds': ['zdel','dump','exists','expire','expireat','keys','move','persist', 18 | 'pexpire','pexpireat','pttl','rename','renamenx','restore','ttl', 19 | 'type','append','bitcount','bitop','bitpos','decr','decrby','get', 20 | 'getbit','getrange','getset','incr','incrby','incrbyfloat','mget', 21 | 'mset','msetnx','psetnx','set','setbit','setex','setnx','setrange', 22 | 'strlen','hdel','hexists','hget','hgetall','hincrby','hincrbyfloat', 23 | 'hkeys','hlen','hmget','hmset','hset','hsetnx','hvals','blpop', 24 | 'brpop','lindex','linsert','llen','lpop','lpush','lpushx','lrange', 25 | 'lrem','lset','ltrim','rpop','rpush','rpushx','sadd','scard','sdiff', 26 | 'sdiffstore','sinter','sinterstore','sismember','smembers','spop', 27 | 'srandmember','srem','sunion','sunionstore', 'sscan','zadd','zcard', 28 | 'zcount','zincrby','zinterstore','zlexcount','zrange','zrangebylex', 29 | 'zrangebyscore','zrank','zrem','zremrangebylex','zremrangebyrank', 30 | 'zremrangebyscore','zrevrange','zrevrangebyscore','zrevrank','zscore', 31 | 'zunionstore','zscan','pfadd','pfcount','pfmerge','watch','eval', 32 | 'evalsha'], 33 | 'StringBasedCmds': ['append','bitcount','bitop','bitpos','decr','decrby','get','getbit', 34 | 'getrange','getset','incr','incrby','incrbyfloat','mget','mset', 35 | 'msetnx','psetnx','set','setbit','setex','setnx','setrange','strlen'], 36 | 'HashBasedCmds': ['hdel','hexists','hget','hgetall','hincrby','hincrbyfloat','hkeys', 37 | 'hlen','hmget','hmset','hset','hsetnx','hvals','hscan'], 38 | 'ListBasedCmds': ['blpop','brpop','brpoplpush','lindex','linsert','llen','lpop','lpush', 39 | 'lpushx','lrange','lrem','lset','ltrim','rpop','rpoplpush','rpush', 40 | 'rpushx'], 41 | 'SetBasedCmds': ['sadd','scard','sdiff','sdiffstore','sinter','sinterstore','sismember', 42 | 'smembers','smove','spop','srandmember','srem','sunion','sunionstore', 43 | 'sscan'], 44 | 'SortedSetBasedCmds': ['zadd','zcard','zcount','zincrby','zinterstore','zlexcount', 45 | 'zrange','zrangebylex','zrangebyscore','zrank','zrem', 46 | 'zremrangebylex','zremrangebyrank','zremrangebyscore','zrevrange', 47 | 'zrevrangebyscore','zrevrank','zscore','zunionstore','zscan'], 48 | 'HyperLogLogBasedCmds': ['pfadd','pfcount','pfmerge'], 49 | 'ScriptBasedCmds': ['eval','evalsha'] 50 | } 51 | 52 | def collect_redis_info(): 53 | r = redis.StrictRedis('localhost', port=6379, db=0) 54 | info = r.info() 55 | cmd_info = r.info('commandstats') 56 | 57 | return dict(info.items() + cmd_info.items()) 58 | 59 | def send_multi_metrics(instance_id, region, metrics, unit='Count', namespace='EC2/Redis'): 60 | cw = cloudwatch.connect_to_region(region) 61 | cw.put_metric_data(namespace, metrics.keys(), metrics.values(), 62 | unit=unit, dimensions={"InstanceId": instance_id}) 63 | 64 | if __name__ == '__main__': 65 | metadata = get_instance_metadata() 66 | instance_id = metadata['instance-id'] 67 | region = metadata['placement']['availability-zone'][0:-1] 68 | redis_data = collect_redis_info() 69 | 70 | count_metrics = { 71 | 'CurrConnections': redis_data['connected_clients'], 72 | 'Evictions': redis_data['evicted_keys'], 73 | 'Reclaimed': redis_data['expired_keys'], 74 | 'CacheHits': redis_data['keyspace_hits'], 75 | 'CacheMisses': redis_data['keyspace_misses'], 76 | 'UsedMemory': redis_data['used_memory'], 77 | 'IOPS': redis_data['instantaneous_ops_per_sec'], 78 | 'InputKbps': redis_data['instantaneous_input_kbps'], 79 | 'OutputKbps': redis_data['instantaneous_output_kbps'], 80 | } 81 | 82 | count_metrics['CurrItems'] = sum([value['keys'] for key, value in redis_data.items() if key.startswith('db')]) 83 | 84 | for command_group, commands in command_groups.items(): 85 | count_metrics[command_group] = 0 86 | for command in commands: 87 | key = 'cmdstat_' + command 88 | if key in redis_data: 89 | count_metrics[command_group] += redis_data[key]['calls'] 90 | 91 | byte_metrics = { 92 | 'BytesUsedForCache': redis_data['used_memory'], 93 | } 94 | 95 | send_multi_metrics(instance_id, region, count_metrics) 96 | send_multi_metrics(instance_id, region, byte_metrics, 'Bytes') 97 | --------------------------------------------------------------------------------