From fd424abd16af6e3205fb6479590ce0e38461bb12 Mon Sep 17 00:00:00 2001 From: Joeyrp Date: Thu, 26 Aug 2021 14:58:35 -0400 Subject: [PATCH] Added some utility files Tested to be working although INIFile is currently a windows-only utility and so is currently removed from the project (but still in the repo as it may be re-written to be cross-platform) --- CMakeLists.txt | 2 + src/test_main.cpp | 9 + src/utils/HighResTimer.cpp | 65 ++++++ src/utils/HighResTimer.h | 60 ++++++ src/utils/INIFile.cpp | 109 ++++++++++ src/utils/INIFile.h | 54 +++++ src/utils/Logger.cpp | 414 +++++++++++++++++++++++++++++++++++++ src/utils/Logger.h | 182 ++++++++++++++++ test_data/test.ini | 2 + 9 files changed, 897 insertions(+) create mode 100644 src/utils/HighResTimer.cpp create mode 100644 src/utils/HighResTimer.h create mode 100644 src/utils/INIFile.cpp create mode 100644 src/utils/INIFile.h create mode 100644 src/utils/Logger.cpp create mode 100644 src/utils/Logger.h create mode 100644 test_data/test.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index 645b7f2..b19a66e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ configure_file(LunariumConfig.h.in LunariumConfig.h) # Source Files set(LUNARIUM_SRC "src/test_main.cpp" +"src/utils/Logger.cpp" +"src/utils/HighResTimer.cpp" ) # add the executable diff --git a/src/test_main.cpp b/src/test_main.cpp index 02145a6..2018e52 100644 --- a/src/test_main.cpp +++ b/src/test_main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include "utils/INIFile.h" +#include "utils/Logger.h" extern "C" { @@ -67,6 +69,13 @@ int main(int argc, char** argv) std::cout << "\nPugiXML worked! -- x is: " << x; } + // UTILS TEST + // NOTE: this works but is windows only so it won't be used in the engine + // unless INIFile is re-written to be cross-platform + // Lunarium::INIFile file("../test_data/test.ini"); + // int meaning = file.ReadInt("section", "var"); + // std::cout << "\nvar from test.ini is: " << meaning; + // GLFW DEAR IMGUI TEST GLFWwindow* window; int width, height; diff --git a/src/utils/HighResTimer.cpp b/src/utils/HighResTimer.cpp new file mode 100644 index 0000000..99542af --- /dev/null +++ b/src/utils/HighResTimer.cpp @@ -0,0 +1,65 @@ +/****************************************************************************** +* Filename: HighResTimer.cpp +* Date: 12/06/2010 +* Mod. Date: 03/21/2018 +* Author: Joseph R. Pollack +* Purpose: A high resolution timer suitable for profiling code. This new +* version has stripped out the game specific functionality and +* does not include windows.h in the header. +******************************************************************************/ + +#include "HighResTimer.h" + +#define WIN32_LEAN_AND_MEAN +#include + +namespace Lunarium +{ + + HighResTimer::HighResTimer() + { + Initialize(); + } + + + + void HighResTimer::Initialize() + { + mStartTime = 0; + LARGE_INTEGER tickFreq; + QueryPerformanceFrequency(&tickFreq); + mTickFrequency = tickFreq.QuadPart; + } + + // Starts or re-starts the timer. + void HighResTimer::Reset() + { + LARGE_INTEGER tickStart; + QueryPerformanceCounter(&tickStart); + mStartTime = tickStart.QuadPart; + } + + + // Returns the amount of time in microseconds since the last Reset() call. + int64_t HighResTimer::GetElapsedTime() + { + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + int64_t elapsedMicro = ticks.QuadPart - mStartTime; + + elapsedMicro *= mcMils; + elapsedMicro /= mTickFrequency; + + return elapsedMicro; + } + + + double HighResTimer::GetElapsedSeconds() + { + int64_t emicro = GetElapsedTime(); + double dmicro = ((double)emicro); + double dmcMils = ((double)mcMils); + double seconds = dmicro / dmcMils; + return seconds; + } +} \ No newline at end of file diff --git a/src/utils/HighResTimer.h b/src/utils/HighResTimer.h new file mode 100644 index 0000000..0d49d13 --- /dev/null +++ b/src/utils/HighResTimer.h @@ -0,0 +1,60 @@ +/****************************************************************************** +* Filename: HighResTimer.h +* Date: 12/06/2010 +* Mod. Date: 03/21/2018 +* Author: Joseph R. Pollack +* Purpose: A high resolution timer suitable for profiling code. This new +* version has stripped out the game specific functionality and +* does not include windows.h in the header. +******************************************************************************/ + +#ifndef HIGH_RES_TIMER_H_ +#define HIGH_RES_TIMER_H_ + +#include + + +namespace Lunarium +{ + + class HighResTimer + { + public: + // NOTE: Currently unused + enum eUnits { SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS, PICOSECONDS }; + + public: + + HighResTimer(); + + void Initialize(); + + // Starts or re starts the timer. + void Reset(); + + // Returns the amount of time in microseconds since the last Reset() call. + int64_t GetElapsedTime(); + + // Returns the elapsed time in terms of seconds + double GetElapsedSeconds(); + + + private: + + int64_t mStartTime; + int64_t mTickFrequency; + + + + const int mcMils = 1000000; + const int mcBils = 1000000000; + const long long mcTrils = 1000000000000; + + /* // OLD MEMBERS + long long m_TickFreqQuadPart; + double m_StartTime; + */ + + }; +} +#endif // HIGH_RES_TIMER_H_ \ No newline at end of file diff --git a/src/utils/INIFile.cpp b/src/utils/INIFile.cpp new file mode 100644 index 0000000..1057f67 --- /dev/null +++ b/src/utils/INIFile.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** +* File - INIFile.h +* Author - Joey Pollack +* Date - 2017/11/28 (y/m/d) +* Mod Date - 2017/11/28 (y/m/d) +* Description - Read/Write from a .ini file +******************************************************************************/ + +#include "INIFile.h" + +// TODO: This needs to be re-written without the windows-specific functions +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include + +namespace Lunarium +{ + INIFile::INIFile(const char * filename) + { + mFullPath = filename; + } + + INIFile::~INIFile() + { + } + + std::string INIFile::Read(const char* section, const char* key) + { + char buffer[256]; + GetPrivateProfileStringA(section, key, nullptr, buffer, 256, mFullPath.c_str()); + return std::string(buffer); + } + + int INIFile::ReadInt(const char* section, const char* key) + { + int val = GetPrivateProfileIntA(section, key, -1, mFullPath.c_str()); + return val; + } + + bool INIFile::ReadBool(const char* section, const char* key) + { + std::string buffer = Read(section, key); + + if (buffer.size() < 1) + throw std::runtime_error("ReadBool() Failed: buffer was empty!"); + + std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower); + + if (buffer == std::string("false")) + return false; + + if (buffer == std::string("true")) + return true; + + throw std::runtime_error("ReadBool() Failed: buffer not a boolean value!"); + + return false; + } + + double INIFile::ReadDouble(const char* section, const char* key) + { + throw "Not implemented!"; + return 0.0; + } + + void INIFile::Write(const char * section, const char * key, const char * value) + { + WritePrivateProfileStringA(section, key, value, mFullPath.c_str()); + } + + void INIFile::WriteInt(const char * section, const char * key, int value) + { + std::string strVal = std::to_string(value); + WritePrivateProfileStringA(section, key, strVal.c_str(), mFullPath.c_str()); + } + + void INIFile::WriteBool(const char * section, const char * key, bool value) + { + std::string strVal = "true"; + if (!value) + { + strVal = "false"; + } + + WritePrivateProfileStringA(section, key, strVal.c_str(), mFullPath.c_str()); + } + + void INIFile::WriteDouble(const char * section, const char * key, double value) + { + throw "INIFile::WriteDouble Not implemented!"; + } + + bool INIFile::KeyExists(const char* section, const char* key) + { + return Read(section, key).length() > 0; + } + + void INIFile::DeleteKey(const char* section, const char* key) + { + Write(section, key, nullptr); + } + + void INIFile::DeleteSection(const char * section) + { + Write(section, nullptr, nullptr); + } +} \ No newline at end of file diff --git a/src/utils/INIFile.h b/src/utils/INIFile.h new file mode 100644 index 0000000..de3d870 --- /dev/null +++ b/src/utils/INIFile.h @@ -0,0 +1,54 @@ +/****************************************************************************** +* File - INIFile.h +* Author - Joey Pollack +* Date - 2017/11/28 (y/m/d) +* Mod Date - 2017/11/28 (y/m/d) +* Description - Read/Write from a .ini file +* Based on the C# class writen by Danny Beckett and released +* to public domain: +* https://stackoverflow.com/questions/217902/reading-writing-an-ini-file +******************************************************************************/ + +#ifndef _INI_FILE_H_ +#define _INI_FILE_H_ + +#include + +namespace Lunarium +{ + class INIFile + { + public: + + // filename must be a full, absolute path to the .ini file. + INIFile(const char* filename); + ~INIFile(); + + // Read methods + std::string Read(const char* section, const char* key); + int ReadInt(const char* section, const char* key); + bool ReadBool(const char* section, const char* key); + double ReadDouble(const char* section, const char* key); + + // Write methods + void Write(const char * section, const char* key, const char* value); + void WriteInt(const char * section, const char* key, int value); + void WriteBool(const char * section, const char* key, bool value); + void WriteDouble(const char * section, const char* key, double value); + + + // Management + bool KeyExists(const char* section, const char* key); + + void DeleteKey(const char* section, const char* key); + void DeleteSection(const char* section); + + + private: + bool mbFileOpen; + + std::string mFullPath; + }; + +} +#endif // _INI_FILE_H_ \ No newline at end of file diff --git a/src/utils/Logger.cpp b/src/utils/Logger.cpp new file mode 100644 index 0000000..452f520 --- /dev/null +++ b/src/utils/Logger.cpp @@ -0,0 +1,414 @@ +/****************************************************************************** +* 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; + } +} \ No newline at end of file diff --git a/src/utils/Logger.h b/src/utils/Logger.h new file mode 100644 index 0000000..1acde22 --- /dev/null +++ b/src/utils/Logger.h @@ -0,0 +1,182 @@ +/****************************************************************************** +* 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. +******************************************************************************/ + +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include +#include +#include +#include +#include + +// TODO: Remove the dependence on HighResTimer.h +// OR re-write HighResTimer with modern timer code +#include "HighResTimer.h" + +// Conflict with a region flag in wingdi.h +#undef ERROR + +namespace Lunarium +{ + namespace LogLevel + { + const uint16_t ANY = 0xFFFF; + const uint16_t INFO = 0x0001; + const uint16_t WARNING = 0x0002; + const uint16_t ERROR = 0x0004; + const uint16_t FATAL_ERROR = 0x0008; + const uint16_t INFO_VERBOSE = 0x0010; + const uint16_t OGL_DEBUG = 0x0020; + } + + namespace LogCategory + { + const uint16_t ANY = 0xFFFF; + const uint16_t CORE = 0x0001; + const uint16_t WINDOW_CONTROLLER = 0x0002; + const uint16_t GRAPHICS = 0x0004; + const uint16_t UTILITIES = 0x0008; + //const uint16_t USER_PROJECT = 0x0010; + } + + struct LogMessage + { + // The ID of the category + uint32_t LogCategory; + + // The ID of the Level + uint32_t LogLevel; + + std::string Message; + std::vector HandledBy; + uint32_t Repeats; + bool RepeatAlertSent; + double SentTimeStamp; + }; + + // //////////////////////////////////////////////////////////////////////// + // ======================================================================== + // LISTENER CLASSES + // ======================================================================== + // //////////////////////////////////////////////////////////////////////// + + // Log Listener interface class + class LogListener + { + public: + LogListener(uint32_t acceptedLogLevels = LogLevel::ANY, uint32_t acceptedLogCategories = LogLevel::ANY, const char* myName = "UNKNOWN"); + virtual bool Log(LogMessage& message) = 0; + + const std::string& GetName() const; + + bool CategoryIsSet(uint32_t id) const; + bool LevelIsSet(uint32_t id) const; + + protected: + uint32_t mAcceptedLogCategories; + uint32_t mAcceptedLogLevels; + std::string mMyName; + }; + + + // Outputs all (by default) messages to std::cout + // Adds a time stamp and the log level to the message. + class StandardListener : public LogListener + { + public: + StandardListener(uint32_t acceptedLogLevels = LogLevel::ANY, + uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "StandardListener"); + bool Log(LogMessage& message); + }; + + + // Outputs Errors and Fatal Errors to std::err + // Adds a time stamp and the log level to the message. + class ErrorListener : public LogListener + { + public: + ErrorListener(uint32_t acceptedLogLevels = LogLevel::ERROR | LogLevel::FATAL_ERROR, + uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "ErrorListener"); + bool Log(LogMessage& message); + }; + + + // Outputs all (by default) messages to the given std::ofstream. + // Adds a time stamp and the log level to the message. + class FileListener : public LogListener + { + public: + FileListener(std::ofstream& of, uint32_t acceptedLogLevels = LogLevel::ANY, + uint32_t acceptedLogCategories = LogCategory::ANY, const char* myName = "FileListener"); + bool Log(LogMessage& message); + + private: + std::ofstream& mOF; + }; + +#define BUFFER_SIZE 1024 + + // //////////////////////////////////////////////////////////////////////// + // ======================================================================== + // MAIN LOGGERCLASS + // ======================================================================== + // //////////////////////////////////////////////////////////////////////// + class Logger + { + public: + static std::string TimeStamp(); + static Logger* GetInstance(); + static void FreeInstance(); + + static uint32_t RegisterCategory(const char* name); + static uint32_t RegisterLevel(const char* name); + static std::string GetCategoryName(uint32_t id); + static std::string GetLevelName(uint32_t id); + LogListener* AddListener(LogListener* pl); + + // This method does NOT delete the listener's memory! + LogListener* RemoveListener(LogListener* pl); + + // This method does NOT delete the listener's memory! + LogListener* RemoveListener(const char* withName); + + // Calls delete on all listeners and clears the list + void FreeAllListeners(); + + void ClearBacklog(); + + // The message argument and the variable argument list work just like + // printf. Use the same formatters as printf when calling this function. + // Messages must be shorter than 1024 bytes. + void Log(uint32_t logCategory, uint32_t logLevel, const char* message, ...); + + const std::vector& GetBacklog() const; + + private: + static Logger* mInstance; + static uint32_t mNextCategoryID; + static uint32_t mNextLevelID; + static std::map mLevelNames; + static std::map mCategoryNames; + std::list mListeners; + std::vector mBacklog; + char mBuffer[BUFFER_SIZE]; + HighResTimer mTimer; + + private: + Logger(); + Logger(const Logger&) = delete; + const Logger& operator=(const Logger&) = delete; + }; + +} + +#endif // LOGGER_H_ \ No newline at end of file diff --git a/test_data/test.ini b/test_data/test.ini new file mode 100644 index 0000000..bf0f49c --- /dev/null +++ b/test_data/test.ini @@ -0,0 +1,2 @@ +[section] +var=42 \ No newline at end of file