1 /*
2  * This software is licensed under the terms of the MIT License.
3  * See COPYING for further information.
4  * ---
5  * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6  * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7  */
8 
9 #ifndef IGUARD_log_h
10 #define IGUARD_log_h
11 
12 #include "taisei.h"
13 
14 #include <SDL.h>
15 
16 enum {
17 	_LOG_DEBUG_ID,
18 	_LOG_INFO_ID,
19 	_LOG_WARN_ID,
20 	_LOG_ERROR_ID,
21 	_LOG_FATAL_ID,
22 
23 	_LOG_NOABORT_BIT,
24 };
25 
26 typedef enum LogLevel {
27 	LOG_NONE  = 0,
28 	LOG_DEBUG = (1 << _LOG_DEBUG_ID),
29 	LOG_INFO  = (1 << _LOG_INFO_ID),
30 	LOG_WARN  = (1 << _LOG_WARN_ID),
31 	LOG_ERROR = (1 << _LOG_ERROR_ID),
32 	LOG_FATAL = (1 << _LOG_FATAL_ID),
33 
34 	LOG_SPAM = LOG_DEBUG | LOG_INFO,
35 	LOG_ALERT = LOG_WARN | LOG_ERROR | LOG_FATAL,
36 
37 	LOG_ALL = LOG_SPAM | LOG_ALERT,
38 
39 	LOG_NOABORT_BIT = (1 << _LOG_NOABORT_BIT),
40 
41 	LOG_FAKEFATAL = LOG_FATAL | LOG_NOABORT_BIT,
42 } LogLevel;
43 
44 #ifndef LOG_DEFAULT_LEVELS
45 	#define LOG_DEFAULT_LEVELS LOG_ALL
46 #endif
47 
48 #ifndef LOG_DEFAULT_LEVELS_FILE
49 	#ifdef __EMSCRIPTEN__
50 		#define LOG_DEFAULT_LEVELS_FILE LOG_NONE
51 	#else
52 		#define LOG_DEFAULT_LEVELS_FILE LOG_ALL
53 	#endif
54 #endif
55 
56 #ifndef LOG_DEFAULT_LEVELS_CONSOLE
57 	#ifdef DEBUG
58 		#define LOG_DEFAULT_LEVELS_CONSOLE LOG_ALL
59 	#elif defined __EMSCRIPTEN__
60 		#define LOG_DEFAULT_LEVELS_CONSOLE LOG_ALERT | LOG_INFO
61 	#else
62 		#define LOG_DEFAULT_LEVELS_CONSOLE LOG_ALERT
63 	#endif
64 #endif
65 
66 #ifndef LOG_DEFAULT_LEVELS_STDOUT
67 	#ifdef __EMSCRIPTEN__
68 		#define LOG_DEFAULT_LEVELS_STDOUT LOG_ALL
69 	#else
70 		#define LOG_DEFAULT_LEVELS_STDOUT LOG_SPAM
71 	#endif
72 #endif
73 
74 #ifndef LOG_DEFAULT_LEVELS_STDERR
75 	#ifdef __EMSCRIPTEN__
76 		#define LOG_DEFAULT_LEVELS_STDERR LOG_NONE
77 	#else
78 		#define LOG_DEFAULT_LEVELS_STDERR LOG_ALERT
79 	#endif
80 #endif
81 
82 typedef struct LogEntry {
83 	const char *message;
84 	const char *file;
85 	const char *func;
86 	uint time;
87 	uint line;
88 	LogLevel level;
89 } LogEntry;
90 
91 typedef struct FormatterObj FormatterObj;
92 
93 struct FormatterObj {
94 	int (*format)(FormatterObj *self, char *buf, size_t buf_size, LogEntry *entry);
95 	void (*free)(FormatterObj *self);
96 	void *data;
97 };
98 
99 // "constructor". initializer, actually.
100 typedef void Formatter(FormatterObj *obj, const SDL_RWops *output);
101 
102 extern Formatter log_formatter_file;
103 extern Formatter log_formatter_console;
104 
105 void log_init(LogLevel lvls);
106 void log_shutdown(void);
107 void log_add_output(LogLevel levels, SDL_RWops *output, Formatter *formatter) attr_nonnull(3);
108 void log_backtrace(LogLevel lvl);
109 LogLevel log_parse_levels(LogLevel lvls, const char *lvlmod) attr_nodiscard;
110 bool log_initialized(void) attr_nodiscard;
111 void log_set_gui_error_appendix(const char *message);
112 
113 #if defined(DEBUG) && !defined(__EMSCRIPTEN__)
114 	#define log_debug(...) log_custom(LOG_DEBUG, __VA_ARGS__)
115 	#undef UNREACHABLE
116 	#define UNREACHABLE log_fatal("This code should never be reached")
117 #else
118 	#define log_debug(...)
119 	#define LOG_NO_FILENAMES
120 #endif
121 
122 #ifdef LOG_NO_FILENAMES
123 	#define _do_log(func, lvl, ...) (func)(lvl, __func__, "<unknown>", 0, __VA_ARGS__)
124 #else
125 	#define _do_log(func, lvl, ...) (func)(lvl, __func__, __FILE__, __LINE__, __VA_ARGS__)
126 #endif
127 
128 #define log_custom(lvl, ...) _do_log(_taisei_log, lvl, __VA_ARGS__)
129 #define log_info(...) log_custom(LOG_INFO, __VA_ARGS__)
130 #define log_warn(...) log_custom(LOG_WARN, __VA_ARGS__)
131 #define log_error(...) log_custom(LOG_ERROR, __VA_ARGS__)
132 #define log_fatal(...) _do_log(_taisei_log_fatal, LOG_FATAL, __VA_ARGS__)
133 
134 #define log_sdl_error(lvl, funcname) log_custom(lvl, "%s() failed: %s", funcname, SDL_GetError())
135 
136 //
137 // don't call these directly, use the macros
138 //
139 
140 void _taisei_log(LogLevel lvl, const char *funcname, const char *filename, uint line, const char *fmt, ...)
141 	attr_printf(5, 6);
142 
143 noreturn void _taisei_log_fatal(LogLevel lvl, const char *funcname, const char *filename, uint line, const char *fmt, ...)
144 	attr_printf(5, 6);
145 
146 #endif // IGUARD_log_h
147