Main Page   Data Structures   File List   Data Fields   Globals   Related Pages  

logger.c

Go to the documentation of this file.
00001 /***************************************************************************
00002                                   logger.c
00003                              -------------------
00004     begin                : Sat Nov 3 2001
00005     copyright            : (C) 2001-2002 by Christian Hönig & Gunter Ohrner
00006     email                : pdepp@CustomCDROM.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00022 #include "common.h"
00023 
00024 #include <assert.h>
00025 #include <errno.h>
00026 #include <unistd.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <sys/types.h>
00030 #include <time.h>
00031 
00032 #include "logger.h"
00033 
00034 #include "buffer.h"
00035 #include "conftable.h"
00036 #include "fileplist.h"
00037 #include "loggerlist.h"
00038 #include "misc.h"
00039 #include "xmalloc.h"
00040 
00041 
00042 /**************************************************************************
00043  *                                                                        *
00044  *   functions/macros to retrieve information about log message sources   *
00045  *                                                                        *
00046  **************************************************************************/
00047 
00048 // 0 is message source description, 1 is msg source conftable key
00049 
00051 #define lgGetSrcDescText(src) (((src) <= LOG_SRC_LAST) ? (log_src_info_tab[src][0]) : "unknown source")
00052 
00054 #define lgGetSrcConfKey(src) (((src) <= LOG_SRC_LAST) ? (log_src_info_tab[src][1]) : "U")
00055 
00057 #define lgGetSrcDescMaxLen() (lgGetSrcMaxLen(0))
00058 
00059 #define lgGetSrcKeyMaxLen() (lgGetSrcMaxLen(1))
00060 
00062 size_t lgGetSrcMaxLen(int which)
00063 {
00064   int src;
00065   size_t maxlen = 0;
00066 
00067   assert(which == 0 || which == 1);
00068 
00069   for(src = 0; src <= LOG_SRC_LAST; ++src)
00070     maxlen = MAX2(maxlen, strlen(log_src_info_tab[src][which]));
00071 
00072   return MAX2(maxlen, strlen(LOG_SRC_PLACEHOLDER_ALL));
00073 }
00074 
00075 
00077 #define lgGetLvlConfKey(lvl) (((lvl) <= LOG_LVL_LAST) ? log_lvl_info_tab[lvl] : "u")
00078 
00080 size_t lgGetLvlKeyMaxLen()
00081 {
00082   int lvl;
00083   size_t maxlen = 0;
00084 
00085   for(lvl = 0; lvl <= LOG_LVL_LAST; ++lvl)
00086     maxlen = MAX2(maxlen, strlen(log_lvl_info_tab[lvl]));
00087 
00088   return MAX2(maxlen, strlen(LOG_LVL_PLACEHOLDER_ALL));
00089 }
00090 
00091 
00092 /**************************************************************************
00093  *                                                                        *
00094  *           functions for dealing with a single logging stream           *
00095  *                                                                        *
00096  **************************************************************************/
00097 /*
00098   each logging stream is associated with exactly ONE log message source
00099   and ONE severity level.
00100  */
00101 
00103 struct Logger
00104 {
00106 FILE* lg_ostream;
00108   int manage_stream;
00110   char *format;
00112   size_t format_len;
00113 };
00114 
00116 int logClose(Logger this) {
00117   int result = 0;
00118   // can I close lg_stream?
00119   if (this->manage_stream && this->lg_ostream != NULL) {
00120     result = fclose(this->lg_ostream);
00121     // invalidate the lg_ostream var
00122     if (result == 0) this->lg_ostream = NULL;
00123   }
00124   return result;
00125 }
00126 
00128 Logger logInitStream_int(FILE * log_stream, const char* format, int manage_stream)
00129 {
00130   Logger this = (Logger) pd_malloc(sizeof(struct Logger));
00131   this->lg_ostream = log_stream;
00132   this->format_len = strlen(format);
00133   this->format = (char*) pd_malloc(this->format_len + 1);
00134   strcpy(this->format, format);
00135 
00136   if (log_stream != NULL)
00137     this->manage_stream = manage_stream;
00138   
00139   // always succeed
00140   return this;
00141 }
00142 
00143 // opens a new logfile. returns 0 on success
00144 Logger logInit(const char* file_name, const char* format)
00145 {
00146   FILE* new_stream;
00147   if (file_name == NULL) file_name = CONFENTRY_KEY_STDLOGFILE;
00148 
00149   if (!strcmp(file_name, LOG_NULL_STREAM))
00150     new_stream = NULL;
00151   else
00152   {
00153     new_stream = fopen(file_name, "a");
00154     if (!new_stream) return NULL;
00155   }
00156 
00157   return logInitStream_int(new_stream, format, true);
00158 }
00159 
00160 // sets the current logging stream to use the given stream log_stream
00161 Logger logInitStream(FILE * log_stream, const char* format)
00162 {
00163   return logInitStream_int(log_stream, format, false);
00164 }
00165 
00166 // logs a string into the current logfile with an additional error description
00167 int logMessagev(Logger this,
00168                 const unsigned int origin, __attribute__((unused)) const unsigned int severity,
00169                 const int errno_val, const char *const log_text_format, va_list daten)
00170 {
00171   const int errno_orig = errno;
00172   const time_t tmptime = time(NULL);
00173   struct tm* now = localtime(&tmptime);
00174   size_t idx;
00175   
00176   if (this->lg_ostream == NULL/* && severity != CRITICAL*/) return -1;
00177   
00178   for (idx = 0; idx < this->format_len; ++idx)
00179   {
00180     char cur_char = *(this->format+idx);
00181     if (cur_char == MP)
00182     {
00183       //check the following char
00184       switch(*(this->format+idx+1))
00185       {
00186       case(MP_YEAR): // year
00187         fprintf(this->lg_ostream, "%d", now->tm_year+1900);
00188         break;
00189       case(MP_MONTH): // month
00190         fprintf(this->lg_ostream, "%02d", now->tm_mon+1);
00191         break;
00192       case(MP_DAY): // day
00193         fprintf(this->lg_ostream, "%02d", now->tm_mday);
00194         break;
00195       case(MP_WDAY): // day of week short
00196         fputs(day_of_week[now->tm_wday][WD_SHORT], this->lg_ostream);
00197         break;
00198       case(MP_WDAYL): // day of week long
00199         fputs(day_of_week[now->tm_wday][WD_LONG], this->lg_ostream);
00200         break;
00201       case(MP_HOUR): // hour
00202         fprintf(this->lg_ostream, "%02d", now->tm_hour);
00203         break;
00204       case(MP_MIN): // minute
00205         fprintf(this->lg_ostream, "%02d", now->tm_min);
00206         break;
00207       case(MP_SEC): // second
00208         fprintf(this->lg_ostream, "%02d", now->tm_sec);
00209         break;
00210       case(MP_SRC): // source
00211         fputs(lgGetSrcDescText(origin), this->lg_ostream);
00212         break;
00213       case(MP_LOG): // logcontent
00214         vfprintf(this->lg_ostream, log_text_format, daten);
00215         if (errno_val != 0) fprintf(this->lg_ostream, ": %s", strerror(errno_val));
00216         break;
00217       default:
00218         //we swallow the MP, but keep the following char
00219         //(remember: a MP without \ is invalid!)
00220         --idx;
00221       }
00222       //jump over this one (%A = 2 chars)
00223       ++idx;
00224     }
00225     // find a sonderzeichen?
00226     // remember: a single '\\' (backslash) is ignored  !!!
00227     else if (cur_char == '\\')
00228     {
00229       switch (*(this->format+idx+1))
00230       {
00231       case('n'):  //newline char
00232         putc('\n', this->lg_ostream);
00233         break;
00234       case('t'):  //tab
00235         putc('\t', this->lg_ostream);
00236         break;
00237       case('\\'): // backslash
00238         putc('\\', this->lg_ostream);
00239         break;
00240       case(MP):
00241         putc(MP, this->lg_ostream);
00242         break;
00243       default:
00244         //a single backslash is written into the data nirvana ;)
00245         
00246         // this is not very clean, but i avoid writing everywhere the ++idx
00247         // here it is not allowed to add one;
00248         --idx;
00249       }
00250       ++idx;
00251     } 
00252     else // no special char
00253       putc(cur_char, this->lg_ostream);
00254   }
00255 
00256   fflush(this->lg_ostream);
00257   
00258   // restore errno value, just in case
00259   errno = errno_orig;
00260   return 0;
00261 }
00262 
00263 // terminates a Logger object
00264 int logTerminate(Logger this)
00265 {
00266   logClose(this);
00267   pd_free(this->format);
00268   pd_free(this);
00269   return 0;
00270 }
00271 
00272 /**************************************************************************
00273  *                                                                        *
00274  *           functions for managing a bunch of logging streams            *
00275  *                                                                        *
00276  **************************************************************************/
00277 
00279 LoggerList loggers[LOG_SRC_LAST+1][LOG_LVL_LAST+1];
00281 FilepList log_streams;
00282 
00283 // adds a logger to the [src][lvl]-matrix
00284 inline int addLogger2List(Logger lgr, int src, int lvl, const char* origin_flags);
00285 // quick and dirty: converts an int to a str
00286 inline char* int2str(int i);
00287 
00295 int logfilesInit()
00296 {
00297   const size_t log_file_name_key_len = MAX2(                      // -%s, +NNN
00298                                         strlen(CONFENTRY_KEY_LOGFILE)-2+3+1,
00299                                         strlen(CONFENTRY_KEY_STDLOGFILE)+1
00300                                        );
00301   char* log_file_name_key = (char*) pd_malloc(log_file_name_key_len);
00302   const char* log_file_name;
00303   int lvl, cnt, src, at_least_one_added = false;
00304 
00305   size_t lvl_key_max = lgGetLvlKeyMaxLen(), src_key_max = lgGetSrcKeyMaxLen();
00306 
00307   // *** size calculations for format string template keys ***
00308   // generic format template key for all loggers if not specified explicitely
00309                                                            // -%s, +\0
00310   size_t std_format_key_len = strlen(CONFENTRY_KEY_LOGFORMAT) - 2 + 1;
00311   // std format template key for a given logger
00312                                       // "NNN.src\0"
00313   size_t log_format_key_id_len = 3 + 1 + src_key_max + 1;
00314   size_t log_format_key_len = std_format_key_len + log_format_key_id_len;
00315   // std format template key for a given msg source in a given logger
00316   size_t src_format_key_id_len = log_format_key_id_len + lvl_key_max + 1;
00317                                               // -%s, +"NNN.s\0"
00318   size_t src_format_key_len = log_format_key_len + 1 + lvl_key_max;
00319   // format template key for a given msg source and severity level in a given lgr
00320                                                     // -%s, +"NNN.s.l\0"
00321   size_t src_lvl_format_key_id_len = src_format_key_id_len;
00322   size_t src_lvl_format_key_len = src_format_key_len;
00323 
00324   // key to get the global log format template
00325   char* std_format_key = (char*) pd_malloc(std_format_key_len);
00326 
00327   // id (middle part) of log_format_key     "NNN.src\0"
00328   char* log_format_key_id = (char*) pd_malloc(log_format_key_id_len);
00329   // key to get a log-specific format template
00330   char* log_format_key = (char*) pd_malloc(log_format_key_len);
00331 
00332   // if (middle part) of src_format_key       "NNN.src.lvl\0"
00333   char* src_format_key_id = (char*) pd_malloc(src_format_key_id_len);
00334   // key to get format for a single mesg. source in a specific logger
00335   char* src_format_key = (char*) pd_malloc(src_format_key_len);
00336 
00337   // if (middle part) of src_lvl_format_key      "NNN.src.lvl\0"
00338   char* src_lvl_format_key_id = (char*) pd_malloc(src_lvl_format_key_id_len);
00339   // key to get the format key for a single msg. src. at a given severity level in one logger
00340   char* src_lvl_format_key = (char*) pd_malloc(src_lvl_format_key_len);
00341 
00342   // *** size calculations for origin flag keys ***
00343   // generic origin flag key for all loggers if not specified explicitely
00344                                                                    // -%s, +\0"
00345   size_t std_origin_flags_key_len = strlen(CONFENTRY_KEY_LOGORIGINFLAGS) - 2 + 1;
00346   // std origin flags key for a given logger
00347                                                                      // -%s, +"NNN\0"
00348   size_t log_origin_flags_key_len = strlen(CONFENTRY_KEY_LOGORIGINFLAGS) - 2 + 3 + 1;
00349 
00350   char* std_origin_flags_key = (char*) pd_malloc(std_origin_flags_key_len);
00351   char* log_origin_flags_key = (char*) pd_malloc(log_origin_flags_key_len);
00352 
00353   const char * std_origin_flags, * std_format;
00354 
00355   log_streams = filepListInit();
00356 
00357   // format standard origin flags and format keys and read the values
00358   sprintf(std_origin_flags_key, CONFENTRY_KEY_LOGORIGINFLAGS, "");
00359   std_origin_flags = getEntryDefl(std_origin_flags_key, STD_CONFENTRY_VAL_LOGORIGINFLAGS);
00360 
00361   sprintf(std_format_key, CONFENTRY_KEY_LOGFORMAT, "");
00362   std_format = getEntryDefl(std_format_key, STD_CONFENTRY_VAL_LOGFORMAT);
00363 
00364   // init the logListists
00365   for (src = 0; src <= LOG_SRC_LAST; ++src)
00366     for(lvl = 0; lvl <= LOG_LVL_LAST; ++lvl)
00367     {
00368       loggers[src][lvl] = logListInit();
00369       if (lvl >= CRITICAL) // log any critical error to stderr
00370       {
00371         Logger std_err_log = logInitStream(stderr, LOG_PANIC_FORMAT);
00372 #ifndef NDEBUG
00373         if (!
00374 #endif
00375             addLogger2List(std_err_log, src, lvl,
00376                            LOG_LVL_PLACEHOLDER_ALL LOG_SRC_PLACEHOLDER_ALL)
00377 #ifndef NDEBUG
00378             )
00379           abort()
00380 #endif
00381             ;
00382       }
00383     }
00384 
00385   // add the standard-logfile first
00386   // use an invalid cnt value so the default values will be used for origin_flags
00387   // and format
00388   cnt = -1;
00389   snprintf(log_file_name_key, log_file_name_key_len, CONFENTRY_KEY_STDLOGFILE);
00390 
00391   while((log_file_name = getEntry(log_file_name_key)) != NULL && cnt < 1000)
00392   {
00393     const char * log_origin_flags, * log_format;
00394     int added = false;
00395 
00396     // open the logfile
00397     FILE* log_stream = fopen(log_file_name, "a");
00398     if (log_stream == NULL)
00399       fprintf(stderr,
00400               "could not open logfile \"%s\": %s\n",
00401               log_file_name, strerror(errno));
00402     else
00403     {
00404       // generate conftable key for the logfile specific format template string
00405       // look for "logN.A.format"
00406       sprintf(log_format_key_id, "%d.%s", cnt, LOG_SRC_PLACEHOLDER_ALL);
00407       sprintf(log_format_key, CONFENTRY_KEY_LOGFORMAT, log_format_key_id);
00408       // Read the logfile specific format template string. If none is given use the
00409       // global default defined in the log file.
00410       log_format = getEntry(log_format_key);
00411 
00412       if (log_format == NULL)
00413       {
00414         // no logN.A.format found...
00415         // look for logN.format
00416         sprintf(log_format_key_id, "%d", cnt);
00417         sprintf(log_format_key, CONFENTRY_KEY_LOGFORMAT, log_format_key_id);
00418         // Read the logfile specific format template string. If none is given use the
00419         // global default defined in the log file.
00420         log_format = getEntryDefl(log_format_key, std_format);
00421       }
00422 
00423       // generate the logfile specifig msg origin flag string
00424       snprintf(log_origin_flags_key, log_origin_flags_key_len,
00425                CONFENTRY_KEY_LOGORIGINFLAGS, int2str(cnt));
00426       log_origin_flags = getEntryDefl(log_origin_flags_key, std_origin_flags);
00427 
00428       // loop over all msg sources of this logger
00429       for (src = 0; src <= LOG_SRC_LAST; ++src)
00430       {
00431         const char * src_format;
00432 
00433         // lookup format templates given explicitely for this msg source with
00434         // this logger
00435         sprintf(src_format_key_id, "%s.%s.%s", int2str(cnt), lgGetSrcConfKey(src), LOG_LVL_PLACEHOLDER_ALL);
00436         sprintf(src_format_key, CONFENTRY_KEY_LOGFORMAT, src_format_key_id);
00437 
00438         // inherit log_format if no specific is given
00439         src_format = getEntry(src_format_key);
00440 
00441         if (src_format == NULL)
00442         {
00443           // lookup format templates given explicitely for this msg source with
00444           //   this logger
00445           sprintf(src_format_key_id, "%s.%s", int2str(cnt), lgGetSrcConfKey(src));
00446           sprintf(src_format_key, CONFENTRY_KEY_LOGFORMAT, src_format_key_id);
00447 
00448           // inherit   log_format if no specific is given
00449           src_format = getEntryDefl(src_format_key, log_format);
00450         }
00451 
00452         // loop over all severity levels for a specific msg source of the current logger
00453         for (lvl = 0; lvl <= LOG_LVL_LAST; ++lvl)
00454         {
00455           const char* src_lvl_format;
00456 
00457           // look up format informations given explicitely for
00458           // this msg source / level pair, overriding any more generic
00459           // format description
00460           sprintf(src_lvl_format_key_id, "%s.%s.%s", 
00461                   int2str(cnt), lgGetSrcConfKey(src), lgGetLvlConfKey(lvl));
00462           sprintf(src_lvl_format_key, CONFENTRY_KEY_LOGFORMAT, src_lvl_format_key_id);
00463 
00464           // inherit the src_format if no specific is given
00465           src_lvl_format = getEntry(src_lvl_format_key);
00466           if (src_lvl_format == NULL)
00467           {
00468             sprintf(src_lvl_format_key_id, "%s.%s.%s", 
00469                     int2str(cnt), LOG_SRC_PLACEHOLDER_ALL, lgGetLvlConfKey(lvl));
00470             sprintf(src_lvl_format_key, CONFENTRY_KEY_LOGFORMAT, src_lvl_format_key_id);
00471             src_lvl_format = getEntryDefl(src_lvl_format_key, src_format);
00472           }
00473 
00474           if (strcmp(src_lvl_format, LOG_NULL_FORMAT) != 0)
00475           {
00476             Logger lgr;
00477             lgr = logInitStream(log_stream, src_lvl_format);
00478 
00479             if (addLogger2List(lgr, src, lvl, log_origin_flags))
00480               added = true;
00481             else
00482             logTerminate(lgr);
00483           }
00484         } // end "for (lvl="
00485       } // end "for (src="
00486 
00487       if (!added)
00488         fclose(log_stream);
00489       else
00490       {
00491         at_least_one_added = true;
00492         filepListAppend(log_streams, log_stream);
00493       }
00494     } // endif FILE == NULL
00495 
00496     // continue with the next logfile defined in the configuration file
00497     snprintf(log_file_name_key, log_file_name_key_len,
00498              CONFENTRY_KEY_LOGFILE, int2str(++cnt));
00499   }
00500 
00501   // no logfile could be created, use panic logging stream for everything (stderr)
00502   if (!at_least_one_added)
00503   {
00504     for (src = 0; src <= LOG_SRC_LAST; ++src)
00505       // everything which is critical is logged to stderr anyway
00506       for (lvl = INFO; lvl < CRITICAL; ++lvl)
00507       {
00508         Logger panic_log = logInitStream(stderr, LOG_PANIC_FORMAT);
00509 #ifndef NDEBUG
00510         if (!
00511 #endif
00512             addLogger2List(panic_log, src, lvl,
00513                             LOG_SRC_PLACEHOLDER_ALL LOG_LVL_PLACEHOLDER_ALL)
00514 #ifndef NDEBUG
00515             )
00516           abort()
00517 #endif
00518             ;
00519       }
00520     /* This message get's output during logging
00521        subsystem-preinitialisation as well so we should better be quiet...
00522        logger(LOGGER, WARNING,
00523            "No logfiles have been configured or could be opened,\n"
00524            "falling back to stderr for outputting all messages!"); */
00525   }
00526 
00527   pd_free(log_file_name_key);
00528   pd_free(std_format_key);
00529   pd_free(log_format_key);
00530   pd_free(log_format_key_id);
00531   pd_free(src_format_key);
00532   pd_free(src_format_key_id);
00533   pd_free(src_lvl_format_key);
00534   pd_free(src_lvl_format_key_id);
00535   pd_free(std_origin_flags_key);
00536   pd_free(log_origin_flags_key);
00537 
00538   return 0;
00539 }
00540 
00541 // close all log files and shut down the logging subsystem
00542 int logfilesTerminate()
00543 {
00544   int src, lvl;
00545   for (src = 0; src <= LOG_SRC_LAST ; ++src)
00546     for (lvl = 0; lvl <= LOG_LVL_LAST; ++lvl)
00547     {
00548       logListFirst(loggers[src][lvl]);
00549       while(!logListIsTail(loggers[src][lvl]))
00550       {
00551         logTerminate(logListGetPayload(loggers[src][lvl]));
00552         logListDelete(loggers[src][lvl]);
00553       }
00554       logListTerminate(loggers[src][lvl]);
00555     }
00556 
00557   filepListFirst(log_streams);
00558   while(!filepListIsTail(log_streams))
00559   {
00560     fclose(filepListGetPayload(log_streams));
00561     filepListDelete(log_streams);
00562   }
00563   filepListTerminate(log_streams);
00564 
00565   return 0;
00566 }
00567 
00568 // logs an error message
00569 int logerr(const unsigned int origin, const unsigned int severity, const int errno_val,
00570            const char *const log_text_format, ...)
00571 {
00572   va_list args;
00573   int res = 0;
00574   LoggerList cur_list = loggers[origin][severity];
00575 
00576   logListFirst(cur_list);
00577   while(!logListIsTail(cur_list))
00578   {
00579     va_start(args, log_text_format);
00580     if ( logMessagev(logListGetPayload(cur_list),
00581                      origin, severity, errno_val, log_text_format, args) == -1 ) res = -1;
00582     va_end(args);
00583     logListNext(cur_list);
00584   }
00585   return res;
00586 }
00587 
00588 // logs a string into the current logfile
00589 int logger(const unsigned int origin, const unsigned int severity,
00590            const char *const log_text_format, ...)
00591 {
00592   va_list args;
00593   int res = 0;
00594   LoggerList cur_list = loggers[origin][severity];
00595 
00596   logListFirst(cur_list);
00597   while(!logListIsTail(cur_list))
00598   {
00599     va_start(args, log_text_format);
00600     if ( logMessagev(logListGetPayload(cur_list),
00601                      origin, severity, 0, log_text_format, args) == -1 ) res = -1;
00602     va_end(args);
00603     logListNext(cur_list);
00604   }
00605   return res;
00606 }
00607 
00609 inline int addLogger2List(Logger lgr, int src, int lvl, const char* origin_flags)
00610 {
00611   assert(src >= 0 && src <= LOG_SRC_LAST && lvl >= 0 && lvl <= LOG_LVL_LAST);
00612 
00613   // This looks fairly complex but is extremely simple in fact:
00614   // If the src conf key is contained in the current origin_flags string
00615   //   or the placeholder for all msg sources is included 
00616   // and the lvl conf key is contained in the current origin_flags string
00617   //   or the placeholder for all severity levels is included 
00618   // then add this logger to the matrix
00619   if ((strstr(origin_flags, lgGetSrcConfKey(src))
00620           || strstr(origin_flags, LOG_SRC_PLACEHOLDER_ALL))
00621       && (strstr(origin_flags, lgGetLvlConfKey(lvl))
00622           || (strstr(origin_flags, LOG_LVL_PLACEHOLDER_ALL) && lvl != DEBUG)))
00623   {
00624     logListAppend(loggers[src][lvl], lgr);
00625     return true;
00626   }
00627   return false;
00628 }
00629 
00636 inline char* int2str(int i)
00637 {
00638   char tmp_buf[4];
00639 
00640   ssize_t res = snprintf(tmp_buf, 4, "%d", i);
00641 
00642   return (res == -1 || res >= 4) ? "" : tmp_buf;
00643 }

Generated on Fri Jan 25 22:40:31 2002 for PDepp Webserver by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001