1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #ifndef __CLG_LOG_H__
18 #define __CLG_LOG_H__
19 
20 /** \file
21  * \ingroup clog
22  *
23  * C Logging Library (clog)
24  * ========================
25  *
26  * Usage
27  * -----
28  *
29  * - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
30  * - `CLOG_` prefixed macros for logging.
31  *
32  * Identifiers
33  * -----------
34  *
35  * #CLG_LogRef holds an identifier which defines the category of the logger.
36  *
37  * You can define and use identifiers as needed, logging will lazily initialize them.
38  *
39  * By convention lower case dot separated identifiers are used, eg:
40  * `module.sub_module`, this allows filtering by `module.*`,
41  * see #CLG_type_filter_include, #CLG_type_filter_exclude
42  *
43  * There is currently no functionality to remove a category once it's created.
44  *
45  * Severity
46  * --------
47  *
48  * - `INFO`: Simply log events, uses verbosity levels to control how much information to show.
49  * - `WARN`: General warnings (which aren't necessary to show to users).
50  * - `ERROR`: An error we can recover from, should not happen.
51  * - `FATAL`: Similar to assert. This logs the message, then a stack trace and abort.
52  * Verbosity Level
53  * ---------------
54  *
55  * Usage:
56  *
57  * - 0: Always show (used for warnings, errors).
58  *   Should never get in the way or become annoying.
59  *
60  * - 1: Top level module actions (eg: load a file, create a new window .. etc).
61  *
62  * - 2: Actions within a module (steps which compose an action, but don't flood output).
63  *   Running a tool, full data recalculation.
64  *
65  * - 3: Detailed actions which may be of interest when debugging internal logic of a module
66  *   These *may* flood the log with details.
67  *
68  * - 4+: May be used for more details than 3, should be avoided but not prevented.
69  */
70 
71 #ifdef __cplusplus
72 extern "C" {
73 #endif /* __cplusplus */
74 
75 #ifdef __GNUC__
76 #  define _CLOG_ATTR_NONNULL(args...) __attribute__((nonnull(args)))
77 #else
78 #  define _CLOG_ATTR_NONNULL(...)
79 #endif
80 
81 #ifdef __GNUC__
82 #  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) \
83     __attribute__((format(printf, format_param, dots_param)))
84 #else
85 #  define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
86 #endif
87 
88 #define STRINGIFY_ARG(x) "" #x
89 #define STRINGIFY_APPEND(a, b) "" a #b
90 #define STRINGIFY(x) STRINGIFY_APPEND("", x)
91 
92 struct CLogContext;
93 
94 /* Don't typedef enums. */
95 enum CLG_LogFlag {
96   CLG_FLAG_USE = (1 << 0),
97 };
98 
99 enum CLG_Severity {
100   CLG_SEVERITY_INFO = 0,
101   CLG_SEVERITY_WARN,
102   CLG_SEVERITY_ERROR,
103   CLG_SEVERITY_FATAL,
104 };
105 #define CLG_SEVERITY_LEN (CLG_SEVERITY_FATAL + 1)
106 
107 /* Each logger ID has one of these. */
108 typedef struct CLG_LogType {
109   struct CLG_LogType *next;
110   char identifier[64];
111   /** FILE output. */
112   struct CLogContext *ctx;
113   /** Control behavior. */
114   int level;
115   enum CLG_LogFlag flag;
116 } CLG_LogType;
117 
118 typedef struct CLG_LogRef {
119   const char *identifier;
120   CLG_LogType *type;
121 } CLG_LogRef;
122 
123 void CLG_log_str(CLG_LogType *lg,
124                  enum CLG_Severity severity,
125                  const char *file_line,
126                  const char *fn,
127                  const char *message) _CLOG_ATTR_NONNULL(1, 3, 4, 5);
128 void CLG_logf(CLG_LogType *lg,
129               enum CLG_Severity severity,
130               const char *file_line,
131               const char *fn,
132               const char *format,
133               ...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
134 
135 /* Main initializer and destructor (per session, not logger). */
136 void CLG_init(void);
137 void CLG_exit(void);
138 
139 void CLG_output_set(void *file_handle);
140 void CLG_output_use_basename_set(int value);
141 void CLG_output_use_timestamp_set(int value);
142 void CLG_error_fn_set(void (*error_fn)(void *file_handle));
143 void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
144 void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle));
145 
146 void CLG_type_filter_include(const char *type_filter, int type_filter_len);
147 void CLG_type_filter_exclude(const char *type_filter, int type_filter_len);
148 
149 void CLG_level_set(int level);
150 
151 void CLG_logref_init(CLG_LogRef *clg_ref);
152 
153 int CLG_color_support_get(CLG_LogRef *clg_ref);
154 
155 /** Declare outside function, declare as extern in header. */
156 #define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
157   static CLG_LogRef _static_##var = {id}; \
158   CLG_LogRef *var = &_static_##var
159 
160 /** Initialize struct once. */
161 #define CLOG_ENSURE(clg_ref) \
162   ((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
163 
164 #define CLOG_CHECK(clg_ref, verbose_level, ...) \
165   ((void)CLOG_ENSURE(clg_ref), \
166    ((clg_ref)->type->flag & CLG_FLAG_USE) && ((clg_ref)->type->level >= verbose_level))
167 
168 #define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) \
169   { \
170     CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
171     if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \
172         (severity >= CLG_SEVERITY_WARN)) { \
173       CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
174     } \
175   } \
176   ((void)0)
177 
178 #define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) \
179   { \
180     CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
181     if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \
182         (severity >= CLG_SEVERITY_WARN)) { \
183       CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
184     } \
185   } \
186   ((void)0)
187 
188 #define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) \
189   { \
190     CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
191     if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || \
192         (severity >= CLG_SEVERITY_WARN)) { \
193       const char *_str = str; \
194       CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \
195       MEM_freeN((void *)_str); \
196     } \
197   } \
198   ((void)0)
199 
200 #define CLOG_INFO(clg_ref, level, ...) \
201   CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
202 #define CLOG_WARN(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
203 #define CLOG_ERROR(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
204 #define CLOG_FATAL(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
205 
206 #define CLOG_STR_INFO(clg_ref, level, str) \
207   CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, str)
208 #define CLOG_STR_WARN(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, str)
209 #define CLOG_STR_ERROR(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, str)
210 #define CLOG_STR_FATAL(clg_ref, str) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, str)
211 
212 /* Allocated string which is immediately freed. */
213 #define CLOG_STR_INFO_N(clg_ref, level, str) \
214   CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, str)
215 #define CLOG_STR_WARN_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, str)
216 #define CLOG_STR_ERROR_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, str)
217 #define CLOG_STR_FATAL_N(clg_ref, str) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, str)
218 
219 #ifdef __cplusplus
220 }
221 #endif
222 
223 #endif /* __CLG_LOG_H__ */
224