~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/logging_query/logging_query.cc

  • Committer: Monty Taylor
  • Date: 2009-01-30 04:45:55 UTC
  • mfrom: (779.4.10 devel)
  • mto: (779.7.3 devel)
  • mto: This revision was merged to the branch mainline in revision 823.
  • Revision ID: mordred@inaugust.com-20090130044555-ntb3509c8o6e3sb5
MergedĀ fromĀ me.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
3
 *
4
 
 *  Copyright (C) 2008 Mark Atwood
 
4
 *  Copyright (C) 2008,2009 Mark Atwood
5
5
 *
6
6
 *  This program is free software; you can redistribute it and/or modify
7
7
 *  it under the terms of the GNU General Public License as published by
27
27
 
28
28
static char* logging_query_filename= NULL;
29
29
static bool logging_query_enable= true;
30
 
static ulong logging_query_enable_time= 0;
 
30
static ulong logging_query_threshold_slow= 0;
31
31
static ulong logging_query_threshold_big_resultset= 0;
 
32
static ulong logging_query_threshold_big_examined= 0;
32
33
 
33
34
static int fd= -1;
34
35
 
35
 
// copied from drizzled/sql_parse.cc
36
 
const LEX_STRING command_name[]={
37
 
  { C_STRING_WITH_LEN("Sleep") },
38
 
  { C_STRING_WITH_LEN("Quit") },
39
 
  { C_STRING_WITH_LEN("InitDB") },
40
 
  { C_STRING_WITH_LEN("Query") },
41
 
  { C_STRING_WITH_LEN("FieldList") },
42
 
  { C_STRING_WITH_LEN("CreateDB") },
43
 
  { C_STRING_WITH_LEN("DropDB") },
44
 
  { C_STRING_WITH_LEN("Refresh") },
45
 
  { C_STRING_WITH_LEN("Shutdown") },
46
 
  { C_STRING_WITH_LEN("Processlist") },
47
 
  { C_STRING_WITH_LEN("Connect") },
48
 
  { C_STRING_WITH_LEN("Kill") },
49
 
  { C_STRING_WITH_LEN("Ping") },
50
 
  { C_STRING_WITH_LEN("Time") },
51
 
  { C_STRING_WITH_LEN("ChangeUser") },
52
 
  { C_STRING_WITH_LEN("BinlogDump") },
53
 
  { C_STRING_WITH_LEN("ConnectOut") },
54
 
  { C_STRING_WITH_LEN("RegisterSlave") },
55
 
  { C_STRING_WITH_LEN("SetOption") },
56
 
  { C_STRING_WITH_LEN("Daemon") },
57
 
  { C_STRING_WITH_LEN("Error") }
58
 
};
59
 
 
60
 
 
61
36
/* stolen from mysys/my_getsystime
62
37
   until the Session has a good utime "now" we can use
63
38
   will have to use this instead */
79
54
#endif  /* defined(HAVE_GETHRTIME) */
80
55
}
81
56
 
 
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 have to be careful about overrunning the targetbuffer
 
63
   or else a very long query can overwrite memory
 
64
 
 
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.
 
70
 
 
71
*/
 
72
 
 
73
static unsigned char *quotify (const unsigned char *src, size_t srclen,
 
74
                               unsigned char *dst, size_t dstlen)
 
75
{
 
76
  static char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
 
77
                          '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
78
  size_t dst_ndx;  /* index down the dst */
 
79
  size_t src_ndx;  /* index down the src */
 
80
 
 
81
  assert(dst);
 
82
  assert(dstlen > 0);
 
83
 
 
84
  for (dst_ndx=0,src_ndx=0; src_ndx<srclen; src_ndx++)
 
85
  {
 
86
 
 
87
    /* Worst case, need 5 dst bytes for the next src byte. 
 
88
       backslash x hexit hexit null
 
89
       so if not enough room, just terminate the string and return
 
90
    */
 
91
    if ((dstlen - dst_ndx) < 5)
 
92
    {
 
93
      dst[dst_ndx]= (unsigned char) 0x00;
 
94
      return dst;
 
95
    }
 
96
 
 
97
    if (src[src_ndx] > 0x7f)
 
98
    {
 
99
      // pass thru high bit characters, they are non-ASCII UTF8 Unicode
 
100
      dst[dst_ndx++]= src[src_ndx];
 
101
    }
 
102
    else if (src[src_ndx] == 0x00)  // null
 
103
    {
 
104
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) '0';
 
105
    }
 
106
    else if (src[src_ndx] == 0x07)  // bell
 
107
    {
 
108
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]=  (unsigned char) 'a';
 
109
    }
 
110
    else if (src[src_ndx] == 0x08)  // backspace
 
111
    {
 
112
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'b';
 
113
    }
 
114
    else if (src[src_ndx] == 0x09)  // horiz tab
 
115
    {
 
116
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 't';
 
117
    }
 
