Commit 07a82c58 authored by Alexey Botchkov's avatar Alexey Botchkov

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
parent 92f31d80
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
/* 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*/
......@@ -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
}
......
......@@ -24,3 +24,4 @@
#define VERSION_thd_wait 0x0100
#define VERSION_my_thread_scheduler 0x0100
#define VERSION_progress_report 0x0100
#define VERSION_logger 0x0100
......@@ -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)
/* 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;
......@@ -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)
......
/* 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;
}
# 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)
/* 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;
......@@ -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}
};
......
......@@ -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 },
};
......@@ -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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment