#include "GlobalConfigFile.h" #include "LogFile.h" #include "SimpleLogFile.h" #include "MultiCast.h" void LogFile::scheduleShutdown() { Request *r = new Request(NULL); r->callbackId = mtShutDownProcess; _incoming.newRequest(r); while (true) sleep(100); } LogFile::LogFile() : ThreadClass("LogFile"), // getConfigItem: By default each log file will be named "log." followed // getConfigItem: by the time when the log file started in time_t format. // getConfigItem: The value of log_file_prefix is put in front of that, // getConfigItem: typically to set the directory. If log_file_suffix is // getConfigItem: is not blank we append a period then the value of // getConfigItem: log_file_suffix to the end of the file name. We // getConfigItem: typically use log_file_suffix when multiple programs // getConfigItem: are running in the same directory. _fileNamePrefix(getConfigItem("log_file_prefix", "Logs/")), _fileNameSuffix(getConfigItem("log_file_suffix")), _incoming("LogFile"), _showClose(true), _nextLogTime(time(NULL)) { sendString(TclList()<callbackId = mtQuit; _incoming.newRequest(r); waitForThread(); } class RequestWrite : public Request { public: std::string _toWrite; SocketInfo *_realSocket; time_t _time; RequestWrite() : Request(NULL) // These should not be deleted when a socket is closed. { } }; void LogFile::sendString(std::string toWrite, SocketInfo *socket, time_t t) { RequestWrite *r = new RequestWrite; r->callbackId = mtSendString; r->_toWrite = toWrite; r->_realSocket = socket; if (t) { r->_time = t; } else { time(&r->_time); } _incoming.newRequest(r); } void LogFile::updateNextLogTime() { // Start from the current time. time_t unixTime = time(NULL); struct tm parts; localtime_r(&unixTime, &parts); // Set time to midnight. parts.tm_sec = 0; parts.tm_min = 0; parts.tm_hour = 0; // Set date to tomorrow. The man page says this is explicitly legal. // mktime() will automatically convert jan 32 to feb 1, etc. parts.tm_mday++; // Do not specify day light savings time. Let mktime() figure that out on // its own. parts.tm_isdst = -1; // Switch the log files at midnight tomorrow morning. _nextLogTime = mktime(&parts); } static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static LogFile *primaryLogFile; LogFile &LogFile::primary() { if (!primaryLogFile) { pthread_mutex_lock(&mutex); if (!primaryLogFile) { primaryLogFile = new LogFile(); } pthread_mutex_unlock(&mutex); } return *primaryLogFile; } class SocketFinishedRequest : public Request { public: SocketInfo::SerialNumber serialNumber; SocketFinishedRequest() : Request(NULL) { } }; void LogFile::socketFinished(SocketInfo::SerialNumber serialNumber) { SocketFinishedRequest *r = new SocketFinishedRequest(); r->callbackId = mtSocketFinished; r->serialNumber = serialNumber; r->callbackId = mtSocketFinished; _incoming.newRequest(r); } void LogFile::ensureOpen(time_t eventTime) { // On a reset, we always use time when the reset was supposed to // happen. If the reset time was midnight, and the first event comes // at 5 minutes after midnight, we use midnight in the filename. // This gives us more regular file names, especially when multiple // servers are running at once. This could cause a problem if there // is no event for more than one day, however we don't expect that // case to happen. // When we first start, we use the time when the log object was // created. This is required so we don't stomp on previous logs if // we restart multiple times in a day. Of course, this is still // a problem if we restart more than once per second. // The suffix is put after the time, to give us a specific sort order. // This way we see all the logs in order of time. time_t fileTime = _nextLogTime; if (_output.is_open() && (eventTime >= _nextLogTime)) { _output.close(); updateNextLogTime(); } if (!_output.is_open()) { std::string fileName = _fileNamePrefix + "log." + itoa(fileTime); if (!_fileNameSuffix.empty()) { fileName += "." + _fileNameSuffix; } _output.open(fileName.c_str(), std::ios::out | std::ios::binary | std::ios::app); std::string status; if (!_output) status = errorString(); else { status = realpathString(fileName.c_str()); if (status.empty()) status = "realpath: " + errorString(); } MultiCast::getInstance().addInfo("log_file", status); } } void LogFile::threadFunction() { while (true) { while (Request *current = _incoming.getRequest()) { switch (current->callbackId) { case mtSendString: { RequestWrite *request = dynamic_cast(current); ensureOpen(request->_time); _output<_time)) <<' ' <_realSocket) <<' ' <_toWrite <<'\n'; break; } case mtShutDownProcess: { // Flush the log file. if (_output.is_open()) { _output.close(); } // Kill us quickly. Presumably there is a script ready to // restart us. Presumably we are dying because of some type // of trouble, and we want to be replaced ASAP by a good // process. _exit(10); } case mtQuit: { delete current; return; } case DeleteSocketThread::callbackId: { // We get a special call of our own. This ensures that we are // last. break; } case mtSocketFinished: { SocketFinishedRequest *request = dynamic_cast(current); time_t now = time(NULL); ensureOpen(now); if (_showClose) { _output<serialNumber <<" Socket_closed\n"; } break; } } delete current; } if (_output.is_open()) { _output.flush(); } _incoming.waitForRequest(); } } // From SimpleLogFile.h void sendToLogFile(TclList const &message) { LogFile::primary().sendString(message); } void scheduleSyncrhonizedShutdown() { LogFile::primary().scheduleShutdown(); }