118
    else if (src[src_ndx] == 0x0a)  // line feed
 
119
    {
 
120
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'n';
 
121
    }
 
122
    else if (src[src_ndx] == 0x0b)  // vert tab
 
123
    {
 
124
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'v';
 
125
    }
 
126
    else if (src[src_ndx] == 0x0c)  // formfeed
 
127
    {
 
128
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'f';
 
129
    }
 
130
    else if (src[src_ndx] == 0x0d)  // carrage return
 
131
    {
 
132
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'r';
 
133
    }
 
134
    else if (src[src_ndx] == 0x1b)  // escape
 
135
    {
 
136
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'e';
 
137
    }
 
138
    else if (src[src_ndx] == 0x22)  // quotation mark
 
139
    {
 
140
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x22;
 
141
    }
 
142
    else if (src[src_ndx] == 0x2C)  // comma
 
143
    {
 
144
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x2C;
 
145
    }
 
146
    else if (src[src_ndx] == 0x5C)  // backslash
 
147
    {
 
148
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x5C;
 
149
    }
 
150
    else if ((src[src_ndx] < 0x20) || (src[src_ndx] == 0x7F))  // other unprintable ASCII
 
151
    {
 
152
      dst[dst_ndx++]= 0x5C;
 
153
      dst[dst_ndx++]= (unsigned char) 'x';
 
154
      dst[dst_ndx++]= hexit[(src[src_ndx] >> 4) & 0x0f];
 
155
      dst[dst_ndx++]= hexit[src[src_ndx] & 0x0f];
 
156
    }
 
157
    else  // everything else
 
158
    {
 
159
      dst[dst_ndx++]= src[src_ndx];
 
160
    }
 
161
    dst[dst_ndx]= '\0';
 
162
  }
 
163
  return dst;
 
164
}
 
165
 
 
166
 
82
167
/* we could just not have a pre entrypoint at all,
83
168
   and have logging_pre == NULL
84
169
   but we have this here for the sake of being an example */
93
178
  int msgbuf_len= 0;
94
179
  int wrv;
95
180
 
96
 
  if (fd < 0) return false;
97
 
 
98
181
  assert(session != NULL);
99
182
 
100
 
  /*
101
 
    here is some time stuff from class Session
102
 
      uint64_t connect_utime;
103
 
        todo, looks like this isnt being set
104
 
        we could store the time this plugin was loaded
105
 
        but that would just be a dumb workaround
106
 
      uint64_t start_utime;
107
 
      uint64_t utime_after_lock;
108
 
      uint64_t current_utime();
109
 
        todo, cant get to because of namemangling
110
 
  */
111
 
 
112
 
  /* todo, the Session should have a "utime command completed" inside
113
 
     itself, so be more accurate, and so plugins dont have to keep
114
 
     calling current_utime, which can be slow */
 
183
  if (fd < 0)
 
184
    return false;
 
185
 
 
186
  /* Yes, we know that checking logging_query_enable,
 
187
     logging_query_threshold_big_resultset, and
 
188
     logging_query_threshold_big_examined is not threadsafe, because some
 
189
     other thread might change these sysvars.  But we don't care.  We
 
190
     might start logging a little late as it spreads to other threads.
 
191
     Big deal. */
 
192
 
 
193
  // return if not enabled or query was too fast or resultset was too small
 
194
  if (logging_query_enable == false)
 
195
    return false;
 
196
  if (session->sent_row_count < logging_query_threshold_big_resultset)
 
197
    return false;
 
198
  if (session->examined_row_count < logging_query_threshold_big_examined)
 
199
    return false;
 
200
 
 
201
  // logging this is far too verbose
 
202
  if (session->command == COM_FIELD_LIST)
 
203
    return false;
 
204
 
 
205
  /* TODO, looks like connect_utime isnt being set in the session
 
206
     object.  We could store the time this plugin was loaded, but that
 
207
     would just be a dumb workaround. */
 
208
  /* TODO, the session object should have a "utime command completed"
 
209
     inside itself, so be more accurate, and so this doesnt have to
 
210
     keep calling current_utime, which can be slow */
 
211
 
115
212
  uint64_t t_mark= get_microtime();
116
213
 
 
214
  if ((t_mark - session->start_utime) < (logging_query_threshold_slow)) return false;
 
215
 
 
216
  // buffer to quotify the query
 
217
  unsigned char qs[255];
 
218
 
117
219
  msgbuf_len=
