Merge lp:~fallenpegasus/drizzle/logging_gearman into lp:~drizzle-trunk/drizzle/development

Proposed by Mark Atwood
Status: Merged
Merged at revision: not available
Proposed branch: lp:~fallenpegasus/drizzle/logging_gearman
Merge into: lp:~drizzle-trunk/drizzle/development
Diff against target: None lines
To merge this branch: bzr merge lp:~fallenpegasus/drizzle/logging_gearman
Reviewer Review Type Date Requested Status
Drizzle Developers Pending
Review via email: mp+4126@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'plugin/logging_gearman'
2=== added file 'plugin/logging_gearman/AUTHORS'
3--- plugin/logging_gearman/AUTHORS 1970-01-01 00:00:00 +0000
4+++ plugin/logging_gearman/AUTHORS 2009-03-04 01:02:00 +0000
5@@ -0,0 +1,2 @@
6+Mark Atwood <mark@fallenpegasus.com>
7+
8
9=== added file 'plugin/logging_gearman/ChangeLog'
10--- plugin/logging_gearman/ChangeLog 1970-01-01 00:00:00 +0000
11+++ plugin/logging_gearman/ChangeLog 2009-03-04 01:02:00 +0000
12@@ -0,0 +1,2 @@
13+0.1
14+ - Added
15
16=== added file 'plugin/logging_gearman/Makefile.am'
17--- plugin/logging_gearman/Makefile.am 1970-01-01 00:00:00 +0000
18+++ plugin/logging_gearman/Makefile.am 2009-03-04 01:02:00 +0000
19@@ -0,0 +1,14 @@
20+if BUILD_LOGGING_GEARMAN
21+
22+EXTRA_LTLIBRARIES = liblogging_gearman.la
23+pkgplugin_LTLIBRARIES = @plugin_logging_gearman_shared_target@
24+liblogging_gearman_la_LDFLAGS = -module -avoid-version -rpath $(pkgplugindir)
25+liblogging_gearman_la_LIBADD= ${LIBGEARMAN}
26+liblogging_gearman_la_CPPFLAGS = ${AM_CPPFLAGS} -DDRIZZLE_DYNAMIC_PLUGIN
27+liblogging_gearman_la_SOURCES = logging_gearman.cc
28+
29+EXTRA_LIBRARIES = liblogging_gearman.a
30+noinst_LIBRARIES = @plugin_logging_gearman_static_target@
31+liblogging_gearman_a_SOURCES = $(liblogging_gearman_la_SOURCES)
32+
33+endif
34
35=== added file 'plugin/logging_gearman/logging_gearman.cc'
36--- plugin/logging_gearman/logging_gearman.cc 1970-01-01 00:00:00 +0000
37+++ plugin/logging_gearman/logging_gearman.cc 2009-03-04 01:02:00 +0000
38@@ -0,0 +1,331 @@
39+/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
40+ * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
41+ *
42+ * Copyright (C) 2008,2009 Mark Atwood
43+ *
44+ * This program is free software; you can redistribute it and/or modify
45+ * it under the terms of the GNU General Public License as published by
46+ * the Free Software Foundation; version 2 of the License.
47+ *
48+ * This program is distributed in the hope that it will be useful,
49+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
50+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51+ * GNU General Public License for more details.
52+ *
53+ * You should have received a copy of the GNU General Public License
54+ * along with this program; if not, write to the Free Software
55+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
56+ */
57+
58+#include <drizzled/server_includes.h>
59+#include <drizzled/plugin_logging.h>
60+#include <drizzled/gettext.h>
61+#include <drizzled/session.h>
62+
63+#include <libgearman/gearman.h>
64+
65+
66+/* TODO make this dynamic as needed */
67+static const int MAX_MSG_LEN= 32*1024;
68+
69+static bool sysvar_logging_gearman_enable= false;
70+static char* sysvar_logging_gearman_host= NULL;
71+
72+static gearman_client_st gearman_client;
73+
74+
75+/* stolen from mysys/my_getsystime
76+ until the Session has a good utime "now" we can use
77+ will have to use this instead */
78+
79+#include <sys/time.h>
80+static uint64_t get_microtime()
81+{
82+#if defined(HAVE_GETHRTIME)
83+ return gethrtime()/1000;
84+#else
85+ uint64_t newtime;
86+ struct timeval t;
87+ /*
88+ The following loop is here because gettimeofday may fail on some systems
89+ */
90+ while (gettimeofday(&t, NULL) != 0) {}
91+ newtime= (uint64_t)t.tv_sec * 1000000 + t.tv_usec;
92+ return newtime;
93+#endif /* defined(HAVE_GETHRTIME) */
94+}
95+
96+/* quote a string to be safe to include in a CSV line
97+ that means backslash quoting all commas, doublequotes, backslashes,
98+ and all the ASCII unprintable characters
99+ as long as we pass the high-bit bytes unchanged
100+ this is safe to do to a UTF8 string
101+ we dont allow overrunning the targetbuffer
102+ to avoid having a very long query overwrite memory
103+
104+ TODO consider remapping the unprintables instead to "Printable
105+ Representation", the Unicode characters from the area U+2400 to
106+ U+2421 reserved for representing control characters when it is
107+ necessary to print or display them rather than have them perform
108+ their intended function.
109+
110+*/
111+static unsigned char *quotify (const unsigned char *src, size_t srclen,
112+ unsigned char *dst, size_t dstlen)
113+{
114+ static const char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
115+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
116+ size_t dst_ndx; /* ndx down the dst */
117+ size_t src_ndx; /* ndx down the src */
118+
119+ assert(dst);
120+ assert(dstlen > 0);
121+
122+ for (dst_ndx= 0,src_ndx= 0; src_ndx < srclen; src_ndx++)
123+ {
124+
125+ /* Worst case, need 5 dst bytes for the next src byte.
126+ backslash x hexit hexit null
127+ so if not enough room, just terminate the string and return
128+ */
129+ if ((dstlen - dst_ndx) < 5)
130+ {
131+ dst[dst_ndx]= (unsigned char)0x00;
132+ return dst;
133+ }
134+
135+ if (src[src_ndx] > 0x7f)
136+ {
137+ // pass thru high bit characters, they are non-ASCII UTF8 Unicode
138+ dst[dst_ndx++]= src[src_ndx];
139+ }
140+ else if (src[src_ndx] == 0x00) // null
141+ {
142+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) '0';
143+ }
144+ else if (src[src_ndx] == 0x07) // bell
145+ {
146+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'a';
147+ }
148+ else if (src[src_ndx] == 0x08) // backspace
149+ {
150+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'b';
151+ }
152+ else if (src[src_ndx] == 0x09) // horiz tab
153+ {
154+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 't';
155+ }
156+ else if (src[src_ndx] == 0x0a) // line feed
157+ {
158+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'n';
159+ }
160+ else if (src[src_ndx] == 0x0b) // vert tab
161+ {
162+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'v';
163+ }
164+ else if (src[src_ndx] == 0x0c) // formfeed
165+ {
166+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'f';
167+ }
168+ else if (src[src_ndx] == 0x0d) // carrage return
169+ {
170+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'r';
171+ }
172+ else if (src[src_ndx] == 0x1b) // escape
173+ {
174+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'e';
175+ }
176+ else if (src[src_ndx] == 0x22) // quotation mark
177+ {
178+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x22;
179+ }
180+ else if (src[src_ndx] == 0x2C) // comma
181+ {
182+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x2C;
183+ }
184+ else if (src[src_ndx] == 0x5C) // backslash
185+ {
186+ dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x5C;
187+ }
188+ else if ((src[src_ndx] < 0x20) || (src[src_ndx] == 0x7F)) // other unprintable ASCII
189+ {
190+ dst[dst_ndx++]= 0x5C;
191+ dst[dst_ndx++]= (unsigned char) 'x';
192+ dst[dst_ndx++]= hexit[(src[src_ndx] >> 4) & 0x0f];
193+ dst[dst_ndx++]= hexit[src[src_ndx] & 0x0f];
194+ }
195+ else // everything else
196+ {
197+ dst[dst_ndx++]= src[src_ndx];
198+ }
199+ dst[dst_ndx]= '\0';
200+ }
201+ return dst;
202+}
203+
204+bool logging_gearman_func_post (Session *session)
205+{
206+ char msgbuf[MAX_MSG_LEN];
207+ int msgbuf_len= 0;
208+
209+ assert(session != NULL);
210+
211+ if (sysvar_logging_gearman_enable == false)
212+ return false;
213+
214+ // logging this is far too verbose
215+ if (session->command == COM_FIELD_LIST)
216+ return false;
217+
218+ /* TODO, looks like connect_utime isnt being set in the session
219+ object. We could store the time this plugin was loaded, but that
220+ would just be a dumb workaround. */
221+ /* TODO, the session object should have a "utime command completed"
222+ inside itself, so be more accurate, and so this doesnt have to
223+ keep calling current_utime, which can be slow */
224+
225+ uint64_t t_mark= get_microtime();
226+
227+ // buffer to quotify the query
228+ unsigned char qs[255];
229+
230+ // to avoid trying to printf %s something that is potentially NULL
231+ const char *dbs= (session->db) ? session->db : "";
232+ int dbl= 0;
233+ if (dbs != NULL)
234+ dbl= session->db_length;
235+
236+ // todo, add hostname, listener port, and server id to this
237+
238+ msgbuf_len=
239+ snprintf(msgbuf, MAX_MSG_LEN,
240+ "%"PRIu64",%"PRIu64",%"PRIu64",\"%.*s\",\"%s\",\"%.*s\","
241+ "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64"",
242+ t_mark,
243+ session->thread_id,
244+ session->query_id,
245+ // dont need to quote the db name, always CSV safe
246+ dbl, dbs,
247+ // do need to quote the query
248+ quotify((unsigned char *)session->query,
249+ session->query_length, qs, sizeof(qs)),
250+ // command_name is defined in drizzled/sql_parse.cc
251+ // dont need to quote the command name, always CSV safe
252+ (int)command_name[session->command].length,
253+ command_name[session->command].str,
254+ // counters are at end, to make it easier to add more
255+ (t_mark - session->connect_utime),
256+ (t_mark - session->start_utime),
257+ (t_mark - session->utime_after_lock),
258+ session->sent_row_count,
259+ session->examined_row_count);
260+
261+ char job_handle[GEARMAN_JOB_HANDLE_SIZE];
262+
263+ (void) gearman_client_do_background(&gearman_client,
264+ "drizzlelog",
265+ NULL,
266+ (void *) msgbuf,
267+ (size_t) msgbuf_len,
268+ job_handle);
269+
270+ return false;
271+}
272+
273+static int logging_gearman_plugin_init(void *p)
274+{
275+ logging_t *l= static_cast<logging_t *>(p);
276+
277+ gearman_return_t ret;
278+
279+ /* TODO
280+ saying "return 0" means "success"
281+ right now, if we return an error
282+ this causes Drizzle to crash
283+ so until that is fixed,
284+ just return a success,
285+ but leave the function pointers as NULL
286+ */
287+
288+ if (sysvar_logging_gearman_host == NULL)
289+ {
290+ /* no destination gearman server host was specified via system variables
291+ return now, dont set the callback pointers
292+ */
293+ return 0;
294+ }
295+
296+ if (gearman_client_create(&gearman_client) == NULL)
297+ {
298+ errmsg_printf(ERRMSG_LVL_ERROR, _("fail gearman_client_create(): %s"),
299+ strerror(errno));
300+ return 0;
301+ }
302+
303+ /* TODO, be able to override the port */
304+ ret= gearman_client_add_server(&gearman_client,
305+ sysvar_logging_gearman_host, 0);
306+ if (ret != GEARMAN_SUCCESS)
307+ {
308+ errmsg_printf(ERRMSG_LVL_ERROR, _("fail gearman_client_add_server(): %s"),
309+ gearman_client_error(&gearman_client));
310+ return 0;
311+ }
312+
313+ l->logging_pre= NULL;
314+ l->logging_post= logging_gearman_func_post;
315+
316+ return 0;
317+}
318+
319+static int logging_gearman_plugin_deinit(void *p)
320+{
321+ logging_st *l= (logging_st *) p;
322+
323+ gearman_client_free(&gearman_client);
324+
325+ l->logging_pre= NULL;
326+ l->logging_post= NULL;
327+
328+ return 0;
329+}
330+
331+static DRIZZLE_SYSVAR_BOOL(
332+ enable,
333+ sysvar_logging_gearman_enable,
334+ PLUGIN_VAR_NOCMDARG,
335+ N_("Enable logging to a gearman server"),
336+ NULL, /* check func */
337+ NULL, /* update func */
338+ false /* default */);
339+
340+static DRIZZLE_SYSVAR_STR(
341+ host,
342+ sysvar_logging_gearman_host,
343+ PLUGIN_VAR_READONLY,
344+ N_("Hostname for logging to a Gearman server"),
345+ NULL, /* check func */
346+ NULL, /* update func*/
347+ NULL /* default */);
348+
349+static struct st_mysql_sys_var* logging_gearman_system_variables[]= {
350+ DRIZZLE_SYSVAR(enable),
351+ DRIZZLE_SYSVAR(host),
352+ NULL
353+};
354+
355+drizzle_declare_plugin(logging_gearman)
356+{
357+ DRIZZLE_LOGGER_PLUGIN,
358+ "logging_gearman",
359+ "0.1",
360+ "Mark Atwood <mark@fallenpegasus.com>",
361+ N_("Log queries to a Gearman server"),
362+ PLUGIN_LICENSE_GPL,
363+ logging_gearman_plugin_init,
364+ logging_gearman_plugin_deinit,
365+ NULL, /* status variables */
366+ logging_gearman_system_variables,
367+ NULL
368+}
369+drizzle_declare_plugin_end;
370
371=== added file 'plugin/logging_gearman/plug.in'
372--- plugin/logging_gearman/plug.in 1970-01-01 00:00:00 +0000
373+++ plugin/logging_gearman/plug.in 2009-03-04 01:02:00 +0000
374@@ -0,0 +1,16 @@
375+DRIZZLE_PLUGIN(logging_gearman,[Gearman Logging Plugin],
376+ [Logging Plugin that logs to Gearman.])
377+DRIZZLE_PLUGIN_DYNAMIC(logging_gearman, [liblogging_gearman.la])
378+DRIZZLE_PLUGIN_STATIC(logging_gearman, [liblogging_gearman.a])
379+DRIZZLE_PLUGIN_MANDATORY(logging_gearman) dnl Default
380+DRIZZLE_PLUGIN_ACTIONS(logging_gearman, [
381+AC_LIB_HAVE_LINKFLAGS(gearman,,
382+ [#include <libgearman/gearman.h>],
383+ [
384+ gearman_client_st gearman_client;
385+ ])
386+ AS_IF([test "x$ac_cv_libgearman" = "xno"],
387+ AC_MSG_WARN([libgearman not found: not building logging_gearman plugin]))
388+ DRIZZLED_PLUGIN_DEP_LIBS="${DRIZZLED_PLUGIN_DEP_LIBS} ${LIBGEARMAN}"
389+ AM_CONDITIONAL(BUILD_LOGGING_GEARMAN,[test "${ac_cv_libgearman}" = "yes"])
390+])