LCOV - code coverage report
Current view: top level - src - log.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 70 100.0 %
Date: 2016-12-21 02:12:01 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /***************************************************************************//**
       2             : 
       3             :   @file         log.c
       4             : 
       5             :   @author       Stephen Brennan
       6             : 
       7             :   @date         Created Sunday, 24 May 2015
       8             : 
       9             :   @brief        Logging facilities implementation for libstephen.
      10             : 
      11             :   @copyright    Copyright (c) 2015-2016, Stephen Brennan.  Released under the
      12             :                 Revised BSD License.  See LICENSE.txt for details.
      13             : 
      14             : *******************************************************************************/
      15             : 
      16             : #include <stdio.h>
      17             : #include <stdarg.h>
      18             : 
      19             : #include "libstephen/cb.h"
      20             : #include "libstephen/log.h"
      21             : 
      22             : /*
      23             :   This is the "permanent" default logger.  Even when you set your own default
      24             :   logger, this one still hangs around, waiting for you to call
      25             :   sl_set_default_logger(NULL).  It's declared here, but the default logger
      26             :   should go to standard out.  Unfortunately, you can't statically declare an
      27             :   instance of a struct that uses the variable stdout, so this is declared
      28             :   without any handlers, and the first time the default logger is referenced, the
      29             :   default log handler is added.
      30             :  */
      31             : static smb_logger default_logger = {
      32             :   .format = SMB_DEFAULT_LOGFORMAT,
      33             :   .num = 0
      34             : };
      35             : 
      36             : /*
      37             :   The only reason this pointer exists is so that reference_logger() knows
      38             :   whether or not the default logger has been set up yet.  This variable remains
      39             :   NULL until the first time the default logger is used, at which point the
      40             :   default handler is added to it, and then pdefault_logger is set to
      41             :   &default_logger.
      42             :  */
      43             : static smb_logger *pdefault_logger = NULL;
      44             : 
      45             : static char *level_names[] = {"NOTSET", "DEBUG", "INFO",
      46             :                               "WARNING", "ERROR", "CRITICAL"};
      47             : 
      48             : /*
      49             :   This function is used by most sl_ functions to initialize their `obj` pointer.
      50             :   Essentially, if obj=NULL, it replaces obj with a pointer to the default
      51             :   logger.  If the default logger hasn't been set up, it sets it up.
      52             :  */
      53          37 : static void reference_logger(smb_logger **obj)
      54             : {
      55          37 :   smb_status status = SMB_SUCCESS;
      56          37 :   if (*obj == NULL) {
      57          35 :     if (pdefault_logger == NULL) {
      58           1 :       sl_add_handler(&default_logger,
      59           1 :                      (smb_loghandler){.level = SMB_DEFAULT_LOGLEVEL,
      60             :                                       .dst   = SMB_DEFAULT_LOGDEST},
      61             :                      &status);
      62           1 :       pdefault_logger = &default_logger;
      63             :     }
      64          35 :     *obj = pdefault_logger;
      65             :   }
      66          37 : }
      67             : 
      68           1 : void sl_init(smb_logger *obj)
      69             : {
      70           1 :   obj->format = SMB_DEFAULT_LOGFORMAT;
      71           1 :   obj->num = 0;
      72           1 : }
      73             : 
      74           1 : smb_logger *sl_create(void)
      75             : {
      76           1 :   smb_logger *obj = smb_new(smb_logger, 1);
      77           1 :   sl_init(obj);
      78           1 :   return obj;
      79             : }
      80             : 
      81           1 : void sl_destroy(smb_logger *obj) {
      82             :   (void)obj; // unused
      83             :   // nothing to delete
      84           1 : }
      85             : 
      86           1 : void sl_delete(smb_logger *obj) {
      87           1 :   sl_destroy(obj);
      88           1 :   smb_free(obj);
      89           1 : }
      90             : 
      91           2 : void sl_set_level(smb_logger *obj, int level)
      92             : {
      93             :   int i;
      94           2 :   reference_logger(&obj);
      95           8 :   for (i = 0; i < obj->num; i++) {
      96           6 :     obj->handlers[i].level = level;
      97             :   }
      98           2 : }
      99             : 
     100          18 : void sl_add_handler(smb_logger *obj, smb_loghandler h, smb_status *status)
     101             : {
     102          18 :   reference_logger(&obj);
     103          18 :   if (obj->num < SMB_MAX_LOGHANDLERS) {
     104          17 :     obj->handlers[obj->num++] = h;
     105             :   } else {
     106           1 :     *status = SMB_INDEX_ERROR;
     107             :   }
     108          18 : }
     109             : 
     110           3 : void sl_clear_handlers(smb_logger *obj)
     111             : {
     112           3 :   reference_logger(&obj);
     113           3 :   obj->num = 0;
     114           3 : }
     115             : 
     116           2 : void sl_set_default_logger(smb_logger *obj)
     117             : {
     118           2 :   if (obj == NULL)
     119           1 :     obj = &default_logger;
     120           2 :   pdefault_logger = obj;
     121           2 : }
     122             : 
     123             : /*
     124             :   Returns a string representing a log level.  Very much not thread safe (but in
     125             :   reality, I don't think anything about this logger library is thread safe, so
     126             :   whatever ¯\_(ツ)_/¯ ).
     127             :  */
     128           8 : static char *sl_level_string(int level) {
     129             :   static char buf[20]; // plenty of space, to be safe.
     130           8 :   if (level % 10 == 0 && level >= LEVEL_NOTSET && level <= LEVEL_CRITICAL) {
     131           7 :     return level_names[level / 10];
     132             :   } else {
     133           1 :     snprintf(buf, 20, "%d", level);
     134           1 :     return buf;
     135             :   }
     136             : }
     137             : 
     138          14 : bool sl_will_log(smb_logger *obj, int level)
     139             : {
     140          20 :   for (int i = 0; i < obj->num; i++) {
     141          14 :     if (obj->handlers[i].level <= level) {
     142           8 :       return true;
     143             :     }
     144             :   }
     145           6 :   return false;
     146             : }
     147             : 
     148          14 : void sl_log(smb_logger *obj, char *file, int line, const char *function, int level, ...) {
     149             :   cbuf file_line_buf, message_buf;
     150             :   char *level_string, *format;
     151             :   va_list va;
     152             :   int i;
     153             : 
     154          14 :   reference_logger(&obj);
     155             : 
     156          14 :   if (!sl_will_log(obj, level)) {
     157          20 :     return; // early termination to prevent formatting if we can avoid it
     158             :   }
     159             : 
     160           8 :   cb_init(&file_line_buf, 256);
     161           8 :   cb_printf(&file_line_buf, "%s:%d", file, line);
     162             : 
     163           8 :   va_start(va, level);
     164           8 :   format = va_arg(va, char*);
     165           8 :   cb_init(&message_buf, 1024);
     166           8 :   cb_vprintf(&message_buf, format, va);
     167           8 :   va_end(va);
     168             : 
     169           8 :   level_string = sl_level_string(level);
     170          44 :   for (i = 0; i < obj->num; i++) {
     171          36 :     if (obj->handlers[i].level <= level) {
     172          26 :       fprintf(obj->handlers[i].dst, obj->format, file_line_buf.buf, function,
     173             :               level_string, message_buf.buf);
     174             :     }
     175             :   }
     176           8 :   cb_destroy(&file_line_buf);
     177           8 :   cb_destroy(&message_buf);
     178             : }

Generated by: LCOV version 1.11