1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008,2009 Sun Microsystems
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
#include <drizzled/server_includes.h>
21
#include <drizzled/plugin/logging.h>
22
#include <drizzled/gettext.h>
23
#include <drizzled/session.h>
25
#include <libgearman/gearman.h>
28
/* TODO make this dynamic as needed */
29
static const int MAX_MSG_LEN= 32*1024;
31
static bool sysvar_logging_gearman_enable= false;
32
static char* sysvar_logging_gearman_host= NULL;
33
static char* sysvar_logging_gearman_function= NULL;
36
/* stolen from mysys/my_getsystime
37
until the Session has a good utime "now" we can use
38
will have to use this instead */
41
static uint64_t get_microtime()
43
#if defined(HAVE_GETHRTIME)
44
return gethrtime()/1000;
49
The following loop is here because gettimeofday may fail on some systems
51
while (gettimeofday(&t, NULL) != 0) {}
52
newtime= (uint64_t)t.tv_sec * 1000000 + t.tv_usec;
54
#endif /* defined(HAVE_GETHRTIME) */
57
/* quote a string to be safe to include in a CSV line
58
that means backslash quoting all commas, doublequotes, backslashes,
59
and all the ASCII unprintable characters
60
as long as we pass the high-bit bytes unchanged
61
this is safe to do to a UTF8 string
62
we dont allow overrunning the targetbuffer
63
to avoid having a very long query overwrite memory
65
TODO consider remapping the unprintables instead to "Printable
66
Representation", the Unicode characters from the area U+2400 to
67
U+2421 reserved for representing control characters when it is
68
necessary to print or display them rather than have them perform
69
their intended function.
72
static unsigned char *quotify (const unsigned char *src, size_t srclen,
73
unsigned char *dst, size_t dstlen)
75
static const char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
76
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
77
size_t dst_ndx; /* ndx down the dst */
78
size_t src_ndx; /* ndx down the src */
83
for (dst_ndx= 0,src_ndx= 0; src_ndx < srclen; src_ndx++)
86
/* Worst case, need 5 dst bytes for the next src byte.
87
backslash x hexit hexit null
88
so if not enough room, just terminate the string and return
90
if ((dstlen - dst_ndx) < 5)
92
dst[dst_ndx]= (unsigned char)0x00;
96
if (src[src_ndx] > 0x7f)
98
// pass thru high bit characters, they are non-ASCII UTF8 Unicode
99
dst[dst_ndx++]= src[src_ndx];
101
else if (src[src_ndx] == 0x00) // null
103
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) '0';
105
else if (src[src_ndx] == 0x07) // bell
107
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'a';
109
else if (src[src_ndx] == 0x08) // backspace
111
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'b';
113
else if (src[src_ndx] == 0x09) // horiz tab
115
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 't';
117
else if (src[src_ndx] == 0x0a) // line feed
119
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'n';
121
else if (src[src_ndx] == 0x0b) // vert tab
123
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'v';
125
else if (src[src_ndx] == 0x0c) // formfeed
127
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'f';
129
else if (src[src_ndx] == 0x0d) // carrage return
131
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'r';
133
else if (src[src_ndx] == 0x1b) // escape
135
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'e';
137
else if (src[src_ndx] == 0x22) // quotation mark
139
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x22;
141
else if (src[src_ndx] == 0x2C) // comma
143
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x2C;
145
else if (src[src_ndx] == 0x5C) // backslash
147
dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x5C;
149
else if ((src[src_ndx] < 0x20) || (src[src_ndx] == 0x7F)) // other unprintable ASCII
151
dst[dst_ndx++]= 0x5C;
152
dst[dst_ndx++]= (unsigned char) 'x';
153
dst[dst_ndx++]= hexit[(src[src_ndx] >> 4) & 0x0f];
154
dst[dst_ndx++]= hexit[src[src_ndx] & 0x0f];
156
else // everything else
158
dst[dst_ndx++]= src[src_ndx];
165
class LoggingGearman : public drizzled::plugin::Logging
168
int gearman_client_ok;
169
gearman_client_st gearman_client;
174
: drizzled::plugin::Logging("LoggingGearman"),
177
gearman_return_t ret;
179
if (sysvar_logging_gearman_enable == false)
182
if (sysvar_logging_gearman_host == NULL)
186
if (gearman_client_create(&gearman_client) == NULL)
188
errmsg_printf(ERRMSG_LVL_ERROR, _("fail gearman_client_create(): %s"),
193
/* TODO, be able to override the port */
194
/* TODO, be able send to multiple servers */
195
ret= gearman_client_add_server(&gearman_client,
196
sysvar_logging_gearman_host, 0);
197
if (ret != GEARMAN_SUCCESS)
199
errmsg_printf(ERRMSG_LVL_ERROR, _("fail gearman_client_add_server(): %s"),
200
gearman_client_error(&gearman_client));
204
gearman_client_ok= 1;
210
if (gearman_client_ok)
212
gearman_client_free(&gearman_client);
216
virtual bool post(Session *session)
218
char msgbuf[MAX_MSG_LEN];
221
assert(session != NULL);
223
/* in theory, we should return "true", meaning that the plugin isn't happy,
224
but that crashes the server, so for now, we just lie a little bit
227
if (!gearman_client_ok)
230
/* TODO, the session object should have a "utime command completed"
231
inside itself, so be more accurate, and so this doesnt have to
232
keep calling current_utime, which can be slow */
234
uint64_t t_mark= get_microtime();
236
// buffer to quotify the query
237
unsigned char qs[255];
239
// to avoid trying to printf %s something that is potentially NULL
240
const char *dbs= (session->db) ? session->db : "";
243
dbl= session->db_length;
248
snprintf(msgbuf, MAX_MSG_LEN,
249
"%"PRIu64",%"PRIu64",%"PRIu64",\"%.*s\",\"%s\",\"%.*s\","
250
"%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64","
251
"%"PRIu32",%"PRIu32",%"PRIu32",\"%s\",%"PRIu32"",
254
session->getQueryId(),
255
// dont need to quote the db name, always CSV safe
257
// do need to quote the query
258
quotify((unsigned char *)session->getQueryString(),
259
session->getQueryLength(), qs, sizeof(qs)),
260
// command_name is defined in drizzled/sql_parse.cc
261
// dont need to quote the command name, always CSV safe
262
(int)command_name[session->command].length,
263
command_name[session->command].str,
264
// counters are at end, to make it easier to add more
265
(t_mark - session->getConnectMicroseconds()),
266
(t_mark - session->start_utime),
267
(t_mark - session->utime_after_lock),
268
session->sent_row_count,
269
session->examined_row_count,
271
session->total_warn_count,
272
session->getServerId(),
277
char job_handle[GEARMAN_JOB_HANDLE_SIZE];
279
(void) gearman_client_do_background(&gearman_client,
280
sysvar_logging_gearman_function,
290
static LoggingGearman *handler= NULL;
292
static int logging_gearman_plugin_init(drizzled::plugin::Registry ®istry)
294
handler= new LoggingGearman();
295
registry.add(handler);
300
static int logging_gearman_plugin_deinit(drizzled::plugin::Registry ®istry)
302
registry.remove(handler);
308
static DRIZZLE_SYSVAR_BOOL(
310
sysvar_logging_gearman_enable,
312
N_("Enable logging to a gearman server"),
313
NULL, /* check func */
314
NULL, /* update func */
315
false /* default */);
317
static DRIZZLE_SYSVAR_STR(
319
sysvar_logging_gearman_host,
321
N_("Hostname for logging to a Gearman server"),
322
NULL, /* check func */
323
NULL, /* update func*/
324
"localhost" /* default */);
326
static DRIZZLE_SYSVAR_STR(
328
sysvar_logging_gearman_function,
330
N_("Gearman Function to send logging to"),
331
NULL, /* check func */
332
NULL, /* update func*/
333
"drizzlelog" /* default */);
335
static struct st_mysql_sys_var* logging_gearman_system_variables[]= {
336
DRIZZLE_SYSVAR(enable),
337
DRIZZLE_SYSVAR(host),
338
DRIZZLE_SYSVAR(function),
342
drizzle_declare_plugin(logging_gearman)
346
"Mark Atwood <mark@fallenpegasus.com>",
347
N_("Log queries to a Gearman server"),
349
logging_gearman_plugin_init,
350
logging_gearman_plugin_deinit,
351
NULL, /* status variables */
352
logging_gearman_system_variables,
355
drizzle_declare_plugin_end;