1/* @TAG(CUSTOM) */
2/* zf log taken from: https://github.com/wonder-mice/zf_log
3 *
4 * "Software source code can not be printed on paper and sticked to a cat."
5 */
6
7#pragma once
8
9#include <utils/attribute.h>
10
11/* To detect incompatible changes you can define ZF_LOG_VERSION_REQUIRED to
12 * the current value of ZF_LOG_VERSION before including this file (or via
13 * compiler command line):
14 *
15 *   #define ZF_LOG_VERSION_REQUIRED 1
16 *   #include <zf_log.h>
17 *
18 * In that case compilation will fail when included file has incompatible
19 * version.
20 */
21#define ZF_LOG_VERSION 1
22#if defined(ZF_LOG_VERSION_REQUIRED)
23	#if ZF_LOG_VERSION_REQUIRED != ZF_LOG_VERSION
24		#error different zf_log version required
25	#endif
26#endif
27
28/* Log level guideline:
29 * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected.
30 *   Process can't continue and must be terminated. In other words, semantic is
31 *   close to assert(). zf_log will call abort() after printing the log message.
32 *   Example: division by zero, unexpected modifications from other thread.
33 * - ZF_LOG_ERROR - happened something impossible and absolutely unexpected, but
34 *   process is able to recover and continue execution.
35 *   Example: out of memory (could also be FATAL if not handled properly).
36 * - ZF_LOG_WARN - happened something that *usually* should not happen and
37 *   significantly changes application behavior for some period of time.
38 *   Example: configuration file not found, auth error.
39 * - ZF_LOG_INFO - happened significant life cycle event or major state
40 *   transition.
41 *   Example: app started, user logged in.
42 * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the
43 *   execution path.
44 * - ZF_LOG_VERBOSE - all other events.
45 *
46 * Ideally, log file of debugged, well tested, production ready application
47 * should be empty (no messages with level ZF_LOG_INFO or higher) or very small.
48 */
49#define ZF_LOG_VERBOSE 1
50#define ZF_LOG_DEBUG   2
51#define ZF_LOG_INFO    3
52#define ZF_LOG_WARN    4
53#define ZF_LOG_ERROR   5
54#define ZF_LOG_FATAL   0xFFFF
55#define ZF_LOG_NONE    0xFFFF
56
57/* Log level configuration:
58 * - ZF_LOG_DEF_LEVEL - defines current log level. Only messages with that level
59 *   and higher will be logged (if ZF_LOG_LEVEL is undefined).
60 * - ZF_LOG_LEVEL - overrides current log level. Only messages with that level
61 *   and higher will be logged.
62 *
63 * Current log level is a compile time check and has no runtime overhead.
64 *
65 * Common pattern is to define ZF_LOG_DEF_LEVEL in the build script (e.g.
66 * Makefile, CMakeLists.txt) for the entire project/target:
67 *
68 *   CC_ARGS := -DZF_LOG_DEF_LEVEL=ZF_LOG_WARN
69 *
70 * And when necessary override it with ZF_LOG_LEVEL in .c/.cpp files (before
71 * including zf_log.h):
72 *
73 *   #define ZF_LOG_LEVEL ZF_LOG_VERBOSE
74 *   #include <zf_log.h>
75 *
76 * Defining either ZF_LOG_DEF_LEVEL or ZF_LOG_LEVEL in header file is usually
77 * undesired and produces weird results.
78 *
79 * If both ZF_LOG_DEF_LEVEL and ZF_LOG_LEVEL are undefined, then ZF_LOG_INFO
80 * will be used for release builds (NDEBUG is defined) and ZF_LOG_DEBUG
81 * otherwise.
82 *
83 * When log message has level bellow current log level it will be compiled out
84 * and its arguments will NOT be evaluated (no "unused variable" warning will be
85 * generated for variables that are only used in compiled out log messages).
86 */
87#if defined(ZF_LOG_LEVEL)
88	#define _ZF_LOG_LEVEL ZF_LOG_LEVEL
89#elif defined(ZF_LOG_DEF_LEVEL)
90	#define _ZF_LOG_LEVEL ZF_LOG_DEF_LEVEL
91#else
92	#ifdef NDEBUG
93		#define _ZF_LOG_LEVEL ZF_LOG_INFO
94	#else
95		#define _ZF_LOG_LEVEL ZF_LOG_DEBUG
96	#endif
97#endif
98
99/* Log tag configuration:
100 * - ZF_LOG_DEF_TAG - defines default log tag.
101 * - ZF_LOG_TAG - overrides default log tag.
102 *
103 * When defined, value must be a string constant (in double quotes):
104 *
105 *   #define ZF_LOG_TAG "MAIN"
106 *   #include <zf_log.h>
107 *
108 * Defining either ZF_LOG_DEF_TAG or ZF_LOG_TAG in header files usually
109 * undesired and produces weird results.
110 *
111 * If both ZF_LOG_DEF_TAG and ZF_LOG_TAG are undefined no tag will be added to
112 * the log message.
113 */
114#if defined(ZF_LOG_TAG)
115	#define _ZF_LOG_TAG ZF_LOG_TAG
116#elif defined(ZF_LOG_DEF_TAG)
117	#define _ZF_LOG_TAG ZF_LOG_DEF_TAG
118#else
119	#define _ZF_LOG_TAG 0
120#endif
121
122/* When defined, all produced linker symbols will be prefixed with the specified
123 * value. That allows to use zf_log privately in another library without
124 * exposing zf_log symbols in their original form (so library will not have
125 * externaly visible dependency on zf_log). Value must be without quotes:
126 *
127 *   CC_ARGS := -DZF_LOG_LIBRARY_PREFIX=my_lib
128 */
129#ifdef ZF_LOG_LIBRARY_PREFIX
130	#define _ZF_LOG_DECOR__(prefix, name) prefix ## name
131	#define _ZF_LOG_DECOR_(prefix, name) _ZF_LOG_DECOR__(prefix, name)
132	#define _ZF_LOG_DECOR(name) _ZF_LOG_DECOR_(ZF_LOG_LIBRARY_PREFIX, name)
133
134	#define zf_log_set_tag_prefix _ZF_LOG_DECOR(zf_log_set_tag_prefix)
135	#define zf_log_set_mem_width _ZF_LOG_DECOR(zf_log_set_mem_width)
136	#define zf_log_set_output_level _ZF_LOG_DECOR(zf_log_set_output_level)
137	#define zf_log_set_output_callback _ZF_LOG_DECOR(zf_log_set_output_callback)
138	#define _zf_log_output_lvl _ZF_LOG_DECOR(_zf_log_output_lvl)
139	#define _zf_log_write_d _ZF_LOG_DECOR(_zf_log_write_d)
140	#define _zf_log_write _ZF_LOG_DECOR(_zf_log_write)
141	#define _zf_log_write_mem_d _ZF_LOG_DECOR(_zf_log_write_mem_d)
142	#define _zf_log_write_mem _ZF_LOG_DECOR(_zf_log_write_mem)
143#endif
144
145/* Runtime configuration */
146#ifdef __cplusplus
147extern "C" {
148#endif
149
150/* Set tag prefix. Prefix will be separated from the tag with dot ('.').
151 * Use 0 or empty string to disable (default). Common use is to set it to
152 * the process (or target) name (e.g. to separate client and server process).
153 */
154void zf_log_set_tag_prefix(const char *const prefix);
155
156/* Set number of bytes per log line in memory dump.
157 */
158void zf_log_set_mem_width(const unsigned w);
159
160/* Set output log level. Output log level is a run time check and has low
161 * overhead of compare operation and conditional jump. When the log message has
162 * level bellow output log level it will not be logged and its arguments will
163 * NOT be evaluated.
164 *
165 * Since all messages that are below current log level are compiled out,
166 * only messages that are on or above the current log level are affected by the
167 * output log level check.
168 *
169 * Output log level can be changed at any time during program execution.
170 */
171void zf_log_set_output_level(const int lvl);
172
173typedef struct zf_log_output_ctx
174{
175	int lvl;
176	const char *tag;
177	char *buf; /* Buffer start */
178	char *e; /* Buffer end (last position where EOL with 0 could be written) */
179	char *p; /* Buffer content end (append position) */
180	char *tag_b; /* Prefixed tag start */
181	char *tag_e; /* Prefixed tag end (if != tag_b, points to msg separator) */
182	char *msg_b; /* Message start (expanded format string) */
183}
184zf_log_output_ctx;
185
186typedef void (*zf_log_output_cb)(zf_log_output_ctx *ctx);
187
188/* Set output callback function. It will be called for each log line allowed
189 * by both current log level and output log level. Callback function is allowed
190 * to modify content of the buffers pointed by the ctx, but it's not allowed to
191 * modify buffer pointers and other fields.
192 */
193void zf_log_set_output_callback(const zf_log_output_cb cb);
194
195#ifdef __cplusplus
196}
197#endif
198
199/* Checking current log level at compile time (ignoring output log level).
200 * For example:
201 *
202 *   #if ZF_LOG_ALLOW_DEBUG
203 *       const char *const g_enum_strings[] = {
204 *           "enum_value_0", "enum_value_1", "enum_value_2"
205 *       };
206 *   #endif
207 *   // ...
208 *   #if ZF_LOG_ALLOW_DEBUG
209 *       ZF_LOGD("enum value: %s", g_enum_strings[v]);
210 *   #endif
211 */
212#define ZF_LOG_ALLOW(lvl) ((lvl) >= _ZF_LOG_LEVEL)
213#define ZF_LOG_ALLOW_VERBOSE ZF_LOG_ALLOW(ZF_LOG_VERBOSE)
214#define ZF_LOG_ALLOW_DEBUG ZF_LOG_ALLOW(ZF_LOG_DEBUG)
215#define ZF_LOG_ALLOW_INFO ZF_LOG_ALLOW(ZF_LOG_INFO)
216#define ZF_LOG_ALLOW_WARN ZF_LOG_ALLOW(ZF_LOG_WARN)
217#define ZF_LOG_ALLOW_ERROR ZF_LOG_ALLOW(ZF_LOG_ERROR)
218#define ZF_LOG_ALLOW_FATAL ZF_LOG_ALLOW(ZF_LOG_FATAL)
219
220/* Checking output log level at run time (taking into account current log
221 * level). For example:
222 *
223 *   if (ZF_LOG_OUTPUT_DEBUG)
224 *   {
225 *       char hash[65];
226 *       sha256(data_ptr, data_sz, hash);
227 *       ZF_LOGD("data: len=%u, sha256=%s", data_sz, hash);
228 *   }
229 */
230#define ZF_LOG_OUTPUT(lvl) \
231		(ZF_LOG_ALLOW((lvl)) && (lvl) >= _zf_log_output_lvl)
232#define ZF_LOG_OUTPUT_VERBOSE ZF_LOG_OUTPUT(ZF_LOG_VERBOSE)
233#define ZF_LOG_OUTPUT_DEBUG ZF_LOG_OUTPUT(ZF_LOG_DEBUG)
234#define ZF_LOG_OUTPUT_INFO ZF_LOG_OUTPUT(ZF_LOG_INFO)
235#define ZF_LOG_OUTPUT_WARN ZF_LOG_OUTPUT(ZF_LOG_WARN)
236#define ZF_LOG_OUTPUT_ERROR ZF_LOG_OUTPUT(ZF_LOG_ERROR)
237#define ZF_LOG_OUTPUT_FATAL ZF_LOG_OUTPUT(ZF_LOG_FATAL)
238
239#define _ZF_LOG_PRINTFLIKE(a, b) FORMAT(printf, a, b)
240
241#ifdef __cplusplus
242extern "C" {
243#endif
244
245extern int _zf_log_output_lvl;
246
247void _zf_log_write_d(const char *const func,
248					 const char *const file, const unsigned line,
249					 const int lvl, const char *const tag,
250					 const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(6, 7);
251void _zf_log_write(const int lvl, const char *const tag,
252				   const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(3, 4);
253
254void _zf_log_write_mem_d(const char *const func,
255						 const char *const file, const unsigned line,
256						 const int lvl, const char *const tag,
257						 const void *const d, const unsigned d_sz,
258						 const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(8, 9);
259void _zf_log_write_mem(const int lvl, const char *const tag,
260					   const void *const d, const unsigned d_sz,
261					   const char *const fmt, ...) _ZF_LOG_PRINTFLIKE(5, 6);
262#ifdef __cplusplus
263}
264#endif
265
266/* Message logging macros:
267 * - ZF_LOGV("format string", args, ...)
268 * - ZF_LOGD("format string", args, ...)
269 * - ZF_LOGI("format string", args, ...)
270 * - ZF_LOGW("format string", args, ...)
271 * - ZF_LOGF("format string", args, ...)
272 *
273 * Memory logging macros:
274 * - ZF_LOGV_MEM(data_ptr, data_sz, "format string", args, ...)
275 * - ZF_LOGD_MEM(data_ptr, data_sz, "format string", args, ...)
276 * - ZF_LOGI_MEM(data_ptr, data_sz, "format string", args, ...)
277 * - ZF_LOGW_MEM(data_ptr, data_sz, "format string", args, ...)
278 * - ZF_LOGF_MEM(data_ptr, data_sz, "format string", args, ...)
279 *
280 * Format string follows printf() conventions. Both data_ptr and data_sz could
281 * be 0.
282 */
283#ifdef NDEBUG
284	#define _ZF_LOG_IMP(lvl, tag, ...) \
285			do { \
286				if (ZF_LOG_OUTPUT(lvl)) \
287					_zf_log_write(lvl, tag, __VA_ARGS__); \
288			} while (0)
289	#define _ZF_LOG_MEM_IMP(lvl, tag, d, d_sz, ...) \
290			do { \
291				if (ZF_LOG_OUTPUT(lvl)) \
292					_zf_log_write_mem(lvl, tag, d, d_sz, __VA_ARGS__); \
293			} while (0)
294#else
295	#define _ZF_LOG_IMP(lvl, tag, ...) \
296			do { \
297				if (ZF_LOG_OUTPUT(lvl)) \
298					_zf_log_write_d(__FUNCTION__, __FILE__, __LINE__, \
299							lvl, tag, __VA_ARGS__); \
300			} while (0)
301	#define _ZF_LOG_MEM_IMP(lvl, tag, d, d_sz, ...) \
302			do { \
303				if (ZF_LOG_OUTPUT(lvl)) \
304					_zf_log_write_mem_d(__FUNCTION__, __FILE__, __LINE__, \
305							lvl, tag, d, d_sz, __VA_ARGS__); \
306			} while (0)
307#endif
308
309static inline void _zf_log_unused(const int dummy, ...) {(void)dummy;}
310
311#define _ZF_LOG_UNUSED(...) \
312		do { if (0) _zf_log_unused(0, __VA_ARGS__); } while (0)
313
314#if ZF_LOG_ALLOW_VERBOSE
315	#define ZF_LOGV(...) \
316			_ZF_LOG_IMP(ZF_LOG_VERBOSE, _ZF_LOG_TAG, __VA_ARGS__)
317	#define ZF_LOGV_MEM(...) \
318			_ZF_LOG_MEM_IMP(ZF_LOG_VERBOSE, _ZF_LOG_TAG, __VA_ARGS__)
319#else
320	#define ZF_LOGV(...) _ZF_LOG_UNUSED(__VA_ARGS__)
321	#define ZF_LOGV_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
322#endif
323
324#if ZF_LOG_ALLOW_DEBUG
325	#define ZF_LOGD(...) \
326			_ZF_LOG_IMP(ZF_LOG_DEBUG, _ZF_LOG_TAG, __VA_ARGS__)
327	#define ZF_LOGD_MEM(...) \
328			_ZF_LOG_MEM_IMP(ZF_LOG_DEBUG, _ZF_LOG_TAG, __VA_ARGS__)
329#else
330	#define ZF_LOGD(...) _ZF_LOG_UNUSED(__VA_ARGS__)
331	#define ZF_LOGD_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
332#endif
333
334#if ZF_LOG_ALLOW_INFO
335	#define ZF_LOGI(...) \
336			_ZF_LOG_IMP(ZF_LOG_INFO, _ZF_LOG_TAG, __VA_ARGS__)
337	#define ZF_LOGI_MEM(...) \
338			_ZF_LOG_MEM_IMP(ZF_LOG_INFO, _ZF_LOG_TAG, __VA_ARGS__)
339#else
340	#define ZF_LOGI(...) _ZF_LOG_UNUSED(__VA_ARGS__)
341	#define ZF_LOGI_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
342#endif
343
344#if ZF_LOG_ALLOW_WARN
345	#define ZF_LOGW(...) \
346			_ZF_LOG_IMP(ZF_LOG_WARN, _ZF_LOG_TAG, __VA_ARGS__)
347	#define ZF_LOGW_MEM(...) \
348			_ZF_LOG_MEM_IMP(ZF_LOG_WARN, _ZF_LOG_TAG, __VA_ARGS__)
349#else
350	#define ZF_LOGW(...) _ZF_LOG_UNUSED(__VA_ARGS__)
351	#define ZF_LOGW_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
352#endif
353
354#if ZF_LOG_ALLOW_ERROR
355	#define ZF_LOGE(...) \
356			_ZF_LOG_IMP(ZF_LOG_ERROR, _ZF_LOG_TAG, __VA_ARGS__)
357	#define ZF_LOGE_MEM(...) \
358			_ZF_LOG_MEM_IMP(ZF_LOG_ERROR, _ZF_LOG_TAG, __VA_ARGS__)
359#else
360	#define ZF_LOGE(...) _ZF_LOG_UNUSED(__VA_ARGS__)
361	#define ZF_LOGE_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
362#endif
363
364#if ZF_LOG_ALLOW_FATAL
365	#define ZF_LOGF(...) do { \
366			    _ZF_LOG_IMP(ZF_LOG_FATAL, _ZF_LOG_TAG, __VA_ARGS__); UNREACHABLE();\
367    } while (0)
368	#define ZF_LOGF_MEM(...) do { \
369			    _ZF_LOG_MEM_IMP(ZF_LOG_FATAL, _ZF_LOG_TAG, __VA_ARGS__); UNREACHABLE();\
370    } while (0)
371#else
372	#define ZF_LOGF(...) _ZF_LOG_UNUSED(__VA_ARGS__)
373	#define ZF_LOGF_MEM(...) _ZF_LOG_UNUSED(__VA_ARGS__)
374#endif
375