LCOV - code coverage report
Current view: top level - libs/rational/src - rational_logger.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.5 % 84 76
Test Date: 2026-03-31 13:51:38 Functions: 100.0 % 6 6
Branches: 85.1 % 74 63

             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(&current_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(&current_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
        

Generated by: LCOV version 2.0-1