Logger.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/File.h>
  6. #include <AnKi/Util/Logger.h>
  7. #include <AnKi/Util/System.h>
  8. #include <cstdarg>
  9. #include <cstdio>
  10. #include <cstdlib>
  11. #include <cstring>
  12. #if ANKI_OS_ANDROID
  13. # include <android/log.h>
  14. #endif
  15. #if ANKI_OS_WINDOWS
  16. # include <AnKi/Util/Win32Minimal.h>
  17. #endif
  18. namespace anki {
  19. Logger::Logger()
  20. {
  21. addMessageHandler(this, &defaultSystemMessageHandler);
  22. const Char* envVar = getenv("ANKI_LOG_VERBOSE");
  23. if(envVar && envVar == CString("1"))
  24. {
  25. m_verbosityEnabled = true;
  26. }
  27. }
  28. Logger::~Logger()
  29. {
  30. }
  31. void Logger::addMessageHandler(void* data, LoggerMessageHandlerCallback callback)
  32. {
  33. LockGuard<Mutex> lock(m_mutex);
  34. m_handlers[m_handlersCount++] = Handler{data, callback};
  35. }
  36. void Logger::removeMessageHandler(void* data, LoggerMessageHandlerCallback callback)
  37. {
  38. LockGuard<Mutex> lock(m_mutex);
  39. U i;
  40. for(i = 0; i < m_handlersCount; ++i)
  41. {
  42. if(m_handlers[i].m_callback == callback && m_handlers[i].m_data == data)
  43. {
  44. break;
  45. }
  46. }
  47. if(i < m_handlersCount)
  48. {
  49. for(U j = i + 1; j < m_handlersCount; ++j)
  50. {
  51. m_handlers[j - 1] = m_handlers[j];
  52. }
  53. --m_handlersCount;
  54. }
  55. }
  56. void Logger::writeInternal(const Char* file, int line, const Char* func, const Char* subsystem, LoggerMessageType type, const Char* threadName,
  57. const Char* msg)
  58. {
  59. const Char* baseFile = strrchr(file, (ANKI_OS_WINDOWS) ? '\\' : '/');
  60. baseFile = (baseFile) ? baseFile + 1 : file;
  61. LoggerMessageInfo inf = {baseFile, line, func, type, msg, subsystem, threadName};
  62. m_mutex.lock();
  63. U count = m_handlersCount;
  64. while(count-- != 0)
  65. {
  66. m_handlers[count].m_callback(m_handlers[count].m_data, inf);
  67. }
  68. m_mutex.unlock();
  69. if(type == LoggerMessageType::kFatal)
  70. {
  71. #if ANKI_OS_WINDOWS
  72. if(!IsDebuggerPresent())
  73. {
  74. abort();
  75. }
  76. else
  77. #endif
  78. {
  79. ANKI_DEBUG_BREAK();
  80. }
  81. }
  82. }
  83. void Logger::writeFormated(const Char* file, int line, const Char* func, const Char* subsystem, LoggerMessageType type, const Char* threadName,
  84. const Char* fmt, ...)
  85. {
  86. // Note: m_verbosityEnabled is not accessed in a thread-safe way. It doesn't really matter though
  87. if(type == LoggerMessageType::kVerbose && !m_verbosityEnabled)
  88. {
  89. return;
  90. }
  91. Array<Char, 256> buffer;
  92. va_list args;
  93. va_start(args, fmt);
  94. I len = vsnprintf(&buffer[0], sizeof(buffer), fmt, args);
  95. if(len < 0)
  96. {
  97. fprintf(stderr, "Logger::writeFormated() failed. Will not recover");
  98. abort();
  99. }
  100. else if(len < I(sizeof(buffer)))
  101. {
  102. writeInternal(file, line, func, subsystem, type, threadName, &buffer[0]);
  103. va_end(args);
  104. }
  105. else
  106. {
  107. // Not enough space.
  108. va_end(args);
  109. va_start(args, fmt);
  110. const PtrSize newSize = len + 1;
  111. Char* newBuffer = static_cast<Char*>(malloc(newSize));
  112. len = vsnprintf(newBuffer, newSize, fmt, args);
  113. writeInternal(file, line, func, subsystem, type, threadName, newBuffer);
  114. free(newBuffer);
  115. va_end(args);
  116. }
  117. }
  118. void Logger::defaultSystemMessageHandler(void*, const LoggerMessageInfo& info)
  119. {
  120. #if ANKI_OS_LINUX
  121. // More info about terminal colors:
  122. // https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
  123. FILE* out = nullptr;
  124. const Char* terminalColor = nullptr;
  125. const Char* terminalColorBg = nullptr;
  126. const Char* endTerminalColor = "\033[0m";
  127. switch(info.m_type)
  128. {
  129. case LoggerMessageType::kNormal:
  130. out = stdout;
  131. terminalColor = "\033[0;32m";
  132. terminalColorBg = "\033[1;42;37m";
  133. break;
  134. case LoggerMessageType::kVerbose:
  135. out = stdout;
  136. terminalColor = "\033[0;34m";
  137. terminalColorBg = "\033[1;44;37m";
  138. break;
  139. case LoggerMessageType::kError:
  140. out = stderr;
  141. terminalColor = "\033[0;31m";
  142. terminalColorBg = "\033[1;41;37m";
  143. break;
  144. case LoggerMessageType::kWarning:
  145. out = stderr;
  146. terminalColor = "\033[2;33m";
  147. terminalColorBg = "\033[1;43;37m";
  148. break;
  149. case LoggerMessageType::kFatal:
  150. out = stderr;
  151. terminalColor = "\033[0;31m";
  152. terminalColorBg = "\033[1;41;37m";
  153. break;
  154. default:
  155. ANKI_ASSERT(0);
  156. }
  157. static_assert(Thread::kThreadNameMaxLength == 15, "See file");
  158. if(!runningFromATerminal())
  159. {
  160. terminalColor = "";
  161. terminalColorBg = "";
  162. endTerminalColor = "";
  163. }
  164. fprintf(out, "%s[%s][%-4s]%s%s %s [%s:%d][%s][%s]%s\n", terminalColorBg, kLoggerMessageTypeText[U(info.m_type)],
  165. info.m_subsystem ? info.m_subsystem : "N/A ", endTerminalColor, terminalColor, info.m_msg, info.m_file, info.m_line, info.m_func,
  166. info.m_threadName, endTerminalColor);
  167. #elif ANKI_OS_WINDOWS
  168. WORD attribs = 0;
  169. FILE* out = nullptr;
  170. switch(info.m_type)
  171. {
  172. case LoggerMessageType::kNormal:
  173. attribs |= FOREGROUND_GREEN;
  174. out = stdout;
  175. break;
  176. case LoggerMessageType::kVerbose:
  177. attribs |= FOREGROUND_BLUE;
  178. out = stdout;
  179. break;
  180. case LoggerMessageType::kError:
  181. attribs |= FOREGROUND_RED;
  182. out = stderr;
  183. break;
  184. case LoggerMessageType::kWarning:
  185. attribs |= FOREGROUND_RED | FOREGROUND_GREEN;
  186. out = stderr;
  187. break;
  188. case LoggerMessageType::kFatal:
  189. attribs |= FOREGROUND_RED | FOREGROUND_INTENSITY;
  190. out = stderr;
  191. break;
  192. default:
  193. ANKI_ASSERT(0);
  194. out = stdout;
  195. }
  196. HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  197. if(consoleHandle != nullptr)
  198. {
  199. CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
  200. WORD savedAttribs;
  201. // Save current attributes
  202. GetConsoleScreenBufferInfo(consoleHandle, &consoleInfo);
  203. savedAttribs = consoleInfo.wAttributes;
  204. // Apply changes
  205. SetConsoleTextAttribute(consoleHandle, attribs);
  206. // Print
  207. fprintf(out, "[%s][%-4s] %s [%s:%d][%s][%s]\n", kLoggerMessageTypeText[info.m_type], info.m_subsystem ? info.m_subsystem : "N/A", info.m_msg,
  208. info.m_file, info.m_line, info.m_func, info.m_threadName);
  209. // Restore state
  210. SetConsoleTextAttribute(consoleHandle, savedAttribs);
  211. }
  212. #elif ANKI_OS_ANDROID
  213. I32 andMsgType = ANDROID_LOG_INFO;
  214. switch(info.m_type)
  215. {
  216. case LoggerMessageType::kNormal:
  217. case LoggerMessageType::kVerbose:
  218. andMsgType = ANDROID_LOG_INFO;
  219. break;
  220. case LoggerMessageType::kError:
  221. andMsgType = ANDROID_LOG_ERROR;
  222. break;
  223. case LoggerMessageType::kWarning:
  224. andMsgType = ANDROID_LOG_WARN;
  225. break;
  226. case LoggerMessageType::kFatal:
  227. andMsgType = ANDROID_LOG_ERROR;
  228. break;
  229. default:
  230. ANKI_ASSERT(0);
  231. }
  232. static_assert(Thread::kThreadNameMaxLength == 15, "See file");
  233. __android_log_print(andMsgType, "AnKi", "[%s][%-4s] %s [%s:%d][%s][%s]\n", kLoggerMessageTypeText[info.m_type],
  234. info.m_subsystem ? info.m_subsystem : "N/A ", info.m_msg, info.m_file, info.m_line, info.m_func, info.m_threadName);
  235. #else
  236. # error "Not implemented"
  237. #endif
  238. }
  239. void Logger::fileMessageHandler(void* pfile, const LoggerMessageInfo& info)
  240. {
  241. File* file = reinterpret_cast<File*>(pfile);
  242. Error err = file->writeTextf("[%s] %s (%s:%d %s)\n", kLoggerMessageTypeText[info.m_type], info.m_msg, info.m_file, info.m_line, info.m_func);
  243. if(!err)
  244. {
  245. err = file->flush();
  246. }
  247. }
  248. } // end namespace anki