Browse Source
MDEV-15 Log all SQL errors.
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
pull/374/head
17 changed files with 543 additions and 4 deletions
-
1include/my_sys.h
-
2include/mysql/plugin.h
-
18include/mysql/plugin_audit.h.pp
-
18include/mysql/plugin_auth.h.pp
-
18include/mysql/plugin_ftparser.h.pp
-
98include/mysql/service_logger.h
-
2include/mysql/services.h
-
1include/service_versions.h
-
3libservices/CMakeLists.txt
-
20libservices/logger_service.c
-
2mysys/CMakeLists.txt
-
168mysys/my_logger.c
-
16plugin/sql_errlog/CMakeLists.txt
-
147plugin/sql_errlog/sql_errlog.c
-
3sql/mysqld.cc
-
11sql/sql_plugin_services.h
-
19strings/my_vsnprintf.c
@ -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*/ |
|||
|
|||
@ -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; |
|||
|
|||
@ -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; |
|||
} |
|||
|
|||
@ -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) |
|||
@ -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; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue