sokol_log.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. #if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL)
  2. #define SOKOL_LOG_IMPL
  3. #endif
  4. #ifndef SOKOL_LOG_INCLUDED
  5. /*
  6. sokol_log.h -- common logging callback for sokol headers
  7. Project URL: https://github.com/floooh/sokol
  8. Example code: https://github.com/floooh/sokol-samples
  9. Do this:
  10. #define SOKOL_IMPL or
  11. #define SOKOL_LOG_IMPL
  12. before you include this file in *one* C or C++ file to create the
  13. implementation.
  14. Optionally provide the following defines when building the implementation:
  15. SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
  16. SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
  17. SOKOL_LOG_API_DECL - public function declaration prefix (default: extern)
  18. SOKOL_API_DECL - same as SOKOL_GFX_API_DECL
  19. SOKOL_API_IMPL - public function implementation prefix (default: -)
  20. Optionally define the following for verbose output:
  21. SOKOL_DEBUG - by default this is defined if _DEBUG is defined
  22. OVERVIEW
  23. ========
  24. sokol_log.h provides a default logging callback for other sokol headers.
  25. To use the default log callback, just include sokol_log.h and provide
  26. a function pointer to the 'slog_func' function when setting up the
  27. sokol library:
  28. For instance with sokol_audio.h:
  29. #include "sokol_log.h"
  30. ...
  31. saudio_setup(&(saudio_desc){ .logger.func = slog_func });
  32. Logging output goes to stderr and/or a platform specific logging subsystem
  33. (which means that in some scenarios you might see logging messages duplicated):
  34. - Windows: stderr + OutputDebugStringA()
  35. - macOS/iOS/Linux: stderr + syslog()
  36. - Emscripten: console.info()/warn()/error()
  37. - Android: __android_log_write()
  38. On Windows with sokol_app.h also note the runtime config items to make
  39. stdout/stderr output visible on the console for WinMain() applications
  40. via sapp_desc.win32_console_attach or sapp_desc.win32_console_create,
  41. however when running in a debugger on Windows, the logging output should
  42. show up on the debug output UI panel.
  43. In debug mode, a log message might look like this:
  44. [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0:
  45. SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas
  46. The source path and line number is formatted like compiler errors, in some IDEs (like VSCode)
  47. such error messages are clickable.
  48. In release mode, logging is less verbose as to not bloat the executable with string data, but you still get
  49. enough information to identify the type and location of an error:
  50. [sspine][error][id:12][line:3472]
  51. RULES FOR WRITING YOUR OWN LOGGING FUNCTION
  52. ===========================================
  53. - must be re-entrant because it might be called from different threads
  54. - must treat **all** provided string pointers as optional (can be null)
  55. - don't store the string pointers, copy the string data instead
  56. - must not return for log level panic
  57. LICENSE
  58. =======
  59. zlib/libpng license
  60. Copyright (c) 2023 Andre Weissflog
  61. This software is provided 'as-is', without any express or implied warranty.
  62. In no event will the authors be held liable for any damages arising from the
  63. use of this software.
  64. Permission is granted to anyone to use this software for any purpose,
  65. including commercial applications, and to alter it and redistribute it
  66. freely, subject to the following restrictions:
  67. 1. The origin of this software must not be misrepresented; you must not
  68. claim that you wrote the original software. If you use this software in a
  69. product, an acknowledgment in the product documentation would be
  70. appreciated but is not required.
  71. 2. Altered source versions must be plainly marked as such, and must not
  72. be misrepresented as being the original software.
  73. 3. This notice may not be removed or altered from any source
  74. distribution.
  75. */
  76. #define SOKOL_LOG_INCLUDED (1)
  77. #include <stdint.h>
  78. #if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL)
  79. #define SOKOL_LOG_API_DECL SOKOL_API_DECL
  80. #endif
  81. #ifndef SOKOL_LOG_API_DECL
  82. #if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL)
  83. #define SOKOL_LOG_API_DECL __declspec(dllexport)
  84. #elif defined(_WIN32) && defined(SOKOL_DLL)
  85. #define SOKOL_LOG_API_DECL __declspec(dllimport)
  86. #else
  87. #define SOKOL_LOG_API_DECL extern
  88. #endif
  89. #endif
  90. #ifdef __cplusplus
  91. extern "C" {
  92. #endif
  93. /*
  94. Plug this function into the 'logger.func' struct item when initializing any of the sokol
  95. headers. For instance for sokol_audio.h it would look like this:
  96. saudio_setup(&(saudio_desc){
  97. .logger = {
  98. .func = slog_func
  99. }
  100. });
  101. */
  102. SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data);
  103. #ifdef __cplusplus
  104. } // extern "C"
  105. #endif
  106. #endif // SOKOL_LOG_INCLUDED
  107. // ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██
  108. // ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
  109. // ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██
  110. // ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
  111. // ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████
  112. //
  113. // >>implementation
  114. #ifdef SOKOL_LOG_IMPL
  115. #define SOKOL_LOG_IMPL_INCLUDED (1)
  116. #ifndef SOKOL_API_IMPL
  117. #define SOKOL_API_IMPL
  118. #endif
  119. #ifndef SOKOL_DEBUG
  120. #ifndef NDEBUG
  121. #define SOKOL_DEBUG
  122. #endif
  123. #endif
  124. #ifndef SOKOL_ASSERT
  125. #include <assert.h>
  126. #define SOKOL_ASSERT(c) assert(c)
  127. #endif
  128. #ifndef _SOKOL_PRIVATE
  129. #if defined(__GNUC__) || defined(__clang__)
  130. #define _SOKOL_PRIVATE __attribute__((unused)) static
  131. #else
  132. #define _SOKOL_PRIVATE static
  133. #endif
  134. #endif
  135. #ifndef _SOKOL_UNUSED
  136. #define _SOKOL_UNUSED(x) (void)(x)
  137. #endif
  138. // platform detection
  139. #if defined(__APPLE__)
  140. #define _SLOG_APPLE (1)
  141. #elif defined(__EMSCRIPTEN__)
  142. #define _SLOG_EMSCRIPTEN (1)
  143. #elif defined(_WIN32)
  144. #define _SLOG_WINDOWS (1)
  145. #elif defined(__ANDROID__)
  146. #define _SLOG_ANDROID (1)
  147. #elif defined(__linux__) || defined(__unix__)
  148. #define _SLOG_LINUX (1)
  149. #else
  150. #error "sokol_log.h: unknown platform"
  151. #endif
  152. #include <stdlib.h> // abort
  153. #include <stdio.h> // fputs
  154. #include <stddef.h> // size_t
  155. #if defined(_SLOG_EMSCRIPTEN)
  156. #include <emscripten/emscripten.h>
  157. #elif defined(_SLOG_WINDOWS)
  158. #ifndef WIN32_LEAN_AND_MEAN
  159. #define WIN32_LEAN_AND_MEAN
  160. #endif
  161. #ifndef NOMINMAX
  162. #define NOMINMAX
  163. #endif
  164. #include <windows.h>
  165. #elif defined(_SLOG_ANDROID)
  166. #include <android/log.h>
  167. #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE)
  168. #include <syslog.h>
  169. #endif
  170. // size of line buffer (on stack!) in bytes including terminating zero
  171. #define _SLOG_LINE_LENGTH (512)
  172. _SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) {
  173. if (str) {
  174. char c;
  175. while (((c = *str++) != 0) && (dst < (end - 1))) {
  176. *dst++ = c;
  177. }
  178. }
  179. *dst = 0;
  180. return dst;
  181. }
  182. _SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) {
  183. const size_t max_digits_and_null = 11;
  184. if (buf_size < max_digits_and_null) {
  185. return 0;
  186. }
  187. char* p = buf + max_digits_and_null;
  188. *--p = 0;
  189. do {
  190. *--p = '0' + (x % 10);
  191. x /= 10;
  192. } while (x != 0);
  193. return p;
  194. }
  195. #if defined(_SLOG_EMSCRIPTEN)
  196. EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), {
  197. const str = UTF8ToString(c_str);
  198. switch (level) {
  199. case 0: console.error(str); break;
  200. case 1: console.error(str); break;
  201. case 2: console.warn(str); break;
  202. default: console.info(str); break;
  203. }
  204. })
  205. #endif
  206. SOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) {
  207. _SOKOL_UNUSED(user_data);
  208. const char* log_level_str;
  209. switch (log_level) {
  210. case 0: log_level_str = "panic"; break;
  211. case 1: log_level_str = "error"; break;
  212. case 2: log_level_str = "warning"; break;
  213. default: log_level_str = "info"; break;
  214. }
  215. // build log output line
  216. char line_buf[_SLOG_LINE_LENGTH];
  217. char* str = line_buf;
  218. char* end = line_buf + sizeof(line_buf);
  219. char num_buf[32];
  220. if (tag) {
  221. str = _slog_append("[", str, end);
  222. str = _slog_append(tag, str, end);
  223. str = _slog_append("]", str, end);
  224. }
  225. str = _slog_append("[", str, end);
  226. str = _slog_append(log_level_str, str, end);
  227. str = _slog_append("]", str, end);
  228. str = _slog_append("[id:", str, end);
  229. str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end);
  230. str = _slog_append("]", str, end);
  231. // if a filename is provided, build a clickable log message that's compatible with compiler error messages
  232. if (filename) {
  233. str = _slog_append(" ", str, end);
  234. #if defined(_MSC_VER)
  235. // MSVC compiler error format
  236. str = _slog_append(filename, str, end);
  237. str = _slog_append("(", str, end);
  238. str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
  239. str = _slog_append("): ", str, end);
  240. #else
  241. // gcc/clang compiler error format
  242. str = _slog_append(filename, str, end);
  243. str = _slog_append(":", str, end);
  244. str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
  245. str = _slog_append(":0: ", str, end);
  246. #endif
  247. }
  248. else {
  249. str = _slog_append("[line:", str, end);
  250. str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end);
  251. str = _slog_append("] ", str, end);
  252. }
  253. if (message) {
  254. str = _slog_append("\n\t", str, end);
  255. str = _slog_append(message, str, end);
  256. }
  257. str = _slog_append("\n\n", str, end);
  258. if (0 == log_level) {
  259. str = _slog_append("ABORTING because of [panic]\n", str, end);
  260. (void)str;
  261. }
  262. // print to stderr?
  263. #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE)
  264. fputs(line_buf, stderr);
  265. #endif
  266. // platform specific logging calls
  267. #if defined(_SLOG_WINDOWS)
  268. OutputDebugStringA(line_buf);
  269. #elif defined(_SLOG_ANDROID)
  270. int prio;
  271. switch (log_level) {
  272. case 0: prio = ANDROID_LOG_FATAL; break;
  273. case 1: prio = ANDROID_LOG_ERROR; break;
  274. case 2: prio = ANDROID_LOG_WARN; break;
  275. default: prio = ANDROID_LOG_INFO; break;
  276. }
  277. __android_log_write(prio, "SOKOL", line_buf);
  278. #elif defined(_SLOG_EMSCRIPTEN)
  279. slog_js_log(log_level, line_buf);
  280. #endif
  281. if (0 == log_level) {
  282. abort();
  283. }
  284. }
  285. #endif // SOKOL_LOG_IMPL