diff --git a/logger.cpp b/logger.cpp index 3ac62f0..2928851 100644 --- a/logger.cpp +++ b/logger.cpp @@ -20,15 +20,65 @@ License along with FlashMQ. If not, see . #include #include #include +#include #include "exceptions.h" +#include "utils.h" Logger *Logger::instance = nullptr; std::string Logger::logPath = ""; +LogLine::LogLine(std::string &&line, bool alsoToStdOut) : + line(line), + alsoToStdOut(alsoToStdOut) +{ + +} + +LogLine::LogLine(const char *s, size_t len, bool alsoToStdOut) : + line(s, len), + alsoToStdOut(alsoToStdOut) +{ + +} + +LogLine::LogLine() : + alsoToStdOut(true) +{ + +} + +const char *LogLine::c_str() const +{ + return line.c_str(); +} + +bool LogLine::alsoLogToStdOut() const +{ + return alsoToStdOut; +} + Logger::Logger() { + memset(&linesPending, 1, sizeof(sem_t)); + sem_init(&linesPending, 0, 0); + + auto f = std::bind(&Logger::writeLog, this); + this->writerThread = std::thread(f, this); + + pthread_t native = this->writerThread.native_handle(); + pthread_setname_np(native, "LogWriter"); +} + +Logger::~Logger() +{ + if (file) + { + fclose(file); + file = nullptr; + } + sem_close(&linesPending); } std::string Logger::getLogLevelString(int level) const @@ -71,10 +121,14 @@ void Logger::logf(int level, const char *str, ...) va_end(valist); } -void Logger::reOpen() +void Logger::queueReOpen() { - std::lock_guard locker(logMutex); + reload = true; + sem_post(&linesPending); +} +void Logger::reOpen() +{ if (file) { fclose(file); @@ -86,8 +140,7 @@ void Logger::reOpen() if ((file = fopen(logPath.c_str(), "a")) == nullptr) { - std::string msg(strerror(errno)); - throw ConfigFileException("Error opening logfile: " + msg); + logf(LOG_ERR, "(Re)opening log file '%s' error: %s. Logging to stdout.", logPath.c_str(), strerror(errno)); } } @@ -106,8 +159,6 @@ void Logger::setLogPath(const std::string &path) void Logger::setFlags(bool logDebug, bool logSubscriptions) { - std::lock_guard locker(logMutex); - if (logDebug) curLogLevel |= LOG_DEBUG; else @@ -119,13 +170,64 @@ void Logger::setFlags(bool logDebug, bool logSubscriptions) curLogLevel &= ~(LOG_UNSUBSCRIBE & LOG_SUBSCRIBE); } +void Logger::quit() +{ + running = false; + sem_post(&linesPending); + writerThread.join(); +} + +void Logger::writeLog() +{ + LogLine line; + while(running) + { + sem_wait(&linesPending); + + if (reload) + { + reOpen(); + } + + { + std::lock_guard locker(logMutex); + + if (lines.empty()) + continue; + + line = std::move(lines.front()); + lines.pop(); + } + + if (this->file) + { + if (fputs(line.c_str(), this->file) < 0 || + fputs("\n", this->file) < 0 || + fflush(this->file) != 0) + { + alsoLogToStd = true; + fputs("Writing to log failed. Enabling stdout logger.", stderr); + } + } + + if (!this->file || line.alsoLogToStdOut()) + { + FILE *output = stdout; +#ifdef TESTING + output = stderr; // the stdout interfers with Qt test XML output, so using stderr. +#endif + fputs(line.c_str(), output); + fputs("\n", output); + fflush(output); + } + } +} + void Logger::logf(int level, const char *str, va_list valist) { if ((level & curLogLevel) == 0) return; - std::lock_guard locker(logMutex); - time_t time = std::time(nullptr); struct tm tm = *std::localtime(&time); std::ostringstream oss; @@ -136,28 +238,21 @@ void Logger::logf(int level, const char *str, va_list valist) const std::string s = oss.str(); const char *logfmtstring = s.c_str(); - if (this->file) - { - va_list valist2; - va_copy(valist2, valist); - vfprintf(this->file, logfmtstring, valist2); - fprintf(this->file, "\n"); - fflush(this->file); - va_end(valist2); - } + char buf[512]; + + va_list valist2; + va_copy(valist2, valist); + vsnprintf(buf, 512, logfmtstring, valist); + size_t len = strlen(buf); + LogLine line(buf, len, alsoLogToStd); + va_end(valist2); - if (!this->file || alsoLogToStd) { -#ifdef TESTING - vfprintf(stderr, logfmtstring, valist); // the stdout interfers with Qt test XML output, so using stderr. - fprintf(stderr, "\n"); - fflush(stderr); -#else - vfprintf(stdout, logfmtstring, valist); - fprintf(stdout, "\n"); - fflush(stdout); -#endif + std::lock_guard locker(logMutex); + lines.push(std::move(line)); } + + sem_post(&linesPending); } int logSslError(const char *str, size_t len, void *u) diff --git a/logger.h b/logger.h index 1819d4f..579bf28 100644 --- a/logger.h +++ b/logger.h @@ -21,33 +21,64 @@ License along with FlashMQ. If not, see . #include #include #include +#include +#include +#include "semaphore.h" #include "flashmq_plugin.h" int logSslError(const char *str, size_t len, void *u); +class LogLine +{ + std::string line; + bool alsoToStdOut; + +public: + LogLine(std::string &&line, bool alsoToStdOut); + LogLine(const char *s, size_t len, bool alsoToStdOut); + LogLine(); + LogLine(const LogLine &other) = delete; + LogLine(LogLine &&other) = default; + LogLine &operator=(LogLine &&other) = default; + + const char *c_str() const; + bool alsoLogToStdOut() const; +}; + class Logger { static Logger *instance; static std::string logPath; int curLogLevel = LOG_ERR | LOG_WARNING | LOG_NOTICE | LOG_INFO | LOG_SUBSCRIBE | LOG_UNSUBSCRIBE ; std::mutex logMutex; + std::queue lines; + sem_t linesPending; + std::thread writerThread; + bool running = true; FILE *file = nullptr; bool alsoLogToStd = true; + bool reload = false; Logger(); + ~Logger(); std::string getLogLevelString(int level) const; + void reOpen(); + void writeLog(); public: static Logger *getInstance(); void logf(int level, const char *str, va_list args); void logf(int level, const char *str, ...); - void reOpen(); + + void queueReOpen(); void noLongerLogToStd(); void setLogPath(const std::string &path); void setFlags(bool logDebug, bool logSubscriptions); + void quit(); + }; #endif // LOGGER_H diff --git a/main.cpp b/main.cpp index 5a4c237..3d4cb48 100644 --- a/main.cpp +++ b/main.cpp @@ -93,6 +93,7 @@ int main(int argc, char *argv[]) logger->logf(LOG_NOTICE, "Starting FlashMQ version %s, debug build %s.", FLASHMQ_VERSION, sse.c_str()); #endif mainApp->start(); + logger->quit(); } catch (ConfigFileException &ex) { diff --git a/mainapp.cpp b/mainapp.cpp index 05facff..0547c32 100644 --- a/mainapp.cpp +++ b/mainapp.cpp @@ -643,7 +643,7 @@ void MainApp::loadConfig() listeners = settings->listeners; logger->setLogPath(settings->logPath); - logger->reOpen(); + logger->queueReOpen(); logger->setFlags(settings->logDebug, settings->logSubscriptions); setlimits();