diff options
| -rw-r--r-- | sokol_log.h | 184 |
1 files changed, 176 insertions, 8 deletions
diff --git a/sokol_log.h b/sokol_log.h index 50f5bdc0..3ea1dbba 100644 --- a/sokol_log.h +++ b/sokol_log.h @@ -27,7 +27,40 @@ SOKOL_DEBUG - by default this is defined if _DEBUG is defined - FIXME: documentation + + OVERVIEW + ======== + sokol_log.h provides a default logging callback for other sokol headers. + + To use the default log callback, just include sokol_log.h and provide + a function pointer to the 'slog_func' function when setting up the + sokol library: + + For instance with sokol_audio.h: + + #include "sokol_log.h" + ... + saudio_setup(&(saudio_desc){ .logger.func = slog_func }); + + Logging output goes to stderr and/or a platform specific logging subsystem: + + - Windows: stderr + OutputDebugStringA() + - macOS/iOS/Linux: stderr + syslog() + - Emscripten: browser console + - Android: __android_log_write() + + In debug mode, a log message might look like this: + + [saudio] [error] /Users/floh/projects/sokol/sokol_audio.h:2422:0: COREAUDIO_NEW_OUTPUT_FAILED (id:32) + + The source path and line number is formatted in like compiler errors, in some IDEs (like VSCode) + such error messages are clickable. + + In release mode, logging is less verbose as to not bloat the executable with string data, but you still get + enough information to identify an error: + + [saudio] [error] line:2422 id:32 + LICENSE ======= @@ -84,7 +117,7 @@ extern "C" { } }); */ -void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, int line_nr, const char* filename, void* user_data); +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); #ifdef __cplusplus } // extern "C" @@ -130,7 +163,7 @@ void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const cha // platform detection #if defined(__APPLE__) #define _SLOG_APPLE (1) -#if defined(__EMSCRIPTEN__) +#elif defined(__EMSCRIPTEN__) #define _SLOG_EMSCRIPTEN (1) #elif defined(_WIN32) #define _SLOG_WINDOWS (1) @@ -142,11 +175,10 @@ void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const cha #error "sokol_log.h: unknown platform" #endif -#include <stdio.h> +#include <stdio.h> // fputs +#include <stddef.h> // size_t -#if defined(_SLOG_APPLE) -#include <os/log.h> -#elif defined(_SLOG_WINDOWS) +#if defined(_SLOG_WINDOWS) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -154,7 +186,143 @@ void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const cha #define NOMINMAX #endif #include <windows.h> +#elif defined(_SLOG_ANDROID) +#include <android/log.h> +#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) +#include <syslog.h> +#endif +_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, const char* end) { + if (str) { + char c; + while (((c = *str++) != 0) && (dst < (end - 1))) { + *dst++ = c; + } + } + *dst = 0; + return dst; +} +_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { + const size_t max_digits_and_null = 11; + if (buf_size < max_digits_and_null) { + return 0; + } + char* p = buf + max_digits_and_null; + *--p = 0; + do { + *--p = '0' + (x % 10); + x /= 10; + } while (x != 0); + return p; +} -#endif // SOKOL_LOG_IMPL
\ No newline at end of file +#if defined(_SLOG_EMSCRIPTEN) +EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { + const str = UTF8ToString(c_str); + switch (level) { + case 0: console.error(str); break; + case 1: console.error(str); break; + case 2: console.warn(str); break; + default: console.info(str); break; + } +}); +#endif + +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) { + _SOKOL_UNUSED(user_data); + + const char* log_level_str; + switch (log_level) { + case 0: log_level_str = "panic"; break; + case 1: log_level_str = "error"; break; + case 2: log_level_str = "warning"; break; + default: log_level_str = "info"; break; + } + + // build log output line + char line_buf[256]; + char* str = &line_buf[0]; + const char* end = str + sizeof(line_buf); + char num_buf[32]; + if (tag) { + str = _slog_append("[", str, end); + str = _slog_append(tag, str, end); + str = _slog_append("] ", str, end); + } + str = _slog_append("[", str, end); + str = _slog_append(log_level_str, str, end); + str = _slog_append("] ", str, end); + // if a filename is provided, build a clickable log message that's compatible with compiler error messages + if (filename) { + #if defined(_MSC_VER) + // MSVC compiler error format + str = _slog_append(filename, str, end); + str = _slog_append("(", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append("): "); + #else + // gcc/clang compiler error format + str = _slog_append(filename, str, end); + str = _slog_append(":", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append(":0: ", str, end); + #endif + if (message) { + str = _slog_append(message, str, end); + str = _slog_append(" ", str, end); + } + else { + str = _slog_append("??? ", str, end); + } + str = _slog_append("(id:", str, end); + str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); + str = _slog_append(")", str, end); + } + else { + // no filename provided, print what we can + str = _slog_append("line:", str, end); + str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); + str = _slog_append(" id:", str, end); + str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); + if (message) { + str = _slog_append(" msg: ", str, end); + str = _slog_append(message, str, end); + } + } + str = _slog_append("\n", str, end); + + // print to stderr? + #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) + fputs(line_buf, stderr); + #endif + + // platform specific logging calls + #if defined(_SLOG_WINDOWS) + OutputDebugStringA(line_buf); + #elif defined(_SLOG_ANDROID) + int prio; + switch (log_level) { + case 0: prio = ANDROID_LOG_FATAL; break; + case 1: prio = ANDROID_LOG_ERROR; break; + case 2: prio = ANDROID_LOG_WARN; break; + default: prio = ANDROID_LOG_INFO; break; + } + __android_log_write(prio, "SOKOL", line_buf); + #elif defined(_SLOG_EMSCRIPTEN) + slog_js_log(log_level, line_buf); + #elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE) + int prio; + switch (log_level) { + case 0: prio = LOG_CRIT; break; + case 1: prio = LOG_ERR; break; + case 2: prio = LOG_WARNING; break; + default: prio = LOG_INFO; break; + } + syslog(prio, "%s", line_buf); + #endif + if (0 == log_level) { + abort(); + } +} +#endif // SOKOL_LOG_IMPL |