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