#include "Log.h" #include "LogListener.h" #include #include #include #include #include namespace coreutils { LogListener *Log::logListener = NULL; File *Log::logFile = NULL; int Log::seq = 0; std::mutex Log::mtx; size_t Log::maxLogSize = 100 * 1024 * 1024; int Log::maxLogBackups = 5; Log::Log(LogListener *logListener) { this->logListener = logListener; } Log::Log(File *logFile) { this->logFile = logFile; } Log::Log(int level) { output = true; std::stringstream temp; struct timespec timex; clock_gettime(CLOCK_REALTIME, &timex); temp << timex.tv_sec << "." << std::setfill('0') << std::setw(9) << timex.tv_nsec; *this << temp.str(); *this << " "; *this << "[" << syscall(SYS_gettid) << "]"; switch (level) { case LOG_NONE: *this << "[NONE] :"; break; case LOG_INFO: *this << "[INFO] :"; break; case LOG_WARN: *this << "[WARN] :"; break; case LOG_EXCEPT: *this << "[EXCEPT]: "; break; case LOG_DEBUG_1: *this << "[DEBUG1]: "; break; case LOG_DEBUG_2: *this << "[DEBUG2]: "; break; case LOG_DEBUG_3: *this << "[DEBUG3]: "; break; case LOG_DEBUG_4: *this << "[DEBUG4]: "; break; default: *this << "[?] ?"; break; }; } Log::~Log() { if (output) { std::stringstream out; out << this->str() << std::endl; std::string text = out.str(); mtx.lock(); if (logFile) { logFile->write(text); if (maxLogSize > 0 && logFile->currentSize() >= maxLogSize) logFile->rotate(maxLogBackups); } // std::cout << text; ++seq; mtx.unlock(); // Called AFTER releasing mtx - a real, confirmed (via gdb thread // dump on a hung production BARANTMail) lock-order inversion: // ConsoleServer::logSend() takes TCPServer's own `lock` // (recursive_mutex) to echo this line to connected console // sessions, but TCPServer::connectionRequest() holds that SAME // lock for the whole span of accepting+TLS-handshaking a new // connection - during which another Log() call (e.g. from TLS's // own handshake-complete callback, on a DIFFERENT thread) needs // mtx. One thread held mtx and waited on TCPServer::lock; another // held TCPServer::lock and waited on mtx - neither the // recursive_mutex fix (which only helps the SAME thread re-enter // TCPServer::lock) nor anything else here prevented it, since // that's a same-thread-only guarantee and this is a two-thread, // two-different-mutex inversion. logListener->logSend() only // relays an already-built string and touches no other Log state, // so it doesn't need mtx held at all. if (logListener) logListener->logSend(text); } } }