1 /**
2 * collectd - src/mysql.c
3 * Copyright (C) 2006 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Florian octo Forster <octo at verplant.org>
21 **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
28 #ifdef HAVE_MYSQL_MYSQL_H
29 #include <mysql/mysql.h>
30 #endif
32 #define MODULE_NAME "mysql"
34 #if COLLECT_LIBMYSQL
35 # define MYSQL_HAVE_READ 1
36 #else
37 # define MYSQL_HAVE_READ 0
38 #endif
40 #define BUFSIZE 512
42 static char *host = "localhost";
43 static char *user;
44 static char *pass;
45 static char *db = NULL;
47 /* TODO
48 * understand `Select_*' and possibly do that stuff as well..
49 */
51 static char *commands_file = "mysql/mysql_commands-%s.rrd";
52 static char *handler_file = "mysql/mysql_handler-%s.rrd";
53 static char *qcache_file = "mysql/mysql_qcache.rrd";
54 static char *threads_file = "mysql/mysql_threads.rrd";
55 static char *traffic_file = "traffic-mysql.rrd";
57 static char *commands_ds_def[] =
58 {
59 "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
60 NULL
61 };
62 static int commands_ds_num = 1;
64 static char *handler_ds_def[] =
65 {
66 "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
67 NULL
68 };
69 static int handler_ds_num = 1;
71 static char *qcache_ds_def[] =
72 {
73 "DS:hits:COUNTER:"COLLECTD_HEARTBEAT":0:U",
74 "DS:inserts:COUNTER:"COLLECTD_HEARTBEAT":0:U",
75 "DS:not_cached:COUNTER:"COLLECTD_HEARTBEAT":0:U",
76 "DS:lowmem_prunes:COUNTER:"COLLECTD_HEARTBEAT":0:U",
77 "DS:queries_in_cache:GAUGE:"COLLECTD_HEARTBEAT":0:U",
78 NULL
79 };
80 static int qcache_ds_num = 5;
82 static char *threads_ds_def[] =
83 {
84 "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:U",
85 "DS:connected:GAUGE:"COLLECTD_HEARTBEAT":0:U",
86 "DS:cached:GAUGE:"COLLECTD_HEARTBEAT":0:U",
87 "DS:created:COUNTER:"COLLECTD_HEARTBEAT":0:U",
88 NULL
89 };
90 static int threads_ds_num = 4;
92 static char *traffic_ds_def[] =
93 {
94 "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
95 "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
96 NULL
97 };
98 static int traffic_ds_num = 2;
100 static char *config_keys[] =
101 {
102 "Host",
103 "User",
104 "Password",
105 "Database",
106 NULL
107 };
108 static int config_keys_num = 4;
110 #if MYSQL_HAVE_READ
111 static MYSQL *getconnection (void)
112 {
113 static MYSQL *con;
114 static int state;
116 static int wait_for = 0;
117 static int wait_increase = 60;
119 int step;
121 if (state != 0)
122 {
123 int err;
124 if ((err = mysql_ping (con)) != 0)
125 {
126 syslog (LOG_WARNING, "mysql_ping failed: %s", mysql_error (con));
127 state = 0;
128 }
129 else
130 {
131 state = 1;
132 return (con);
133 }
134 }
136 step = atoi (COLLECTD_STEP);
138 if (wait_for > 0)
139 {
140 wait_for -= step;
141 return (NULL);
142 }
144 wait_for = wait_increase;
145 wait_increase *= 2;
146 if (wait_increase > 86400)
147 wait_increase = 86400;
149 if ((con = mysql_init (con)) == NULL)
150 {
151 syslog (LOG_ERR, "mysql_init failed: %s", mysql_error (con));
152 state = 0;
153 return (NULL);
154 }
156 if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL)
157 {
158 syslog (LOG_ERR, "mysql_real_connect failed: %s", mysql_error (con));
159 state = 0;
160 return (NULL);
161 }
162 else
163 {
164 state = 1;
165 wait_for = 0;
166 wait_increase = 60;
167 return (con);
168 }
169 } /* static MYSQL *getconnection (void) */
170 #endif /* MYSQL_HAVE_READ */
172 static void init (void)
173 {
174 return;
175 }
177 static int config (char *key, char *value)
178 {
179 if (strcasecmp (key, "host") == 0)
180 return ((host = strdup (value)) == NULL ? 1 : 0);
181 else if (strcasecmp (key, "user") == 0)
182 return ((user = strdup (value)) == NULL ? 1 : 0);
183 else if (strcasecmp (key, "password") == 0)
184 return ((pass = strdup (value)) == NULL ? 1 : 0);
185 else if (strcasecmp (key, "database") == 0)
186 return ((db = strdup (value)) == NULL ? 1 : 0);
187 else
188 return (-1);
189 }
191 static void commands_write (char *host, char *inst, char *val)
192 {
193 char buf[BUFSIZE];
195 if (snprintf (buf, BUFSIZE, commands_file, inst) >= BUFSIZE)
196 return;
198 rrd_update_file (host, buf, val, commands_ds_def, commands_ds_num);
199 }
201 static void handler_write (char *host, char *inst, char *val)
202 {
203 char buf[BUFSIZE];
205 if (snprintf (buf, BUFSIZE, handler_file, inst) >= BUFSIZE)
206 return;
208 rrd_update_file (host, buf, val, handler_ds_def, handler_ds_num);
209 }
211 static void qcache_write (char *host, char *inst, char *val)
212 {
213 rrd_update_file (host, qcache_file, val,
214 qcache_ds_def, qcache_ds_num);
215 }
217 static void threads_write (char *host, char *inst, char *val)
218 {
219 rrd_update_file (host, threads_file, val,
220 threads_ds_def, threads_ds_num);
221 }
223 static void traffic_write (char *host, char *inst, char *val)
224 {
225 rrd_update_file (host, traffic_file, val,
226 traffic_ds_def, traffic_ds_num);
227 }
229 #if MYSQL_HAVE_READ
230 static void commands_submit (char *inst, unsigned long long value)
231 {
232 char buf[BUFSIZE];
233 int status;
235 status = snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, value);
237 if (status < 0)
238 {
239 syslog (LOG_ERR, "snprintf failed");
240 return;
241 }
242 else if (status >= BUFSIZE)
243 {
244 syslog (LOG_WARNING, "snprintf was truncated");
245 return;
246 }
248 plugin_submit ("mysql_commands", inst, buf);
249 }
251 static void handler_submit (char *inst, unsigned long long value)
252 {
253 char buf[BUFSIZE];
254 int status;
256 status = snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, value);
258 if (status < 0)
259 {
260 syslog (LOG_ERR, "snprintf failed");
261 return;
262 }
263 else if (status >= BUFSIZE)
264 {
265 syslog (LOG_WARNING, "snprintf was truncated");
266 return;
267 }
269 plugin_submit ("mysql_handler", inst, buf);
270 }
272 static void qcache_submit (unsigned long long hits, unsigned long long inserts,
273 unsigned long long not_cached, unsigned long long lowmem_prunes,
274 int queries_in_cache)
275 {
276 char buf[BUFSIZE];
277 int status;
279 status = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%i",
280 (unsigned int) curtime, hits, inserts, not_cached,
281 lowmem_prunes, queries_in_cache);
283 if (status < 0)
284 {
285 syslog (LOG_ERR, "snprintf failed");
286 return;
287 }
288 else if (status >= BUFSIZE)
289 {
290 syslog (LOG_WARNING, "snprintf was truncated");
291 return;
292 }
294 plugin_submit ("mysql_qcache", "-", buf);
295 }
297 static void threads_submit (int running, int connected, int cached,
298 unsigned long long created)
299 {
300 char buf[BUFSIZE];
301 int status;
303 status = snprintf (buf, BUFSIZE, "%u:%i:%i:%i:%llu",
304 (unsigned int) curtime,
305 running, connected, cached, created);
307 if (status < 0)
308 {
309 syslog (LOG_ERR, "snprintf failed");
310 return;
311 }
312 else if (status >= BUFSIZE)
313 {
314 syslog (LOG_WARNING, "snprintf was truncated");
315 return;
316 }
318 plugin_submit ("mysql_threads", "-", buf);
319 }
321 static void traffic_submit (unsigned long long incoming,
322 unsigned long long outgoing)
323 {
324 char buf[BUFSIZE];
325 int status;
327 status = snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime,
328 incoming, outgoing);
330 if (status < 0)
331 {
332 syslog (LOG_ERR, "snprintf failed");
333 return;
334 }
335 else if (status >= BUFSIZE)
336 {
337 syslog (LOG_WARNING, "snprintf was truncated");
338 return;
339 }
341 plugin_submit ("mysql_traffic", "-", buf);
342 }
344 static void mysql_read (void)
345 {
346 MYSQL *con;
347 MYSQL_RES *res;
348 MYSQL_ROW row;
349 char *query;
350 int query_len;
351 int field_num;
353 unsigned long long qcache_hits = 0ULL;
354 unsigned long long qcache_inserts = 0ULL;
355 unsigned long long qcache_not_cached = 0ULL;
356 unsigned long long qcache_lowmem_prunes = 0ULL;
357 int qcache_queries_in_cache = -1;
359 int threads_running = -1;
360 int threads_connected = -1;
361 int threads_cached = -1;
362 unsigned long long threads_created = 0ULL;
364 unsigned long long traffic_incoming = 0ULL;
365 unsigned long long traffic_outgoing = 0ULL;
367 /* An error message will have been printed in this case */
368 if ((con = getconnection ()) == NULL)
369 return;
371 query = "SHOW STATUS";
372 if (mysql_get_server_version (con) >= 50002)
373 query = "SHOW GLOBAL STATUS";
375 query_len = strlen (query);
377 if (mysql_real_query (con, query, query_len))
378 {
379 syslog (LOG_ERR, "mysql_real_query failed: %s\n",
380 mysql_error (con));
381 return;
382 }
384 if ((res = mysql_store_result (con)) == NULL)
385 {
386 syslog (LOG_ERR, "mysql_store_result failed: %s\n",
387 mysql_error (con));
388 return;
389 }
391 field_num = mysql_num_fields (res);
392 while ((row = mysql_fetch_row (res)))
393 {
394 char *key;
395 unsigned long long val;
397 key = row[0];
398 val = atoll (row[1]);
400 if (strncmp (key, "Com_", 4) == 0)
401 {
402 if (val == 0ULL)
403 continue;
405 /* Ignore `prepared statements' */
406 if (strncmp (key, "Com_stmt_", 9) != 0)
407 commands_submit (key + 4, val);
408 }
409 else if (strncmp (key, "Handler_", 8) == 0)
410 {
411 if (val == 0ULL)
412 continue;
414 handler_submit (key + 8, val);
415 }
416 else if (strncmp (key, "Qcache_", 7) == 0)
417 {
418 if (strcmp (key, "Qcache_hits") == 0)
419 qcache_hits = val;
420 else if (strcmp (key, "Qcache_inserts") == 0)
421 qcache_inserts = val;
422 else if (strcmp (key, "Qcache_not_cached") == 0)
423 qcache_not_cached = val;
424 else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
425 qcache_lowmem_prunes = val;
426 else if (strcmp (key, "Qcache_queries_in_cache") == 0)
427 qcache_queries_in_cache = (int) val;
428 }
429 else if (strncmp (key, "Bytes_", 6) == 0)
430 {
431 if (strcmp (key, "Bytes_received") == 0)
432 traffic_incoming += val;
433 else if (strcmp (key, "Bytes_sent") == 0)
434 traffic_outgoing += val;
435 }
436 else if (strncmp (key, "Threads_", 8) == 0)
437 {
438 if (strcmp (key, "Threads_running") == 0)
439 threads_running = (int) val;
440 else if (strcmp (key, "Threads_connected") == 0)
441 threads_connected = (int) val;
442 else if (strcmp (key, "Threads_cached") == 0)
443 threads_cached = (int) val;
444 else if (strcmp (key, "Threads_created") == 0)
445 threads_created = val;
446 }
447 }
448 mysql_free_result (res); res = NULL;
450 if ((qcache_hits != 0ULL)
451 || (qcache_inserts != 0ULL)
452 || (qcache_not_cached != 0ULL)
453 || (qcache_lowmem_prunes != 0ULL))
454 qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
455 qcache_lowmem_prunes, qcache_queries_in_cache);
457 if (threads_created != 0ULL)
458 threads_submit (threads_running, threads_connected,
459 threads_cached, threads_created);
461 traffic_submit (traffic_incoming, traffic_outgoing);
463 /* mysql_close (con); */
465 return;
466 }
467 #else
468 # define mysql_read NULL
469 #endif /* MYSQL_HAVE_READ */
471 void module_register (void)
472 {
473 plugin_register (MODULE_NAME, init, mysql_read, NULL);
474 plugin_register ("mysql_commands", NULL, NULL, commands_write);
475 plugin_register ("mysql_handler", NULL, NULL, handler_write);
476 plugin_register ("mysql_qcache", NULL, NULL, qcache_write);
477 plugin_register ("mysql_threads", NULL, NULL, threads_write);
478 plugin_register ("mysql_traffic", NULL, NULL, traffic_write);
479 cf_register (MODULE_NAME, config, config_keys, config_keys_num);
480 }
482 #undef BUFSIZE
483 #undef MODULE_NAME