Commit | Line | Data |
---|---|---|
71436ae4 SM |
1 | /* |
2 | * SPDX-License-Identifier: MIT | |
3 | * | |
4 | * Copyright (c) 2016 wonder-mice | |
5 | * Copyright (c) 2016-2023 Philippe Proulx <pproulx@efficios.com> | |
6 | * | |
7 | * This is very inspired by zf_log.h (see | |
8 | * <https://github.com/wonder-mice/zf_log/>), but modified (mostly | |
9 | * stripped down) for the use cases of Babeltrace. | |
10 | */ | |
11 | ||
12 | #ifndef BABELTRACE_LOGGING_LOG_API_H | |
13 | #define BABELTRACE_LOGGING_LOG_API_H | |
14 | ||
15 | #include <errno.h> | |
16 | #include <stdlib.h> | |
17 | #include <stdio.h> | |
18 | #include <string.h> | |
19 | #include <glib.h> | |
20 | #include <babeltrace2/babeltrace.h> | |
21 | ||
22 | /* Access private `__BT_LOGGING_LEVEL_*` macros. */ | |
23 | #define __BT_IN_BABELTRACE_H | |
24 | #include <babeltrace2/logging-defs.h> | |
25 | #undef __BT_IN_BABELTRACE_H | |
26 | ||
27 | #include "common/macros.h" | |
28 | #include "common/assert.h" | |
29 | ||
30 | /* Log levels */ | |
31 | enum bt_log_level { | |
32 | BT_LOG_TRACE = __BT_LOGGING_LEVEL_TRACE, | |
33 | BT_LOG_DEBUG = __BT_LOGGING_LEVEL_DEBUG, | |
34 | BT_LOG_INFO = __BT_LOGGING_LEVEL_INFO, | |
35 | BT_LOG_WARNING = __BT_LOGGING_LEVEL_WARNING, | |
36 | BT_LOG_ERROR = __BT_LOGGING_LEVEL_ERROR, | |
37 | BT_LOG_FATAL = __BT_LOGGING_LEVEL_FATAL, | |
38 | BT_LOG_NONE = __BT_LOGGING_LEVEL_NONE, | |
39 | }; | |
40 | ||
41 | /* | |
42 | * `BT_LOG_MINIMAL_LEVEL` (constant integer, mandatory): minimal log | |
43 | * level to completely disable (not build) logging with levels that are | |
44 | * more verbose. | |
45 | */ | |
46 | #ifndef BT_LOG_MINIMAL_LEVEL | |
47 | # error "`BT_LOG_MINIMAL_LEVEL` must to defined" | |
48 | #endif | |
49 | ||
50 | /* Internal: portable printf()-like attribute */ | |
51 | #if defined(__printflike) | |
52 | # define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check) \ | |
53 | __printflike(_str_index, _first_to_check) | |
54 | #elif defined(__MINGW_PRINTF_FORMAT) | |
55 | # define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check) \ | |
56 | __attribute__((format(__MINGW_PRINTF_FORMAT, _str_index, _first_to_check))) | |
57 | #elif defined(__GNUC__) | |
58 | # define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check) \ | |
59 | __attribute__((format(__printf__, _str_index, _first_to_check))) | |
60 | #else | |
61 | # define _BT_LOG_PRINTFLIKE(_str_index, _first_to_check) | |
62 | #endif | |
63 | ||
64 | /* | |
65 | * Runs `_expr` if `_cond` is true. | |
66 | */ | |
67 | #define BT_LOG_IF(_cond, _expr) do { if (_cond) { _expr; } } while (0) | |
68 | ||
69 | /* | |
70 | * Returns whether or not `_lvl` is enabled at build time, that is, it's | |
71 | * equally or less verbose than `BT_LOG_MINIMAL_LEVEL`. | |
72 | * | |
73 | * See `BT_LOG_MINIMAL_LEVEL` to learn more. | |
74 | */ | |
75 | #define BT_LOG_ENABLED(_lvl) ((_lvl) >= BT_LOG_MINIMAL_LEVEL) | |
76 | #define BT_LOG_ENABLED_TRACE BT_LOG_ENABLED(__BT_LOGGING_LEVEL_TRACE) | |
77 | #define BT_LOG_ENABLED_DEBUG BT_LOG_ENABLED(__BT_LOGGING_LEVEL_DEBUG) | |
78 | #define BT_LOG_ENABLED_INFO BT_LOG_ENABLED(__BT_LOGGING_LEVEL_INFO) | |
79 | #define BT_LOG_ENABLED_WARNING BT_LOG_ENABLED(__BT_LOGGING_LEVEL_WARNING) | |
80 | #define BT_LOG_ENABLED_ERROR BT_LOG_ENABLED(__BT_LOGGING_LEVEL_ERROR) | |
81 | #define BT_LOG_ENABLED_FATAL BT_LOG_ENABLED(__BT_LOGGING_LEVEL_FATAL) | |
82 | ||
83 | /* | |
84 | * Returns whether or not `_lvl` is enabled at run time, that is, it's | |
85 | * equally or less verbose than some current (run-time) level | |
86 | * `_cur_lvl`. | |
87 | */ | |
88 | #define BT_LOG_ON_CUR_LVL(_lvl, _cur_lvl) \ | |
89 | G_UNLIKELY(BT_LOG_ENABLED((_lvl)) && (_lvl) >= (_cur_lvl)) | |
90 | ||
91 | #ifdef __cplusplus | |
92 | extern "C" { | |
93 | #endif | |
94 | ||
95 | /* | |
96 | * Writes the log message `msg` using the file name `file_name`, the | |
97 | * function name `func_name`, the line number `line_no`, the log level | |
98 | * `lvl`, and the tag `tag`. | |
99 | * | |
100 | * NOTE: This function writes unconditionally, without checking the | |
101 | * current (run-time) log level. | |
102 | */ | |
103 | void bt_log_write(const char *file_name, const char *func_name, | |
104 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
105 | const char *msg); | |
106 | ||
107 | /* | |
108 | * Calls bt_log_write(), formatting the log message through sprintf() | |
109 | * with `fmt` and the following arguments. | |
110 | */ | |
111 | void bt_log_write_printf(const char *file_name, const char *func_name, | |
112 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
113 | const char *fmt, ...) _BT_LOG_PRINTFLIKE(6, 7); | |
114 | ||
115 | /* | |
116 | * Writes the log message `msg` using the file name `file_name`, the | |
117 | * function name `func_name`, the line number `line_no`, the log level | |
118 | * `lvl`, and the tag `tag`, and also dumps `mem_len` bytes of | |
119 | * `mem_data`. | |
120 | * | |
121 | * NOTE: This function writes unconditionally, without checking the | |
122 | * current (run-time) log level. | |
123 | */ | |
124 | void bt_log_write_mem(const char *file_name, const char *func_name, | |
125 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
126 | const void *mem_data, size_t mem_len, | |
127 | const char *msg); | |
128 | ||
129 | /* | |
130 | * Calls bt_log_write_mem(), formatting the log message through | |
131 | * sprintf() with `fmt` and the following arguments. | |
132 | */ | |
133 | void bt_log_write_mem_printf(const char *file_name, const char *func_name, | |
134 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
135 | const void *mem_data, size_t mem_len, | |
136 | const char *fmt, ...) _BT_LOG_PRINTFLIKE(8, 9); | |
137 | ||
138 | /* | |
139 | * Writes: | |
140 | * | |
141 | * 1. `init_msg` | |
142 | * 2. The string `: ` | |
143 | * 3. The message corresponding to `errno` (current error number) | |
144 | * 4. `msg` | |
145 | * | |
146 | * This function uses the file name `file_name`, the function name | |
147 | * `func_name`, the line number `line_no`, the log level `lvl`, and the | |
148 | * tag `tag`. | |
149 | * | |
150 | * NOTE: This function writes unconditionally, without checking the | |
151 | * current (run-time) log level. | |
152 | */ | |
153 | void bt_log_write_errno(const char *file_name, const char *func_name, | |
154 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
155 | const char *init_msg, const char *msg); | |
156 | ||
157 | /* | |
158 | * Calls bt_log_write_errno(), formatting the log message through | |
159 | * sprintf() with `fmt` and the following arguments. | |
160 | */ | |
161 | void bt_log_write_errno_printf(const char *file_name, const char *func_name, | |
162 | unsigned int line_no, enum bt_log_level lvl, const char *tag, | |
163 | const char *init_msg, | |
164 | const char *fmt, ...) _BT_LOG_PRINTFLIKE(7, 8); | |
165 | ||
166 | #ifdef __cplusplus | |
167 | } | |
168 | #endif | |
169 | ||
170 | /* | |
171 | * Calls bt_log_write() if logging is enabled at run time for the | |
172 | * current level `_cur_lvl`. | |
173 | * | |
174 | * Passes the current file name, function name, and line number to | |
175 | * bt_log_write(). | |
176 | */ | |
177 | #define BT_LOG_WRITE_CUR_LVL(_lvl, _cur_lvl, _tag, _msg) \ | |
178 | do { \ | |
179 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
180 | bt_log_write(__FILE__, __func__, __LINE__, \ | |
181 | (_lvl), (_tag), (_msg)); \ | |
182 | } \ | |
183 | } while (0) | |
184 | ||
185 | /* | |
186 | * Calls bt_log_write_printf() if logging is enabled at run time for the | |
187 | * current level `_cur_lvl`. | |
188 | * | |
189 | * Passes the current file name, function name, and line number to | |
190 | * bt_log_write_printf(). | |
191 | */ | |
192 | #define BT_LOG_WRITE_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _fmt, ...) \ | |
193 | do { \ | |
194 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
195 | bt_log_write_printf(__FILE__, __func__, \ | |
196 | __LINE__, (_lvl), (_tag), (_fmt), \ | |
197 | ##__VA_ARGS__); \ | |
198 | } \ | |
199 | } while (0) | |
200 | ||
201 | /* | |
202 | * Calls bt_log_write_mem() if logging is enabled at run time for the | |
203 | * current level `_cur_lvl`. | |
204 | * | |
205 | * Passes the current file name, function name, and line number to | |
206 | * bt_log_write_mem(). | |
207 | */ | |
208 | #define BT_LOG_WRITE_MEM_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _msg) \ | |
209 | do { \ | |
210 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
211 | bt_log_write_mem(__FILE__, __func__, __LINE__, \ | |
212 | (_lvl), (_tag), (_mem_data), \ | |
213 | (_mem_len), (_msg)); \ | |
214 | } \ | |
215 | } while (0) | |
216 | ||
217 | /* | |
218 | * Calls bt_log_write_mem_printf() if logging is enabled at run time for | |
219 | * the current level `_cur_lvl`. | |
220 | * | |
221 | * Passes the current file name, function name, and line number to | |
222 | * bt_log_write_mem_printf(). | |
223 | */ | |
224 | #define BT_LOG_WRITE_MEM_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _mem_data, _mem_len, _fmt, ...) \ | |
225 | do { \ | |
226 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
227 | bt_log_write_mem_printf(__FILE__, __func__, \ | |
228 | __LINE__, (_lvl), (_tag), (_mem_data), \ | |
229 | (_mem_len), (_fmt), ##__VA_ARGS__); \ | |
230 | } \ | |
231 | } while (0) | |
232 | ||
233 | /* | |
234 | * Calls bt_log_write_errno() if logging is enabled at run time for the | |
235 | * current level `_cur_lvl`. | |
236 | * | |
237 | * Passes the current file name, function name, and line number to | |
238 | * bt_log_write_errno(). | |
239 | */ | |
240 | #define BT_LOG_WRITE_ERRNO_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _msg) \ | |
241 | do { \ | |
242 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
243 | bt_log_write_errno(__FILE__, __func__, \ | |
244 | __LINE__, (_lvl), (_tag), (_init_msg), \ | |
245 | (_msg)); \ | |
246 | } \ | |
247 | } while (0) | |
248 | ||
249 | /* | |
250 | * Calls bt_log_write_errno_printf() if logging is enabled at run time | |
251 | * for the current level `_cur_lvl`. | |
252 | * | |
253 | * Passes the current file name, function name, and line number to | |
254 | * bt_log_write_errno_printf(). | |
255 | */ | |
256 | #define BT_LOG_WRITE_ERRNO_PRINTF_CUR_LVL(_lvl, _cur_lvl, _tag, _init_msg, _fmt, ...) \ | |
257 | do { \ | |
258 | if (BT_LOG_ON_CUR_LVL((_lvl), (_cur_lvl))) { \ | |
259 | bt_log_write_errno_printf(__FILE__, __func__, \ | |
260 | __LINE__, (_lvl), (_tag), (_init_msg), \ | |
261 | (_fmt), ##__VA_ARGS__); \ | |
262 | } \ | |
263 | } while (0) | |
264 | ||
265 | /* | |
266 | * Returns the equivalent letter of the log level `level`. | |
267 | * | |
268 | * `level` must be a valid log level. | |
269 | */ | |
270 | static inline | |
271 | char bt_log_get_letter_from_level(const enum bt_log_level level) | |
272 | { | |
273 | char letter; | |
274 | ||
275 | switch (level) { | |
276 | case BT_LOG_TRACE: | |
277 | letter = 'T'; | |
278 | break; | |
279 | case BT_LOG_DEBUG: | |
280 | letter = 'D'; | |
281 | break; | |
282 | case BT_LOG_INFO: | |
283 | letter = 'I'; | |
284 | break; | |
285 | case BT_LOG_WARNING: | |
286 | letter = 'W'; | |
287 | break; | |
288 | case BT_LOG_ERROR: | |
289 | letter = 'E'; | |
290 | break; | |
291 | case BT_LOG_FATAL: | |
292 | letter = 'F'; | |
293 | break; | |
294 | case BT_LOG_NONE: | |
295 | letter = 'N'; | |
296 | break; | |
297 | default: | |
298 | abort(); | |
299 | } | |
300 | ||
301 | return letter; | |
302 | } | |
303 | ||
304 | /* | |
305 | * Returns the log level as an integer for the string `str`, or -1 if | |
306 | * `str` is not a valid log level string. | |
307 | */ | |
308 | static inline | |
309 | int bt_log_get_level_from_string(const char * const str) | |
310 | { | |
311 | int level = -1; | |
312 | ||
313 | BT_ASSERT(str); | |
314 | ||
315 | if (strcmp(str, "TRACE") == 0 || strcmp(str, "T") == 0) { | |
316 | level = BT_LOG_TRACE; | |
317 | } else if (strcmp(str, "DEBUG") == 0 || strcmp(str, "D") == 0) { | |
318 | level = BT_LOG_DEBUG; | |
319 | } else if (strcmp(str, "INFO") == 0 || strcmp(str, "I") == 0) { | |
320 | level = BT_LOG_INFO; | |
321 | } else if (strcmp(str, "WARN") == 0 || | |
322 | strcmp(str, "WARNING") == 0 || | |
323 | strcmp(str, "W") == 0) { | |
324 | level = BT_LOG_WARNING; | |
325 | } else if (strcmp(str, "ERROR") == 0 || strcmp(str, "E") == 0) { | |
326 | level = BT_LOG_ERROR; | |
327 | } else if (strcmp(str, "FATAL") == 0 || strcmp(str, "F") == 0) { | |
328 | level = BT_LOG_FATAL; | |
329 | } else if (strcmp(str, "NONE") == 0 || strcmp(str, "N") == 0) { | |
330 | level = BT_LOG_NONE; | |
331 | } else { | |
332 | /* FIXME: Should we warn here? How? */ | |
333 | } | |
334 | ||
335 | return level; | |
336 | } | |
337 | ||
338 | /* | |
339 | * Returns the log level as an integer for the letter `letter`, or -1 if | |
340 | * `letter` is not a valid log level string. | |
341 | */ | |
342 | static inline | |
343 | int bt_log_get_level_from_letter(const char letter) | |
344 | { | |
345 | const char str[] = {letter, '\0'}; | |
346 | ||
347 | return bt_log_get_level_from_string(str); | |
348 | } | |
349 | ||
350 | /* | |
351 | * Returns the log level for the value of the environment variable named | |
352 | * `env_var_name`, or `BT_LOG_NONE` if not a valid log level string. | |
353 | */ | |
354 | static inline | |
355 | enum bt_log_level bt_log_get_level_from_env(const char *env_var_name) | |
356 | { | |
357 | const char * const varval = getenv(env_var_name); | |
358 | enum bt_log_level level = BT_LOG_NONE; | |
359 | int int_level; | |
360 | ||
361 | if (!varval) { | |
362 | goto end; | |
363 | } | |
364 | ||
365 | int_level = bt_log_get_level_from_string(varval); | |
366 | if (int_level < 0) { | |
367 | /* FIXME: Should we warn here? How? */ | |
368 | int_level = BT_LOG_NONE; | |
369 | } | |
370 | ||
371 | level = (enum bt_log_level) int_level; | |
372 | ||
373 | end: | |
374 | return level; | |
375 | } | |
376 | ||
377 | /* | |
378 | * Declares the variable named `_level_sym` as an external symbol | |
379 | * containing a log level. | |
380 | */ | |
381 | #define BT_LOG_LEVEL_EXTERN_SYMBOL(_level_sym) \ | |
382 | extern enum bt_log_level _level_sym | |
383 | ||
384 | /* | |
385 | * 1. Defines the log level variable `_level_sym`, initializing it to | |
386 | * `BT_LOG_NONE` (logging disabled). | |
387 | * | |
388 | * 2. Defines a library constructor named _bt_log_level_ctor() which | |
389 | * initializes the log level variable `_level_sym` from the value of | |
390 | * the environment variable named `_env_var_name`. | |
391 | */ | |
392 | #define BT_LOG_INIT_LOG_LEVEL(_level_sym, _env_var_name) \ | |
393 | enum bt_log_level _level_sym = BT_LOG_NONE; \ | |
394 | static \ | |
395 | void __attribute__((constructor)) _bt_log_level_ctor(void) \ | |
396 | { \ | |
397 | _level_sym = bt_log_get_level_from_env(_env_var_name); \ | |
398 | } | |
399 | ||
400 | #endif /* BABELTRACE_LOGGING_LOG_API_H */ |