Logger.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. //========================================================================
  2. // Logger.cpp :
  3. //
  4. // Part of the GameCode4 Application
  5. //
  6. // GameCode4 is the sample application that encapsulates much of the source code
  7. // discussed in "Game Coding Complete - 4th Edition" by Mike McShaffry and David
  8. // "Rez" Graham, published by Charles River Media.
  9. // ISBN-10: 1133776574 | ISBN-13: 978-1133776574
  10. //
  11. // If this source code has found it's way to you, and you think it has helped you
  12. // in any way, do the authors a favor and buy a new copy of the book - there are
  13. // detailed explanations in it that compliment this code well. Buy a copy at Amazon.com
  14. // by clicking here:
  15. // http://www.amazon.com/gp/product/1133776574/ref=olp_product_details?ie=UTF8&me=&seller=
  16. //
  17. // There's a companion web site at http://www.mcshaffry.com/GameCode/
  18. //
  19. // The source code is managed and maintained through Google Code:
  20. // http://code.google.com/p/gamecode4/
  21. //
  22. // (c) Copyright 2012 Michael L. McShaffry and David Graham
  23. //
  24. // This program is free software; you can redistribute it and/or
  25. // modify it under the terms of the GNU Lesser GPL v3
  26. // as published by the Free Software Foundation.
  27. //
  28. // This program is distributed in the hope that it will be useful,
  29. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
  31. // http://www.gnu.org/licenses/lgpl-3.0.txt for more details.
  32. //
  33. // You should have received a copy of the GNU Lesser GPL v3
  34. // along with this program; if not, write to the Free Software
  35. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  36. //
  37. //========================================================================
  38. #include "../Unix/GameCodeStd.h"
  39. #include "Logger.h"
  40. #include "../Utilities/Dialogs.h"
  41. #include "../Multicore/CriticalSection.h"
  42. using std::string;
  43. //------------------------------------------------------------------------------------------------------------------------------------
  44. // Some constants
  45. //------------------------------------------------------------------------------------------------------------------------------------
  46. // the log filename
  47. static const char* ERRORLOG_FILENAME = "error.log";
  48. #define NDEBUG 1
  49. // default display flags
  50. #ifdef _DEBUG
  51. const unsigned char ERRORFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
  52. const unsigned char WARNINGFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
  53. const unsigned char LOGFLAG_DEFAULT = LOGFLAG_WRITE_TO_DEBUGGER;
  54. #elif NDEBUG
  55. const unsigned char ERRORFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
  56. const unsigned char WARNINGFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
  57. const unsigned char LOGFLAG_DEFAULT = (LOGFLAG_WRITE_TO_DEBUGGER | LOGFLAG_WRITE_TO_LOG_FILE);
  58. #else
  59. const unsigned char ERRORFLAG_DEFAULT = 0;
  60. const unsigned char WARNINGFLAG_DEFAULT = 0;
  61. const unsigned char LOGFLAG_DEFAULT = 0;
  62. #endif
  63. // singleton
  64. class LogMgr;
  65. static LogMgr* s_pLogMgr = NULL;
  66. //------------------------------------------------------------------------------------------------------------------------------------
  67. // LogMgr class
  68. //------------------------------------------------------------------------------------------------------------------------------------
  69. class LogMgr
  70. {
  71. public:
  72. enum ErrorDialogResult
  73. {
  74. LOGMGR_ERROR_ABORT,
  75. LOGMGR_ERROR_RETRY,
  76. LOGMGR_ERROR_IGNORE
  77. };
  78. typedef std::map<string, unsigned char> Tags;
  79. typedef std::list<Logger::ErrorMessenger*> ErrorMessengerList;
  80. Tags m_tags;
  81. ErrorMessengerList m_errorMessengers;
  82. // thread safety
  83. CriticalSection m_tagCriticalSection;
  84. CriticalSection m_messengerCriticalSection;
  85. public:
  86. // construction
  87. LogMgr(void);
  88. ~LogMgr(void);
  89. void Init(const char* loggingConfigFilename);
  90. // logs
  91. void Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum);
  92. void SetDisplayFlags(const std::string& tag, unsigned char flags);
  93. // error messengers
  94. void AddErrorMessenger(Logger::ErrorMessenger* pMessenger);
  95. LogMgr::ErrorDialogResult Error(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum);
  96. private:
  97. // log helpers
  98. void OutputFinalBufferToLogs(const string& finalBuffer, unsigned char flags);
  99. void WriteToLogFile(const string& data);
  100. void GetOutputBuffer(std::string& outOutputBuffer, const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum);
  101. };
  102. //------------------------------------------------------------------------------------------------------------------------------------
  103. // Constructor
  104. //------------------------------------------------------------------------------------------------------------------------------------
  105. LogMgr::LogMgr(void)
  106. {
  107. // set up the default log tags
  108. SetDisplayFlags("ERROR", ERRORFLAG_DEFAULT);
  109. SetDisplayFlags("WARNING", WARNINGFLAG_DEFAULT);
  110. SetDisplayFlags("INFO", LOGFLAG_DEFAULT);
  111. }
  112. //------------------------------------------------------------------------------------------------------------------------------------
  113. // Destructor
  114. //------------------------------------------------------------------------------------------------------------------------------------
  115. LogMgr::~LogMgr(void)
  116. {
  117. m_messengerCriticalSection.Lock();
  118. for (auto it = m_errorMessengers.begin(); it != m_errorMessengers.end(); ++it)
  119. {
  120. Logger::ErrorMessenger* pMessenger = (*it);
  121. delete pMessenger;
  122. }
  123. m_errorMessengers.clear();
  124. m_messengerCriticalSection.Unlock();
  125. }
  126. //------------------------------------------------------------------------------------------------------------------------------------
  127. // Initializes the logger.
  128. //------------------------------------------------------------------------------------------------------------------------------------
  129. void LogMgr::Init(const char* loggingConfigFilename)
  130. {
  131. if (loggingConfigFilename)
  132. {
  133. TiXmlDocument loggingConfigFile(loggingConfigFilename);
  134. if (loggingConfigFile.LoadFile())
  135. {
  136. TiXmlElement* pRoot = loggingConfigFile.RootElement();
  137. if (!pRoot)
  138. return;
  139. // Loop through each child element and load the component
  140. for (TiXmlElement* pNode = pRoot->FirstChildElement(); pNode; pNode = pNode->NextSiblingElement())
  141. {
  142. unsigned char flags = 0;
  143. std::string tag(pNode->Attribute("tag"));
  144. int debugger = 0;
  145. pNode->Attribute("debugger", &debugger);
  146. if (debugger)
  147. flags |= LOGFLAG_WRITE_TO_DEBUGGER;
  148. int logfile = 0;
  149. pNode->Attribute("file", &logfile);
  150. if (logfile)
  151. flags |= LOGFLAG_WRITE_TO_LOG_FILE;
  152. SetDisplayFlags(tag, flags);
  153. }
  154. }
  155. }
  156. }
  157. //------------------------------------------------------------------------------------------------------------------------------------
  158. // This function builds up the log string and outputs it to various places based on the display flags (m_displayFlags).
  159. //------------------------------------------------------------------------------------------------------------------------------------
  160. void LogMgr::Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
  161. {
  162. m_tagCriticalSection.Lock();
  163. Tags::iterator findIt = m_tags.find(tag);
  164. if (findIt != m_tags.end())
  165. {
  166. m_tagCriticalSection.Unlock();
  167. string buffer;
  168. GetOutputBuffer(buffer, tag, message, funcName, sourceFile, lineNum);
  169. OutputFinalBufferToLogs(buffer, findIt->second);
  170. }
  171. else
  172. {
  173. // Critical section is exited in the if statement above, so we need to exit it here if that didn't
  174. // get executed
  175. m_tagCriticalSection.Unlock();
  176. }
  177. } // end LogMgr::Log()
  178. //------------------------------------------------------------------------------------------------------------------------------------
  179. // Sets one or more display flags
  180. //------------------------------------------------------------------------------------------------------------------------------------
  181. void LogMgr::SetDisplayFlags(const std::string& tag, unsigned char flags)
  182. {
  183. m_tagCriticalSection.Lock();
  184. if (flags != 0)
  185. {
  186. Tags::iterator findIt = m_tags.find(tag);
  187. if (findIt == m_tags.end())
  188. m_tags.insert(std::make_pair(tag, flags));
  189. else
  190. findIt->second = flags;
  191. }
  192. else
  193. {
  194. m_tags.erase(tag);
  195. }
  196. m_tagCriticalSection.Unlock();
  197. }
  198. //------------------------------------------------------------------------------------------------------------------------------------
  199. // Adds an error messenger to the list
  200. //------------------------------------------------------------------------------------------------------------------------------------
  201. void LogMgr::AddErrorMessenger(Logger::ErrorMessenger* pMessenger)
  202. {
  203. m_messengerCriticalSection.Lock();
  204. m_errorMessengers.push_back(pMessenger);
  205. m_messengerCriticalSection.Unlock();
  206. }
  207. //------------------------------------------------------------------------------------------------------------------------------------
  208. // Helper for ErrorMessenger.
  209. //------------------------------------------------------------------------------------------------------------------------------------
  210. LogMgr::ErrorDialogResult LogMgr::Error(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum)
  211. {
  212. string tag = ((isFatal) ? ("FATAL") : ("ERROR"));
  213. // buffer for our final output string
  214. string buffer;
  215. GetOutputBuffer(buffer, tag, errorMessage, funcName, sourceFile, lineNum);
  216. // write the final buffer to all the various logs
  217. m_tagCriticalSection.Lock();
  218. Tags::iterator findIt = m_tags.find(tag);
  219. if (findIt != m_tags.end())
  220. OutputFinalBufferToLogs(buffer, findIt->second);
  221. m_tagCriticalSection.Unlock();
  222. // show the dialog box
  223. int result = MessageBox(NULL, buffer.c_str(), tag.c_str(), MB_ABORTRETRYIGNORE|MB_ICONERROR);
  224. // act upon the choice
  225. switch (result)
  226. {
  227. case IDIGNORE : return LogMgr::LOGMGR_ERROR_IGNORE;
  228. case IDABORT : /*__debugbreak(); */ return LogMgr::LOGMGR_ERROR_ABORT; // assembly language instruction to break into the debugger
  229. case IDRETRY : return LogMgr::LOGMGR_ERROR_RETRY;
  230. default : return LogMgr::LOGMGR_ERROR_RETRY;
  231. }
  232. }
  233. //------------------------------------------------------------------------------------------------------------------------------------
  234. // This is a helper function that checks all the display flags and outputs the passed in finalBuffer to the appropriate places.
  235. //
  236. // IMPORTANT: The two places this function is called from wrap the code in the tag critical section (m_pTagCriticalSection),
  237. // so that makes this call thread safe. If you call this from anywhere else, make sure you wrap it in that critical section.
  238. //------------------------------------------------------------------------------------------------------------------------------------
  239. void LogMgr::OutputFinalBufferToLogs(const string& finalBuffer, unsigned char flags)
  240. {
  241. // Write the log to each display based on the display flags
  242. if ((flags & LOGFLAG_WRITE_TO_LOG_FILE) > 0) // log file
  243. WriteToLogFile(finalBuffer);
  244. if ((flags & LOGFLAG_WRITE_TO_DEBUGGER) > 0) // log file
  245. fputs(finalBuffer.c_str(), stderr);
  246. // ::OutputDebugStringA(finalBuffer.c_str());
  247. }
  248. //------------------------------------------------------------------------------------------------------------------------------------
  249. // This is a helper function that writes the data string to the log file.
  250. //------------------------------------------------------------------------------------------------------------------------------------
  251. void LogMgr::WriteToLogFile(const string& data)
  252. {
  253. FILE* pLogFile = NULL;
  254. pLogFile = fopen(ERRORLOG_FILENAME, "a+");
  255. if (!pLogFile)
  256. return;
  257. fputs(data.c_str(), pLogFile);
  258. fclose(pLogFile);
  259. }
  260. //------------------------------------------------------------------------------------------------------------------------------------
  261. // Fills outOutputBuffer with the find error string.
  262. //------------------------------------------------------------------------------------------------------------------------------------
  263. void LogMgr::GetOutputBuffer(std::string& outOutputBuffer, const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
  264. {
  265. if (!tag.empty())
  266. outOutputBuffer = "[" + tag + "] " + message;
  267. else
  268. outOutputBuffer = message;
  269. if (funcName != NULL)
  270. {
  271. outOutputBuffer += "\nFunction: ";
  272. outOutputBuffer += funcName;
  273. }
  274. if (sourceFile != NULL)
  275. {
  276. outOutputBuffer += "\n";
  277. outOutputBuffer += sourceFile;
  278. }
  279. if (lineNum != 0)
  280. {
  281. outOutputBuffer += "\nLine: ";
  282. char lineNumBuffer[11];
  283. memset(lineNumBuffer, 0, sizeof(char));
  284. snprintf(lineNumBuffer, sizeof(lineNumBuffer), "%u", lineNum);
  285. outOutputBuffer += lineNumBuffer;
  286. }
  287. outOutputBuffer += "\n";
  288. }
  289. //-----------------------------------------------------------------------------------------------------------------------
  290. // ErrorMessenger
  291. //-----------------------------------------------------------------------------------------------------------------------
  292. Logger::ErrorMessenger::ErrorMessenger(void)
  293. {
  294. s_pLogMgr->AddErrorMessenger(this);
  295. m_enabled = true;
  296. }
  297. void Logger::ErrorMessenger::Show(const std::string& errorMessage, bool isFatal, const char* funcName, const char* sourceFile, unsigned int lineNum)
  298. {
  299. if (m_enabled)
  300. {
  301. if (s_pLogMgr->Error(errorMessage, isFatal, funcName, sourceFile, lineNum) == LogMgr::LOGMGR_ERROR_IGNORE)
  302. m_enabled = false;
  303. }
  304. }
  305. //-----------------------------------------------------------------------------------------------------------------------
  306. // C Interface
  307. //-----------------------------------------------------------------------------------------------------------------------
  308. namespace Logger {
  309. void Init(const char* loggingConfigFilename)
  310. {
  311. if (!s_pLogMgr)
  312. {
  313. s_pLogMgr = GCC_NEW LogMgr;
  314. s_pLogMgr->Init(loggingConfigFilename);
  315. }
  316. }
  317. void Destroy(void)
  318. {
  319. delete s_pLogMgr;
  320. s_pLogMgr = NULL;
  321. }
  322. void Log(const string& tag, const string& message, const char* funcName, const char* sourceFile, unsigned int lineNum)
  323. {
  324. GCC_ASSERT(s_pLogMgr);
  325. s_pLogMgr->Log(tag, message, funcName, sourceFile, lineNum);
  326. }
  327. void SetDisplayFlags(const std::string& tag, unsigned char flags)
  328. {
  329. GCC_ASSERT(s_pLogMgr);
  330. s_pLogMgr->SetDisplayFlags(tag, flags);
  331. }
  332. } // end namespace Logger