118
220
    snprintf(msgbuf, MAX_MSG_LEN,
119
 
             "thread_id=%ld query_id=%ld"
120
 
             " t_connect=%lld t_start=%lld t_lock=%lld"
121
 
             " command=%.*s"
122
 
             " rows_sent=%ld rows_examined=%u\n"
123
 
             " db=\"%.*s\" query=\"%.*s\"\n",
 
221
             "%lld,%ld,%ld,\"%.*s\",\"%s\",\"%.*s\",%lld,%lld,%lld,%ld,%ld\n",
 
222
             (unsigned long long) t_mark,
124
223
             (unsigned long) session->thread_id,
125
224
             (unsigned long) session->query_id,
126
 
             (unsigned long long)(t_mark - session->connect_utime),
127
 
             (unsigned long long)(t_mark - session->start_utime),
128
 
             (unsigned long long)(t_mark - session->utime_after_lock),
129
 
             (uint32_t)command_name[session->command].length,
 
225
             // dont need to quote the db name, always CSV safe
 
226
             session->db_length, session->db,
 
227
             // do need to quote the query
 
228
             (char *) quotify((unsigned char *) session->query, session->query_length,
 
229
                              qs, sizeof(qs)),
 
230
             // command_name is defined in drizzled/sql_parse.cc
 
231
             // dont need to quote the command name, always CSV safe
 
232
             (int) command_name[session->command].length,
130
233
             command_name[session->command].str,
 
234
             // counters are at end, to make it easier to add more
 
235
             (unsigned long long) (t_mark - session->connect_utime),
 
236
             (unsigned long long) (t_mark - session->start_utime),
 
237
             (unsigned long long) (t_mark - session->utime_after_lock),
131
238
             (unsigned long) session->sent_row_count,
132
 
             (uint32_t) session->examined_row_count,
133
 
             session->db_length, session->db,
134
 
             session->query_length, session->query);
135
 
  /* a single write has a OS level thread lock
136
 
     so there is no need to have mutexes guarding this write,
137
 
  */
 
239
             (unsigned long) session->examined_row_count);
 
240
 
 
241
 
 
242
  // a single write has a kernel thread lock, thus no need mutex guard this
138
243
  wrv= write(fd, msgbuf, msgbuf_len);
139
244
  assert(wrv == msgbuf_len);
140
245
 
153
258
    return 0;
154
259
  }
155
260
 
156
 
  fd= open(logging_query_filename, O_WRONLY | O_APPEND | O_CREAT,
 
261
  fd= open(logging_query_filename,
 
262
           O_WRONLY | O_APPEND | O_CREAT,
157
263
           S_IRUSR|S_IWUSR);
158
264
  if (fd < 0)
159
265
  {
161
267
                  logging_query_filename,
162
268
                  strerror(errno));
163
269
 
164
 
    /* todo
 
270
    /* TODO
165
271
       we should return an error here, so the plugin doesnt load
166
272
       but this causes Drizzle to crash
167
273
       so until that is fixed,
212
318
  true /* default */);
213
319
 
214
320
static DRIZZLE_SYSVAR_ULONG(
215
 
  enable_time,
216
 
  logging_query_enable_time,
217
 
  PLUGIN_VAR_OPCMDARG,
218
 
  N_("Disable after this many seconds. Zero for forever"),
219
 
  NULL, /* check func */
220
 
  NULL, /* update func */
221
 
  0, /* default */
222
 
  0, /* min */
223
 
  ULONG_MAX, /* max */
224
 
  0 /* blksiz */);
225
 
 
226
 
#ifdef NOT_YET
227
 
static DRIZZLE_SYSVAR_ULONG(
228
 
  threshhold_slow,
 
321
  threshold_slow,
229
322
  logging_query_threshold_slow,
230
323
  PLUGIN_VAR_OPCMDARG,
231
324
  N_("Threshold for logging slow queries, in microseconds"),
235
328
  0, /* min */
236
329
  ULONG_MAX, /* max */
237
330
  0 /* blksiz */);
238
 
#endif
239
331
 
240
332
static DRIZZLE_SYSVAR_ULONG(
241
333
  threshold_big_resultset,
249
341
  ULONG_MAX, /* max */
250
342
  0 /* blksiz */);
251
343
 
252
 
#ifdef NOT_YET
253
344
static DRIZZLE_SYSVAR_ULONG(
254
 
  threshhold_big_examined,
 
345
  threshold_big_examined,
255
346
  logging_query_threshold_big_examined,
256
347
  PLUGIN_VAR_OPCMDARG,
257
348
  N_("Threshold for logging big queries, for rows examined"),
261
352
  0, /* min */
262
353
  ULONG_MAX, /* max */
263
354
  0 /* blksiz */);
264
 
#endif
265
355
 
266
356
static struct st_mysql_sys_var* logging_query_system_variables[]= {
267
357
  DRIZZLE_SYSVAR(filename),
268
358
  DRIZZLE_SYSVAR(enable),
269
 
  DRIZZLE_SYSVAR(enable_time),
 
359
  DRIZZLE_SYSVAR(threshold_slow),
270
360
  DRIZZLE_SYSVAR(threshold_big_resultset),
 
361
  DRIZZLE_SYSVAR(threshold_big_examined),
271
362
  NULL
272
363
};
273
364