From 90e5b80b7130d01b7c959db832e97fa0e7f93095 Mon Sep 17 00:00:00 2001 From: Esa Korhonen Date: Tue, 12 Feb 2019 17:47:17 +0200 Subject: [PATCH] MXS-2050 Use global counter to detect log rotation request Modules need to check the number regularly to detect a new log rotation. --- include/maxscale/log.hh | 17 +++++- server/core/log.cc | 22 ++++++++ server/modules/filter/qlafilter/qlafilter.cc | 55 +++++++++++++------- server/modules/filter/qlafilter/qlafilter.hh | 6 ++- 4 files changed, 77 insertions(+), 23 deletions(-) diff --git a/include/maxscale/log.hh b/include/maxscale/log.hh index 643275628..e4215c738 100644 --- a/include/maxscale/log.hh +++ b/include/maxscale/log.hh @@ -51,9 +51,24 @@ typedef MXB_LOG_THROTTLING MXS_LOG_THROTTLING; */ bool mxs_log_init(const char* ident, const char* logdir, mxs_log_target_t target); +/** + * Close and reopen MaxScale log files. Also increments a global rotation counter which modules + * can read to see if they should rotate their own logs. + * + * @return True if MaxScale internal logs were rotated. If false is returned, the rotation counter is not + * incremented. + */ +bool mxs_log_rotate(); + +/** + * Get the value of the log rotation counter. The counter is incremented when user requests a log rotation. + * + * @return Counter value + */ +int mxs_get_log_rotation_count(); + #define mxs_log_finish mxb_log_finish #define mxs_log_message mxb_log_message -#define mxs_log_rotate mxb_log_rotate #define mxs_log_get_throttling mxb_log_get_throttling #define mxs_log_is_priority_enabled mxb_log_is_priority_enabled diff --git a/server/core/log.cc b/server/core/log.cc index 7e14a8d89..2bfddf4c0 100644 --- a/server/core/log.cc +++ b/server/core/log.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -28,6 +29,12 @@ namespace { +struct ThisUnit +{ + std::atomic rotation_count {0}; +}; +ThisUnit this_unit; + const char* LOGFILE_NAME = "maxscale.log"; size_t mxs_get_context(char* buffer, size_t len) @@ -129,3 +136,18 @@ json_t* mxs_logs_to_json(const char* host) return mxs_json_resource(host, MXS_JSON_API_LOGS, data); } + +bool mxs_log_rotate() +{ + bool rotated = mxb_log_rotate(); + if (rotated) + { + this_unit.rotation_count.fetch_add(1, std::memory_order_relaxed); + } + return rotated; +} + +int mxs_get_log_rotation_count() +{ + return this_unit.rotation_count.load(std::memory_order_relaxed); +} \ No newline at end of file diff --git a/server/modules/filter/qlafilter/qlafilter.cc b/server/modules/filter/qlafilter/qlafilter.cc index 2e79f445f..cd7609558 100644 --- a/server/modules/filter/qlafilter/qlafilter.cc +++ b/server/modules/filter/qlafilter/qlafilter.cc @@ -99,14 +99,6 @@ const MXS_ENUM_VALUE log_data_values[] = void print_string_replace_newlines(const char* sql_string, size_t sql_str_len, const char* rep_newline, std::stringstream* output); -/** - * Open a file if it doesn't exist. - * - * @param filename Filename - * @param ppFile Double pointer to old file. The file can be null. - * @return True if new file was opened successfully. False, if file already existed or if new file - * could not be opened. If false is returned, the caller should check that the file object exists. - */ bool check_replace_file(const string& filename, FILE** ppFile); } @@ -115,6 +107,7 @@ QlaInstance::QlaInstance(const string& name, MXS_CONFIG_PARAMETER* params) : m_settings(params) , m_name(name) , m_session_data_flags(m_settings.log_file_data_flags & ~LOG_DATA_SESSION) + , m_rotation_count(mxs_get_log_rotation_count()) { } @@ -151,6 +144,7 @@ QlaFilterSession::QlaFilterSession(QlaInstance& instance, MXS_SESSION* session) , m_remote(session_get_remote(session)) , m_service(session->service->name()) , m_ses_id(session->ses_id) + , m_rotation_count(mxs_get_log_rotation_count()) { } @@ -374,22 +368,29 @@ json_t* QlaInstance::diagnostics_json() const return rval; } -void QlaFilterSession::check_session_log_rotation() +void QlaInstance::check_reopen_file(const string& filename, uint64_t data_flags, FILE** ppFile) const { - if (check_replace_file(m_filename, &m_logfile)) + if (check_replace_file(filename, ppFile)) { + auto fp = *ppFile; // New file created, print the log header. - string header = m_instance.generate_log_header(m_instance.m_session_data_flags); - if (!m_instance.write_to_logfile(m_logfile, header)) + string header = generate_log_header(data_flags); + if (!write_to_logfile(fp, header)) { - MXS_ERROR(HEADER_ERROR, m_filename.c_str(), errno, mxs_strerror(errno)); - fclose(m_logfile); - m_logfile = nullptr; + MXS_ERROR(HEADER_ERROR, filename.c_str(), errno, mxs_strerror(errno)); + fclose(fp); + fp = nullptr; + *ppFile = fp; } } // Either the old file existed or file creation failed. } +void QlaInstance::check_reopen_session_file(const std::string& filename, FILE** ppFile) const +{ + check_reopen_file(filename, m_session_data_flags, ppFile); +} + /** * Write QLA log entry/entries to disk * @@ -397,13 +398,13 @@ void QlaFilterSession::check_session_log_rotation() */ void QlaFilterSession::write_log_entries(const LogEventElems& elems) { - const int check_interval = 60; // Check log rotation once per minute. if (m_instance.m_settings.write_session_log) { - if (m_file_check_timer.split().secs() > check_interval) + int global_rot_count = mxs_get_log_rotation_count(); + if (global_rot_count > m_rotation_count) { - check_session_log_rotation(); - m_file_check_timer.restart(); + m_rotation_count = global_rot_count; + m_instance.check_reopen_session_file(m_filename, &m_logfile); } if (m_logfile) @@ -703,7 +704,13 @@ void QlaFilterSession::write_session_log_entry(const string& entry) void QlaInstance::write_unified_log_entry(const string& entry) { std::lock_guard guard(m_file_lock); - // TODO: Handle log rotation here. + int global_rot_count = mxs_get_log_rotation_count(); + if (global_rot_count > m_rotation_count) + { + m_rotation_count = global_rot_count; + check_reopen_file(m_unified_filename, m_settings.log_file_data_flags, &m_unified_fp); + } + if (m_unified_fp) { if (!write_to_logfile(m_unified_fp, entry)) @@ -773,6 +780,14 @@ void print_string_replace_newlines(const char* sql_string, } } +/** + * Open a file if it doesn't exist. + * + * @param filename Filename + * @param ppFile Double pointer to old file. The file can be null. + * @return True if new file was opened successfully. False, if file already existed or if new file + * could not be opened. If false is returned, the caller should check that the file object exists. + */ bool check_replace_file(const string& filename, FILE** ppFile) { auto zfilename = filename.c_str(); diff --git a/server/modules/filter/qlafilter/qlafilter.hh b/server/modules/filter/qlafilter/qlafilter.hh index 978be6b06..24f55bb42 100644 --- a/server/modules/filter/qlafilter/qlafilter.hh +++ b/server/modules/filter/qlafilter/qlafilter.hh @@ -86,6 +86,7 @@ public: std::string generate_log_header(uint64_t data_flags) const; FILE* open_session_log_file(const std::string& filename) const; + void check_reopen_session_file(const std::string& filename, FILE** ppFile) const; void write_unified_log_entry(const std::string& contents); bool write_to_logfile(FILE* fp, const std::string& contents) const; @@ -121,10 +122,12 @@ public: private: bool open_unified_logfile(); FILE* open_log_file(uint64_t data_flags, const std::string& filename) const; + void check_reopen_file(const std::string& filename, uint64_t data_flags, FILE** ppFile) const; std::mutex m_file_lock; /* Protects access to the unified log file */ std::string m_unified_filename; /* Filename of the unified log file */ FILE* m_unified_fp {nullptr}; /* Unified log file. */ + int m_rotation_count {0}; /* Log rotation counter */ bool m_write_error_logged {false}; /* Avoid repeatedly printing some errors/warnings. */ }; @@ -183,7 +186,7 @@ private: pcre2_match_data* m_mdata {nullptr}; /* Regex match data */ FILE* m_logfile {nullptr}; /* The session-specific log file */ - mxb::StopWatch m_file_check_timer; /* When was file checked for rotation */ + int m_rotation_count {0}; /* Log rotation counter */ bool m_write_error_logged {false}; /* Has write error been logged */ /** @@ -223,7 +226,6 @@ private: LogEventData m_event_data; /* Information about the latest event, used if logging execution time. */ - void check_session_log_rotation(); void write_log_entries(const LogEventElems& elems); void write_session_log_entry(const std::string& entry); std::string generate_log_entry(uint64_t data_flags, const LogEventElems& elems) const;