summaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2012-03-24 11:24:20 +0100
committerAlexey Botchkov <holyfoot@askmonty.org>2012-03-24 11:24:20 +0100
commit5c01f1ac1498a9b32bdccb817cdbc0b3ca3687d6 (patch)
treefe8eb54f1eaff9a41fe617b35356f5bd3e35746b /plugin
parent619f67b1b4694add244377a0cb2507a035ebd306 (diff)
downloadmariadb-git-5c01f1ac1498a9b32bdccb817cdbc0b3ca3687d6.tar.gz
MDEV-15 Log all sql errors.
modified for MySQL 5.5. Logger service moved to the plugin/sql_errlog directory to be properly used later. plugin/sql_errlog/sql_errlog.c: Fixes for bugs #956427 (SQL_ERROR_LOG plugin produces bogus warnings about sql-error-log-size-limit value) and #956463 (Server crashes if SQL_ERROR_LOG fails to initialize) they're also MDEV-184 and MDEV-183 The sql_error_log_deinit() should be prepared for the logger_file to be NULL. The logger_file_size_limit upper limit wasn't properly set.
Diffstat (limited to 'plugin')
-rw-r--r--plugin/sql_errlog/CMakeLists.txt2
-rw-r--r--plugin/sql_errlog/logger_service.c21
-rw-r--r--plugin/sql_errlog/service_logger.h100
-rw-r--r--plugin/sql_errlog/sql_errlog.c27
-rw-r--r--plugin/sql_errlog/sql_logger.cc194
5 files changed, 341 insertions, 3 deletions
diff --git a/plugin/sql_errlog/CMakeLists.txt b/plugin/sql_errlog/CMakeLists.txt
index 18fb9f5421d..50c4b7d806d 100644
--- a/plugin/sql_errlog/CMakeLists.txt
+++ b/plugin/sql_errlog/CMakeLists.txt
@@ -13,4 +13,4 @@
# 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)
+MYSQL_ADD_PLUGIN(sql_errlog sql_errlog.c sql_logger.cc service_logger.h MODULE_ONLY)
diff --git a/plugin/sql_errlog/logger_service.c b/plugin/sql_errlog/logger_service.c
new file mode 100644
index 00000000000..c45e978413c
--- /dev/null
+++ b/plugin/sql_errlog/logger_service.c
@@ -0,0 +1,21 @@
+/* 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>
+
+
+/* file reserved for the future use */
+SERVICE_VERSION *logger_service= (void *) VERSION_logger;
+
diff --git a/plugin/sql_errlog/service_logger.h b/plugin/sql_errlog/service_logger.h
new file mode 100644
index 00000000000..2aa4d94f010
--- /dev/null
+++ b/plugin/sql_errlog/service_logger.h
@@ -0,0 +1,100 @@
+/* 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 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;
+
+#if 0 /*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 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, ...);
+
+ void init_logger_mutexes();
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/
+
diff --git a/plugin/sql_errlog/sql_errlog.c b/plugin/sql_errlog/sql_errlog.c
index ee2765a7143..ce383916621 100644
--- a/plugin/sql_errlog/sql_errlog.c
+++ b/plugin/sql_errlog/sql_errlog.c
@@ -16,6 +16,7 @@
#include <mysql/plugin_audit.h>
#include <stdio.h>
#include <time.h>
+#include "service_logger.h"
/*
Disable __attribute__() on non-gcc compilers.
@@ -32,6 +33,7 @@
rate 0 means the logging was disabled.
*/
+
static char *filename;
static unsigned int rate;
static unsigned long long size_limit;
@@ -50,7 +52,7 @@ static MYSQL_SYSVAR_UINT(rate, rate, PLUGIN_VAR_RQCMDARG,
static MYSQL_SYSVAR_ULONGLONG(size_limit, size_limit,
PLUGIN_VAR_READONLY, "Log file size limit", NULL, NULL,
- 1000000, 100, 0, 1);
+ 1000000, 100, ((long long) 0x7FFFFFFFFFFFFFFFLL), 1);
static MYSQL_SYSVAR_UINT(rotations, rotations,
PLUGIN_VAR_READONLY, "Number of rotations before log is removed.",
@@ -104,6 +106,8 @@ static void log_sql_errors(MYSQL_THD thd __attribute__((unused)),
static int sql_error_log_init(void *p __attribute__((unused)))
{
+ init_logger_mutexes();
+
logfile= logger_open(filename, size_limit, rotations);
if (logfile == NULL) {
fprintf(stderr, "Could not create file '%s'\n",
@@ -117,7 +121,8 @@ static int sql_error_log_init(void *p __attribute__((unused)))
static int sql_error_log_deinit(void *p __attribute__((unused)))
{
- logger_close(logfile);
+ if (logfile)
+ logger_close(logfile);
return 0;
}
@@ -139,6 +144,24 @@ static struct st_mysql_audit descriptor =
{ MYSQL_AUDIT_GENERAL_CLASSMASK }
};
+mysql_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,
+ NULL,
+ 0
+}
+mysql_declare_plugin_end;
+
maria_declare_plugin(sql_errlog)
{
MYSQL_AUDIT_PLUGIN,
diff --git a/plugin/sql_errlog/sql_logger.cc b/plugin/sql_errlog/sql_logger.cc
new file mode 100644
index 00000000000..8bf757f2fd7
--- /dev/null
+++ b/plugin/sql_errlog/sql_logger.cc
@@ -0,0 +1,194 @@
+/* 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 "service_logger.h"
+#include <my_pthread.h>
+
+extern MYSQL_PLUGIN_IMPORT char *mysql_data_home;
+
+/* These belong to the service initialization */
+static PSI_mutex_key key_LOCK_logger_service;
+static PSI_mutex_info mutex_list[]=
+{{ &key_LOCK_logger_service, "logger_service_file_st::lock", PSI_FLAG_GLOBAL}};
+
+typedef struct logger_handle_st {
+ File file;
+ char path[FN_REFLEN];
+ unsigned long long size_limit;
+ unsigned int rotations;
+ size_t path_len;
+ mysql_mutex_t lock;
+} LSFS;
+
+
+#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY)
+
+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 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= my_open(new_log.path, LOG_FLAGS, MYF(0))) < 0)
+ {
+ errno= my_errno;
+ /* Check errno for the cause */
+ return 0;
+ }
+
+ if (!(l_perm= (LOGGER_HANDLE *) my_malloc(sizeof(LOGGER_HANDLE), MYF(0))))
+ {
+ my_close(new_log.file, MYF(0));
+ new_log.file= -1;
+ 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;
+ File file= log->file;
+ mysql_mutex_destroy(&log->lock);
+ my_free(log);
+ if ((result= my_close(file, MYF(0))))
+ errno= my_errno;
+ 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))))
+ goto exit;
+ tmp= buf_old;
+ buf_old= buf_new;
+ buf_new= tmp;
+ }
+ if ((result= my_close(log->file, MYF(0))))
+ goto exit;
+ namebuf[log->path_len]= 0;
+ result= my_rename(namebuf, logname(log, log->path, 1), MYF(0));
+ log->file= my_open(namebuf, LOG_FLAGS, MYF(0));
+exit:
+ errno= my_errno;
+ return log->file < 0 || result;
+}
+
+
+int logger_vprintf(LOGGER_HANDLE *log, const char* fmt, va_list ap)
+{
+ int result;
+ my_off_t filesize;
+ char cvtbuf[1024];
+ size_t n_bytes;
+
+ mysql_mutex_lock(&log->lock);
+ if ((filesize= my_tell(log->file, MYF(0))) == (my_off_t) -1 ||
+ ((unsigned long long)filesize >= log->size_limit &&
+ do_rotate(log)))
+ {
+ result= -1;
+ errno= my_errno;
+ goto exit; /* Log rotation needed but failed */
+ }
+
+ n_bytes= my_vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap);
+ if (n_bytes >= sizeof(cvtbuf))
+ n_bytes= sizeof(cvtbuf) - 1;
+
+ result= my_write(log->file, (uchar *) cvtbuf, n_bytes, MYF(0));
+
+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;
+}
+
+void init_logger_mutexes()
+{
+ if (PSI_server)
+ PSI_server->register_mutex("sql_logger", mutex_list, 1);
+}
+