~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to plugin/logging_query/logging_query.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-11-12 12:26:01 UTC
  • mfrom: (1.1.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20101112122601-myppfj3tfmlkccuq
Tags: upstream-2010.11.03
ImportĀ upstreamĀ versionĀ 2010.11.03

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
#include <sys/types.h>
28
28
#include <sys/stat.h>
29
29
#include <fcntl.h>
30
 
 
31
 
 
 
30
#include <string>
 
31
#include <boost/format.hpp>
 
32
#include <boost/program_options.hpp>
 
33
#include <drizzled/module/option_map.h>
 
34
#include <cstdio>
 
35
#include <cerrno>
 
36
 
 
37
namespace po= boost::program_options;
32
38
using namespace drizzled;
 
39
using namespace std;
33
40
 
34
 
/* TODO make this dynamic as needed */
35
 
static const int MAX_MSG_LEN= 32*1024;
 
41
#define ESCAPE_CHAR      '\\'
 
42
#define SEPARATOR_CHAR   ','
36
43
 
37
44
static bool sysvar_logging_query_enable= false;
38
45
static char* sysvar_logging_query_filename= NULL;
78
85
 
79
86
*/
80
87
 
81
 
static unsigned char *quotify (const unsigned char *src, size_t srclen,
82
 
                               unsigned char *dst, size_t dstlen)
 
88
static void quotify(const string &src, string &dst)
83
89
{
84
90
  static const char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
85
91
                          '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
86
 
  size_t dst_ndx;  /* ndx down the dst */
87
 
  size_t src_ndx;  /* ndx down the src */
88
 
 
89
 
  assert(dst);
90
 
  assert(dstlen > 0);
91
 
 
92
 
  for (dst_ndx= 0,src_ndx= 0; src_ndx < srclen; src_ndx++)
 
92
  string::const_iterator src_iter;
 
93
  
 
94
  for (src_iter= src.begin(); src_iter < src.end(); ++src_iter)
93
95
  {
94
 
 
95
 
    /* Worst case, need 5 dst bytes for the next src byte.
96
 
       backslash x hexit hexit null
97
 
       so if not enough room, just terminate the string and return
98
 
    */
99
 
    if ((dstlen - dst_ndx) < 5)
100
 
    {
101
 
      dst[dst_ndx]= (unsigned char)0x00;
102
 
      return dst;
103
 
    }
104
 
 
105
 
    if (src[src_ndx] > 0x7f)
106
 
    {
107
 
      // pass thru high bit characters, they are non-ASCII UTF8 Unicode
108
 
      dst[dst_ndx++]= src[src_ndx];
109
 
    }
110
 
    else if (src[src_ndx] == 0x00)  // null
111
 
    {
112
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) '0';
113
 
    }
114
 
    else if (src[src_ndx] == 0x07)  // bell
115
 
    {
116
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'a';
117
 
    }
118
 
    else if (src[src_ndx] == 0x08)  // backspace
119
 
    {
120
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'b';
121
 
    }
122
 
    else if (src[src_ndx] == 0x09)  // horiz tab
123
 
    {
124
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 't';
125
 
    }
126
 
    else if (src[src_ndx] == 0x0a)  // line feed
127
 
    {
128
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'n';
129
 
    }
130
 
    else if (src[src_ndx] == 0x0b)  // vert tab
131
 
    {
132
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'v';
133
 
    }
134
 
    else if (src[src_ndx] == 0x0c)  // formfeed
135
 
    {
136
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'f';
137
 
    }
138
 
    else if (src[src_ndx] == 0x0d)  // carrage return
139
 
    {
140
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'r';
141
 
    }
142
 
    else if (src[src_ndx] == 0x1b)  // escape
143
 
    {
144
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= (unsigned char) 'e';
145
 
    }
146
 
    else if (src[src_ndx] == 0x22)  // quotation mark
147
 
    {
148
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x22;
149
 
    }
150
 
    else if (src[src_ndx] == 0x2C)  // comma
151
 
    {
152
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x2C;
153
 
    }
154
 
    else if (src[src_ndx] == 0x5C)  // backslash
155
 
    {
156
 
      dst[dst_ndx++]= 0x5C; dst[dst_ndx++]= 0x5C;
157
 
    }
158
 
    else if ((src[src_ndx] < 0x20) || (src[src_ndx] == 0x7F))  // other unprintable ASCII
159
 
    {
160
 
      dst[dst_ndx++]= 0x5C;
161
 
      dst[dst_ndx++]= (unsigned char) 'x';
162
 
      dst[dst_ndx++]= hexit[(src[src_ndx] >> 4) & 0x0f];
163
 
      dst[dst_ndx++]= hexit[src[src_ndx] & 0x0f];
 
96
    if (static_cast<unsigned char>(*src_iter) > 0x7f)
 
97
    {
 
98
      dst.push_back(*src_iter);
 
99
    }
 
100
    else if (*src_iter == 0x00)  // null
 
101
    {
 
102
      dst.push_back(ESCAPE_CHAR); dst.push_back('0');
 
103
    }
 
104
    else if (*src_iter == 0x07)  // bell
 
105
    {
 
106
      dst.push_back(ESCAPE_CHAR); dst.push_back('a');
 
107
    }
 
108
    else if (*src_iter == 0x08)  // backspace
 
109
    {
 
110
      dst.push_back(ESCAPE_CHAR); dst.push_back('b');
 
111
    }
 
112
    else if (*src_iter == 0x09)  // horiz tab
 
113
    {
 
114
      dst.push_back(ESCAPE_CHAR); dst.push_back('t');
 
115
    }
 
116
    else if (*src_iter == 0x0a)  // line feed
 
117
    {
 
118
      dst.push_back(ESCAPE_CHAR); dst.push_back('n');
 
119
    }
 
120
    else if (*src_iter == 0x0b)  // vert tab
 
121
    {
 
122
      dst.push_back(ESCAPE_CHAR); dst.push_back('v');
 
123
    }
 
124
    else if (*src_iter == 0x0c)  // formfeed
 
125
    {
 
126
      dst.push_back(ESCAPE_CHAR); dst.push_back('f');
 
127
    }
 
128
    else if (*src_iter == 0x0d)  // carrage return
 
129
    {
 
130
      dst.push_back(ESCAPE_CHAR); dst.push_back('r');
 
131
    }
 
132
    else if (*src_iter == 0x1b)  // escape
 
133
    {
 
134
      dst.push_back(ESCAPE_CHAR); dst.push_back('e');
 
135
    }
 
136
    else if (*src_iter == 0x22)  // quotation mark
 
137
    {
 
138
      dst.push_back(ESCAPE_CHAR); dst.push_back(0x22);
 
139
    }
 
140
    else if (*src_iter == SEPARATOR_CHAR)
 
141
    {
 
142
      dst.push_back(ESCAPE_CHAR); dst.push_back(SEPARATOR_CHAR);
 
143
    }
 
144
    else if (*src_iter == ESCAPE_CHAR)
 
145
    {
 
146
      dst.push_back(ESCAPE_CHAR); dst.push_back(ESCAPE_CHAR);
 
147
    }
 
148
    else if ((*src_iter < 0x20) || (*src_iter == 0x7F))  // other unprintable ASCII
 
149
    {
 
150
      dst.push_back(ESCAPE_CHAR);
 
151
      dst.push_back('x');
 
152
      dst.push_back(hexit[(*src_iter >> 4) & 0x0f]);
 
153
      dst.push_back(hexit[*src_iter & 0x0f]);
164
154
    }
165
155
    else  // everything else
166
156
    {
167
 
      dst[dst_ndx++]= src[src_ndx];
 
157
      dst.push_back(*src_iter);
168
158
    }
169
 
    dst[dst_ndx]= '\0';
170
159
  }
171
 
  return dst;
172
160
}
173
161
 
174
162
 
178
166
  pcre *re;
179
167
  pcre_extra *pe;
180
168
 
 
169
  /** Format of the output string */
 
170
  boost::format formatter;
 
171
 
181
172
public:
182
173
 
183
174
  Logging_query()
184
175
    : drizzled::plugin::Logging("Logging_query"),
185
 
      fd(-1), re(NULL), pe(NULL)
 
176
      fd(-1), re(NULL), pe(NULL),
 
177
      formatter("%1%,%2%,%3%,\"%4%\",\"%5%\",\"%6%\",%7%,%8%,"
 
178
                "%9%,%10%,%11%,%12%,%13%,%14%,\"%15%\"\n")
186
179
  {
187
180
 
188
181
    /* if there is no destination filename, dont bother doing anything */
194
187
             S_IRUSR|S_IWUSR);
195
188
    if (fd < 0)
196
189
    {
 
190
      char errmsg[STRERROR_MAX];
 
191
      strerror_r(errno, errmsg, sizeof(errmsg));
197
192
      errmsg_printf(ERRMSG_LVL_ERROR, _("fail open() fn=%s er=%s\n"),
198
193
                    sysvar_logging_query_filename,
199
 
                    strerror(errno));
 
194
                    errmsg);
200
195
      return;
201
196
    }
