-
Notifications
You must be signed in to change notification settings - Fork 6
/
memcached_slab.py
122 lines (103 loc) · 3.38 KB
/
memcached_slab.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
Collect memcached slab stats
#### Example Configuration
[[MemcachedSlabCollector]]
enabled = True
host = localhost # optional
port = 11211 # optional
"""
from collections import defaultdict
import socket
import diamond.collector
from diamond.metric import Metric
def parse_slab_stats(slab_stats):
"""Convert output from memcached's `stats slabs` into a Python dict.
Newlines are returned by memcached along with carriage returns
(i.e. '\r\n').
>>> parse_slab_stats("STAT 1:chunk_size 96\r\nSTAT 1:chunks_per_page 10922\r\nSTAT active_slabs 1\r\nSTAT total_malloced 1048512\r\nEND\r\n")
{
'slabs': {
1: {
'chunk_size': 96,
'chunks_per_page': 10922,
# ...
},
},
'active_slabs': 1,
'total_malloced': 1048512,
}
"""
stats_dict = {}
stats_dict['slabs'] = defaultdict(lambda: {})
for line in slab_stats.splitlines():
if line == 'END':
break
# e.g.: "STAT 1:chunks_per_page 10922"
cmd, key, value = line.split(' ')
if cmd != 'STAT':
continue
# e.g.: "STAT active_slabs 1"
if ":" not in key:
stats_dict[key] = int(value)
continue
slab, key = key.split(':')
stats_dict['slabs'][int(slab)][key] = int(value)
return stats_dict
def dict_to_paths(dict_):
"""Convert a dict to metric paths.
>>> dict_to_paths({'foo': {'bar': 1}, 'baz': 2})
{
'foo.bar': 1,
'baz': 2,
}
"""
metrics = {}
for k, v in dict_.iteritems():
if isinstance(v, dict):
submetrics = dict_to_paths(v)
for subk, subv in submetrics.iteritems():
metrics['.'.join([str(k), str(subk)])] = subv
else:
metrics[k] = v
return metrics
class MemcachedSlabCollector(diamond.collector.Collector):
def process_config(self):
super(MemcachedSlabCollector, self).process_config()
self.host = self.config['host']
self.port = int(self.config['port'])
def get_default_config(self):
config = super(MemcachedSlabCollector, self).get_default_config()
# Output stats in the format:
# 'servers.cache-main-01.memcached_slab.slabs.1.chunk_size'
config.update({
'interval': 60,
'path_prefix': 'servers',
'path': 'memcached_slab',
'host': 'localhost',
'port': 11211,
})
return config
def get_slab_stats(self):
"""Retrieve slab stats from memcached."""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
s.send("stats slabs\n")
try:
data = ""
while True:
data += s.recv(4096)
if data.endswith('END\r\n'):
break
return data
finally:
s.close()
def collect(self):
unparsed_slab_stats = self.get_slab_stats()
slab_stats = parse_slab_stats(unparsed_slab_stats)
paths = dict_to_paths(slab_stats)
for path, value in paths.iteritems():
# Add path and prefix to metric (e.g.
# 'servers.cache-main-01.memchached_slab')
full_path = self.get_metric_path(path)
metric = Metric(full_path, value)
self.publish_metric(metric)