00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021 #include <drizzled/plugin.h>
00022 #include <drizzled/plugin/logging.h>
00023 #include <drizzled/gettext.h>
00024 #include <drizzled/session.h>
00025 #include <drizzled/session/times.h>
00026 #include <drizzled/sql_parse.h>
00027 #include PCRE_HEADER
00028 #include <limits.h>
00029 #include <sys/time.h>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <fcntl.h>
00033 #include <string>
00034 #include <boost/format.hpp>
00035 #include <boost/program_options.hpp>
00036 #include <drizzled/module/option_map.h>
00037 #include <cstdio>
00038 #include <cerrno>
00039
00040 namespace po= boost::program_options;
00041 using namespace drizzled;
00042 using namespace std;
00043
00044 #define ESCAPE_CHAR '\\'
00045 #define SEPARATOR_CHAR ','
00046
00047 namespace drizzle_plugin
00048 {
00049
00050 static bool sysvar_logging_query_enable= false;
00051
00052 static uint32_constraint sysvar_logging_query_threshold_slow;
00053 static uint32_constraint sysvar_logging_query_threshold_big_resultset;
00054 static uint32_constraint sysvar_logging_query_threshold_big_examined;
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 static void quotify(const string &src, string &dst)
00073 {
00074 static const char hexit[]= { '0', '1', '2', '3', '4', '5', '6', '7',
00075 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
00076 string::const_iterator src_iter;
00077
00078 for (src_iter= src.begin(); src_iter < src.end(); ++src_iter)
00079 {
00080 if (static_cast<unsigned char>(*src_iter) > 0x7f)
00081 {
00082 dst.push_back(*src_iter);
00083 }
00084 else if (*src_iter == 0x00)
00085 {
00086 dst.push_back(ESCAPE_CHAR); dst.push_back('0');
00087 }
00088 else if (*src_iter == 0x07)
00089 {
00090 dst.push_back(ESCAPE_CHAR); dst.push_back('a');
00091 }
00092 else if (*src_iter == 0x08)
00093 {
00094 dst.push_back(ESCAPE_CHAR); dst.push_back('b');
00095 }
00096 else if (*src_iter == 0x09)
00097 {
00098 dst.push_back(ESCAPE_CHAR); dst.push_back('t');
00099 }
00100 else if (*src_iter == 0x0a)
00101 {
00102 dst.push_back(ESCAPE_CHAR); dst.push_back('n');
00103 }
00104 else if (*src_iter == 0x0b)
00105 {
00106 dst.push_back(ESCAPE_CHAR); dst.push_back('v');
00107 }
00108 else if (*src_iter == 0x0c)
00109 {
00110 dst.push_back(ESCAPE_CHAR); dst.push_back('f');
00111 }
00112 else if (*src_iter == 0x0d)
00113 {
00114 dst.push_back(ESCAPE_CHAR); dst.push_back('r');
00115 }
00116 else if (*src_iter == 0x1b)
00117 {
00118 dst.push_back(ESCAPE_CHAR); dst.push_back('e');
00119 }
00120 else if (*src_iter == 0x22)
00121 {
00122 dst.push_back(ESCAPE_CHAR); dst.push_back(0x22);
00123 }
00124 else if (*src_iter == SEPARATOR_CHAR)
00125 {
00126 dst.push_back(ESCAPE_CHAR); dst.push_back(SEPARATOR_CHAR);
00127 }
00128 else if (*src_iter == ESCAPE_CHAR)
00129 {
00130 dst.push_back(ESCAPE_CHAR); dst.push_back(ESCAPE_CHAR);
00131 }
00132 else if ((*src_iter < 0x20) || (*src_iter == 0x7F))
00133 {
00134 dst.push_back(ESCAPE_CHAR);
00135 dst.push_back('x');
00136 dst.push_back(hexit[(*src_iter >> 4) & 0x0f]);
00137 dst.push_back(hexit[*src_iter & 0x0f]);
00138 }
00139 else
00140 {
00141 dst.push_back(*src_iter);
00142 }
00143 }
00144 }
00145
00146
00147 class Logging_query: public drizzled::plugin::Logging
00148 {
00149 const std::string _filename;
00150 const std::string _query_pcre;
00151 int fd;
00152 pcre *re;
00153 pcre_extra *pe;
00154
00156 boost::format formatter;
00157
00158 public:
00159
00160 Logging_query(const std::string &filename,
00161 const std::string &query_pcre) :
00162 drizzled::plugin::Logging("Logging_query"),
00163 _filename(filename),
00164 _query_pcre(query_pcre),
00165 fd(-1), re(NULL), pe(NULL),
00166 formatter("%1%,%2%,%3%,\"%4%\",\"%5%\",\"%6%\",%7%,%8%,"
00167 "%9%,%10%,%11%,%12%,%13%,%14%,\"%15%\"\n")
00168 {
00169
00170
00171 if (_filename.empty())
00172 return;
00173
00174 fd= open(_filename.c_str(),
00175 O_WRONLY | O_APPEND | O_CREAT,
00176 S_IRUSR|S_IWUSR);
00177
00178 if (fd < 0)
00179 {
00180 sql_perror( _("fail open()"), _filename);
00181 return;
00182 }
00183
00184 if (not _query_pcre.empty())
00185 {
00186 const char *this_pcre_error;
00187 int this_pcre_erroffset;
00188 re= pcre_compile(_query_pcre.c_str(), 0, &this_pcre_error,
00189 &this_pcre_erroffset, NULL);
00190 pe= pcre_study(re, 0, &this_pcre_error);
00191
00192 }
00193 }
00194
00195 ~Logging_query()
00196 {
00197 if (fd >= 0)
00198 {
00199 close(fd);
00200 }
00201
00202 if (pe != NULL)
00203 {
00204 pcre_free(pe);
00205 }
00206
00207 if (re != NULL)
00208 {
00209 pcre_free(re);
00210 }
00211 }
00212
00213 virtual bool post (Session *session)
00214 {
00215 size_t wrv;
00216
00217 assert(session != NULL);
00218
00219 if (fd < 0)
00220 return false;
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230 if (sysvar_logging_query_enable == false)
00231 return false;
00232 if (session->sent_row_count < sysvar_logging_query_threshold_big_resultset.get())
00233 return false;
00234 if (session->examined_row_count < sysvar_logging_query_threshold_big_examined.get())
00235 return false;
00236
00237
00238
00239
00240
00241
00242 uint64_t t_mark= session->times.getCurrentTimestamp(false);
00243
00244 if (session->times.getElapsedTime() < sysvar_logging_query_threshold_slow.get())
00245 return false;
00246
00247 Session::QueryString query_string(session->getQueryString());
00248 if (query_string == NULL)
00249 {
00250 return false;
00251 }
00252
00253 if (re)
00254 {
00255 int this_pcre_rc;
00256 this_pcre_rc= pcre_exec(re, pe, query_string->c_str(), query_string->length(), 0, 0, NULL, 0);
00257 if (this_pcre_rc < 0)
00258 return false;
00259 }
00260
00261
00262 string qs;
00263
00264
00265
00266 qs.reserve(query_string->length());
00267
00268 quotify(*query_string, qs);
00269
00270
00271 util::string::ptr schema(session->schema());
00272 formatter % t_mark
00273 % session->thread_id
00274 % session->getQueryId()
00275 % (schema ? schema->c_str() : "")
00276 % qs
00277 % getCommandName(session->command)
00278 % (t_mark - session->times.getConnectMicroseconds())
00279 % session->times.getElapsedTime()
00280 % (t_mark - session->times.utime_after_lock)
00281 % session->sent_row_count
00282 % session->examined_row_count
00283 % session->tmp_table
00284 % session->total_warn_count
00285 % session->getServerId()
00286 % getServerHostname();
00287
00288 string msgbuf= formatter.str();
00289
00290
00291 wrv= write(fd, msgbuf.c_str(), msgbuf.length());
00292 assert(wrv == msgbuf.length());
00293
00294 return false;
00295 }
00296 };
00297
00298 static int logging_query_plugin_init(drizzled::module::Context &context)
00299 {
00300 const module::option_map &vm= context.getOptions();
00301
00302 if (vm.count("filename"))
00303 {
00304 context.add(new Logging_query(vm["filename"].as<string>(),
00305 vm["pcre"].as<string>()));
00306 context.registerVariable(new sys_var_bool_ptr("enable", &sysvar_logging_query_enable));
00307 context.registerVariable(new sys_var_const_string_val("filename", vm["filename"].as<string>()));
00308 context.registerVariable(new sys_var_const_string_val("pcre", vm["pcre"].as<string>()));
00309 context.registerVariable(new sys_var_constrained_value<uint32_t>("threshold_slow", sysvar_logging_query_threshold_slow));
00310 context.registerVariable(new sys_var_constrained_value<uint32_t>("threshold_big_resultset", sysvar_logging_query_threshold_big_resultset));
00311 context.registerVariable(new sys_var_constrained_value<uint32_t>("threshold_big_examined", sysvar_logging_query_threshold_big_examined));
00312 }
00313
00314 return 0;
00315 }
00316
00317 static void init_options(drizzled::module::option_context &context)
00318 {
00319 context("enable",
00320 po::value<bool>(&sysvar_logging_query_enable)->default_value(false)->zero_tokens(),
00321 _("Enable logging to CSV file"));
00322 context("filename",
00323 po::value<string>(),
00324 _("File to log to"));
00325 context("pcre",
00326 po::value<string>()->default_value(""),
00327 _("PCRE to match the query against"));
00328 context("threshold-slow",
00329 po::value<uint32_constraint>(&sysvar_logging_query_threshold_slow)->default_value(0),
00330 _("Threshold for logging slow queries, in microseconds"));
00331 context("threshold-big-resultset",
00332 po::value<uint32_constraint>(&sysvar_logging_query_threshold_big_resultset)->default_value(0),
00333 _("Threshold for logging big queries, for rows returned"));
00334 context("threshold-big-examined",
00335 po::value<uint32_constraint>(&sysvar_logging_query_threshold_big_examined)->default_value(0),
00336 _("Threshold for logging big queries, for rows examined"));
00337 }
00338
00339 }
00340
00341 DRIZZLE_DECLARE_PLUGIN
00342 {
00343 DRIZZLE_VERSION_ID,
00344 "logging-query",
00345 "0.2",
00346 "Mark Atwood <mark@fallenpegasus.com>",
00347 N_("Log queries to a CSV file"),
00348 PLUGIN_LICENSE_GPL,
00349 drizzle_plugin::logging_query_plugin_init,
00350 NULL,
00351 drizzle_plugin::init_options
00352 }
00353 DRIZZLE_DECLARE_PLUGIN_END;