Branch data Line data Source code
1 : : #include "rational.h"
2 : :
3 : : // Global flag to manage output of all logging messages
4 : : // in an application and its default value
5 : : _Atomic char rational_logger_mode = REGULAR;
6 : : _Atomic Return global_return_status = SUCCESS;
7 : :
8 : : /**
9 : : * @brief Converts LOGMODES bit flags to their string representation
10 : : *
11 : : * @details This function takes a combination of LOGMODES flags and converts them
12 : : * into a human-readable string representation where individual flags
13 : : * are separated by " | ". For example, (VERBOSE | SILENT) will be
14 : : * converted to "VERBOSE | SILENT"
15 : : *
16 : : * @param mode Integer containing the combination of LOGMODES flags
17 : : * @return char* Pointer to static string containing flag names
18 : : *
19 : : * @note The function uses a static buffer which means:
20 : : * 1. No memory allocation/deallocation is needed
21 : : * 2. The buffer contents will be overwritten on next function call
22 : : * 3. The function is not thread-safe
23 : : * 4. The returned pointer should not be freed
24 : : *
25 : : * @warning Maximum resulting string length is limited to 256 characters
26 : : */
27 : 1223 : char *rational_reconvert(int mode)
28 : : {
29 : : /* Static buffer to store the resulting string */
30 : : static char buffer[MAX_CHARACTERS];
31 : 1223 : buffer[0] = '\0'; /* Initialize buffer as empty string */
32 : :
33 : : /* Flag to track if we're adding the first item (for | separator) */
34 : 1223 : int first = 1;
35 : :
36 : : /* Define mapping between flag values and their string representations
37 : : * The array is terminated with {0, NULL} for easy iteration
38 : : */
39 : : static const struct {
40 : : int flag; /* Flag value from LOGMODES enum */
41 : : const char *name; /* String representation of the flag */
42 : : } mapping[] = {
43 : : {REGULAR,"REGULAR"},
44 : : {VERBOSE,"VERBOSE"},
45 : : {TESTING,"TESTING"},
46 : : {ERROR,"ERROR"},
47 : : {SILENT,"SILENT"},
48 : : {UNDECOR,"UNDECOR"},
49 : : {REMEMBER,"REMEMBER"},
50 : : {VISIBLE_IN_SILENT,"VISIBLE_IN_SILENT"},
51 : : {0,NULL} /* Terminator element */
52 : : };
53 : :
54 : : /* Iterate through all possible flags */
55 [ + + ]: 11007 : for(int i = 0; mapping[i].name != NULL; i++)
56 : : {
57 : : /* Check if current flag is set in mode using bitwise AND */
58 [ + + ]: 9784 : if(mode & mapping[i].flag)
59 : : {
60 : : /* Add separator before all elements except the first one */
61 [ + + ]: 1231 : if(!first)
62 : : {
63 : 8 : strcat(buffer," | ");
64 : : }
65 : :
66 : : /* Add flag name to the result string */
67 : 1231 : strcat(buffer,mapping[i].name);
68 : :
69 : : /* Clear first flag as we've added an element */
70 : 1231 : first = 0;
71 : : }
72 : : }
73 : :
74 : 1223 : return buffer;
75 : : }
76 : :
77 : : /**
78 : : *
79 : : * @brief Format current date and time in ISO format
80 : : * @param time_string Pointer to the destination buffer that receives the timestamp.
81 : : * @param buffer_size Size of the destination buffer in bytes.
82 : : * @return Return SUCCESS on success, FAILURE on error (buffer contents will be empty on failure).
83 : : *
84 : : */
85 : 328 : static Return logger_show_time(
86 : : char *time_string,
87 : : size_t buffer_size)
88 : : {
89 : : /* Status returned by this function through provide()
90 : : Default value assumes successful completion */
91 : 328 : Return status = SUCCESS;
92 : :
93 : : struct timeval current_time;
94 : : struct tm local_time;
95 : :
96 [ - + ]: 328 : if(gettimeofday(¤t_time,NULL) != 0)
97 : : {
98 : 0 : time_string[0] = '\0';
99 : 0 : status = FAILURE;
100 : : }
101 : :
102 [ + - ]: 328 : if(SUCCESS == status)
103 : : {
104 [ - + ]: 328 : if(localtime_r(¤t_time.tv_sec,&local_time) == NULL)
105 : : {
106 : 0 : time_string[0] = '\0';
107 : 0 : status = FAILURE;
108 : : }
109 : : }
110 : :
111 [ + - ]: 328 : if(SUCCESS == status)
112 : : {
113 : 328 : const int milliseconds = (int)(current_time.tv_usec / 1000);
114 : :
115 : 328 : if(snprintf(time_string,
116 : : buffer_size,
117 : : "%04d-%02d-%02d %02d:%02d:%02d:%03d",
118 : 328 : local_time.tm_year + 1900,
119 [ - + ]: 328 : local_time.tm_mon + 1,
120 : : local_time.tm_mday,
121 : : local_time.tm_hour,
122 : : local_time.tm_min,
123 : : local_time.tm_sec,
124 : : milliseconds) < 0)
125 : : {
126 : 0 : time_string[0] = '\0';
127 : 0 : status = FAILURE;
128 : : }
129 : : }
130 : :
131 : 328 : return(status);
132 : : }
133 : :
134 : : __attribute__((format(printf,3,0)))
135 : 37919 : static void logger_line_append_va(
136 : : char **line,
137 : : int *line_len,
138 : : const char *fmt,
139 : : va_list args)
140 : : {
141 : : va_list args_copy;
142 : 37919 : va_copy(args_copy,args);
143 : 37919 : const int needed = vsnprintf(NULL,0,fmt,args_copy);
144 : 37919 : va_end(args_copy);
145 : :
146 [ - + ]: 37919 : if(needed < 0)
147 : : {
148 : 0 : return;
149 : : }
150 : :
151 : 37919 : const size_t new_len = (size_t)(*line_len) + (size_t)needed;
152 : 37919 : char *tmp = realloc(*line,new_len + 1);
153 : :
154 [ - + ]: 37919 : if(tmp == NULL)
155 : : {
156 : 0 : return;
157 : : }
158 : :
159 : 37919 : *line = tmp;
160 : :
161 : : va_list args_copy2;
162 : 37919 : va_copy(args_copy2,args);
163 : 37919 : vsnprintf(*line + *line_len,(size_t)needed + 1,fmt,args_copy2);
164 : 37919 : va_end(args_copy2);
165 : :
166 : 37919 : *line_len = (int)new_len;
167 : : }
168 : :
169 : : __attribute__((format(printf,3,4)))
170 : 16880 : static void logger_line_append(
171 : : char **line,
172 : : int *line_len,
173 : : const char *fmt,
174 : : ...)
175 : : {
176 : : va_list args;
177 : 16880 : va_start(args,fmt);
178 : 16880 : logger_line_append_va(line,line_len,fmt,args);
179 : 16880 : va_end(args);
180 : 16880 : }
181 : :
182 : : __attribute__((format(printf,7,0)))
183 : 31316 : void logger_line(
184 : : char **line,
185 : : int *line_len,
186 : : const unsigned int level,
187 : : const char *const filename,
188 : : size_t line_number,
189 : : const char *const funcname,
190 : : const char *fmt,
191 : : va_list args)
192 : : {
193 [ + + ]: 31316 : if(rational_logger_mode & SILENT)
194 : : {
195 [ + + ]: 4262 : if(level & VISIBLE_IN_SILENT)
196 : : {
197 : 28 : logger_line_append_va(line,line_len,fmt,args);
198 : : }
199 : :
200 : 4262 : return;
201 : : }
202 : :
203 [ + + + + : 27054 : if(!(level & UNDECOR) && (level & TESTING) && (rational_logger_mode & TESTING))
+ + ]
204 : : {
205 : : // Print out the word "TESTING:"
206 : 15372 : logger_line_append(line,line_len,"TESTING:");
207 : : }
208 : :
209 [ + + + + : 27054 : if(!(level & UNDECOR) && (level & (VERBOSE|ERROR)) && (rational_logger_mode & VERBOSE))
+ + ]
210 : : {
211 : : char time_string[sizeof "2011-10-18 07:07:09:000"];
212 : 328 : (void)logger_show_time(time_string,sizeof(time_string));
213 : :
214 : : // Print out current time
215 : 328 : logger_line_append(line,line_len,"%s ",time_string);
216 : :
217 : : // Print out the source file name
218 : 328 : logger_line_append(line,line_len,"%s:",filename);
219 : :
220 : : // Print out line number in source file
221 : 328 : logger_line_append(line,line_len,"%03zu:",line_number);
222 : :
223 : : // Print out name of the function itself
224 : 328 : logger_line_append(line,line_len,"%s:",funcname);
225 : : }
226 : :
227 [ + + + + : 27054 : if(!(level & UNDECOR) && (level & ERROR) && (rational_logger_mode & (REGULAR | ERROR)))
+ + ]
228 : : {
229 : : // Print out error prefix
230 : 15 : logger_line_append(line,line_len,"ERROR: ");
231 : :
232 [ + + + + : 27039 : } else if(!(level & UNDECOR) && (level & ERROR) && (rational_logger_mode & (TESTING | VERBOSE))){
+ - ]
233 : : // Print out the word "ERROR:"
234 : 181 : logger_line_append(line,line_len,"ERROR:");
235 : : }
236 : :
237 [ + + + + ]: 27054 : if(level & ERROR && rational_logger_mode & ERROR)
238 : : {
239 : : // Print out other arguments
240 : 2 : logger_line_append_va(line,line_len,fmt,args);
241 : :
242 [ + + + + ]: 27052 : } else if(level & (REGULAR|ERROR) && rational_logger_mode & REGULAR){
243 : : // Print out other arguments
244 : 1075 : logger_line_append_va(line,line_len,fmt,args);
245 : :
246 [ + + + + ]: 25977 : } else if(level & (VERBOSE|ERROR) && rational_logger_mode & VERBOSE){
247 : : // Print out other arguments
248 : 643 : logger_line_append_va(line,line_len,fmt,args);
249 : :
250 [ + + + + ]: 25334 : } else if(level & (TESTING|ERROR) && rational_logger_mode & TESTING){
251 : : // Print out other arguments
252 : 19291 : logger_line_append_va(line,line_len,fmt,args);
253 : : }
254 : : }
255 : :
256 : : /**
257 : : *
258 : : * @brief Build and print a formatted log line with file, line, and function metadata
259 : : *
260 : : * @details When REMEMBER is set and the weak rational_remember() symbol is defined,
261 : : * the formatted line (without a trailing newline) and its length are
262 : : * passed to that callback.
263 : : *
264 : : */
265 : : __attribute__((format(printf,5,6))) // Without this we will get warning
266 : 31316 : void rational_logger(
267 : : const unsigned int level,
268 : : const char *const filename,
269 : : size_t line,
270 : : const char *const funcname,
271 : : const char *fmt,
272 : : ...)
273 : : {
274 : :
275 : 31316 : char *logger_line_text = NULL;
276 : 31316 : int line_len = 0;
277 : :
278 : : va_list args;
279 : 31316 : va_start(args,fmt);
280 : 31316 : logger_line(&logger_line_text,&line_len,level,filename,line,funcname,fmt,args);
281 : 31316 : va_end(args);
282 : :
283 [ + + + - : 31316 : if((level & REMEMBER) && rational_remember && logger_line_text != NULL && line_len > 0)
+ - + - ]
284 : : {
285 : 105 : rational_remember(logger_line_text,line_len);
286 : : }
287 : :
288 [ + + ]: 31316 : if(logger_line_text != NULL)
289 : : {
290 : 21039 : fwrite(logger_line_text,sizeof(char),(size_t)line_len,stdout);
291 : : }
292 : :
293 : 31316 : free(logger_line_text);
294 : 31316 : }
295 : :
296 : : #ifdef TEST
297 : : /**
298 : : * @file test_slog.c
299 : : * @brief Complete test suite for log functionality
300 : : */
301 : : int main(void)
302 : : {
303 : : printf("All available combinations:\n");
304 : : printf("%s\n",rational_convert(REGULAR));
305 : : printf("%s\n",rational_convert(VERBOSE));
306 : : printf("%s\n",rational_convert(TESTING));
307 : : printf("%s\n",rational_convert(SILENT));
308 : : printf("%s\n",rational_convert(REGULAR|VERBOSE));
309 : : printf("%s\n",rational_convert(REGULAR|TESTING));
310 : : printf("%s\n",rational_convert(VERBOSE|TESTING));
311 : : printf("%s\n",rational_convert(REGULAR|VERBOSE|TESTING));
312 : : printf("%s\n",rational_convert(ERROR));
313 : : printf("%s\n",rational_convert(UNDECOR));
314 : : printf("%s\n",rational_convert(EVERY|UNDECOR));
315 : : printf("%s\n",rational_convert(ERROR|UNDECOR));
316 : : printf("%s\n",rational_convert(VISIBLE_IN_SILENT));
317 : :
318 : : /* Test REGULAR mode combinations */
319 : : rational_logger_mode = REGULAR;
320 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
321 : : printf("1. Must print:"); slog(REGULAR,"true"); printf("\n");
322 : : printf("2. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
323 : : printf("3. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
324 : : printf("4. Must print:"); slog(ERROR,"true"); printf("\n");
325 : :
326 : : /* Test VERBOSE mode combinations */
327 : : rational_logger_mode = VERBOSE;
328 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
329 : : printf("5. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
330 : : printf("6. Must print:"); slog(VERBOSE,"true"); printf("\n");
331 : : printf("7. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
332 : : printf("8. Must print:"); slog(ERROR,"true"); printf("\n");
333 : :
334 : : /* Test TESTING mode combinations */
335 : : rational_logger_mode = TESTING;
336 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
337 : : printf("9. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
338 : : printf("10. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
339 : : printf("11. Must print:"); slog(TESTING,"true"); printf("\n");
340 : : printf("12. Must print:"); slog(ERROR,"true"); printf("\n");
341 : :
342 : : /* Test SILENT mode combinations */
343 : : rational_logger_mode = SILENT;
344 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
345 : : printf("13. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
346 : : printf("14. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
347 : : printf("15. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
348 : : printf("16. Won't print:"); slog(ERROR,"but printed!"); printf("\n");
349 : :
350 : : /* Test REGULAR|VERBOSE combinations */
351 : : rational_logger_mode = REGULAR|VERBOSE;
352 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
353 : : printf("17. Must print:"); slog(REGULAR,"true"); printf("\n");
354 : : printf("18. Must print:"); slog(VERBOSE,"true"); printf("\n");
355 : : printf("19. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
356 : : printf("20. Must print:"); slog(ERROR,"true"); printf("\n");
357 : :
358 : : /* Test REGULAR|TESTING combinations */
359 : : rational_logger_mode = REGULAR|TESTING;
360 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
361 : : printf("21. Must print:"); slog(REGULAR,"true"); printf("\n");
362 : : printf("22. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
363 : : printf("23. Must print:"); slog(TESTING,"true"); printf("\n");
364 : : printf("24. Must print:"); slog(ERROR,"true"); printf("\n");
365 : :
366 : : /* Test VERBOSE|TESTING combinations */
367 : : rational_logger_mode = VERBOSE|TESTING;
368 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
369 : : printf("25. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
370 : : printf("26. Must print:"); slog(VERBOSE,"true"); printf("\n");
371 : : printf("27. Must print:"); slog(TESTING,"true"); printf("\n");
372 : : printf("28. Must print:"); slog(ERROR,"true"); printf("\n");
373 : :
374 : : /* Test REGULAR|VERBOSE|TESTING combinations */
375 : : rational_logger_mode = REGULAR|VERBOSE|TESTING;
376 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
377 : : printf("29. Must print:"); slog(REGULAR,"true"); printf("\n");
378 : : printf("30. Must print:"); slog(VERBOSE,"true"); printf("\n");
379 : : printf("31. Must print:"); slog(TESTING,"true"); printf("\n");
380 : : printf("32. Must print:"); slog(ERROR,"true"); printf("\n");
381 : :
382 : : /* Test ERROR mode combinations */
383 : : rational_logger_mode = ERROR;
384 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
385 : : printf("33. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
386 : : printf("34. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
387 : : printf("35. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
388 : : printf("36. Must print:"); slog(ERROR,"true"); printf("\n");
389 : :
390 : : /*
391 : : * Test UNDECOR flag: suppress logger prefixes (TESTING:, time/file/line/func, ERROR:)
392 : : * The output between the '|' markers should contain only the message payload.
393 : : */
394 : :
395 : : rational_logger_mode = EVERY|ERROR;
396 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
397 : : printf("37. Must print no prefixes:|"); slog(EVERY|UNDECOR,"true"); printf("|\n");
398 : : printf("38. Must print no ERROR prefix:|"); slog(ERROR|UNDECOR,"true"); printf("|\n");
399 : :
400 : : rational_logger_mode = VERBOSE;
401 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
402 : : printf("39. Must print no time/file/line/func:|"); slog(VERBOSE|UNDECOR,"true"); printf("|\n");
403 : :
404 : : rational_logger_mode = TESTING;
405 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
406 : : printf("40. Must print no TESTING prefix:|"); slog(TESTING|UNDECOR,"true"); printf("|\n");
407 : :
408 : : rational_logger_mode = REGULAR;
409 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
410 : : printf("41. Must not print (VERBOSE not enabled):|"); slog(VERBOSE|UNDECOR,"but printed!"); printf("|\n");
411 : :
412 : : rational_logger_mode = SILENT;
413 : : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
414 : : printf("42. Must print in SILENT without prefixes:|"); slog(EVERY|VISIBLE_IN_SILENT,"true"); printf("|\n");
415 : : printf("43. Must print no ERROR prefix in SILENT:|"); slog(ERROR|VISIBLE_IN_SILENT,"true"); printf("|\n");
416 : :
417 : : return 0;
418 : : }
419 : : #endif
|