LCOV - code coverage report
Current view: top level - libs/rational/src - rational_logger.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 90.4 % 83 75
Test Date: 2026-03-01 04:31:48 Functions: 100.0 % 6 6
Branches: 84.7 % 72 61

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

Generated by: LCOV version 2.0-1