29
29
# collectd-python:
30
30
# http://collectd.org/documentation/manpages/collectd-python.5.shtml
31
31
32
- import collectd
33
- import socket
34
32
import re
33
+ import socket
34
+
35
+ import collectd
35
36
from redis_client import RedisClient , RedisError
36
37
37
38
38
- class RedisCollector () :
39
+ class RedisCollector :
39
40
def __init__ (self , host , port , auth , instance , metric_types , verbose , llen_keys ):
40
41
self .host = host
41
42
self .port = port
@@ -47,18 +48,17 @@ def __init__(self, host, port, auth, instance, metric_types, verbose, llen_keys)
47
48
48
49
def fetch_info (self , client ):
49
50
"""Request info from the Redis server"""
50
- self .log_verbose (' Sending info command' )
51
- client .send (' info' )
51
+ self .log_verbose (" Sending info command" )
52
+ client .send (" info" )
52
53
try :
53
54
data = client .read_response ()
54
- except RedisError , e :
55
- collectd .error ('redis_info plugin: Error response from %s:%d - %r'
56
- % (self .host , self .port , e ))
55
+ except RedisError as e :
56
+ collectd .error ("redis_info plugin: Error response from %s:%d - %r" % (self .host , self .port , e ))
57
57
return None
58
58
59
- self .log_verbose (' Received data: %s' % data )
59
+ self .log_verbose (" Received data: %s" % data )
60
60
61
- linesep = ' \r \n ' if ' \r \n ' in data else ' \n '
61
+ linesep = " \r \n " if " \r \n " in data else " \n "
62
62
info_dict = self .parse_info (data .split (linesep ))
63
63
64
64
return info_dict
@@ -67,58 +67,51 @@ def parse_info(self, info_lines):
67
67
"""Parse info response from Redis"""
68
68
info = {}
69
69
for line in info_lines :
70
- if "" == line or line .startswith ('#' ):
70
+ if not line or line .startswith ("#" ):
71
71
continue
72
72
73
- if ':' not in line :
74
- collectd .warning ('redis_info plugin: Bad format for info line: %s'
75
- % line )
73
+ if ":" not in line :
74
+ collectd .warning ("redis_info plugin: Bad format for info line: %s" % line )
76
75
continue
77
76
78
- key , val = line .split (':' )
77
+ key , val = line .split (":" )
79
78
80
79
# Handle multi-value keys (for dbs and slaves).
81
80
# db lines look like "db0:keys=10,expire=0"
82
81
# slave lines look like
83
82
# "slave0:ip=192.168.0.181,port=6379,
84
83
# state=online,offset=1650991674247,lag=1"
85
- if ',' in val :
86
- split_val = val .split (',' )
84
+ if "," in val :
85
+ split_val = val .split ("," )
87
86
for sub_val in split_val :
88
- k , _ , v = sub_val .rpartition ('=' )
87
+ k , _ , v = sub_val .rpartition ("=" )
89
88
sub_key = "{0}_{1}" .format (key , k )
90
89
info [sub_key ] = v
91
90
else :
92
91
info [key ] = val
93
92
94
93
# compatibility with pre-2.6 redis (used changes_since_last_save)
95
- info ["changes_since_last_save" ] = info .get ("changes_since_last_save" ,
96
- info .get (
97
- "rdb_changes_since_last_save" ))
94
+ info ["changes_since_last_save" ] = info .get ("changes_since_last_save" , info .get ("rdb_changes_since_last_save" ))
98
95
99
96
return info
100
97
101
98
def dispatch_info (self , client ):
102
99
info = self .fetch_info (client )
103
100
104
101
if not info :
105
- collectd .error (' redis plugin: No info received' )
102
+ collectd .error (" redis plugin: No info received" )
106
103
return
107
104
108
- for keyTuple , val in self .metric_types .iteritems ():
105
+ for keyTuple , val in self .metric_types .items ():
109
106
key , mtype = keyTuple
110
- if key == 'total_connections_received' and mtype == 'counter' :
111
- self .dispatch_value (info ['total_connections_received' ],
112
- 'counter' ,
113
- type_instance = 'connections_received' )
114
- elif key == 'total_commands_processed' and mtype == 'counter' :
115
- self .dispatch_value (info ['total_commands_processed' ],
116
- 'counter' ,
117
- type_instance = 'commands_processed' )
107
+ if key == "total_connections_received" and mtype == "counter" :
108
+ self .dispatch_value (info ["total_connections_received" ], "counter" , type_instance = "connections_received" )
109
+ elif key == "total_commands_processed" and mtype == "counter" :
110
+ self .dispatch_value (info ["total_commands_processed" ], "counter" , type_instance = "commands_processed" )
118
111
else :
119
112
try :
120
113
self .dispatch_value (info [key ], mtype , type_instance = key )
121
- except KeyError :
114
+ except ( KeyError , TypeError ) :
122
115
collectd .error ("Metric %s not found in Redis INFO output" % key )
123
116
continue
124
117
@@ -129,19 +122,19 @@ def dispatch_list_lengths(self, client):
129
122
database index.
130
123
"""
131
124
key_dict = {}
132
- for db , patterns in self .llen_keys .items ():
133
- client .send (' select %d' % db )
125
+ for db , patterns in list ( self .llen_keys .items () ):
126
+ client .send (" select %d" % db )
134
127
try :
135
128
resp = client .read_response ()
136
- except RedisError , e :
129
+ except RedisError as e :
137
130
collectd .error ("Could not select Redis db %s: %s" % (db , e ))
138
131
continue
139
132
140
133
for pattern in patterns :
141
134
keys = []
142
135
# If there is a glob, get every key matching it
143
- if '*' in pattern :
144
- client .send (' KEYS %s' % pattern )
136
+ if "*" in pattern :
137
+ client .send (" KEYS %s" % pattern )
145
138
keys = client .read_response ()
146
139
else :
147
140
keys = [pattern ]
@@ -150,70 +143,65 @@ def dispatch_list_lengths(self, client):
150
143
self .fetch_and_dispatch_llen_for_key (client , key , db )
151
144
152
145
def fetch_and_dispatch_llen_for_key (self , client , key , db ):
153
- client .send (' llen %s' % key )
146
+ client .send (" llen %s" % key )
154
147
try :
155
148
val = client .read_response ()
156
- except RedisError , e :
157
- collectd .warning (' redis_info plugin: could not get length of key %s in db %s: %s' % (key , db , e ))
149
+ except RedisError as e :
150
+ collectd .warning (" redis_info plugin: could not get length of key %s in db %s: %s" % (key , db , e ))
158
151
return
159
152
160
- dimensions = {' key_name' : key , ' db_index' : db }
161
- self .dispatch_value (val , ' gauge' , type_instance = ' key_llen' , dimensions = dimensions )
153
+ dimensions = {" key_name" : key , " db_index" : db }
154
+ self .dispatch_value (val , " gauge" , type_instance = " key_llen" , dimensions = dimensions )
162
155
163
- def dispatch_value (self , value , type , plugin_instance = None , type_instance = None ,
164
- dimensions = {}):
156
+ def dispatch_value (self , value , type , plugin_instance = None , type_instance = None , dimensions = {}):
165
157
"""Read a key from info response data and dispatch a value"""
166
158
167
159
try :
168
160
value = int (value )
169
161
except ValueError :
170
162
value = float (value )
171
163
172
- self .log_verbose (' Sending value: %s=%s (%s)' % (type_instance , value , dimensions ))
164
+ self .log_verbose (" Sending value: %s=%s (%s)" % (type_instance , value , dimensions ))
173
165
174
- val = collectd .Values (plugin = ' redis_info' )
166
+ val = collectd .Values (plugin = " redis_info" )
175
167
val .type = type
176
168
val .type_instance = type_instance
177
169
val .values = [value ]
178
170
179
171
plugin_instance = self .instance
180
172
if plugin_instance is None :
181
- plugin_instance = '{host}:{port}' .format (host = self .host ,
182
- port = self .port )
173
+ plugin_instance = "{host}:{port}" .format (host = self .host , port = self .port )
183
174
184
175
val .plugin_instance = "{0}{1}" .format (plugin_instance , _format_dimensions (dimensions ))
185
176
186
177
# With some versions of CollectD, a dummy metadata map must be added
187
178
# to each value for it to be correctly serialized to JSON by the
188
179
# write_http plugin. See
189
180
# https://github.com/collectd/collectd/issues/716
190
- val .meta = {'0' : True }
181
+ val .meta = {"0" : True }
191
182
val .dispatch ()
192
183
193
184
def read_callback (self ):
194
185
try :
195
186
self .get_metrics ()
196
- except socket .error , e :
197
- collectd .error ('redis_info plugin: Error connecting to %s:%d - %r'
198
- % (self .host , self .port , e ))
187
+ except socket .error as e :
188
+ collectd .error ("redis_info plugin: Error connecting to %s:%d - %r" % (self .host , self .port , e ))
199
189
return
200
- except RedisError , e :
201
- collectd .error ('redis_info plugin: Error getting metrics from %s:%d - %r'
202
- % (self .host , self .port , e ))
190
+ except RedisError as e :
191
+ collectd .error ("redis_info plugin: Error getting metrics from %s:%d - %r" % (self .host , self .port , e ))
203
192
return
204
193
205
194
def get_metrics (self ):
206
195
with RedisClient (self .host , self .port , self .auth ) as client :
207
- self .log_verbose (' Connected to Redis at %s:%s' % (self .host , self .port ))
196
+ self .log_verbose (" Connected to Redis at %s:%s" % (self .host , self .port ))
208
197
209
198
self .dispatch_info (client )
210
199
self .dispatch_list_lengths (client )
211
200
212
201
def log_verbose (self , msg ):
213
202
if not self .verbose :
214
203
return
215
- collectd .info ('redis plugin [verbose]: %s' % msg )
216
-
204
+ collectd .info ("redis plugin [verbose]: %s" % msg )
217
205
218
206
219
207
def configure_callback (conf ):
@@ -229,44 +217,50 @@ def configure_callback(conf):
229
217
for node in conf .children :
230
218
key = node .key .lower ()
231
219
val = node .values [0 ]
232
- searchObj = re .search (r' redis_(.*)$' , key , re .M | re .I )
220
+ searchObj = re .search (r" redis_(.*)$" , key , re .M | re .I )
233
221
234
- if key == ' host' :
222
+ if key == " host" :
235
223
host = val
236
- elif key == ' port' :
224
+ elif key == " port" :
237
225
port = int (val )
238
- elif key == ' auth' :
226
+ elif key == " auth" :
239
227
auth = val
240
- elif key == ' verbose' :
228
+ elif key == " verbose" :
241
229
verbose = node .values [0 ]
242
- elif key == ' instance' :
230
+ elif key == " instance" :
243
231
instance = val
244
- elif key == ' sendlistlength' :
232
+ elif key == " sendlistlength" :
245
233
if (len (node .values )) == 2 :
246
234
llen_keys .setdefault (int (node .values [0 ]), []).append (node .values [1 ])
247
235
else :
248
- collectd .warning ("redis_info plugin: monitoring length of keys requires both \
249
- database index and key value" )
236
+ collectd .warning (
237
+ "redis_info plugin: monitoring length of keys requires both \
238
+ database index and key value"
239
+ )
250
240
251
241
elif searchObj :
252
242
metric_types [searchObj .group (1 ), val ] = True
253
243
else :
254
- collectd .warning ('redis_info plugin: Unknown config key: %s.' %
255
- key )
244
+ collectd .warning ("redis_info plugin: Unknown config key: %s." % key )
256
245
continue
257
246
258
247
if verbose :
259
- collectd .info ('Configured with host=%s, port=%s, instance name=%s, using_auth=%s, llen_keys=%s'
260
- % (host , port , instance , (auth is not None ), llen_keys ))
261
-
262
- collector = RedisCollector (** {
263
- 'host' : host ,
264
- 'port' : port ,
265
- 'auth' : auth ,
266
- 'instance' : instance ,
267
- 'metric_types' : metric_types ,
268
- 'verbose' : verbose ,
269
- 'llen_keys' : llen_keys })
248
+ collectd .info (
249
+ "Configured with host=%s, port=%s, instance name=%s, using_auth=%s, llen_keys=%s"
250
+ % (host , port , instance , (auth is not None ), llen_keys )
251
+ )
252
+
253
+ collector = RedisCollector (
254
+ ** {
255
+ "host" : host ,
256
+ "port" : port ,
257
+ "auth" : auth ,
258
+ "instance" : instance ,
259
+ "metric_types" : metric_types ,
260
+ "verbose" : verbose ,
261
+ "llen_keys" : llen_keys ,
262
+ }
263
+ )
270
264
271
265
collectd .register_read (collector .read_callback , name = "%s:%s:%s" % (host , port , instance ))
272
266
@@ -286,10 +280,9 @@ def _format_dimensions(dimensions):
286
280
if not dimensions :
287
281
return ""
288
282
289
- dim_pairs = ["%s=%s" % (k , v ) for k , v in dimensions .iteritems ()]
283
+ dim_pairs = ["%s=%s" % (k , v ) for k , v in dimensions .items ()]
290
284
return "[%s]" % ("," .join (dim_pairs ))
291
285
292
286
293
-
294
287
# register callbacks
295
288
collectd .register_config (configure_callback )
0 commit comments