/* Copyright (C) 2012 Monty Program Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #include #include #include #include #include /* Disable __attribute__() on non-gcc compilers. */ #if !defined(__attribute__) && !defined(__GNUC__) #define __attribute__(A) #endif #ifdef _WIN32 #define localtime_r(a, b) localtime_s(b, a) #endif /*WIN32*/ /* rate 0 means the logging was disabled. */ static char *filename; static unsigned int rate; static unsigned long long size_limit; static unsigned int rotations; static char rotate; static char warnings; static char with_db_and_thread_info; static unsigned int count; LOGGER_HANDLE *logfile; static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); static MYSQL_SYSVAR_UINT(rate, rate, PLUGIN_VAR_RQCMDARG, "Sampling rate. If set to 0(zero), the logging is disabled.", NULL, NULL, 1, 0, 1000000, 1); static MYSQL_SYSVAR_ULONGLONG(size_limit, size_limit, PLUGIN_VAR_READONLY, "Log file size limit", NULL, NULL, 1000000, 100, ((long long) 0x7FFFFFFFFFFFFFFFLL), 1); static MYSQL_SYSVAR_UINT(rotations, rotations, PLUGIN_VAR_READONLY, "Number of rotations before log is removed.", NULL, NULL, 9, 1, 999, 1); static MYSQL_SYSVAR_BOOL(rotate, rotate, PLUGIN_VAR_OPCMDARG, "Force log rotation", NULL, rotate_log, 0); static MYSQL_SYSVAR_STR(filename, filename, PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG, "The file to log sql errors to", NULL, NULL, "sql_errors.log"); static MYSQL_SYSVAR_BOOL(warnings, warnings, PLUGIN_VAR_OPCMDARG, "Warnings. If set to 0, warnings are not logged.", NULL, NULL, 1); static MYSQL_SYSVAR_BOOL(with_db_and_thread_info, with_db_and_thread_info, PLUGIN_VAR_READONLY | PLUGIN_VAR_OPCMDARG, "Show details about thread id and database name in the log", NULL, NULL, 0); static struct st_mysql_sys_var* vars[] = { MYSQL_SYSVAR(rate), MYSQL_SYSVAR(size_limit), MYSQL_SYSVAR(rotations), MYSQL_SYSVAR(rotate), MYSQL_SYSVAR(filename), MYSQL_SYSVAR(warnings), MYSQL_SYSVAR(with_db_and_thread_info), NULL }; static void log_sql_errors(MYSQL_THD thd __attribute__((unused)), unsigned int event_class __attribute__((unused)), const void *ev) { const struct mysql_event_general *event = (const struct mysql_event_general*)ev; if (rate && (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR || (warnings && event->event_subclass == MYSQL_AUDIT_GENERAL_WARNING))) { const char *type= (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR ? "ERROR" : "WARNING"); if (++count >= rate) { struct tm t; time_t event_time = event->general_time; count = 0; (void) localtime_r(&event_time, &t); if (with_db_and_thread_info) { if (event->database.str) { logger_printf(logfile, "%04d-%02d-%02d %2d:%02d:%02d %lu " "%s %`s %s %d: %s : %s \n", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, event->general_thread_id, event->general_user, event->database.str, type, event->general_error_code, event->general_command, event->general_query); } else { logger_printf(logfile, "%04d-%02d-%02d %2d:%02d:%02d %lu " "%s NULL %s %d: %s : %s \n", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, event->general_thread_id, event->general_user, type, event->general_error_code, event->general_command, event->general_query); } } else { logger_printf(logfile, "%04d-%02d-%02d %2d:%02d:%02d " "%s %s %d: %s : %s\n", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, event->general_user, type, event->general_error_code, event->general_command, event->general_query); } } } } static int sql_error_log_init(void *p __attribute__((unused))) { logger_init_mutexes(); logfile= logger_open(filename, size_limit, rotations); if (logfile == NULL) { fprintf(stderr, "Could not create file '%s'\n", filename); return 1; } count = 0; return 0; } static int sql_error_log_deinit(void *p __attribute__((unused))) { if (logfile) logger_close(logfile); return 0; } static void rotate_log(MYSQL_THD thd __attribute__((unused)), struct st_mysql_sys_var *var __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save __attribute__((unused))) { (void) logger_rotate(logfile); } static struct st_mysql_audit descriptor = { MYSQL_AUDIT_INTERFACE_VERSION, NULL, log_sql_errors, { MYSQL_AUDIT_GENERAL_CLASSMASK } }; maria_declare_plugin(sql_errlog) { MYSQL_AUDIT_PLUGIN, &descriptor, "SQL_ERROR_LOG", "Alexey Botchkov", "Log SQL level errors to a file with rotation", PLUGIN_LICENSE_GPL, sql_error_log_init, sql_error_log_deinit, 0x0100, NULL, vars, "1.1", MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end;