/****************************************************************************** * File - Logger.h * Author - Joey Pollack * Date - 2019/06/24 (y/m/d) * Mod Date - 2019/06/26 (y/m/d) * Description - Main interface to the logging system. Manages a linked * list of built-in and user-defined listerers. ******************************************************************************/ #include "Logger.h" #include #include #include #include #include namespace Lunarium { //namespace LogLevel //{ // bool LevelIsSet(uint16_t buf, uint16_t mask) // { // return buf & mask; // } // std::string FormatLevel(uint16_t level) // { // if (LevelIsSet(level, FATAL_ERROR)) // { // return "[FATAL ERROR] "; // } // else if (LevelIsSet(level, ERROR)) // { // return "[ERROR] "; // } // else if (LevelIsSet(level, WARNING)) // { // return "[WARNING] "; // } // else if (LevelIsSet(level, INFO)) // { // return "[INFO] "; // } // else if (LevelIsSet(level, INFO_VERBOSE)) // { // return "[INFO_VERBOSE] "; // } // else if (LevelIsSet(level, OGL_DEBUG)) // { // return "[OGL_DEBUG] "; // } // else // { // return "[UNKNOWN] "; // } // } //} //namespace LogCategory //{ // bool CategoryIsSet(uint16_t buf, uint16_t mask) // { // return buf & mask; // } // std::string FormatCategory(uint16_t level) // { // if (CategoryIsSet(level, CORE)) // { // return "[CORE] "; // } // else if (CategoryIsSet(level, WINDOW_CONTROLLER)) // { // return "[WINDOW CONTROLLER] "; // } // else if (CategoryIsSet(level, GRAPHICS)) // { // return "[GRAPHICS] "; // } // else if (CategoryIsSet(level, UTILITIES)) // { // return "[UTILITIES] "; // } // else if (CategoryIsSet(level, USER_PROJECT)) // { // return "[USER PROJECT] "; // } // else // { // return "[UNKNOWN] "; // } // } //} LogListener::LogListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName) : mAcceptedLogLevels(acceptedLogLevels), mAcceptedLogCategories(acceptedLogCategories), mMyName(myName) { } const std::string & LogListener::GetName() const { return mMyName; } bool LogListener::CategoryIsSet(uint32_t id) const { return mAcceptedLogCategories & id; } bool LogListener::LevelIsSet(uint32_t id) const { return mAcceptedLogLevels & id; } // //////////////////////////////////////////////////////////////////////// // ======================================================================== // Standard Listener // ======================================================================== // //////////////////////////////////////////////////////////////////////// StandardListener::StandardListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName) : LogListener(acceptedLogLevels, acceptedLogCategories, myName) { } bool StandardListener::Log(LogMessage& message) { if (!LevelIsSet(message.LogLevel) || !CategoryIsSet(message.LogCategory)) return false; std::cout << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory) << Logger::GetLevelName(message.LogLevel) << message.Message; return true; } // //////////////////////////////////////////////////////////////////////// // ======================================================================== // Error Listener // ======================================================================== // //////////////////////////////////////////////////////////////////////// ErrorListener::ErrorListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName) : LogListener(acceptedLogLevels, acceptedLogCategories, myName) { } bool ErrorListener::Log(LogMessage& message) { if (!LevelIsSet(message.LogLevel) || !CategoryIsSet(message.LogCategory)) return false; std::cerr << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory) << Logger::GetLevelName(message.LogLevel) << message.Message; return true; } // //////////////////////////////////////////////////////////////////////// // ======================================================================== // File Listener // ======================================================================== // //////////////////////////////////////////////////////////////////////// FileListener::FileListener(std::ofstream & of, uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName) : mOF(of), LogListener(acceptedLogLevels, acceptedLogCategories, myName) { } bool FileListener::Log(LogMessage& message) { if (!LevelIsSet(message.LogLevel) || !CategoryIsSet(message.LogCategory)) return false; mOF << std::endl << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory) << Logger::GetLevelName(message.LogLevel) << message.Message; return true; } // //////////////////////////////////////////////////////////////////////// // ======================================================================== // Main Logger // ======================================================================== // //////////////////////////////////////////////////////////////////////// Logger* Logger::mInstance = nullptr; uint32_t Logger::mNextCategoryID = 0x01; uint32_t Logger::mNextLevelID = 0x01; std::map Logger::mCategoryNames; std::map Logger::mLevelNames; std::string Logger::TimeStamp() { time_t t = time(0); struct tm now; localtime_s(&now, &t); std::ostringstream oss(""); oss << "[" << (now.tm_year + 1900) << "/"; oss << std::setfill('0') << std::setw(2); oss << (now.tm_mon + 1) << "/" << std::setw(2) << now.tm_mday << " -- " << std::setw(2) << now.tm_hour << ":" << std::setw(2) << now.tm_min << ":" << std::setw(2) << now.tm_sec << "] "; return oss.str(); } Logger::Logger() { // Register built-in levels RegisterLevel("INFO"); RegisterLevel("WARNING"); RegisterLevel("ERROR"); RegisterLevel("FATAL_ERROR"); RegisterLevel("INFO_VERBOSE"); RegisterLevel("OGL_DEBUG"); RegisterCategory("CORE"); RegisterCategory("WINDOW_CONTROLLER"); RegisterCategory("GRAPHICS"); RegisterCategory("UTILITIES"); } Logger * Logger::GetInstance() { if (!Logger::mInstance) { Logger::mInstance = new Logger; Logger::mInstance->mTimer.Reset(); } return Logger::mInstance; } void Logger::FreeInstance() { delete mInstance; mInstance = nullptr; Logger::mNextCategoryID = 0x01; Logger::mNextLevelID = 0x01; Logger::mCategoryNames.clear(); Logger::mLevelNames.clear(); } uint32_t Logger::RegisterCategory(const char * name) { Logger::mCategoryNames[mNextCategoryID] = name; uint32_t id = mNextCategoryID; mNextCategoryID = mNextCategoryID << 1; return id; } uint32_t Logger::RegisterLevel(const char * name) { Logger::mLevelNames[mNextLevelID] = name; uint32_t id = mNextLevelID; mNextLevelID = mNextLevelID << 1; return id; } std::string Logger::GetCategoryName(uint32_t id) { if (Logger::mCategoryNames.find(id) == Logger::mCategoryNames.end()) return "[]"; std::string name = "["; name += Logger::mCategoryNames[id]; name += "] "; return name; } std::string Logger::GetLevelName(uint32_t id) { if (Logger::mLevelNames.find(id) == Logger::mLevelNames.end()) return "[]"; std::string name = "["; name += Logger::mLevelNames[id]; name += "] "; return name; } LogListener* Logger::AddListener(LogListener * pl) { mListeners.push_back(pl); return pl; } LogListener* Logger::RemoveListener(LogListener * pl) { mListeners.remove(pl); return pl; } LogListener* Logger::RemoveListener(const char * withName) { LogListener* pl = nullptr; for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++) { if ((*iter)->GetName() == withName) { pl = (*iter); break; } } mListeners.remove(pl); return pl; } void Logger::FreeAllListeners() { for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++) { delete (*iter); } mListeners.clear(); } void Logger::ClearBacklog() { mBacklog.clear(); } void Logger::Log(uint32_t logCategory, uint32_t logLevel, const char * message, ...) { // clear the buffer memset(mBuffer, 0, 1024); // Fill the buffer with the formatted message va_list args; va_start(args, message); vsprintf_s(mBuffer, BUFFER_SIZE, message, args); va_end(args); double timeStamp = mTimer.GetElapsedSeconds(); LogMessage lm; lm.LogCategory = logCategory; lm.LogLevel = logLevel; lm.Message = mBuffer; lm.Repeats = 0; lm.RepeatAlertSent = false; lm.SentTimeStamp = timeStamp; bool send = true; bool track = true; // Check for repeats for (int i = 0; i < mBacklog.size(); i++) { LogMessage& prevMsg = mBacklog[i]; if (prevMsg.Message == lm.Message) { if (prevMsg.RepeatAlertSent) { send = false; break; } // only suppress the messsage if it has been less than // 1 second since it was last sent double timeSinceLastSend = timeStamp - prevMsg.SentTimeStamp; if (timeSinceLastSend <= 1.0) { prevMsg.Repeats += 1; if (prevMsg.Repeats > 100 && !prevMsg.RepeatAlertSent) { lm.Message = lm.Message + " [This message has rapidly repeated over 100 times! No more repeats will be sent out!]"; prevMsg.RepeatAlertSent = true; track = false; } else { send = false; } } prevMsg.SentTimeStamp = timeStamp; break; } } if (!send) return; // Send to all listeners for (auto iter = mListeners.begin(); iter != mListeners.end(); iter++) { if ((*iter)->Log(lm)) { // Track which listeners handle the message lm.HandledBy.push_back((*iter)->GetName()); } } if (track) mBacklog.push_back(lm); } const std::vector& Logger::GetBacklog() const { return mBacklog; } }