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