202
197
 
229
224
    }
230
225
  }
231
226
 
232
 
 
233
 
  virtual bool pre (Session *)
234
 
  {
235
 
    /* we could just not have a pre entrypoint at all,
236
 
       and have logging_pre == NULL
237
 
       but we have this here for the sake of being an example */
238
 
    return false;
239
 
  }
240
 
 
241
227
  virtual bool post (Session *session)
242
228
  {
243
 
    char msgbuf[MAX_MSG_LEN];
244
 
    int msgbuf_len= 0;
245
 
    int wrv;
 
229
    size_t wrv;
246
230
 
247
231
    assert(session != NULL);
248
232
 
276
260
    if (re)
277
261
    {
278
262
      int this_pcre_rc;
279
 
      this_pcre_rc = pcre_exec(re, pe, session->query.c_str(), session->query.length(), 0, 0, NULL, 0);
 
263
      this_pcre_rc= pcre_exec(re, pe, session->query.c_str(), session->query.length(), 0, 0, NULL, 0);
280
264
      if (this_pcre_rc < 0)
281
265
        return false;
282
266
    }
283
267
 
284
268
    // buffer to quotify the query
285
 
    unsigned char qs[255];
286
 
  
 
269
    string qs;
 
270
    
 
271
    // Since quotify() builds the quoted string incrementally, we can
 
272
    // avoid some reallocating if we reserve some space up front.
 
273
    qs.reserve(session->getQueryLength());
 
274
    
 
275
    quotify(session->getQueryString(), qs);
 
276
    
287
277
    // to avoid trying to printf %s something that is potentially NULL
288
278
    const char *dbs= session->db.empty() ? "" : session->db.c_str();
289
 
  
290
 
    msgbuf_len=
291
 
      snprintf(msgbuf, MAX_MSG_LEN,
292
 
               "%"PRIu64",%"PRIu64",%"PRIu64",\"%.*s\",\"%s\",\"%.*s\","
293
 
               "%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64","
294
 
               "%"PRIu32",%"PRIu32",%"PRIu32",\"%s\"\n",
295
 
               t_mark,
296
 
               session->thread_id,
297
 
               session->getQueryId(),
298
 
               // dont need to quote the db name, always CSV safe
299
 
               (int)session->db.length(), dbs,
300
 
               // do need to quote the query
301
 
               quotify((unsigned char *)session->getQueryString().c_str(),
302
 
                       session->getQueryLength(), qs, sizeof(qs)),
303
 
               // command_name is defined in drizzled/sql_parse.cc
304
 
               // dont need to quote the command name, always CSV safe
305
 
               (int)command_name[session->command].length,
306
 
               command_name[session->command].str,
307
 
               // counters are at end, to make it easier to add more
308
 
               (t_mark - session->getConnectMicroseconds()),
309
 
               (t_mark - session->start_utime),
310
 
               (t_mark - session->utime_after_lock),
311
 
               session->sent_row_count,
312
 
               session->examined_row_count,
313
 
               session->tmp_table,
314
 
               session->total_warn_count,
315
 
               session->getServerId(),
316
 
               glob_hostname
317
 
               );
318
 
  
 
279
 
 
280
    formatter % t_mark
 
281
              % session->thread_id
 
282
              % session->getQueryId()
 
283
              % dbs
 
284
              % qs
 
285
              % command_name[session->command].str
 
286
              % (t_mark - session->getConnectMicroseconds())
 
287
              % (t_mark - session->start_utime)
 
288
              % (t_mark - session->utime_after_lock)
 
289
              % session->sent_row_count
 
290
              % session->examined_row_count
 
291
              % session->tmp_table
 
292
              % session->total_warn_count
 
293
              % session->getServerId()
 
294
              % glob_hostname;
 
295
 
 
296
    string msgbuf= formatter.str();
 
297
 
319
298
    // a single write has a kernel thread lock, thus no need mutex guard this
320
 
    wrv= write(fd, msgbuf, msgbuf_len);
321
 
    assert(wrv == msgbuf_len);
322
 
  
 
299
    wrv= write(fd, msgbuf.c_str(), msgbuf.length());
 
300
    assert(wrv == msgbuf.length());
 
301
 
323
302
    return false;
324
303
  }
