contrib/collection.cgi: Added basic support for "postgresql" statistics.
[collectd.git] / src / mysql.c
1 /**
2  * collectd - src/mysql.c
3  * Copyright (C) 2006,2007  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; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26
27 #ifdef HAVE_MYSQL_MYSQL_H
28 #include <mysql/mysql.h>
29 #endif
30
31 /* TODO: Understand `Select_*' and possibly do that stuff as well.. */
32
33 static const char *config_keys[] =
34 {
35         "Host",
36         "User",
37         "Password",
38         "Database",
39         NULL
40 };
41 static int config_keys_num = 4;
42
43 static char *host = "localhost";
44 static char *user;
45 static char *pass;
46 static char *db = NULL;
47
48 static MYSQL *getconnection (void)
49 {
50         static MYSQL *con;
51         static int    state;
52
53         static int wait_for = 0;
54         static int wait_increase = 60;
55
56         if (state != 0)
57         {
58                 int err;
59                 if ((err = mysql_ping (con)) != 0)
60                 {
61                         WARNING ("mysql_ping failed: %s", mysql_error (con));
62                         state = 0;
63                 }
64                 else
65                 {
66                         state = 1;
67                         return (con);
68                 }
69         }
70
71         if (wait_for > 0)
72         {
73                 wait_for -= interval_g;
74                 return (NULL);
75         }
76
77         wait_for = wait_increase;
78         wait_increase *= 2;
79         if (wait_increase > 86400)
80                 wait_increase = 86400;
81
82         if ((con = mysql_init (con)) == NULL)
83         {
84                 ERROR ("mysql_init failed: %s", mysql_error (con));
85                 state = 0;
86                 return (NULL);
87         }
88
89         if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL)
90         {
91                 ERROR ("mysql_real_connect failed: %s", mysql_error (con));
92                 state = 0;
93                 return (NULL);
94         }
95         else
96         {
97                 state = 1;
98                 wait_for = 0;
99                 wait_increase = 60;
100                 return (con);
101         }
102 } /* static MYSQL *getconnection (void) */
103
104 static int config (const char *key, const char *value)
105 {
106         if (strcasecmp (key, "host") == 0)
107                 return ((host = strdup (value)) == NULL ? 1 : 0);
108         else if (strcasecmp (key, "user") == 0)
109                 return ((user = strdup (value)) == NULL ? 1 : 0);
110         else if (strcasecmp (key, "password") == 0)
111                 return ((pass = strdup (value)) == NULL ? 1 : 0);
112         else if (strcasecmp (key, "database") == 0)
113                 return ((db = strdup (value)) == NULL ? 1 : 0);
114         else
115                 return (-1);
116 }
117
118 static void counter_submit (const char *type, const char *type_instance,
119                 counter_t value)
120 {
121         value_t values[1];
122         value_list_t vl = VALUE_LIST_INIT;
123
124         values[0].counter = value;
125
126         vl.values = values;
127         vl.values_len = 1;
128         vl.time = time (NULL);
129         strcpy (vl.host, hostname_g);
130         strcpy (vl.plugin, "mysql");
131         sstrncpy (vl.type, type, sizeof (vl.type));
132         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
133
134         plugin_dispatch_values (&vl);
135 } /* void counter_submit */
136
137 static void qcache_submit (counter_t hits, counter_t inserts,
138                 counter_t not_cached, counter_t lowmem_prunes,
139                 gauge_t queries_in_cache)
140 {
141         value_t values[5];
142         value_list_t vl = VALUE_LIST_INIT;
143
144         values[0].counter = hits;
145         values[1].counter = inserts;
146         values[2].counter = not_cached;
147         values[3].counter = lowmem_prunes;
148         values[4].gauge   = queries_in_cache;
149
150         vl.values = values;
151         vl.values_len = 5;
152         vl.time = time (NULL);
153         strcpy (vl.host, hostname_g);
154         strcpy (vl.plugin, "mysql");
155         strcpy (vl.type, "mysql_qcache");
156
157         plugin_dispatch_values (&vl);
158 } /* void qcache_submit */
159
160 static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
161                 counter_t created)
162 {
163         value_t values[4];
164         value_list_t vl = VALUE_LIST_INIT;
165
166         values[0].gauge   = running;
167         values[1].gauge   = connected;
168         values[2].gauge   = cached;
169         values[3].counter = created;
170
171         vl.values = values;
172         vl.values_len = 4;
173         vl.time = time (NULL);
174         strcpy (vl.host, hostname_g);
175         strcpy (vl.plugin, "mysql");
176         strcpy (vl.type, "mysql_threads");
177
178         plugin_dispatch_values (&vl);
179 } /* void threads_submit */
180
181 static void traffic_submit (counter_t rx, counter_t tx)
182 {
183         value_t values[2];
184         value_list_t vl = VALUE_LIST_INIT;
185
186         values[0].counter = rx;
187         values[1].counter = tx;
188
189         vl.values = values;
190         vl.values_len = 2;
191         vl.time = time (NULL);
192         strcpy (vl.host, hostname_g);
193         strcpy (vl.plugin, "mysql");
194         strcpy (vl.type, "mysql_octets");
195
196         plugin_dispatch_values (&vl);
197 } /* void traffic_submit */
198
199 static int mysql_read (void)
200 {
201         MYSQL     *con;
202         MYSQL_RES *res;
203         MYSQL_ROW  row;
204         char      *query;
205         int        query_len;
206         int        field_num;
207
208         unsigned long long qcache_hits          = 0ULL;
209         unsigned long long qcache_inserts       = 0ULL;
210         unsigned long long qcache_not_cached    = 0ULL;
211         unsigned long long qcache_lowmem_prunes = 0ULL;
212         int qcache_queries_in_cache = -1;
213
214         int threads_running   = -1;
215         int threads_connected = -1;
216         int threads_cached    = -1;
217         unsigned long long threads_created = 0ULL;
218
219         unsigned long long traffic_incoming = 0ULL;
220         unsigned long long traffic_outgoing = 0ULL;
221
222         /* An error message will have been printed in this case */
223         if ((con = getconnection ()) == NULL)
224                 return (-1);
225
226         query = "SHOW STATUS";
227         if (mysql_get_server_version (con) >= 50002)
228                 query = "SHOW GLOBAL STATUS";
229
230         query_len = strlen (query);
231
232         if (mysql_real_query (con, query, query_len))
233         {
234                 ERROR ("mysql_real_query failed: %s\n",
235                                 mysql_error (con));
236                 return (-1);
237         }
238
239         if ((res = mysql_store_result (con)) == NULL)
240         {
241                 ERROR ("mysql_store_result failed: %s\n",
242                                 mysql_error (con));
243                 return (-1);
244         }
245
246         field_num = mysql_num_fields (res);
247         while ((row = mysql_fetch_row (res)))
248         {
249                 char *key;
250                 unsigned long long val;
251
252                 key = row[0];
253                 val = atoll (row[1]);
254
255                 if (strncmp (key, "Com_", 4) == 0)
256                 {
257                         if (val == 0ULL)
258                                 continue;
259
260                         /* Ignore `prepared statements' */
261                         if (strncmp (key, "Com_stmt_", 9) != 0)
262                                 counter_submit ("mysql_commands", key + 4, val);
263                 }
264                 else if (strncmp (key, "Handler_", 8) == 0)
265                 {
266                         if (val == 0ULL)
267                                 continue;
268
269                         counter_submit ("mysql_handler", key + 8, val);
270                 }
271                 else if (strncmp (key, "Qcache_", 7) == 0)
272                 {
273                         if (strcmp (key, "Qcache_hits") == 0)
274                                 qcache_hits = val;
275                         else if (strcmp (key, "Qcache_inserts") == 0)
276                                 qcache_inserts = val;
277                         else if (strcmp (key, "Qcache_not_cached") == 0)
278                                 qcache_not_cached = val;
279                         else if (strcmp (key, "Qcache_lowmem_prunes") == 0)
280                                 qcache_lowmem_prunes = val;
281                         else if (strcmp (key, "Qcache_queries_in_cache") == 0)
282                                 qcache_queries_in_cache = (int) val;
283                 }
284                 else if (strncmp (key, "Bytes_", 6) == 0)
285                 {
286                         if (strcmp (key, "Bytes_received") == 0)
287                                 traffic_incoming += val;
288                         else if (strcmp (key, "Bytes_sent") == 0)
289                                 traffic_outgoing += val;
290                 }
291                 else if (strncmp (key, "Threads_", 8) == 0)
292                 {
293                         if (strcmp (key, "Threads_running") == 0)
294                                 threads_running = (int) val;
295                         else if (strcmp (key, "Threads_connected") == 0)
296                                 threads_connected = (int) val;
297                         else if (strcmp (key, "Threads_cached") == 0)
298                                 threads_cached = (int) val;
299                         else if (strcmp (key, "Threads_created") == 0)
300                                 threads_created = val;
301                 }
302         }
303         mysql_free_result (res); res = NULL;
304
305         if ((qcache_hits != 0ULL)
306                         || (qcache_inserts != 0ULL)
307                         || (qcache_not_cached != 0ULL)
308                         || (qcache_lowmem_prunes != 0ULL))
309                 qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached,
310                                 qcache_lowmem_prunes, qcache_queries_in_cache);
311
312         if (threads_created != 0ULL)
313                 threads_submit (threads_running, threads_connected,
314                                 threads_cached, threads_created);
315
316         traffic_submit  (traffic_incoming, traffic_outgoing);
317
318         /* mysql_close (con); */
319
320         return (0);
321 } /* int mysql_read */
322
323 void module_register (void)
324 {
325         plugin_register_config ("mysql", config, config_keys, config_keys_num);
326         plugin_register_read ("mysql", mysql_read);
327 } /* void module_register */