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 619 : char *rational_reconvert(int mode)
28 : {
29 : /* Static buffer to store the resulting string */
30 : static char buffer[MAX_CHARACTERS];
31 619 : buffer[0] = '\0'; /* Initialize buffer as empty string */
32 :
33 : /* Flag to track if we're adding the first item (for | separator) */
34 619 : 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 619 : } mapping[] = {
43 : {REGULAR,"REGULAR"},
44 : {VERBOSE,"VERBOSE"},
45 : {TESTING,"TESTING"},
46 : {ERROR,"ERROR"},
47 : {SILENT,"SILENT"},
48 : {UNDECOR,"UNDECOR"},
49 : {0,NULL} /* Terminator element */
50 : };
51 :
52 : /* Iterate through all possible flags */
53 4333 : for(int i = 0; mapping[i].name != NULL; i++)
54 : {
55 : /* Check if current flag is set in mode using bitwise AND */
56 3714 : if(mode & mapping[i].flag)
57 : {
58 : /* Add separator before all elements except the first one */
59 627 : if(!first)
60 : {
61 8 : strcat(buffer," | ");
62 : }
63 :
64 : /* Add flag name to the result string */
65 627 : strcat(buffer,mapping[i].name);
66 :
67 : /* Clear first flag as we've added an element */
68 627 : first = 0;
69 : }
70 : }
71 :
72 619 : return buffer;
73 : }
74 :
75 : /**
76 : *
77 : * @brief Print out current date and time in ISO format
78 : * @param out_buffer Pointer to the destination buffer that receives the timestamp.
79 : * @param buffer_size Size of the destination buffer in bytes.
80 : * @return Return SUCCESS on success, FAILURE on error (buffer contents will contain fallback value).
81 : *
82 : */
83 218 : static Return logger_show_time(
84 : char *time_string,
85 : size_t buffer_size)
86 : {
87 : /** Return status
88 : * The status that will be passed to return() before exiting
89 : * By default, the function worked without errors
90 : */
91 218 : Return status = SUCCESS;
92 :
93 : struct timeval current_time;
94 : struct tm local_time;
95 :
96 218 : if(gettimeofday(¤t_time,NULL) != 0)
97 : {
98 0 : time_string[0] = '\0';
99 0 : status = FAILURE;
100 : }
101 :
102 218 : if(SUCCESS == status)
103 : {
104 218 : 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 218 : if(SUCCESS == status)
112 : {
113 218 : const int milliseconds = (int)(current_time.tv_usec / 1000);
114 :
115 218 : if(snprintf(time_string,
116 : buffer_size,
117 : "%04d-%02d-%02d %02d:%02d:%02d:%03d",
118 218 : local_time.tm_year + 1900,
119 218 : 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 218 : return(status);
132 : }
133 :
134 : /**
135 : *
136 : * @brief Logging to the screen with the source file name, line number
137 : * and name of the function that generated the message itself
138 : *
139 : */
140 : __attribute__((format(printf,5,6))) // Without this we will get warning
141 15478 : void rational_logger(
142 : const char level,
143 : const char *const filename,
144 : size_t line,
145 : const char *const funcname,
146 : const char *fmt,
147 : ...)
148 : {
149 15478 : if(rational_logger_mode & SILENT)
150 : {
151 : // Output nothing
152 1458 : return;
153 : }
154 :
155 14020 : if(!(level & UNDECOR) && (level & TESTING) && (rational_logger_mode & TESTING))
156 : {
157 : // Print out the word "TESTING:"
158 8109 : printf("TESTING:");
159 : }
160 :
161 14020 : if(!(level & UNDECOR) && (level & (VERBOSE|ERROR)) && (rational_logger_mode & VERBOSE))
162 : {
163 : char time_string[sizeof "2011-10-18 07:07:09:000"];
164 218 : (void)logger_show_time(time_string,sizeof(time_string));
165 :
166 : // Print out current time
167 218 : printf("%s ",time_string);
168 :
169 : // Print out the source file name
170 218 : printf("%s:",filename);
171 :
172 : // Print out line number in source file
173 218 : printf("%03zu:",line);
174 :
175 : // Print out name of the function itself
176 218 : printf("%s:",funcname);
177 : }
178 :
179 14020 : if(!(level & UNDECOR) && (level & ERROR) && (rational_logger_mode & (REGULAR | ERROR)))
180 : {
181 : // Print out error prefix
182 5 : printf("ERROR: ");
183 :
184 14015 : } else if(!(level & UNDECOR) && (level & ERROR) && (rational_logger_mode & (TESTING | VERBOSE))){
185 : // Print out the word "ERROR:"
186 77 : printf("ERROR:");
187 : }
188 :
189 14020 : if(level & ERROR && rational_logger_mode & ERROR)
190 2 : {
191 : // Print out other arguments
192 : va_list args;
193 2 : va_start(args,fmt);
194 2 : vprintf(fmt,args);
195 2 : va_end(args);
196 :
197 14018 : } else if(level & (REGULAR|ERROR) && rational_logger_mode & REGULAR){
198 : // Print out other arguments
199 : va_list args;
200 389 : va_start(args,fmt);
201 389 : vprintf(fmt,args);
202 389 : va_end(args);
203 :
204 13629 : } else if(level & (VERBOSE|ERROR) && rational_logger_mode & VERBOSE){
205 : // Print out other arguments
206 : va_list args;
207 489 : va_start(args,fmt);
208 489 : vprintf(fmt,args);
209 489 : va_end(args);
210 :
211 13140 : } else if(level & (TESTING|ERROR) && rational_logger_mode & TESTING){
212 : // Print out other arguments
213 : va_list args;
214 10275 : va_start(args,fmt);
215 10275 : vprintf(fmt,args);
216 10275 : va_end(args);
217 : }
218 : }
219 :
220 : #ifdef TEST
221 : /**
222 : * @file test_slog.c
223 : * @brief Complete test suite for log functionality
224 : */
225 : int main(void)
226 : {
227 : printf("All available combinations:\n");
228 : printf("%s\n",rational_convert(REGULAR));
229 : printf("%s\n",rational_convert(VERBOSE));
230 : printf("%s\n",rational_convert(TESTING));
231 : printf("%s\n",rational_convert(SILENT));
232 : printf("%s\n",rational_convert(REGULAR|VERBOSE));
233 : printf("%s\n",rational_convert(REGULAR|TESTING));
234 : printf("%s\n",rational_convert(VERBOSE|TESTING));
235 : printf("%s\n",rational_convert(REGULAR|VERBOSE|TESTING));
236 : printf("%s\n",rational_convert(ERROR));
237 : printf("%s\n",rational_convert(UNDECOR));
238 : printf("%s\n",rational_convert(EVERY|UNDECOR));
239 : printf("%s\n",rational_convert(ERROR|UNDECOR));
240 :
241 : /* Test REGULAR mode combinations */
242 : rational_logger_mode = REGULAR;
243 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
244 : printf("1. Must print:"); slog(REGULAR,"true"); printf("\n");
245 : printf("2. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
246 : printf("3. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
247 : printf("4. Must print:"); slog(ERROR,"true"); printf("\n");
248 :
249 : /* Test VERBOSE mode combinations */
250 : rational_logger_mode = VERBOSE;
251 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
252 : printf("5. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
253 : printf("6. Must print:"); slog(VERBOSE,"true"); printf("\n");
254 : printf("7. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
255 : printf("8. Must print:"); slog(ERROR,"true"); printf("\n");
256 :
257 : /* Test TESTING mode combinations */
258 : rational_logger_mode = TESTING;
259 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
260 : printf("9. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
261 : printf("10. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
262 : printf("11. Must print:"); slog(TESTING,"true"); printf("\n");
263 : printf("12. Must print:"); slog(ERROR,"true"); printf("\n");
264 :
265 : /* Test SILENT mode combinations */
266 : rational_logger_mode = SILENT;
267 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
268 : printf("13. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
269 : printf("14. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
270 : printf("15. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
271 : printf("16. Won't print:"); slog(ERROR,"but printed!"); printf("\n");
272 :
273 : /* Test REGULAR|VERBOSE combinations */
274 : rational_logger_mode = REGULAR|VERBOSE;
275 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
276 : printf("17. Must print:"); slog(REGULAR,"true"); printf("\n");
277 : printf("18. Must print:"); slog(VERBOSE,"true"); printf("\n");
278 : printf("19. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
279 : printf("20. Must print:"); slog(ERROR,"true"); printf("\n");
280 :
281 : /* Test REGULAR|TESTING combinations */
282 : rational_logger_mode = REGULAR|TESTING;
283 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
284 : printf("21. Must print:"); slog(REGULAR,"true"); printf("\n");
285 : printf("22. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
286 : printf("23. Must print:"); slog(TESTING,"true"); printf("\n");
287 : printf("24. Must print:"); slog(ERROR,"true"); printf("\n");
288 :
289 : /* Test VERBOSE|TESTING combinations */
290 : rational_logger_mode = VERBOSE|TESTING;
291 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
292 : printf("25. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
293 : printf("26. Must print:"); slog(VERBOSE,"true"); printf("\n");
294 : printf("27. Must print:"); slog(TESTING,"true"); printf("\n");
295 : printf("28. Must print:"); slog(ERROR,"true"); printf("\n");
296 :
297 : /* Test REGULAR|VERBOSE|TESTING combinations */
298 : rational_logger_mode = REGULAR|VERBOSE|TESTING;
299 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
300 : printf("29. Must print:"); slog(REGULAR,"true"); printf("\n");
301 : printf("30. Must print:"); slog(VERBOSE,"true"); printf("\n");
302 : printf("31. Must print:"); slog(TESTING,"true"); printf("\n");
303 : printf("32. Must print:"); slog(ERROR,"true"); printf("\n");
304 :
305 : /* Test ERROR mode combinations */
306 : rational_logger_mode = ERROR;
307 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
308 : printf("33. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
309 : printf("34. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
310 : printf("35. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
311 : printf("36. Must print:"); slog(ERROR,"true"); printf("\n");
312 :
313 : /*
314 : * Test UNDECOR flag: suppress logger prefixes (TESTING:, time/file/line/func, ERROR:)
315 : * The output between the '|' markers should contain only the message payload.
316 : */
317 :
318 : rational_logger_mode = EVERY|ERROR;
319 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
320 : printf("37. Must print no prefixes:|"); slog(EVERY|UNDECOR,"true"); printf("|\n");
321 : printf("38. Must print no ERROR prefix:|"); slog(ERROR|UNDECOR,"true"); printf("|\n");
322 :
323 : rational_logger_mode = VERBOSE;
324 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
325 : printf("39. Must print no time/file/line/func:|"); slog(VERBOSE|UNDECOR,"true"); printf("|\n");
326 :
327 : rational_logger_mode = TESTING;
328 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
329 : printf("40. Must print no TESTING prefix:|"); slog(TESTING|UNDECOR,"true"); printf("|\n");
330 :
331 : rational_logger_mode = REGULAR;
332 : printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
333 : printf("41. Must not print (VERBOSE not enabled):|"); slog(VERBOSE|UNDECOR,"but printed!"); printf("|\n");
334 :
335 : return 0;
336 : }
337 : #endif
|