325
304
};
326
305
 
327
306
static Logging_query *handler= NULL;
328
307
 
329
 
static int logging_query_plugin_init(drizzled::plugin::Registry &registry)
 
308
static int logging_query_plugin_init(drizzled::module::Context &context)
330
309
{
 
310
 
 
311
  const module::option_map &vm= context.getOptions();
 
312
  if (vm.count("threshold-slow"))
 
313
  {
 
314
    if (sysvar_logging_query_threshold_slow > UINT32_MAX)
 
315
    {
 
316
      errmsg_printf(ERRMSG_LVL_ERROR, _("Invalid value for threshold-slow"));
 
317
      return 1;
 
318
    }
 
319
  }
 
320
 
 
321
  if (vm.count("threshold-big-resultset"))
 
322
  {
 
323
    if (sysvar_logging_query_threshold_big_resultset > UINT32_MAX)
 
324
    {
 
325
      errmsg_printf(ERRMSG_LVL_ERROR, _("Invalid value for threshold-big-resultset"));
 
326
      return 1;
 
327
    }
 
328
  }
 
329
 
 
330
  if (vm.count("threshold-big-examined"))
 
331
  {
 
332
    if (sysvar_logging_query_threshold_big_examined > UINT32_MAX)
 
333
    {
 
334
      errmsg_printf(ERRMSG_LVL_ERROR, _("Invalid value for threshold-big-examined"));
 
335
      return 1;
 
336
    }
 
337
  }
331
338
  handler= new Logging_query();
332
 
  registry.add(handler);
 
339
  context.add(handler);
333
340
 
334
341
  return 0;
335
342
}
336
343
 
