summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2012-03-14 00:55:56 +0400
committerAlexey Botchkov <holyfoot@askmonty.org>2012-03-14 00:55:56 +0400
commit07a82c58a7306bd8ef889dd912cdc437a9f83a89 (patch)
treeedd7edc02e96ad32d8f7d8d5137db548562be16e
parent92f31d8070738206c5b64d3ab5cc9a0cb05c56fa (diff)
downloadmariadb-git-07a82c58a7306bd8ef889dd912cdc437a9f83a89.tar.gz
MDEV-15 Log all SQL errors.
Added the logger service that provides us with the rotating logs. The plugin SQL_ERROR_LOG added. It logs the errors using the 'logger service' for the rotating log files. the example record from the log: 2012-03-09 15:07:29 root[root] @ localhost [] ERROR 1146: Table 'test.xyz' doesn't exist : select * from test.xyz
-rw-r--r--include/my_sys.h1
-rw-r--r--include/mysql/plugin.h2
-rw-r--r--include/mysql/plugin_audit.h.pp18
-rw-r--r--include/mysql/plugin_auth.h.pp18
-rw-r--r--include/mysql/plugin_ftparser.h.pp18
-rw-r--r--include/mysql/service_logger.h98
-rw-r--r--include/mysql/services.h2
-rw-r--r--include/service_versions.h1
-rw-r--r--libservices/CMakeLists.txt3
-rw-r--r--libservices/logger_service.c20
-rw-r--r--mysys/CMakeLists.txt2
-rw-r--r--mysys/my_logger.c168
-rw-r--r--plugin/sql_errlog/CMakeLists.txt16
-rw-r--r--plugin/sql_errlog/sql_errlog.c147
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/sql_plugin_services.h11
-rw-r--r--strings/my_vsnprintf.c19
17 files changed, 543 insertions, 4 deletions
diff --git a/include/my_sys.h b/include/my_sys.h
index fa79af51c7e..3fcaea95f7b 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -636,6 +636,7 @@ extern FILE *my_fopen(const char *FileName,int Flags,myf MyFlags);
extern FILE *my_fdopen(File Filedes,const char *name, int Flags,myf MyFlags);
extern FILE *my_freopen(const char *path, const char *mode, FILE *stream);
extern int my_fclose(FILE *fd,myf MyFlags);
+extern int my_vfprintf(FILE *stream, const char* format, va_list args);
extern File my_fileno(FILE *fd);
extern int my_chsize(File fd,my_off_t newlength, int filler, myf MyFlags);
extern int my_chmod(const char *name, mode_t mode, myf my_flags);
diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h
index e46bea9f24f..614bb028a0d 100644
--- a/include/mysql/plugin.h
+++ b/include/mysql/plugin.h
@@ -72,7 +72,7 @@ typedef struct st_mysql_xid MYSQL_XID;
#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0103
/* MariaDB plugin interface version */
-#define MARIA_PLUGIN_INTERFACE_VERSION 0x0101
+#define MARIA_PLUGIN_INTERFACE_VERSION 0x0102
/*
The allowable types of plugins
diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp
index cdd9324a5f7..42ad957e556 100644
--- a/include/mysql/plugin_audit.h.pp
+++ b/include/mysql/plugin_audit.h.pp
@@ -80,6 +80,24 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_logger.h>
+typedef struct logger_handle_st LOGGER_HANDLE;
+extern struct logger_service_st {
+ LOGGER_HANDLE* (*open)(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int (*close)(LOGGER_HANDLE *log);
+ int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...);
+ int (*rotate)(LOGGER_HANDLE *log);
+} *logger_service;
+ LOGGER_HANDLE *logger_open(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int logger_close(LOGGER_HANDLE *log);
+ int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int logger_rotate(LOGGER_HANDLE *log);
+ int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp
index e06494746dd..146768f9575 100644
--- a/include/mysql/plugin_auth.h.pp
+++ b/include/mysql/plugin_auth.h.pp
@@ -80,6 +80,24 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_logger.h>
+typedef struct logger_handle_st LOGGER_HANDLE;
+extern struct logger_service_st {
+ LOGGER_HANDLE* (*open)(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int (*close)(LOGGER_HANDLE *log);
+ int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...);
+ int (*rotate)(LOGGER_HANDLE *log);
+} *logger_service;
+ LOGGER_HANDLE *logger_open(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int logger_close(LOGGER_HANDLE *log);
+ int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int logger_rotate(LOGGER_HANDLE *log);
+ int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp
index a990c62e8e9..59afe4276b4 100644
--- a/include/mysql/plugin_ftparser.h.pp
+++ b/include/mysql/plugin_ftparser.h.pp
@@ -80,6 +80,24 @@ void thd_progress_next_stage(void* thd);
void thd_progress_end(void* thd);
const char *set_thd_proc_info(void*, const char * info, const char *func,
const char *file, unsigned int line);
+#include <mysql/service_logger.h>
+typedef struct logger_handle_st LOGGER_HANDLE;
+extern struct logger_service_st {
+ LOGGER_HANDLE* (*open)(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int (*close)(LOGGER_HANDLE *log);
+ int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...);
+ int (*rotate)(LOGGER_HANDLE *log);
+} *logger_service;
+ LOGGER_HANDLE *logger_open(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int logger_close(LOGGER_HANDLE *log);
+ int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int logger_rotate(LOGGER_HANDLE *log);
+ int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...);
struct st_mysql_xid {
long formatID;
long gtrid_length;
diff --git a/include/mysql/service_logger.h b/include/mysql/service_logger.h
new file mode 100644
index 00000000000..315c382791e
--- /dev/null
+++ b/include/mysql/service_logger.h
@@ -0,0 +1,98 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef MYSQL_SERVICE_LOGGER_INCLUDED
+#define MYSQL_SERVICE_LOGGER_INCLUDED
+
+#ifndef MYSQL_ABI_CHECK
+#include <stdarg.h>
+#endif
+
+/**
+ @file
+ logger service
+
+ Log file with rotation implementation.
+
+ This service implements logging with possible rotation
+ of the log files. Interface intentionally tries to be similar to FILE*
+ related functions.
+
+ So that one can open the log with logger_open(), specifying
+ the limit on the logfile size and the rotations number.
+
+ Then it's possible to write messages to the log with
+ logger_printf or logger_vprintf functions.
+
+ As the size of the logfile grows over the specified limit,
+ it is renamed to 'logfile.1'. The former 'logfile.1' becomes
+ 'logfile.2', etc. The file 'logfile.rotations' is removed.
+ That's how the rotation works.
+
+ The rotation can be forced with the logger_rotate() call.
+
+ Finally the log should be closed with logger_close().
+
+@notes:
+ Implementation checks the size of the log file before it starts new
+ printf into it. So the size of the file gets over the limit when it rotates.
+
+ The access is secured with the mutex, so the log is threadsafe.
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct logger_handle_st LOGGER_HANDLE;
+
+extern struct logger_service_st {
+ LOGGER_HANDLE* (*open)(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int (*close)(LOGGER_HANDLE *log);
+ int (*vprintf)(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int (*printf)(LOGGER_HANDLE *log, const char *fmt, ...);
+ int (*rotate)(LOGGER_HANDLE *log);
+} *logger_service;
+
+#ifdef MYSQL_DYNAMIC_PLUGIN
+
+#define logger_open(path, size_limit, rotations) \
+ (logger_service->open(path, size_limit, rotations))
+#define logger_close(log) (logger_service->close(log))
+#define logger_rotate(log) (logger_service->rotate(log))
+#define logger_vprintf(log, fmt, argptr) (logger_service->\
+ vprintf(log, fmt, argptr))
+#define logger_printf logger_service->printf
+#else
+
+ LOGGER_HANDLE *logger_open(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations);
+ int logger_close(LOGGER_HANDLE *log);
+ int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list argptr);
+ int logger_rotate(LOGGER_HANDLE *log);
+ int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/
+
diff --git a/include/mysql/services.h b/include/mysql/services.h
index eca0e88c8da..258f7b90658 100644
--- a/include/mysql/services.h
+++ b/include/mysql/services.h
@@ -23,6 +23,8 @@ extern "C" {
#include <mysql/service_thd_wait.h>
#include <mysql/service_thread_scheduler.h>
#include <mysql/service_progress_report.h>
+#include <mysql/service_logger.h>
+
#ifdef __cplusplus
}
diff --git a/include/service_versions.h b/include/service_versions.h
index ee50d4856e9..8a397d71b12 100644
--- a/include/service_versions.h
+++ b/include/service_versions.h
@@ -24,3 +24,4 @@
#define VERSION_thd_wait 0x0100
#define VERSION_my_thread_scheduler 0x0100
#define VERSION_progress_report 0x0100
+#define VERSION_logger 0x0100
diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt
index ee6a7c73abe..dd16558ef9c 100644
--- a/libservices/CMakeLists.txt
+++ b/libservices/CMakeLists.txt
@@ -20,7 +20,8 @@ SET(MYSQLSERVICES_SOURCES
thd_alloc_service.c
thd_wait_service.c
my_thread_scheduler_service.c
- progress_report_service.c)
+ progress_report_service.c
+ logger_service.c)
ADD_CONVENIENCE_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES})
INSTALL(TARGETS mysqlservices DESTINATION ${INSTALL_LIBDIR} COMPONENT Development)
diff --git a/libservices/logger_service.c b/libservices/logger_service.c
new file mode 100644
index 00000000000..896b0714293
--- /dev/null
+++ b/libservices/logger_service.c
@@ -0,0 +1,20 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <service_versions.h>
+
+
+SERVICE_VERSION *logger_service= (void *) VERSION_logger;
+
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt
index 7397990eddb..5204f7e873c 100644
--- a/mysys/CMakeLists.txt
+++ b/mysys/CMakeLists.txt
@@ -37,7 +37,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c
safemalloc.c my_new.cc
my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c
my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c
- my_rdtsc.c my_context.c)
+ my_rdtsc.c my_context.c my_logger.c)
IF (WIN32)
SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c)
diff --git a/mysys/my_logger.c b/mysys/my_logger.c
new file mode 100644
index 00000000000..292c98f4ad1
--- /dev/null
+++ b/mysys/my_logger.c
@@ -0,0 +1,168 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "my_global.h"
+#include <my_sys.h>
+#include <mysql/service_logger.h>
+
+extern char *mysql_data_home;
+extern PSI_mutex_key key_LOCK_logger_service;
+
+typedef struct logger_handle_st {
+ FILE *file;
+ char path[FN_REFLEN];
+ long size_limit;
+ unsigned int rotations;
+ size_t path_len;
+ mysql_mutex_t lock;
+} LSFS;
+
+
+static unsigned int n_dig(unsigned int i)
+{
+ return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3));
+}
+
+
+LOGGER_HANDLE *logger_open(const char *path,
+ unsigned long size_limit,
+ unsigned int rotations)
+{
+ LOGGER_HANDLE new_log, *l_perm;
+
+ /*
+ I don't think we ever need more rotations,
+ but if it's so, the rotation procedure should be adapted to it.
+ */
+ if (rotations > 999)
+ return 0;
+
+ new_log.rotations= rotations;
+ new_log.size_limit= size_limit;
+ new_log.path_len= strlen(fn_format(new_log.path, path,
+ mysql_data_home, "", MY_UNPACK_FILENAME));
+
+ if (new_log.path_len+n_dig(rotations)+1 > FN_REFLEN)
+ {
+ errno= ENAMETOOLONG;
+ /* File path too long */
+ return 0;
+ }
+ if (!(new_log.file= fopen(new_log.path, "a+")))
+ {
+ /* Check errno for the cause */
+ return 0;
+ }
+
+ setbuf(new_log.file, 0);
+ fseek(new_log.file, 0, SEEK_END);
+ if (!(l_perm= (LOGGER_HANDLE *)
+ my_malloc(sizeof(LOGGER_HANDLE), MYF(0))))
+ {
+ fclose(new_log.file);
+ new_log.file= NULL;
+ return 0; /* End of memory */
+ }
+ *l_perm= new_log;
+ mysql_mutex_init(key_LOCK_logger_service, &l_perm->lock, MY_MUTEX_INIT_FAST);
+ return l_perm;
+}
+
+int logger_close(LOGGER_HANDLE *log)
+{
+ int result;
+ mysql_mutex_destroy(&log->lock);
+ result= fclose(log->file);
+ my_free(log);
+ return result;
+}
+
+
+static char *logname(LOGGER_HANDLE *log, char *buf, unsigned int n_log)
+{
+ sprintf(buf+log->path_len, ".%0*u", n_dig(log->rotations), n_log);
+ return buf;
+}
+
+
+static int do_rotate(LOGGER_HANDLE *log)
+{
+ char namebuf[FN_REFLEN];
+ int result;
+ unsigned int i;
+ char *buf_old, *buf_new, *tmp;
+
+ memcpy(namebuf, log->path, log->path_len);
+
+ buf_new= logname(log, namebuf, log->rotations);
+ buf_old= log->path;
+ for (i=log->rotations-1; i>0; i--)
+ {
+ logname(log, buf_old, i);
+ if (!access(buf_old, F_OK) &&
+ (result= my_rename(buf_old, buf_new, MYF(0))))
+ return result;
+ tmp= buf_old;
+ buf_old= buf_new;
+ buf_new= tmp;
+ }
+ if ((result= fclose(log->file)))
+ return result;
+ namebuf[log->path_len]= 0;
+ result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
+ log->file= fopen(namebuf, "a+");
+ return log->file==NULL || result;
+}
+
+
+int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap)
+{
+ int result;
+ mysql_mutex_lock(&log->lock);
+ if (ftell(log->file) >= log->size_limit &&
+ do_rotate(log))
+ {
+ result= -1;
+ goto exit; /* Log rotation needed but failed */
+ }
+
+ result= my_vfprintf(log->file, fmt, ap);
+exit:
+ mysql_mutex_unlock(&log->lock);
+ return result;
+}
+
+
+int logger_rotate(LOGGER_HANDLE *log)
+{
+ int result;
+ mysql_mutex_lock(&log->lock);
+ result= do_rotate(log);
+ mysql_mutex_unlock(&log->lock);
+ return result;
+}
+
+
+int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...)
+{
+ int result;
+ va_list args;
+ va_start(args,fmt);
+ result= logger_vprintf(log, fmt, args);
+ va_end(args);
+ return result;
+}
+
diff --git a/plugin/sql_errlog/CMakeLists.txt b/plugin/sql_errlog/CMakeLists.txt
new file mode 100644
index 00000000000..18fb9f5421d
--- /dev/null
+++ b/plugin/sql_errlog/CMakeLists.txt
@@ -0,0 +1,16 @@
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+
+MYSQL_ADD_PLUGIN(sql_errlog sql_errlog.c MODULE_ONLY)
diff --git a/plugin/sql_errlog/sql_errlog.c b/plugin/sql_errlog/sql_errlog.c
new file mode 100644
index 00000000000..53d804feeb3
--- /dev/null
+++ b/plugin/sql_errlog/sql_errlog.c
@@ -0,0 +1,147 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <mysql/plugin_audit.h>
+#include <stdio.h>
+#include <time.h>
+
+/*
+ rate 0 means the logging was disabled.
+*/
+
+static char *filename;
+static unsigned int rate;
+static unsigned int size_limit;
+static unsigned int rotations;
+static char rotate;
+
+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_UINT(size_limit, size_limit,
+ PLUGIN_VAR_READONLY, "Log file size limit", NULL, NULL,
+ 1000000, 100, 1024*1024L*1024L, 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 struct st_mysql_sys_var* vars[] = {
+ MYSQL_SYSVAR(rate),
+ MYSQL_SYSVAR(size_limit),
+ MYSQL_SYSVAR(rotations),
+ MYSQL_SYSVAR(rotate),
+ MYSQL_SYSVAR(filename),
+ 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)
+ {
+ if (++count >= rate)
+ {
+ struct tm t;
+ time_t event_time = event->general_time;
+
+ count = 0;
+ localtime_r(&event_time, &t);
+ logger_printf(logfile, "%04d-%02d-%02d %2d:%02d:%02d "
+ "%s ERROR %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, event->general_error_code,
+ event->general_command, event->general_query);
+ }
+ }
+}
+
+
+static int sql_error_log_init(void *p __attribute__((unused)))
+{
+ 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)))
+{
+ 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.0",
+ MariaDB_PLUGIN_MATURITY_ALPHA
+}
+maria_declare_plugin_end;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 36b6897c56d..7394ce5c931 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -748,6 +748,7 @@ PSI_mutex_key key_LOCK_stats,
key_LOCK_wakeup_ready;
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
+PSI_mutex_key key_LOCK_logger_service;
static PSI_mutex_info all_server_mutexes[]=
{
@@ -807,6 +808,8 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
{ &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
{ &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
+ { &key_LOCK_logger_service, "logger_service_file_st::lock",
+ PSI_FLAG_GLOBAL},
{ &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0}
};
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index 7f2a4e5f955..bd6d136585a 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -54,12 +54,21 @@ static struct progress_report_service_st progress_report_handler= {
set_thd_proc_info
};
+static struct logger_service_st logger_handler= {
+ logger_open,
+ logger_close,
+ logger_vprintf,
+ logger_printf,
+ logger_rotate
+};
+
static struct st_service_ref list_of_services[]=
{
{ "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
{ "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "my_thread_scheduler_service", VERSION_my_thread_scheduler, &my_thread_scheduler_handler },
- { "progress_report_service", VERSION_progress_report, &progress_report_handler }
+ { "progress_report_service", VERSION_progress_report, &progress_report_handler },
+ { "logger_service", VERSION_logger, &logger_handler },
};
diff --git a/strings/my_vsnprintf.c b/strings/my_vsnprintf.c
index 8d8c078849c..64c37ca0537 100644
--- a/strings/my_vsnprintf.c
+++ b/strings/my_vsnprintf.c
@@ -678,3 +678,22 @@ size_t my_snprintf(char* to, size_t n, const char* fmt, ...)
va_end(args);
return result;
}
+
+
+/**
+ Writes output to the stream according to a format string.
+
+ @param stream file to write to
+ @param format string format
+ @param args list of parameters
+
+ @retval
+ number of the characters written.
+*/
+
+int my_vfprintf(FILE *stream, const char* format, va_list args)
+{
+ char cvtbuf[1024];
+ (void) my_vsnprintf(cvtbuf, sizeof(cvtbuf), format, args);
+ return fprintf(stream, "%s\n", cvtbuf);
+}