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
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.empty() ? "" : session->db.c_str();
243
snprintf(msgbuf, MAX_MSG_LEN,
244
"%"PRIu64",%"PRIu64",%"PRIu64",\"%.*s\",\"%s\",\"%.*s\","
245
"%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64","
246
"%"PRIu32",%"PRIu32",%"PRIu32",\"%s\"",
249
session->getQueryId(),
250
// dont need to quote the db name, always CSV safe
251
(int)session->db.length(), dbs,
252
// do need to quote the query
253
quotify((unsigned char *)session->getQueryString(),
254
session->getQueryLength(), qs, sizeof(qs)),
255
// command_name is defined in drizzled/sql_parse.cc
256
// dont need to quote the command name, always CSV safe
257
(int)command_name[session->command].length,
258
command_name[session->command].str,
259
// counters are at end, to make it easier to add more
260
(t_mark - session->getConnectMicroseconds()),
261
(t_mark - session->start_utime),
262
(t_mark - session->utime_after_lock),
263
session->sent_row_count,
264
session->examined_row_count,
266
session->total_warn_count,
267
session->getServerId(),
271
char job_handle[GEARMAN_JOB_HANDLE_SIZE];
273
(void) gearman_client_do_background(&gearman_client,
274
sysvar_logging_gearman_function,
284
static LoggingGearman *handler= NULL;
286
static int logging_gearman_plugin_init(drizzled::plugin::Registry ®istry)
288
handler= new LoggingGearman();
289
registry.add(handler);
294
static int logging_gearman_plugin_deinit(drizzled::plugin::Registry ®istry)
296
registry.remove(handler);
302
static DRIZZLE_SYSVAR_BOOL(
304
sysvar_logging_gearman_enable,
306
N_("Enable logging to a gearman server"),
307
NULL, /* check func */
308
NULL, /* update func */
309
false /* default */);
311
static DRIZZLE_SYSVAR_STR(
313
sysvar_logging_gearman_host,
315
N_("Hostname for logging to a Gearman server"),
316
NULL, /* check func */
317
NULL, /* update func*/
318
"localhost" /* default */);
320
static DRIZZLE_SYSVAR_STR(
322
sysvar_logging_gearman_function,
324
N_("Gearman Function to send logging to"),
325
NULL, /* check func */
326
NULL, /* update func*/
327
"drizzlelog" /* default */);
329
static drizzle_sys_var* logging_gearman_system_variables[]= {
330
DRIZZLE_SYSVAR(enable),
331
DRIZZLE_SYSVAR(host),
332
DRIZZLE_SYSVAR(function),
336
DRIZZLE_DECLARE_PLUGIN
341
"Mark Atwood <mark@fallenpegasus.com>",
342
N_("Log queries to a Gearman server"),
344
logging_gearman_plugin_init,
345
logging_gearman_plugin_deinit,
346
NULL, /* status variables */
347
logging_gearman_system_variables,
350
DRIZZLE_DECLARE_PLUGIN_END;