337
 
static int logging_query_plugin_deinit(drizzled::plugin::Registry &registry)
 
344
static void init_options(drizzled::module::option_context &context)
338
345
{
339
 
  registry.remove(handler);
340
 
  delete handler;
341
 
 
342
 
  return 0;
 
346
  context("enable",
 
347
          po::value<bool>(&sysvar_logging_query_enable)->default_value(false)->zero_tokens(),
 
348
          N_("Enable logging to CSV file"));
 
349
  context("threshold-slow",
 
350
          po::value<unsigned long>(&sysvar_logging_query_threshold_slow)->default_value(0),
 
351
          N_("Threshold for logging slow queries, in microseconds"));
 
352
  context("threshold-big-resultset",
 
353
          po::value<unsigned long>(&sysvar_logging_query_threshold_big_resultset)->default_value(0),
 
354
          N_("Threshold for logging big queries, for rows returned"));
 
355
  context("threshold-big-examined",
 
356
          po::value<unsigned long>(&sysvar_logging_query_threshold_big_examined)->default_value(0),
 
357
          N_("Threshold for logging big queries, for rows examined"));
343
358
}
344
359
 
345
360
static DRIZZLE_SYSVAR_BOOL(
418
433
DRIZZLE_DECLARE_PLUGIN
419
434
{
420
435
  DRIZZLE_VERSION_ID,
421
 
  "logging_query",
 
436
  "logging-query",
422
437
  "0.2",
423
438
  "Mark Atwood <mark@fallenpegasus.com>",
424
439
  N_("Log queries to a CSV file"),
425
440
  PLUGIN_LICENSE_GPL,
426
441
  logging_query_plugin_init,
427
 
  logging_query_plugin_deinit,
428
442
  logging_query_system_variables,
429
 
  NULL
 
443
  init_options
430
444
}
431
445
DRIZZLE_DECLARE_PLUGIN_END;