Turn QLA filter to C++, part1
This commit is contained in:
parent
998652bf24
commit
459d4bb002
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file qlafilter.c - Quary Log All Filter
|
||||
* @file qlafilter.cc - Quary Log All Filter
|
||||
*
|
||||
* QLA Filter - Query Log All. A simple query logging filter. All queries passing
|
||||
* through the filter are written to a text file.
|
||||
@ -33,6 +33,8 @@
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/atomic.h>
|
||||
@ -46,6 +48,11 @@
|
||||
#include <maxscale/modulecmd.h>
|
||||
#include <maxscale/json_api.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
class QlaFilterSession;
|
||||
class QlaInstance;
|
||||
|
||||
/* Date string buffer size */
|
||||
#define QLA_DATE_BUFFER_SIZE 20
|
||||
|
||||
@ -53,6 +60,22 @@
|
||||
#define CONFIG_FILE_SESSION (1 << 0) // Default value, session specific files
|
||||
#define CONFIG_FILE_UNIFIED (1 << 1) // One file shared by all sessions
|
||||
|
||||
/* Default values for logged data */
|
||||
#define LOG_DATA_DEFAULT "date,user,query"
|
||||
|
||||
static const char PARAM_MATCH[] = "match";
|
||||
static const char PARAM_EXCLUDE[] = "exclude";
|
||||
static const char PARAM_USER[] = "user";
|
||||
static const char PARAM_SOURCE[] = "source";
|
||||
static const char PARAM_FILEBASE[] = "filebase";
|
||||
static const char PARAM_OPTIONS[] = "options";
|
||||
static const char PARAM_LOG_TYPE[] = "log_type";
|
||||
static const char PARAM_LOG_DATA[] = "log_data";
|
||||
static const char PARAM_FLUSH[] = "flush";
|
||||
static const char PARAM_APPEND[] = "append";
|
||||
static const char PARAM_NEWLINE[] = "newline_replacement";
|
||||
static const char PARAM_SEPARATOR[] = "separator";
|
||||
|
||||
/* Flags for controlling extra log entry contents */
|
||||
enum log_options
|
||||
{
|
||||
@ -64,9 +87,6 @@ enum log_options
|
||||
LOG_DATA_REPLY_TIME = (1 << 5),
|
||||
};
|
||||
|
||||
/* Default values for logged data */
|
||||
#define LOG_DATA_DEFAULT "date,user,query"
|
||||
|
||||
/* The filter entry points */
|
||||
static MXS_FILTER *createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *);
|
||||
static MXS_FILTER_SESSION *newSession(MXS_FILTER *instance, MXS_SESSION *session);
|
||||
@ -80,84 +100,9 @@ static void diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *
|
||||
static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESSION *fsession);
|
||||
static uint64_t getCapabilities(MXS_FILTER* instance);
|
||||
|
||||
/**
|
||||
* A instance structure, the assumption is that the option passed
|
||||
* to the filter is simply a base for the filename to which the queries
|
||||
* are logged.
|
||||
*
|
||||
* To this base a session number is attached such that each session will
|
||||
* have a unique name.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int sessions; /* The count of sessions */
|
||||
char *name; /* Filter definition name */
|
||||
char *filebase; /* The filename base */
|
||||
char *source; /* The source of the client connection to filter on */
|
||||
char *user_name; /* The user name to filter on */
|
||||
char *match; /* Optional text to match against */
|
||||
pcre2_code* re_match; /* Compiled regex text */
|
||||
char *exclude; /* Optional text to match against for exclusion */
|
||||
pcre2_code* re_exclude; /* Compiled regex nomatch text */
|
||||
uint32_t ovec_size; /* PCRE2 match data ovector size */
|
||||
uint32_t log_mode_flags; /* Log file mode settings */
|
||||
uint32_t log_file_data_flags; /* What data is saved to the files */
|
||||
FILE *unified_fp; /* Unified log file. The pointer needs to be shared here
|
||||
* to avoid garbled printing. */
|
||||
char *unified_filename; /* Filename of the unified log file */
|
||||
bool flush_writes; /* Flush log file after every write? */
|
||||
bool append; /* Open files in append-mode? */
|
||||
char *query_newline; /* Character(s) used to replace a newline within a query */
|
||||
char *separator; /* Character(s) used to separate elements */
|
||||
bool write_warning_given; /* Avoid repeatedly printing some errors/warnings. */
|
||||
} QLA_INSTANCE;
|
||||
|
||||
/**
|
||||
* Helper struct for holding data before it's written to file.
|
||||
*/
|
||||
struct LOG_EVENT_DATA
|
||||
{
|
||||
bool has_message; // Does message data exist?
|
||||
GWBUF* query_clone; // Clone of the query buffer.
|
||||
char query_date[QLA_DATE_BUFFER_SIZE]; // Text representation of date.
|
||||
timespec begin_time; // Timer value at the moment of receiving query.
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Resets event data. Since QLA_SESSION is allocated with calloc, separate initialisation is not needed.
|
||||
*
|
||||
* @param event Event to reset
|
||||
*/
|
||||
void clear(LOG_EVENT_DATA& event)
|
||||
{
|
||||
event.has_message = false;
|
||||
gwbuf_free(event.query_clone);
|
||||
event.query_clone = NULL;
|
||||
event.query_date[0] = '\0';
|
||||
event.begin_time = {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
/* The session structure for this QLA filter. */
|
||||
typedef struct
|
||||
{
|
||||
int active;
|
||||
MXS_UPSTREAM up;
|
||||
MXS_DOWNSTREAM down;
|
||||
char *filename; /* The session-specific log file name */
|
||||
FILE *fp; /* The session-specific log file */
|
||||
const char *remote; /* Client address */
|
||||
char *service; /* The service name this filter is attached to. Not owned. */
|
||||
size_t ses_id; /* The session this filter serves */
|
||||
const char *user; /* The client */
|
||||
pcre2_match_data* match_data; /* Regex match data */
|
||||
LOG_EVENT_DATA event_data; /* Information about the latest event, required if logging execution time. */
|
||||
} QLA_SESSION;
|
||||
|
||||
static FILE* open_log_file(QLA_INSTANCE *, uint32_t, const char *);
|
||||
static int write_log_entry(FILE*, QLA_INSTANCE*, QLA_SESSION*, uint32_t,
|
||||
static FILE* open_log_file(QlaInstance *, uint32_t, const char *);
|
||||
static int write_log_entry(FILE*, QlaInstance*, QlaFilterSession*, uint32_t,
|
||||
const char*, const char*, size_t, int);
|
||||
static bool cb_log(const MODULECMD_ARG *argv, json_t** output);
|
||||
|
||||
@ -187,18 +132,171 @@ static const MXS_ENUM_VALUE log_data_values[] =
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static const char PARAM_MATCH[] = "match";
|
||||
static const char PARAM_EXCLUDE[] = "exclude";
|
||||
static const char PARAM_USER[] = "user";
|
||||
static const char PARAM_SOURCE[] = "source";
|
||||
static const char PARAM_FILEBASE[] = "filebase";
|
||||
static const char PARAM_OPTIONS[] = "options";
|
||||
static const char PARAM_LOG_TYPE[] = "log_type";
|
||||
static const char PARAM_LOG_DATA[] = "log_data";
|
||||
static const char PARAM_FLUSH[] = "flush";
|
||||
static const char PARAM_APPEND[] = "append";
|
||||
static const char PARAM_NEWLINE[] = "newline_replacement";
|
||||
static const char PARAM_SEPARATOR[] = "separator";
|
||||
/**
|
||||
* Helper struct for holding data before it's written to file.
|
||||
*/
|
||||
class LogEventData
|
||||
{
|
||||
private:
|
||||
LogEventData(const LogEventData&);
|
||||
LogEventData& operator = (const LogEventData&);
|
||||
|
||||
public:
|
||||
LogEventData()
|
||||
: has_message(false)
|
||||
, query_clone(NULL)
|
||||
, begin_time(
|
||||
{
|
||||
0, 0
|
||||
})
|
||||
{}
|
||||
|
||||
~LogEventData()
|
||||
{
|
||||
ss_dassert(query_clone == NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets event data.
|
||||
*
|
||||
* @param event Event to reset
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
has_message = false;
|
||||
gwbuf_free(query_clone);
|
||||
query_clone = NULL;
|
||||
query_date[0] = '\0';
|
||||
begin_time = {0, 0};
|
||||
}
|
||||
|
||||
bool has_message; // Does message data exist?
|
||||
GWBUF* query_clone; // Clone of the query buffer.
|
||||
char query_date[QLA_DATE_BUFFER_SIZE]; // Text representation of date.
|
||||
timespec begin_time; // Timer value at the moment of receiving query.
|
||||
};
|
||||
|
||||
/**
|
||||
* A instance structure, the assumption is that the option passed
|
||||
* to the filter is simply a base for the filename to which the queries
|
||||
* are logged.
|
||||
*
|
||||
* To this base a session number is attached such that each session will
|
||||
* have a unique name.
|
||||
*/
|
||||
class QlaInstance
|
||||
{
|
||||
private:
|
||||
QlaInstance(const QlaInstance&);
|
||||
QlaInstance& operator = (const QlaInstance&);
|
||||
|
||||
public:
|
||||
QlaInstance(const char* name, MXS_CONFIG_PARAMETER *params);
|
||||
~QlaInstance();
|
||||
|
||||
string name; /* Filter definition name */
|
||||
|
||||
uint32_t log_mode_flags; /* Log file mode settings */
|
||||
uint32_t log_file_data_flags; /* What data is saved to the files */
|
||||
|
||||
string filebase; /* The filename base */
|
||||
string unified_filename; /* Filename of the unified log file */
|
||||
FILE* unified_fp; /* Unified log file. The pointer needs to be shared here
|
||||
* to avoid garbled printing. */
|
||||
bool flush_writes; /* Flush log file after every write? */
|
||||
bool append; /* Open files in append-mode? */
|
||||
string query_newline; /* Character(s) used to replace a newline within a query */
|
||||
string separator; /* Character(s) used to separate elements */
|
||||
bool write_warning_given; /* Avoid repeatedly printing some errors/warnings. */
|
||||
|
||||
string user_name; /* The user name to filter on */
|
||||
string source; /* The source of the client connection to filter on */
|
||||
|
||||
string match; /* Optional text to match against */
|
||||
string exclude; /* Optional text to match against for exclusion */
|
||||
pcre2_code* re_match; /* Compiled regex text */
|
||||
pcre2_code* re_exclude; /* Compiled regex nomatch text */
|
||||
uint32_t ovec_size; /* PCRE2 match data ovector size */
|
||||
};
|
||||
|
||||
QlaInstance::QlaInstance(const char* name, MXS_CONFIG_PARAMETER *params)
|
||||
: name(name)
|
||||
, log_mode_flags(config_get_enum(params, PARAM_LOG_TYPE, log_type_values))
|
||||
, log_file_data_flags(config_get_enum(params, PARAM_LOG_DATA, log_data_values))
|
||||
, filebase(config_get_string(params, PARAM_FILEBASE))
|
||||
, unified_fp(NULL)
|
||||
, flush_writes(config_get_bool(params, PARAM_FLUSH))
|
||||
, append(config_get_bool(params, PARAM_APPEND))
|
||||
, query_newline(config_get_string(params, PARAM_NEWLINE))
|
||||
, separator(config_get_string(params, PARAM_SEPARATOR))
|
||||
, write_warning_given(false)
|
||||
, user_name(config_get_string(params, PARAM_USER))
|
||||
, source(config_get_string(params, PARAM_SOURCE))
|
||||
, match(config_get_string(params, PARAM_MATCH))
|
||||
, exclude(config_get_string(params, PARAM_EXCLUDE))
|
||||
, re_match(NULL)
|
||||
, re_exclude(NULL)
|
||||
, ovec_size(0)
|
||||
{}
|
||||
|
||||
QlaInstance::~QlaInstance()
|
||||
{
|
||||
pcre2_code_free(re_match);
|
||||
pcre2_code_free(re_exclude);
|
||||
if (unified_fp != NULL)
|
||||
{
|
||||
fclose(unified_fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* The session structure for this QLA filter. */
|
||||
class QlaFilterSession
|
||||
{
|
||||
private:
|
||||
QlaFilterSession(const QlaFilterSession&);
|
||||
QlaFilterSession& operator = (const QlaFilterSession&);
|
||||
|
||||
public:
|
||||
QlaFilterSession(const char* user, const char* remote, bool ses_active,
|
||||
pcre2_match_data* mdata,
|
||||
const string& ses_filename, FILE* ses_file,
|
||||
size_t ses_id, const char* service);
|
||||
~QlaFilterSession();
|
||||
|
||||
const char* m_user; /* Client username */
|
||||
const char* m_remote; /* Client address */
|
||||
bool m_active; /* Is session active? */
|
||||
pcre2_match_data* m_mdata; /* Regex match data */
|
||||
string m_filename; /* The session-specific log file name */
|
||||
FILE *m_logfile; /* The session-specific log file */
|
||||
size_t m_ses_id; /* The session this filter session serves. */
|
||||
const char* m_service; /* The service name this filter is attached to. */
|
||||
LogEventData m_event_data; /* Information about the latest event, used if logging execution time. */
|
||||
|
||||
MXS_UPSTREAM up;
|
||||
MXS_DOWNSTREAM down;
|
||||
};
|
||||
|
||||
QlaFilterSession::QlaFilterSession(const char* user, const char* remote, bool ses_active,
|
||||
pcre2_match_data* mdata,
|
||||
const string& ses_filename, FILE* ses_file,
|
||||
size_t ses_id, const char* service)
|
||||
: m_user(user)
|
||||
, m_remote(remote)
|
||||
, m_active(ses_active)
|
||||
, m_mdata(mdata)
|
||||
, m_filename(ses_filename)
|
||||
, m_logfile(ses_file)
|
||||
, m_ses_id(ses_id)
|
||||
, m_service(service)
|
||||
{}
|
||||
|
||||
QlaFilterSession::~QlaFilterSession()
|
||||
{
|
||||
pcre2_match_data_free(m_mdata);
|
||||
// File should be closed and event data freed by now
|
||||
ss_dassert(m_logfile == NULL && m_event_data.has_message == false);
|
||||
}
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -345,95 +443,63 @@ MXS_END_DECLS
|
||||
static MXS_FILTER *
|
||||
createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE*) MXS_MALLOC(sizeof(QLA_INSTANCE));
|
||||
bool error = false;
|
||||
QlaInstance* my_instance = NULL;
|
||||
|
||||
if (my_instance)
|
||||
const char* keys[] = {PARAM_MATCH, PARAM_EXCLUDE};
|
||||
pcre2_code* re_match = NULL;
|
||||
pcre2_code* re_exclude = NULL;
|
||||
uint32_t ovec_size = 0;
|
||||
int cflags = config_get_enum(params, PARAM_OPTIONS, option_values);
|
||||
pcre2_code** code_arr[] = {&re_match, &re_exclude};
|
||||
if (config_get_compiled_regexes(params, keys, sizeof(keys) / sizeof(char*),
|
||||
cflags, &ovec_size, code_arr))
|
||||
{
|
||||
my_instance->name = MXS_STRDUP_A(name);
|
||||
my_instance->sessions = 0;
|
||||
my_instance->ovec_size = 0;
|
||||
my_instance->unified_fp = NULL;
|
||||
my_instance->unified_filename = NULL;
|
||||
my_instance->write_warning_given = false;
|
||||
|
||||
my_instance->source = config_copy_string(params, PARAM_SOURCE);
|
||||
my_instance->user_name = config_copy_string(params, PARAM_USER);
|
||||
|
||||
my_instance->filebase = MXS_STRDUP_A(config_get_string(params, PARAM_FILEBASE));
|
||||
my_instance->append = config_get_bool(params, PARAM_APPEND);
|
||||
my_instance->flush_writes = config_get_bool(params, PARAM_FLUSH);
|
||||
my_instance->log_file_data_flags = config_get_enum(params, PARAM_LOG_DATA, log_data_values);
|
||||
my_instance->log_mode_flags = config_get_enum(params, PARAM_LOG_TYPE, log_type_values);
|
||||
my_instance->query_newline = config_copy_string(params, PARAM_NEWLINE);
|
||||
my_instance->separator = config_copy_string(params, PARAM_SEPARATOR);
|
||||
|
||||
my_instance->match = config_copy_string(params, PARAM_MATCH);
|
||||
my_instance->exclude = config_copy_string(params, PARAM_EXCLUDE);
|
||||
my_instance->re_exclude = NULL;
|
||||
my_instance->re_match = NULL;
|
||||
bool error = false;
|
||||
|
||||
int cflags = config_get_enum(params, PARAM_OPTIONS, option_values);
|
||||
|
||||
const char* keys[] = {PARAM_MATCH, PARAM_EXCLUDE};
|
||||
pcre2_code** code_arr[] = {&my_instance->re_match, &my_instance->re_exclude};
|
||||
if (!config_get_compiled_regexes(params, keys, sizeof(keys) / sizeof(char*),
|
||||
cflags, &my_instance->ovec_size, code_arr))
|
||||
// The instance is allocated before opening the file since open_log_file() takes the instance as a
|
||||
// parameter. Will be fixed (or at least cleaned) with a later refactoring of functions/methods.
|
||||
my_instance = new (std::nothrow) QlaInstance(name, params);
|
||||
if (my_instance)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
|
||||
// Try to open the unified log file
|
||||
if (!error && (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED))
|
||||
{
|
||||
// First calculate filename length
|
||||
const char UNIFIED[] = ".unified";
|
||||
int namelen = strlen(my_instance->filebase) + sizeof(UNIFIED);
|
||||
char *filename = NULL;
|
||||
if ((filename = (char*)MXS_CALLOC(namelen, sizeof(char))) != NULL)
|
||||
my_instance->re_match = re_match;
|
||||
my_instance->re_exclude = re_exclude;
|
||||
my_instance->ovec_size = ovec_size;
|
||||
// Try to open the unified log file
|
||||
if (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED)
|
||||
{
|
||||
snprintf(filename, namelen, "%s.unified", my_instance->filebase);
|
||||
// Open the file. It is only closed at program exit
|
||||
my_instance->unified_fp = open_log_file(my_instance, my_instance->log_file_data_flags, filename);
|
||||
|
||||
if (my_instance->unified_fp == NULL)
|
||||
string unified_filename = my_instance->filebase + ".unified";
|
||||
// Open the file. It is only closed at program exit.
|
||||
FILE* unified_fp = open_log_file(my_instance, my_instance->log_file_data_flags,
|
||||
unified_filename.c_str());
|
||||
if (unified_fp != NULL)
|
||||
{
|
||||
MXS_FREE(filename);
|
||||
MXS_ERROR("Opening output file for qla-filter failed due to %d, %s",
|
||||
errno, mxs_strerror(errno));
|
||||
error = true;
|
||||
my_instance->unified_filename = unified_filename;
|
||||
my_instance->unified_fp = unified_fp;
|
||||
}
|
||||
else
|
||||
{
|
||||
my_instance->unified_filename = filename;
|
||||
MXS_ERROR("Opening output file for qla-filter failed due to %d, %s",
|
||||
errno, mxs_strerror(errno));
|
||||
delete my_instance;
|
||||
my_instance = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
else
|
||||
{
|
||||
MXS_FREE(my_instance->name);
|
||||
MXS_FREE(my_instance->match);
|
||||
pcre2_code_free(my_instance->re_match);
|
||||
MXS_FREE(my_instance->exclude);
|
||||
pcre2_code_free(my_instance->re_exclude);
|
||||
if (my_instance->unified_fp != NULL)
|
||||
{
|
||||
fclose(my_instance->unified_fp);
|
||||
}
|
||||
MXS_FREE(my_instance->filebase);
|
||||
MXS_FREE(my_instance->source);
|
||||
MXS_FREE(my_instance->user_name);
|
||||
MXS_FREE(my_instance->query_newline);
|
||||
MXS_FREE(my_instance->separator);
|
||||
MXS_FREE(my_instance);
|
||||
my_instance = NULL;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
pcre2_code_free(re_match);
|
||||
pcre2_code_free(re_exclude);
|
||||
}
|
||||
|
||||
return (MXS_FILTER *) my_instance;
|
||||
}
|
||||
|
||||
@ -449,70 +515,73 @@ createInstance(const char *name, char **options, MXS_CONFIG_PARAMETER *params)
|
||||
static MXS_FILTER_SESSION *
|
||||
newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session;
|
||||
const char *remote, *userName;
|
||||
// Need the following values before session constructor
|
||||
const char* remote = session_get_remote(session);
|
||||
const char* userName = session_get_user(session);
|
||||
pcre2_match_data* mdata = NULL;
|
||||
bool ses_active = true;
|
||||
string filename;
|
||||
FILE* session_file = NULL;
|
||||
// ---------------------------------------------------
|
||||
|
||||
if ((my_session = (QLA_SESSION*)MXS_CALLOC(1, sizeof(QLA_SESSION))) != NULL)
|
||||
QlaInstance *my_instance = (QlaInstance *) instance;
|
||||
bool error = false;
|
||||
|
||||
ss_dassert(userName && remote);
|
||||
if ((!my_instance->source.empty() && remote && my_instance->source != remote) ||
|
||||
(!my_instance->user_name.empty() && userName && my_instance->user_name != userName))
|
||||
{
|
||||
my_session->fp = NULL;
|
||||
my_session->match_data = NULL;
|
||||
my_session->filename = (char *)MXS_MALLOC(strlen(my_instance->filebase) + 20);
|
||||
const uint32_t ovec_size = my_instance->ovec_size;
|
||||
if (ovec_size)
|
||||
ses_active = false;
|
||||
}
|
||||
|
||||
if (my_instance->ovec_size > 0)
|
||||
{
|
||||
mdata = pcre2_match_data_create(my_instance->ovec_size, NULL);
|
||||
if (mdata == NULL)
|
||||
{
|
||||
my_session->match_data = pcre2_match_data_create(ovec_size, NULL);
|
||||
// Can this happen? Would require pcre2 to fail completely.
|
||||
MXS_ERROR("pcre2_match_data_create returned NULL.");
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!my_session->filename || (ovec_size && !my_session->match_data))
|
||||
// Only open the session file if the corresponding mode setting is used
|
||||
if (!error && ses_active && my_instance->log_mode_flags & CONFIG_FILE_SESSION)
|
||||
{
|
||||
std::stringstream filename_helper;
|
||||
filename_helper << my_instance->filebase << "." << session->ses_id;
|
||||
filename = filename_helper.str();
|
||||
|
||||
// Session numbers are not printed to session files
|
||||
uint32_t data_flags = (my_instance->log_file_data_flags & ~LOG_DATA_SESSION);
|
||||
|
||||
session_file = open_log_file(my_instance, data_flags, filename.c_str());
|
||||
if (session_file == NULL)
|
||||
{
|
||||
MXS_FREE(my_session->filename);
|
||||
pcre2_match_data_free(my_session->match_data);
|
||||
MXS_FREE(my_session);
|
||||
return NULL;
|
||||
MXS_ERROR("Opening output file for qla-filter failed due to %d, %s",
|
||||
errno, mxs_strerror(errno));
|
||||
error = true;
|
||||
}
|
||||
my_session->active = 1;
|
||||
}
|
||||
|
||||
remote = session_get_remote(session);
|
||||
userName = session_get_user(session);
|
||||
ss_dassert(userName && remote);
|
||||
|
||||
if ((my_instance->source && remote &&
|
||||
strcmp(remote, my_instance->source)) ||
|
||||
(my_instance->user_name && userName &&
|
||||
strcmp(userName, my_instance->user_name)))
|
||||
QlaFilterSession* my_session = NULL;
|
||||
if (!error)
|
||||
{
|
||||
my_session = new (std::nothrow) QlaFilterSession(userName, remote, ses_active, mdata,
|
||||
filename, session_file,
|
||||
session->ses_id, session->service->name);
|
||||
if (my_session == NULL)
|
||||
{
|
||||
my_session->active = 0;
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
my_session->user = userName;
|
||||
my_session->remote = remote;
|
||||
my_session->ses_id = session->ses_id;
|
||||
my_session->service = session->service->name;
|
||||
|
||||
sprintf(my_session->filename, "%s.%lu",
|
||||
my_instance->filebase,
|
||||
my_session->ses_id);
|
||||
|
||||
// Multiple sessions can try to update my_instance->sessions simultaneously
|
||||
atomic_add(&(my_instance->sessions), 1);
|
||||
|
||||
// Only open the session file if the corresponding mode setting is used
|
||||
if (my_session->active && (my_instance->log_mode_flags & CONFIG_FILE_SESSION))
|
||||
if (error)
|
||||
{
|
||||
pcre2_match_data_free(mdata);
|
||||
if (session_file)
|
||||
{
|
||||
uint32_t data_flags = (my_instance->log_file_data_flags &
|
||||
~LOG_DATA_SESSION); // No point printing "Session"
|
||||
my_session->fp = open_log_file(my_instance, data_flags, my_session->filename);
|
||||
|
||||
if (my_session->fp == NULL)
|
||||
{
|
||||
MXS_ERROR("Opening output file for qla-filter failed due to %d, %s",
|
||||
errno, mxs_strerror(errno));
|
||||
MXS_FREE(my_session->filename);
|
||||
pcre2_match_data_free(my_session->match_data);
|
||||
MXS_FREE(my_session);
|
||||
my_session = NULL;
|
||||
}
|
||||
fclose(session_file);
|
||||
}
|
||||
}
|
||||
return (MXS_FILTER_SESSION*)my_session;
|
||||
@ -529,13 +598,14 @@ newSession(MXS_FILTER *instance, MXS_SESSION *session)
|
||||
static void
|
||||
closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
||||
{
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
|
||||
if (my_session->active && my_session->fp)
|
||||
if (my_session->m_active && my_session->m_logfile)
|
||||
{
|
||||
fclose(my_session->fp);
|
||||
fclose(my_session->m_logfile);
|
||||
my_session->m_logfile = NULL;
|
||||
}
|
||||
clear(my_session->event_data);
|
||||
my_session->m_event_data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -547,12 +617,8 @@ closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
||||
static void
|
||||
freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
||||
{
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
|
||||
MXS_FREE(my_session->filename);
|
||||
pcre2_match_data_free(my_session->match_data);
|
||||
MXS_FREE(session);
|
||||
return;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
delete my_session;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -566,8 +632,7 @@ freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
||||
static void
|
||||
setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_DOWNSTREAM *downstream)
|
||||
{
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
my_session->down = *downstream;
|
||||
}
|
||||
|
||||
@ -582,7 +647,7 @@ setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_DOWNSTREAM
|
||||
static void
|
||||
setUpstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_UPSTREAM *upstream)
|
||||
{
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
my_session->up = *upstream;
|
||||
}
|
||||
|
||||
@ -596,7 +661,7 @@ setUpstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_UPSTREAM *ups
|
||||
* @param querylen Query string length
|
||||
* @param elapsed_ms Query execution time, in milliseconds
|
||||
*/
|
||||
void write_log_entries(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
void write_log_entries(QlaInstance* my_instance, QlaFilterSession* my_session,
|
||||
const char* date_string, const char* query, int querylen, int elapsed_ms)
|
||||
{
|
||||
bool write_error = false;
|
||||
@ -605,7 +670,7 @@ void write_log_entries(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
// In this case there is no need to write the session
|
||||
// number into the files.
|
||||
uint32_t data_flags = (my_instance->log_file_data_flags & ~LOG_DATA_SESSION);
|
||||
if (write_log_entry(my_session->fp, my_instance, my_session, data_flags,
|
||||
if (write_log_entry(my_session->m_logfile, my_instance, my_session, data_flags,
|
||||
date_string, query, querylen, elapsed_ms) < 0)
|
||||
{
|
||||
write_error = true;
|
||||
@ -624,7 +689,7 @@ void write_log_entries(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
{
|
||||
MXS_ERROR("qla-filter '%s': Log file write failed. "
|
||||
"Suppressing further similar warnings.",
|
||||
my_instance->name);
|
||||
my_instance->name.c_str());
|
||||
my_instance->write_warning_given = true;
|
||||
}
|
||||
}
|
||||
@ -642,18 +707,18 @@ void write_log_entries(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
static int
|
||||
routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
QlaInstance *my_instance = (QlaInstance *) instance;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
char *query = NULL;
|
||||
int query_len = 0;
|
||||
|
||||
if (my_session->active &&
|
||||
if (my_session->m_active &&
|
||||
modutil_extract_SQL(queue, &query, &query_len) &&
|
||||
mxs_pcre2_check_match_exclude(my_instance->re_match, my_instance->re_exclude,
|
||||
my_session->match_data, query, query_len, MXS_MODULE_NAME))
|
||||
my_session->m_mdata, query, query_len, MXS_MODULE_NAME))
|
||||
{
|
||||
const uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
LOG_EVENT_DATA& event = my_session->event_data;
|
||||
LogEventData& event = my_session->m_event_data;
|
||||
if (data_flags & LOG_DATA_DATE)
|
||||
{
|
||||
// Print current date to a buffer. Use the buffer in the event data struct even if execution time
|
||||
@ -671,7 +736,7 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
// receiving reply to previous query.
|
||||
if (event.has_message)
|
||||
{
|
||||
clear(event);
|
||||
event.clear();
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &event.begin_time);
|
||||
if (data_flags & LOG_DATA_QUERY)
|
||||
@ -701,9 +766,9 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
static int
|
||||
clientReply(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
LOG_EVENT_DATA& event = my_session->event_data;
|
||||
QlaInstance *my_instance = (QlaInstance *) instance;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) session;
|
||||
LogEventData& event = my_session->m_event_data;
|
||||
if (event.has_message)
|
||||
{
|
||||
const uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
@ -721,7 +786,7 @@ clientReply(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
(now.tv_nsec - event.begin_time.tv_nsec) / (double)1E6;
|
||||
write_log_entries(my_instance, my_session, event.query_date, query, query_len,
|
||||
std::floor(elapsed_ms + 0.5));
|
||||
clear(event);
|
||||
event.clear();
|
||||
}
|
||||
return my_session->up.clientReply(my_session->up.instance, my_session->up.session, queue);
|
||||
}
|
||||
@ -740,33 +805,33 @@ clientReply(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
static void
|
||||
diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) fsession;
|
||||
QlaInstance *my_instance = (QlaInstance *) instance;
|
||||
QlaFilterSession *my_session = (QlaFilterSession *) fsession;
|
||||
|
||||
if (my_session)
|
||||
{
|
||||
dcb_printf(dcb, "\t\tLogging to file %s.\n",
|
||||
my_session->filename);
|
||||
my_session->m_filename.c_str());
|
||||
}
|
||||
if (my_instance->source)
|
||||
if (!my_instance->source.empty())
|
||||
{
|
||||
dcb_printf(dcb, "\t\tLimit logging to connections from %s\n",
|
||||
my_instance->source);
|
||||
my_instance->source.c_str());
|
||||
}
|
||||
if (my_instance->user_name)
|
||||
if (!my_instance->user_name.empty())
|
||||
{
|
||||
dcb_printf(dcb, "\t\tLimit logging to user %s\n",
|
||||
my_instance->user_name);
|
||||
my_instance->user_name.c_str());
|
||||
}
|
||||
if (my_instance->match)
|
||||
if (!my_instance->match.empty())
|
||||
{
|
||||
dcb_printf(dcb, "\t\tInclude queries that match %s\n",
|
||||
my_instance->match);
|
||||
my_instance->match.c_str());
|
||||
}
|
||||
if (my_instance->exclude)
|
||||
if (!my_instance->exclude.empty())
|
||||
{
|
||||
dcb_printf(dcb, "\t\tExclude queries that match %s\n",
|
||||
my_instance->exclude);
|
||||
my_instance->exclude.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,34 +847,34 @@ diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb)
|
||||
*/
|
||||
static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESSION *fsession)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE*)instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION*)fsession;
|
||||
QlaInstance *my_instance = (QlaInstance*)instance;
|
||||
QlaFilterSession *my_session = (QlaFilterSession*)fsession;
|
||||
|
||||
json_t* rval = json_object();
|
||||
|
||||
if (my_session)
|
||||
{
|
||||
json_object_set_new(rval, "session_filename", json_string(my_session->filename));
|
||||
json_object_set_new(rval, "session_filename", json_string(my_session->m_filename.c_str()));
|
||||
}
|
||||
|
||||
if (my_instance->source)
|
||||
if (!my_instance->source.empty())
|
||||
{
|
||||
json_object_set_new(rval, PARAM_SOURCE, json_string(my_instance->source));
|
||||
json_object_set_new(rval, PARAM_SOURCE, json_string(my_instance->source.c_str()));
|
||||
}
|
||||
|
||||
if (my_instance->user_name)
|
||||
if (!my_instance->user_name.empty())
|
||||
{
|
||||
json_object_set_new(rval, PARAM_USER, json_string(my_instance->user_name));
|
||||
json_object_set_new(rval, PARAM_USER, json_string(my_instance->user_name.c_str()));
|
||||
}
|
||||
|
||||
if (my_instance->match)
|
||||
if (!my_instance->match.empty())
|
||||
{
|
||||
json_object_set_new(rval, PARAM_MATCH, json_string(my_instance->match));
|
||||
json_object_set_new(rval, PARAM_MATCH, json_string(my_instance->match.c_str()));
|
||||
}
|
||||
|
||||
if (my_instance->exclude)
|
||||
if (!my_instance->exclude.empty())
|
||||
{
|
||||
json_object_set_new(rval, PARAM_EXCLUDE, json_string(my_instance->exclude));
|
||||
json_object_set_new(rval, PARAM_EXCLUDE, json_string(my_instance->exclude.c_str()));
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -833,7 +898,7 @@ static uint64_t getCapabilities(MXS_FILTER* instance)
|
||||
* @param filename Target file path
|
||||
* @return A valid file on success, null otherwise.
|
||||
*/
|
||||
static FILE* open_log_file(QLA_INSTANCE *instance, uint32_t data_flags, const char *filename)
|
||||
static FILE* open_log_file(QlaInstance *instance, uint32_t data_flags, const char *filename)
|
||||
{
|
||||
bool file_existed = false;
|
||||
FILE *fp = NULL;
|
||||
@ -872,8 +937,8 @@ static FILE* open_log_file(QLA_INSTANCE *instance, uint32_t data_flags, const ch
|
||||
const char REPLY_TIME[] = "Reply_time";
|
||||
|
||||
std::stringstream header;
|
||||
const char* curr_sep = ""; // Use empty string as the first separator
|
||||
const char* real_sep = instance->separator;
|
||||
string curr_sep; // Use empty string as the first separator
|
||||
const string& real_sep = instance->separator;
|
||||
|
||||
if (data_flags & LOG_DATA_SERVICE)
|
||||
{
|
||||
@ -984,7 +1049,8 @@ static void print_string_replace_newlines(const char *sql_string,
|
||||
* @param elapsed_ms Query execution time, in milliseconds
|
||||
* @return The number of characters written, or a negative value on failure
|
||||
*/
|
||||
static int write_log_entry(FILE *logfile, QLA_INSTANCE *instance, QLA_SESSION *session, uint32_t data_flags,
|
||||
static int write_log_entry(FILE *logfile, QlaInstance *instance, QlaFilterSession *session,
|
||||
uint32_t data_flags,
|
||||
const char *time_string, const char *sql_string, size_t sql_str_len,
|
||||
int elapsed_ms)
|
||||
{
|
||||
@ -998,17 +1064,17 @@ static int write_log_entry(FILE *logfile, QLA_INSTANCE *instance, QLA_SESSION *s
|
||||
/* Printing to the file in parts would likely cause garbled printing if several threads write
|
||||
* simultaneously, so we have to first print to a string. */
|
||||
std::stringstream output;
|
||||
const char* curr_sep = ""; // Use empty string as the first separator
|
||||
const char* real_sep = instance->separator;
|
||||
string curr_sep; // Use empty string as the first separator
|
||||
const string& real_sep = instance->separator;
|
||||
|
||||
if (data_flags & LOG_DATA_SERVICE)
|
||||
{
|
||||
output << session->service;
|
||||
output << session->m_service;
|
||||
curr_sep = real_sep;
|
||||
}
|
||||
if (data_flags & LOG_DATA_SESSION)
|
||||
{
|
||||
output << curr_sep << session->ses_id;
|
||||
output << curr_sep << session->m_ses_id;
|
||||
curr_sep = real_sep;
|
||||
}
|
||||
if (data_flags & LOG_DATA_DATE)
|
||||
@ -1018,7 +1084,7 @@ static int write_log_entry(FILE *logfile, QLA_INSTANCE *instance, QLA_SESSION *s
|
||||
}
|
||||
if (data_flags & LOG_DATA_USER)
|
||||
{
|
||||
output << curr_sep << session->user << "@" << session->remote;
|
||||
output << curr_sep << session->m_user << "@" << session->m_remote;
|
||||
curr_sep = real_sep;
|
||||
}
|
||||
if (data_flags & LOG_DATA_REPLY_TIME)
|
||||
@ -1029,9 +1095,9 @@ static int write_log_entry(FILE *logfile, QLA_INSTANCE *instance, QLA_SESSION *s
|
||||
if (data_flags & LOG_DATA_QUERY)
|
||||
{
|
||||
output << curr_sep;
|
||||
if (*instance->query_newline)
|
||||
if (!instance->query_newline.empty())
|
||||
{
|
||||
print_string_replace_newlines(sql_string, sql_str_len, instance->query_newline, &output);
|
||||
print_string_replace_newlines(sql_string, sql_str_len, instance->query_newline.c_str(), &output);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1066,12 +1132,12 @@ static bool cb_log(const MODULECMD_ARG *argv, json_t** output)
|
||||
ss_dassert(argv->argv[0].type.type == MODULECMD_ARG_FILTER);
|
||||
|
||||
MXS_FILTER_DEF* filter = argv[0].argv->value.filter;
|
||||
QLA_INSTANCE* instance = reinterpret_cast<QLA_INSTANCE*>(filter_def_get_instance(filter));
|
||||
QlaInstance* instance = reinterpret_cast<QlaInstance*>(filter_def_get_instance(filter));
|
||||
bool rval = false;
|
||||
|
||||
if (instance->log_mode_flags & CONFIG_FILE_UNIFIED)
|
||||
{
|
||||
ss_dassert(instance->unified_fp && instance->unified_filename);
|
||||
ss_dassert(instance->unified_fp && !instance->unified_filename.empty());
|
||||
std::ifstream file(instance->unified_filename);
|
||||
|
||||
if (file)
|
||||
|
Loading…
x
Reference in New Issue
Block a user