1 #pragma once
2 #ifndef IWLOG_H
3 #define IWLOG_H
4 
5 /**************************************************************************************************
6  * IOWOW library
7  *
8  * MIT License
9  *
10  * Copyright (c) 2012-2021 Softmotions Ltd <info@softmotions.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to deal
14  * in the Software without restriction, including without limitation the rights
15  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16  *  copies of the Software, and to permit persons to whom the Software is
17  * furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in all
20  * copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28  * SOFTWARE.
29  *************************************************************************************************/
30 
31 #ifdef __clang__
32 #pragma clang diagnostic push
33 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
34 #endif
35 
36 /**
37  * @file
38  * @brief Error logging/reporting routines.
39  * @author Anton Adamansky (adamansky@softmotions.com)
40  *
41  * Before using API of this module you should call
42  * `iw_init(void)` iowow module initialization routine.
43  *
44  * By default all logging output redirected to the `stderr`
45  * you can owerride it by passing  instance of `IWLOG_DEFAULT_OPTS`
46  * to the `iwlog_set_logfn_opts(void*)`
47  *
48  * A custom error logging function may be implemented with `IWLOG_FN` signature
49  * and registered by `void iwlog_set_logfn(IWLOG_FN fp)`
50  *
51  * The following methods normally used for logging:
52  * @verbatim
53     iwlog_{debug,info,warn,error}
54     iwlog_ecode_{debug,info,warn,error} @endverbatim
55  */
56 
57 #include "iowow.h"
58 
59 #include <stdint.h>
60 #include <stdarg.h>
61 #include <stdio.h>
62 
63 #ifdef __APPLE__
64 #include <xlocale.h>
65 #else
66 #include <locale.h>
67 #endif
68 
69 IW_EXTERN_C_START
70 
71 #ifndef IW_ERROR_START
72 #define IW_ERROR_START 70000
73 #endif
74 
75 /**
76  * @enum iw_ecode
77  * @brief Common used error codes.
78  */
79 typedef enum {
80   IW_OK         = 0,              /**< No error. */
81   IW_ERROR_FAIL = IW_ERROR_START, /**< Unspecified error. */
82   IW_ERROR_ERRNO,                 /**< Error with expected errno status set. */
83   IW_ERROR_IO_ERRNO,              /**< IO error with expected errno status set. */
84   IW_ERROR_AGAIN,
85   IW_ERROR_NOT_EXISTS,            /**< Resource is not exists. */
86   IW_ERROR_READONLY,              /**< Resource is readonly. */
87   IW_ERROR_ALREADY_OPENED,        /**< Resource is already opened. */
88   IW_ERROR_THREADING,             /**< Threading error. */
89   IW_ERROR_THREADING_ERRNO,       /**< Threading error with errno status set. */
90   IW_ERROR_ASSERTION,             /**< Generic assertion error. */
91   IW_ERROR_INVALID_HANDLE,        /**< Invalid HANDLE value. */
92   IW_ERROR_OUT_OF_BOUNDS,         /**< Invalid bounds specified. */
93   IW_ERROR_NOT_IMPLEMENTED,       /**< Method is not implemented. */
94   IW_ERROR_ALLOC,                 /**< Memory allocation failed. */
95   IW_ERROR_INVALID_STATE,         /**< Illegal state error. */
96   IW_ERROR_NOT_ALIGNED,           /**< Argument is not aligned properly. */
97   IW_ERROR_FALSE,                 /**< Request rejection/false response. */
98   IW_ERROR_INVALID_ARGS,          /**< Invalid function arguments. */
99   IW_ERROR_OVERFLOW,              /**< Overflow. */
100   IW_ERROR_INVALID_VALUE,         /**< Invalid value. */
101   IW_ERROR_UNEXPECTED_RESPONSE,   /**< Unexpected response (IW_ERROR_UNEXPECTED_RESPONSE) */
102   IW_ERROR_NOT_ALLOWED,           /**< Action is not allowed. (IW_ERROR_NOT_ALLOWED) */
103   IW_ERROR_UNSUPPORTED,           /**< Unsupported opration. (IW_ERROR_UNSUPPORTED) */
104 } iw_ecode;
105 
106 /**
107  * @enum iwlog_lvl
108  * @brief Available logging vebosity levels.
109  */
110 typedef enum {
111   IWLOG_ERROR = 0,
112   IWLOG_WARN  = 1,
113   IWLOG_INFO  = 2,
114   IWLOG_DEBUG = 3,
115 } iwlog_lvl;
116 
117 /**
118  * @brief Options for the default logging function.
119  * @see iwlog_set_logfn_opts(void*)
120  */
121 typedef struct {
122   FILE *out; /**< Output file stream. Default: `stderr`  */
123 } IWLOG_DEFAULT_OPTS;
124 
125 /**
126  * @brief Logging function pointer.
127  *
128  * @param locale Locale used to print error message.
129  * @param lvl Log level.
130  * @param ecode Error code specified.
131  * @param errno_code Optional errno code. Set it to 0 if errno not used.
132  * @param file File name. Can be `NULL`
133  * @param line Line number in the file.
134  * @param ts Message time-stamp
135  * @param fmt `printf` style message format
136  * @return Not zero error code in the case of error.
137  *
138  * @see iwlog_set_logfn(IWLOG_FN)
139  */
140 typedef iwrc (*IWLOG_FN)(
141   FILE *out, locale_t locale, iwlog_lvl lvl, iwrc ecode,
142   int errno_code, int werror_code, const char *file,
143   int line, uint64_t ts, void *opts, const char *fmt,
144   va_list argp);
145 
146 /**
147  * @brief Return the locale aware error code explanation message.
148  *
149  * @param locale Locale used. Can be `NULL`
150  * @param ecode Error code
151  * @return Message string describes a given error code or `NULL` if
152  *         no message found.
153  */
154 typedef const char* (*IWLOG_ECODE_FN)(locale_t locale, uint32_t ecode);
155 
156 /**
157  * @brief Attach the specified @a errno_code code into @a rc code
158  * @param rc IOWOW error code
159  * @param errno_code Error code will be embedded into.
160  * @return Updated rc code
161  */
162 IW_EXPORT iwrc iwrc_set_errno(iwrc rc, int errno_code);
163 
164 /**
165  * @brief Strip the attached `errno` code from the specified @a rc and
166  * return errno code.
167  *
168  * @param rc `errno` code or `0`
169  */
170 IW_EXPORT uint32_t iwrc_strip_errno(iwrc *rc);
171 
172 #ifdef _WIN32
173 
174 /**
175  * @brief Attach the specified windows @a werror code into @a rc code
176  * @param rc IOWOW error code
177  * @param errno_code Error code will be embedded into.
178  * @return Updated rc code
179  */
180 IW_EXPORT iwrc iwrc_set_werror(iwrc rc, uint32_t werror);
181 
182 /**
183  * @brief Strip the attached windows `werror` code from the specified @a rc and
184  * return this errno code.
185  *
186  * @param rc `errno` code or `0`
187  */
188 IW_EXPORT uint32_t iwrc_strip_werror(iwrc *rc);
189 
190 #endif
191 
192 /**
193  * @brief Remove embedded @a errno code from the passed @a rc
194  * @param [in,out] rc
195  */
196 IW_EXPORT void iwrc_strip_code(iwrc *rc);
197 
198 /**
199  * @brief Sets current logging function.
200  * @warning Not thread safe.
201  *
202  * @param fp Logging function pointer.
203  * @return Not zero if error occured.
204  */
205 IW_EXPORT void iwlog_set_logfn(IWLOG_FN fp, void *opts);
206 
207 /**
208  * @brief Get a default logging function.
209  *
210  */
211 IW_EXPORT IWLOG_FN iwlog_get_logfn(void);
212 
213 /**
214  * @brief Returns string representation of a given error code.
215  * @param ecode Error code
216  * @return
217  */
218 IW_EXPORT const char *iwlog_ecode_explained(iwrc ecode);
219 
220 /**
221  * @brief Register error code explanation function.
222  * @note Up to `128` @a fp functions can be registered.
223  * @param fp
224  * @return `0` on success or error code.
225  */
226 IW_EXPORT iwrc iwlog_register_ecodefn(IWLOG_ECODE_FN fp);
227 
228 /**
229  * @brief Logs a message.
230  * @param lvl       Logging level.
231  * @param ecode     Error code or zero.
232  * @param file      Module file, can be `NULL`
233  * @param line      Line in module.
234  * @param fmt       Printf like message format.
235  * @return
236  */
237 IW_EXPORT iwrc iwlog(
238   iwlog_lvl lvl, iwrc ecode, const char *file, int line,
239   const char *fmt, ...);
240 
241 IW_EXPORT void iwlog2(
242   iwlog_lvl lvl, iwrc ecode, const char *file, int line,
243   const char *fmt, ...);
244 
245 IW_EXPORT iwrc iwlog_va(
246   FILE *out, iwlog_lvl lvl, iwrc ecode, const char *file, int line,
247   const char *fmt, va_list argp);
248 
249 #ifdef _DEBUG
250 #define iwlog_debug(IW_fmt, ...) \
251   iwlog2(IWLOG_DEBUG, 0, __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
252 #else
253 #define iwlog_debug(IW_fmt, ...)
254 #endif
255 #define iwlog_info(IW_fmt, ...) \
256   iwlog2(IWLOG_INFO, 0, __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
257 #define iwlog_warn(IW_fmt, ...) \
258   iwlog2(IWLOG_WARN, 0, __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
259 #define iwlog_error(IW_fmt, ...) \
260   iwlog2(IWLOG_ERROR, 0, __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
261 
262 #ifdef _DEBUG
263 #define iwlog_debug2(IW_fmt) \
264   iwlog2(IWLOG_DEBUG, 0, __FILE__, __LINE__, (IW_fmt))
265 #else
266 #define iwlog_debug2(IW_fmt)
267 #endif
268 #define iwlog_info2(IW_fmt) iwlog2(IWLOG_INFO, 0, __FILE__, __LINE__, (IW_fmt))
269 #define iwlog_warn2(IW_fmt) iwlog2(IWLOG_WARN, 0, __FILE__, __LINE__, (IW_fmt))
270 #define iwlog_error2(IW_fmt) \
271   iwlog2(IWLOG_ERROR, 0, __FILE__, __LINE__, (IW_fmt))
272 
273 #ifdef _DEBUG
274 #define iwlog_ecode_debug(IW_ecode, IW_fmt, ...) \
275   iwlog2(IWLOG_DEBUG, (IW_ecode), __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
276 #else
277 #define iwlog_ecode_debug(IW_ecode, IW_fmt, ...)
278 #endif
279 #define iwlog_ecode_info(IW_ecode, IW_fmt, ...) \
280   iwlog2(IWLOG_INFO, (IW_ecode), __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
281 #define iwlog_ecode_warn(IW_ecode, IW_fmt, ...) \
282   iwlog2(IWLOG_WARN, (IW_ecode), __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
283 #define iwlog_ecode_error(IW_ecode, IW_fmt, ...) \
284   iwlog2(IWLOG_ERROR, (IW_ecode), __FILE__, __LINE__, (IW_fmt), ## __VA_ARGS__)
285 
286 #ifdef _DEBUG
287 #define iwlog_ecode_debug2(IW_ecode, IW_fmt) \
288   iwlog2(IWLOG_DEBUG, (IW_ecode), __FILE__, __LINE__, (IW_fmt))
289 #else
290 #define iwlog_ecode_debug2(IW_ecode, IW_fmt)
291 #endif
292 #define iwlog_ecode_info2(IW_ecode, IW_fmt) \
293   iwlog2(IWLOG_INFO, (IW_ecode), __FILE__, __LINE__, (IW_fmt))
294 #define iwlog_ecode_warn2(IW_ecode, IW_fmt) \
295   iwlog2(IWLOG_WARN, (IW_ecode), __FILE__, __LINE__, (IW_fmt))
296 #define iwlog_ecode_error2(IW_ecode, IW_fmt) \
297   iwlog2(IWLOG_ERROR, (IW_ecode), __FILE__, __LINE__, (IW_fmt))
298 
299 #ifdef _DEBUG
300 #define iwlog_ecode_debug3(IW_ecode) \
301   iwlog2(IWLOG_DEBUG, (IW_ecode), __FILE__, __LINE__, "")
302 #else
303 #define iwlog_ecode_debug3(IW_ecode)
304 #endif
305 #define iwlog_ecode_info3(IW_ecode) iwlog2(IWLOG_INFO, (IW_ecode), __FILE__, __LINE__, ""))
306 #define iwlog_ecode_warn3(IW_ecode) \
307   iwlog2(IWLOG_WARN, (IW_ecode), __FILE__, __LINE__, "")
308 #define iwlog_ecode_error3(IW_ecode) \
309   iwlog2(IWLOG_ERROR, (IW_ecode), __FILE__, __LINE__, "")
310 
311 #define IWRC(IW_act, IW_rc)                                   \
312   {                                                           \
313     iwrc __iwrc = (IW_act);                                   \
314     if (__iwrc) {                                             \
315       if (!(IW_rc))                                           \
316       (IW_rc) = __iwrc;                                       \
317       else                                                    \
318       iwlog2(IWLOG_ERROR, __iwrc, __FILE__, __LINE__, "");    \
319     }                                                         \
320   }
321 
322 #define IWRC2(IW_act, IW_lvl)                                   \
323   {                                                             \
324     iwrc __iwrc = (IW_act);                                     \
325     if (__iwrc) {                                               \
326       iwlog2(IWLOG_ ## IW_lvl, __iwrc, __FILE__, __LINE__, ""); \
327     }                                                           \
328   }
329 
330 #define IWRC3(IW_act, IW_rc, IW_lvl)                            \
331   {                                                             \
332     iwrc __iwrc = (IW_act);                                     \
333     if (__iwrc) {                                               \
334       if (!(IW_rc))                                             \
335       (IW_rc) = __iwrc;                                         \
336       else                                                      \
337       iwlog2(IWLOG_ ## IW_lvl, __iwrc, __FILE__, __LINE__, ""); \
338     }                                                           \
339   }
340 
341 /**
342  * @brief Initiate this submodule.
343  * @return `0` on success or error code.
344  */
345 IW_EXPORT iwrc iwlog_init(void);
346 
347 #ifdef __clang__
348 #pragma clang diagnostic pop
349 #endif
350 
351 IW_EXTERN_C_END
352 #endif
353