From 1f66846bfd5e6ac93e732d36b87077cb4afed553 Mon Sep 17 00:00:00 2001 From: Alex Zolotarev Date: Mon, 6 Jul 2015 19:13:20 +0300 Subject: [PATCH] [alohalytics] Correct logrotate support in FastCGI server on SIGHUP and SIGUSR1 --- 3party/Alohalytics/server/fcgi_server.cc | 55 ++++++++++++------- .../Alohalytics/server/statistics_receiver.h | 3 + 3party/Alohalytics/src/messages_queue.h | 15 +++++ 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/3party/Alohalytics/server/fcgi_server.cc b/3party/Alohalytics/server/fcgi_server.cc index d1daaaa72f..3ee52ea799 100644 --- a/3party/Alohalytics/server/fcgi_server.cc +++ b/3party/Alohalytics/server/fcgi_server.cc @@ -73,7 +73,6 @@ http { */ // clang-format on -#include #include #include #include @@ -105,28 +104,29 @@ void Reply200OKWithBody(FCGX_Stream * out, const string & body) { body.c_str()); } -// We need this global variable to reopen log file from SIGHUP handler. -struct CoutToFileRedirector; -CoutToFileRedirector * gRedirector = nullptr; +// Global variables to correctly reopen data and log files after signals from logrotate utility. +volatile sig_atomic_t gReceivedSIGHUP = 0; +volatile sig_atomic_t gReceivedSIGUSR1 = 0; // Redirects all cout output into a file if good log_file_path was given in constructor. // Can always ReopenLogFile() if needed (e.g. for log rotation). struct CoutToFileRedirector { char const * path; unique_ptr log_file; streambuf * original_cout_buf; + CoutToFileRedirector(const char * log_file_path) : path(log_file_path), original_cout_buf(cout.rdbuf()) { ReopenLogFile(); } void ReopenLogFile() { - if (path) { - log_file.reset(nullptr); - log_file.reset(new ofstream(path, ofstream::out | ofstream::app)); - if (log_file->good()) { - cout.rdbuf(log_file->rdbuf()); - gRedirector = this; - } else { - ATLOG("ERROR: Can't open log file", path, "for writing."); - } + if (!path) { + return; + } + log_file.reset(nullptr); + log_file.reset(new ofstream(path, ofstream::out | ofstream::app)); + if (log_file->good()) { + cout.rdbuf(log_file->rdbuf()); + } else { + ATLOG("ERROR: Can't open log file", path, "for writing."); } } // Restore original cout streambuf. @@ -135,24 +135,31 @@ struct CoutToFileRedirector { int main(int argc, char * argv[]) { if (argc < 2) { - ATLOG("Usage:", argv[0], " "); + ALOG("Usage:", argv[0], " "); + ALOG(" - SIGHUP reopens main data file and SIGUSR1 reopens debug log file for logrotate utility."); return -1; } // Redirect cout into a file if it was given in the command line. - const CoutToFileRedirector log_redirector(argc > 2 ? argv[2] : nullptr); - // Reopen log file on SIGHUP without server restart. - ::signal(SIGHUP, [](int) { gRedirector->ReopenLogFile(); }); + CoutToFileRedirector log_redirector(argc > 2 ? argv[2] : nullptr); + // Correctly reopen data file on SIGHUP for logrotate. + if (SIG_ERR == ::signal(SIGHUP, [](int) { gReceivedSIGHUP = SIGHUP; })) { + ATLOG("WARNING: Can't set SIGHUP handler. Logrotate will not work correctly."); + } + // Correctly reopen debug log file on SIGUSR1 for logrotate. + if (SIG_ERR == ::signal(SIGUSR1, [](int) { gReceivedSIGUSR1 = SIGUSR1; })) { + ATLOG("WARNING: Can't set SIGUSR1 handler. Logrotate will not work correctly."); + } int result = FCGX_Init(); if (0 != result) { - ATLOG("ERROR: FCGX_Init has failed with code", result); + ALOG("ERROR: FCGX_Init has failed with code", result); return result; } FCGX_Request request; result = FCGX_InitRequest(&request, 0, FCGI_FAIL_ACCEPT_ON_INTR); if (0 != result) { - ATLOG("ERROR: FCGX_InitRequest has failed with code", result); + ALOG("ERROR: FCGX_InitRequest has failed with code", result); return result; } alohalytics::StatisticsReceiver receiver(argv[1]); @@ -163,6 +170,16 @@ int main(int argc, char * argv[]) { const char * user_agent_str; ATLOG("FastCGI Server instance is ready to serve clients' requests."); while (FCGX_Accept_r(&request) >= 0) { + // Correctly reopen data file in the queue. + if (gReceivedSIGHUP == SIGHUP) { + receiver.ReopenDataFile(); + gReceivedSIGHUP = 0; + } + // Correctly reopen debug log file. + if (gReceivedSIGUSR1 == SIGUSR1) { + log_redirector.ReopenLogFile(); + gReceivedSIGUSR1 = 0; + } FCGX_FPrintF(request.out, "Status: 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %ld\r\n\r\n%s\n", kBodyTextForGoodServerReply.size(), kBodyTextForGoodServerReply.c_str()); try { diff --git a/3party/Alohalytics/server/statistics_receiver.h b/3party/Alohalytics/server/statistics_receiver.h index c5b1453ae9..434c7a4098 100644 --- a/3party/Alohalytics/server/statistics_receiver.h +++ b/3party/Alohalytics/server/statistics_receiver.h @@ -83,6 +83,9 @@ class StatisticsReceiver { } file_storage_queue_.PushMessage(out_stream.str()); } + + // Correct logrotate utility support for queue's file. + void ReopenDataFile() { file_storage_queue_.LogrotateCurrentFile(); } }; } // namespace alohalytics diff --git a/3party/Alohalytics/src/messages_queue.h b/3party/Alohalytics/src/messages_queue.h index f28a3b99b3..ced2c0b4b2 100644 --- a/3party/Alohalytics/src/messages_queue.h +++ b/3party/Alohalytics/src/messages_queue.h @@ -117,6 +117,13 @@ class MessagesQueue final { commands_condition_variable_.notify_all(); } + // This may be needed for correct logrotate utility support on *nix systems. + void LogrotateCurrentFile() { + std::lock_guard lock(commands_mutex_); + commands_queue_.push_back(std::bind(&MessagesQueue::ProcessLogrotateCurrentFileCommand, this)); + commands_condition_variable_.notify_all(); + } + private: // Returns full path to unique, non-existing file in the given directory. // Uses default extension for easier archives scan later. @@ -227,6 +234,14 @@ class MessagesQueue final { } } + void ProcessLogrotateCurrentFileCommand() { + // Here we simply reopen the file. It should be already moved by logrotate. + current_file_.reset(nullptr); + current_file_.reset( + new std::ofstream(storage_directory_ + kCurrentFileName, std::ios_base::app | std::ios_base::binary)); + // TODO(AlexZ): Should we check for possible reopen errors? + } + void WorkerThread() { TCommand command_to_execute; while (true) {