| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- //========================================================================
- // Logger.cpp :
- //
- // Part of the GameCode4 Application
- //
- // GameCode4 is the sample application that encapsulates much of the source code
- // discussed in "Game Coding Complete - 4th Edition" by Mike McShaffry and David
- // "Rez" Graham, published by Charles River Media.
- // ISBN-10: 1133776574 | ISBN-13: 978-1133776574
- //
- // If this source code has found it's way to you, and you think it has helped you
- // in any way, do the authors a favor and buy a new copy of the book - there are
- // detailed explanations in it that compliment this code well. Buy a copy at Amazon.com
- // by clicking here:
- // http://www.amazon.com/gp/product/1133776574/ref=olp_product_details?ie=UTF8&me=&seller=
- //
- // There's a companion web site at http://www.mcshaffry.com/GameCode/
- //
- // The source code is managed and maintained through Google Code:
- // http://code.google.com/p/gamecode4/
- //
- // (c) Copyright 2012 Michael L. McShaffry and David Graham
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU Lesser GPL v3
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- // http://www.gnu.org/licenses/lgpl-3.0.txt for more details.
- //
- // You should have received a copy of the GNU Lesser GPL v3
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- //
- //========================================================================
- #include "../Unix/GameCodeStd.h"
- #include "Logger.h"
- #include "../Utilities/Dialogs.h"
- #include "../Multicore/CriticalSection.h"
- using std::string;
- //------------------------------------------------------------------------------------------------------------------------------------
- // Some constants
- //------------------------------------------------------------------------------------------------------------------------------------
- // the log filename
- static const char* ERRORLOG_FILENAME = "error.log";
- #define NDEBUG 1
- // default display flags
- #ifdef _DEBUG
- const unsigned char ERRORFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
- const unsigned char WARNINGFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
- const unsigned char LOGFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
- #elif NDEBUG
- const unsigned char ERRORFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
- const unsigned char WARNINGFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
- const unsigned char LOGFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
- #else
- const unsigned char ERRORFLAG_DEFAULT = 0;
- const unsigned char WARNINGFLAG_DEFAULT = 0;
- const unsigned char LOGFLAG_DEFAULT = 0;
- #endif
- // singleton
- class LogMgr;
- static LogMgr* s_pLogMgr = NULL;
- //------------------------------------------------------------------------------------------------------------------------------------
- // LogMgr class
- //------------------------------------------------------------------------------------------------------------------------------------
- class LogMgr
- {
- public:
- enum ErrorDialogResult
- {
- LOGMGR_ERROR_ABORT,
- LOGMGR_ERROR_RETRY,
- LOGMGR_ERROR_IGNORE
- };
- typedef std::map<string, unsigned char> Tags;
- typedef std::list<Logger::ErrorMessenger*> ErrorMessengerList;
- Tags m_tags;
- ErrorMessengerList m_errorMessengers;
- // thread safety
- CriticalSection m_tagCriticalSection;
- CriticalSection m_messengerCriticalSection;
- public:
- // construction
- LogMgr(void);
- ~LogMgr(void);
- void Init(const char* loggingConfigFilename);
- // logs
- void Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum);
- void SetDisplayFlags(const std::string& tag, unsigned char flags);
- // error messengers
- void AddErrorMessenger(Logger::ErrorMessenger* pMessenger);
- LogMgr::ErrorDialogResult Error(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum);
- private:
- // log helpers
- void OutputFinalBufferToLogs(const string& finalBuffer, unsigned char flags);
- void WriteToLogFile(const string& data);
- void GetOutputBuffer(std::string& outOutputBuffer, const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum);
- };
- //------------------------------------------------------------------------------------------------------------------------------------
- // Constructor
- //------------------------------------------------------------------------------------------------------------------------------------
- LogMgr::LogMgr(void)
- {
- // set up the default log tags
- SetDisplayFlags("ERROR", ERRORFLAG_DEFAULT);
- SetDisplayFlags("WARNING", WARNINGFLAG_DEFAULT);
- SetDisplayFlags("INFO", LOGFLAG_DEFAULT);
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // Destructor
- //------------------------------------------------------------------------------------------------------------------------------------
- LogMgr::~LogMgr(void)
- {
- m_messengerCriticalSection.Lock();
- for (auto it = m_errorMessengers.begin(); it != m_errorMessengers.end(); ++it)
- {
- Logger::ErrorMessenger* pMessenger = (*it);
- delete pMessenger;
- }
- m_errorMessengers.clear();
- m_messengerCriticalSection.Unlock();
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // Initializes the logger.
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::Init(const char* loggingConfigFilename)
- {
- if (loggingConfigFilename)
- {
- TiXmlDocument loggingConfigFile(loggingConfigFilename);
- if (loggingConfigFile.LoadFile())
- {
- TiXmlElement* pRoot = loggingConfigFile.RootElement();
- if (!pRoot)
- return;
- // Loop through each child element and load the component
- for (TiXmlElement* pNode = pRoot->FirstChildElement(); pNode; pNode = pNode->NextSiblingElement())
- {
- unsigned char flags = 0;
- std::string tag(pNode->Attribute("tag"));
- int debugger = 0;
- pNode->Attribute("debugger", &debugger);
- if (debugger)
- flags |= LOGFLAG_WRITE_TO_DEBUGGER;
- int logfile = 0;
- pNode->Attribute("file", &logfile);
- if (logfile)
- flags |= LOGFLAG_WRITE_TO_LOG_FILE;
- SetDisplayFlags(tag, flags);
- }
- }
- }
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // This function builds up the log string and outputs it to various places based on the display flags (m_displayFlags).
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
- {
- m_tagCriticalSection.Lock();
- Tags::iterator findIt = m_tags.find(tag);
- if (findIt != m_tags.end())
- {
- m_tagCriticalSection.Unlock();
- string buffer;
- GetOutputBuffer(buffer, tag, message, funcName, sourceFile, lineNum);
- OutputFinalBufferToLogs(buffer, findIt->second);
- }
- else
- {
- // Critical section is exited in the if statement above, so we need to exit it here if that didn't
- // get executed
- m_tagCriticalSection.Unlock();
- }
- } // end LogMgr::Log()
- //------------------------------------------------------------------------------------------------------------------------------------
- // Sets one or more display flags
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::SetDisplayFlags(const std::string& tag, unsigned char flags)
- {
- m_tagCriticalSection.Lock();
- if (flags != 0)
- {
- Tags::iterator findIt = m_tags.find(tag);
- if (findIt == m_tags.end())
- m_tags.insert(std::make_pair(tag, flags));
- else
- findIt->second = flags;
- }
- else
- {
- m_tags.erase(tag);
- }
- m_tagCriticalSection.Unlock();
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // Adds an error messenger to the list
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::AddErrorMessenger(Logger::ErrorMessenger* pMessenger)
- {
- m_messengerCriticalSection.Lock();
- m_errorMessengers.push_back(pMessenger);
- m_messengerCriticalSection.Unlock();
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // Helper for ErrorMessenger.
- //------------------------------------------------------------------------------------------------------------------------------------
- LogMgr::ErrorDialogResult LogMgr::Error(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum)
- {
- string tag = ((isFatal) ? ("FATAL") : ("ERROR"));
- // buffer for our final output string
- string buffer;
- GetOutputBuffer(buffer, tag, errorMessage, funcName, sourceFile, lineNum);
- // write the final buffer to all the various logs
- m_tagCriticalSection.Lock();
- Tags::iterator findIt = m_tags.find(tag);
- if (findIt != m_tags.end())
- OutputFinalBufferToLogs(buffer, findIt->second);
- m_tagCriticalSection.Unlock();
- // show the dialog box
- int result = MessageBox(NULL, buffer.c_str(), tag.c_str(), MB_ABORTRETRYIGNORE|MB_ICONERROR);
- // act upon the choice
- switch (result)
- {
- case IDIGNORE : return LogMgr::LOGMGR_ERROR_IGNORE;
- case IDABORT : /*__debugbreak(); */ return LogMgr::LOGMGR_ERROR_ABORT; // assembly language instruction to break into the debugger
- case IDRETRY : return LogMgr::LOGMGR_ERROR_RETRY;
- default : return LogMgr::LOGMGR_ERROR_RETRY;
- }
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // This is a helper function that checks all the display flags and outputs the passed in finalBuffer to the appropriate places.
- //
- // IMPORTANT: The two places this function is called from wrap the code in the tag critical section (m_pTagCriticalSection),
- // so that makes this call thread safe. If you call this from anywhere else, make sure you wrap it in that critical section.
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::OutputFinalBufferToLogs(const string& finalBuffer, unsigned char flags)
- {
- // Write the log to each display based on the display flags
- if ((flags & LOGFLAG_WRITE_TO_LOG_FILE) > 0) // log file
- WriteToLogFile(finalBuffer);
- if ((flags & LOGFLAG_WRITE_TO_DEBUGGER) > 0) // log file
- fputs(finalBuffer.c_str(), stderr);
- // ::OutputDebugStringA(finalBuffer.c_str());
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // This is a helper function that writes the data string to the log file.
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::WriteToLogFile(const string& data)
- {
- FILE* pLogFile = NULL;
- pLogFile = fopen(ERRORLOG_FILENAME, "a+");
- if (!pLogFile)
- return;
- fputs(data.c_str(), pLogFile);
- fclose(pLogFile);
- }
- //------------------------------------------------------------------------------------------------------------------------------------
- // Fills outOutputBuffer with the find error string.
- //------------------------------------------------------------------------------------------------------------------------------------
- void LogMgr::GetOutputBuffer(std::string& outOutputBuffer, const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
- {
- if (!tag.empty())
- outOutputBuffer = "[" + tag + "] " + message;
- else
- outOutputBuffer = message;
- if (funcName != NULL)
- {
- outOutputBuffer += "\nFunction: ";
- outOutputBuffer += funcName;
- }
- if (sourceFile != NULL)
- {
- outOutputBuffer += "\n";
- outOutputBuffer += sourceFile;
- }
- if (lineNum != 0)
- {
- outOutputBuffer += "\nLine: ";
- char lineNumBuffer[11];
- memset(lineNumBuffer, 0, sizeof(char));
- snprintf(lineNumBuffer, sizeof(lineNumBuffer), "%u", lineNum);
- outOutputBuffer += lineNumBuffer;
- }
- outOutputBuffer += "\n";
- }
- //-----------------------------------------------------------------------------------------------------------------------
- // ErrorMessenger
- //-----------------------------------------------------------------------------------------------------------------------
- Logger::ErrorMessenger::ErrorMessenger(void)
- {
- s_pLogMgr->AddErrorMessenger(this);
- m_enabled = true;
- }
- void Logger::ErrorMessenger::Show(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum)
- {
- if (m_enabled)
- {
- if (s_pLogMgr->Error(errorMessage, isFatal, funcName, sourceFile, lineNum) == LogMgr::LOGMGR_ERROR_IGNORE)
- m_enabled = false;
- }
- }
- //-----------------------------------------------------------------------------------------------------------------------
- // C Interface
- //-----------------------------------------------------------------------------------------------------------------------
- namespace Logger {
- void Init(const char* loggingConfigFilename)
- {
- if (!s_pLogMgr)
- {
- s_pLogMgr = GCC_NEW LogMgr;
- s_pLogMgr->Init(loggingConfigFilename);
- }
- }
- void Destroy(void)
- {
- delete s_pLogMgr;
- s_pLogMgr = NULL;
- }
- void Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
- {
- GCC_ASSERT(s_pLogMgr);
- s_pLogMgr->Log(tag, message, funcName, sourceFile, lineNum);
- }
- void SetDisplayFlags(const std::string& tag, unsigned char flags)
- {
- GCC_ASSERT(s_pLogMgr);
- s_pLogMgr->SetDisplayFlags(tag, flags);
- }
- } // end namespace Logger
|