forked from organicmaps/organicmaps
[alohalytics] Correct logrotate support in FastCGI server on SIGHUP and SIGUSR1
This commit is contained in:
parent
3895bfbbf4
commit
1f66846bfd
3 changed files with 54 additions and 19 deletions
|
@ -73,7 +73,6 @@ http {
|
|||
*/
|
||||
// clang-format on
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
|
@ -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<ofstream> 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], "<directory to store received data> <path to error log file>");
|
||||
ALOG("Usage:", argv[0], "<directory to store received data> <path to error log file>");
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<std::mutex> 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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue