00001 /***************************************************************************** 00002 * Copyright (C) 2002, F. Hoffmann-La Roche & Co., AG, Basel, Switzerland. * 00003 * * 00004 * This file is part of "Roche Bioinformatics Software Objects and Services" * 00005 * * 00006 * This file is free software; you can redistribute it and/or * 00007 * modify it under the terms of the GNU Lesser General Public * 00008 * License as published by the Free Software Foundation; either * 00009 * version 2.1 of the License, or (at your option) any later version. * 00010 * * 00011 * This file is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00014 * Lesser General Public License for more details. * 00015 * * 00016 * To obtain a copy of the GNU Lesser General Public License * 00017 * please write to the Free Software * 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 00019 * or visit the WWW site http://www.gnu.org/copyleft/lesser.txt * 00020 * * 00021 * SCOPE: this licence applies to this file. Other files of the * 00022 * "Roche Bioinformatics Software Objects and Services" may be * 00023 * subject to other licences. * 00024 * * 00025 * CONTACT: clemens.broger@roche.com or detlef.wolf@roche.com * 00026 * * 00027 *****************************************************************************/ 00028 00029 00036 /* 00037 PART I: printing messages to stderr - die/warn/usage/romsg 00038 PART II: printing messages to a log file 00039 PART III: assertions and messages at debug time 00040 PART IV: buffering warnings 00041 */ 00042 00043 #include "plabla.h" 00044 00045 #include <sys/types.h> 00046 #include <sys/stat.h> 00047 #include <fcntl.h> 00048 #include PLABLA_INCLUDE_IO_UNISTD 00049 #include <stdlib.h> 00050 #include <stdarg.h> 00051 #include <time.h> 00052 #include <string.h> 00053 00054 #include "log.h" 00055 00056 00057 /* -------------------------------------------------- 00058 PART I: printing messages to stderr - die/warn/usage/romsg 00059 00060 The idea is to write out parameterized messages potentially starting 00061 with "PROBLEM: " and potentially terminating the program. 00062 - die() automatically prints "PROBLEM: " before the message 00063 - warn() automatically prints "WARNING: " before the message 00064 - die() and usage() automatically terminate the program 00065 - all (die(), warn(), usage(), romsg()): 00066 -- append a "\n" to the message 00067 -- flush all buffers before printing the message to stderr 00068 -- have the same interface as printf() i.e. a format string and a 00069 variable number of arguments 00070 00071 However, depending on the environment, something must 00072 happen before the message is printed (e.g. write a HTTP CGI 00073 header) or the program is terminated (e.g. close a connection). 00074 00075 For allowing this type of flexibility, own functions can be declared 00076 using the type 'RoMsgFunc' and registered using log_registerDie(), 00077 log_registerWarn(), log_registerUsage(), and log_registerRomsg(). 00078 The number of functions preceding the message printing is currently 00079 limited to 5. If you need more check if you still understand your 00080 program... 00081 00082 The application program can simply call die(), warn(), usage(), or 00083 romsg() which will behave appropriately. The registered functions 00084 will be called in the order they have been registered. To avoid 00085 problems caused by direct or indirect recursion the program will be 00086 terminated if one of the registered functions calls one of the error 00087 reporting functions e.g. a call to die() in a function registered 00088 with log_registerDie() will cause termination. It is however legal, 00089 to register the same function twice although I'm not sure it makes 00090 sense. 00091 00092 Registered functions can be deregistered by calling 00093 log_deregisterDie(), log_deregisterWarn(), log_deregisterUsage(), or 00094 log_deregisterRomsg. The order of deregistering calls is of no 00095 importance and will delete the first occurrence of a function 00096 registration when a function has been registered twice. 00097 */ 00098 00099 #define MAX_HOOKS 5 00100 00101 static int gReentryFlag = 0 ; 00102 static int gDieHookCount = 0 ; 00103 static RoMsgFunc gDieHooks[MAX_HOOKS] ; 00104 static int gWarnHookCount = 0 ; 00105 static RoMsgFunc gWarnHooks[MAX_HOOKS] ; 00106 static int gUsageHookCount = 0 ; 00107 static RoMsgFunc gUsageHooks[MAX_HOOKS] ; 00108 static int gRomsgHookCount = 0 ; 00109 static RoMsgFunc gRomsgHooks[MAX_HOOKS] ; 00110 static LogExitFunc gExitHook = NULL ; 00111 00112 static void logExit(void) 00113 { 00114 if (gExitHook) 00115 (*gExitHook)() ; 00116 exit(1) ; 00117 } 00118 00119 00120 00121 static void logRegisterHook(int *count, RoMsgFunc hooks[], RoMsgFunc f) { 00122 if (*count >= MAX_HOOKS) 00123 die("logRegisterHook(): too many hooks (max: %d)", MAX_HOOKS) ; 00124 hooks[*count] = f ; 00125 (*count) ++ ; 00126 } 00127 00128 00129 00130 static void logDeregisterHook(int *count, RoMsgFunc hooks[], RoMsgFunc f) { 00131 int i, j ; 00132 for ( i = 0; i < *count; i ++ ) { 00133 if (hooks[i] == f) { 00134 (*count) -- ; 00135 for ( j = i; j < *count; j++) 00136 hooks[j] = hooks[j+1] ; 00137 return ; 00138 } 00139 } 00140 die("logDeregisterHook(): cannot deregister unknown hook") ; 00141 } 00142 00143 00144 00145 static void logExecuteHooks(int count, RoMsgFunc hooks[], int doProblem, int doExit, 00146 char *format, va_list args) 00147 { 00148 int i; 00149 if (gReentryFlag) { 00150 fflush(NULL) ; /* write out all buffers */ 00151 fprintf(stderr, "PROBLEM: fatal reentry of log module: ") ; 00152 vfprintf(stderr, format, args) ; 00153 va_end(args) ; 00154 fprintf(stderr, "\n") ; 00155 logExit(); 00156 } 00157 else { 00158 gReentryFlag = 1 ; 00159 for ( i = 0; i < count; i ++ ) 00160 (*hooks[i])(format, args) ; 00161 gReentryFlag = 0 ; 00162 fflush(NULL) ; /* write out all buffers */ 00163 if (doProblem) 00164 fprintf(stderr, doExit ? "PROBLEM: " : "WARNING: ") ; 00165 vfprintf(stderr, format, args) ; 00166 va_end(args) ; 00167 fprintf(stderr, "\n") ; 00168 if (doExit) 00169 logExit(); 00170 } 00171 } 00172 00173 00174 00175 void log_registerExit(LogExitFunc f) 00176 { 00177 gExitHook = f ; 00178 } 00179 00180 00181 00182 void log_registerDie(RoMsgFunc f) { 00183 logRegisterHook(&gDieHookCount, gDieHooks, f) ; 00184 } 00185 00186 00187 00188 void log_deregisterDie(RoMsgFunc f) { 00189 logDeregisterHook(&gDieHookCount, gDieHooks, f) ; 00190 } 00191 00192 00193 00194 void log_registerWarn(RoMsgFunc f) { 00195 logRegisterHook(&gWarnHookCount, gWarnHooks, f) ; 00196 } 00197 00198 00199 00200 void log_deregisterWarn(RoMsgFunc f) { 00201 logDeregisterHook(&gWarnHookCount, gWarnHooks, f) ; 00202 } 00203 00204 00205 00206 void log_registerUsage(RoMsgFunc f) { 00207 logRegisterHook(&gUsageHookCount, gUsageHooks, f) ; 00208 } 00209 00210 00211 00212 void log_deregisterUsage(RoMsgFunc f) { 00213 logDeregisterHook(&gUsageHookCount, gUsageHooks, f) ; 00214 } 00215 00216 00217 00218 void log_registerRomsg(RoMsgFunc f) { 00219 logRegisterHook(&gRomsgHookCount, gRomsgHooks, f) ; 00220 } 00221 00222 00223 00224 void log_deregisterRomsg(RoMsgFunc f) { 00225 logDeregisterHook(&gRomsgHookCount, gRomsgHooks, f) ; 00226 } 00227 00228 00229 00230 void die(char *format,...) { 00231 va_list args ; 00232 va_start (args, format) ; 00233 logExecuteHooks(gDieHookCount, gDieHooks, /* problem */ 1 , /* exit */ 1 , format, args) ; 00234 } 00235 00236 00237 00238 void warn(char *format,...) { 00239 va_list args ; 00240 va_start (args, format) ; 00241 logExecuteHooks(gWarnHookCount, gWarnHooks, /* problem */ 1, /* exit */ 0, format, args) ; 00242 } 00243 00244 00245 00246 void usage(char *format,...) { 00247 va_list args ; 00248 va_start (args, format) ; 00249 logExecuteHooks(gUsageHookCount, gUsageHooks, /* problem */ 0, /* exit */ 1, format, args) ; 00250 } 00251 00252 00253 00254 void romsg(char *format,...) { 00255 va_list args ; 00256 va_start (args, format) ; 00257 logExecuteHooks(gRomsgHookCount, gRomsgHooks, /* problem */ 0, /* exit */ 0, format, args) ; 00258 } 00259 00260 00261 /* -------------------------------------------------------------------------- 00262 PART II: printing messages to a log file 00263 00264 */ 00265 00266 static FILE *gLogFile = NULL ; 00267 00268 00269 00270 int LogOpened(void) 00271 { /* 00272 returns 1 if Log() message have been redirected, 00273 0 if such messsages go to stderr 00274 */ 00275 return gLogFile != NULL ; 00276 } 00277 00278 00279 00280 void LogOpen (char *fname) 00281 {/* 00282 the file name the log should go to can either be set statically 00283 via the argument fname, or if not specified can be set dynamically 00284 via the environment 00285 input: fname -- name of logfile 00286 if fname==NULL, fname is taken from 00287 the environment variable LOGFILE 00288 postcondition: stderr is redirected into 'fname' 00289 Log() writes into file 'fname' 00290 LogClose() can be called 00291 00292 */ 00293 int fd ; 00294 if (!fname) 00295 fname = getenv("LOGFILE") ; 00296 if (!fname[0]) 00297 fname = NULL ; /* ignore empty file name */ 00298 if (fname) { 00299 00300 /* redirect stderr into a file while keeping a stream variable 00301 as a name for the log stream 00302 so log output can go to gLogFile, errors from anywhere go to stderr, 00303 but reach the same log file 00304 */ 00305 00306 if (fflush(stderr)) 00307 perror("PROBLEM: log: fflush") ; 00308 if (PLABLA_CLOSE(2) == -1) 00309 perror("PROBLEM: log: close(2)") ; 00310 fd = PLABLA_OPEN(fname, O_WRONLY | O_CREAT | O_APPEND, 0664) ; 00311 if (fd == -1) { 00312 printf("PROBLEM: could not open log file %s\n", fname) ; 00313 perror("PROBLEM: log: open") ; 00314 } 00315 if (fd != 2) 00316 printf("PROBLEM: could not redirect stderr (fd %d)\n",fd) ; 00317 setbuf(stderr,0) ; 00318 gLogFile = stderr ; 00319 00320 if (!gLogFile) 00321 die("LogOpen: could not open log file %s\n", fname) ; 00322 } 00323 } 00324 00325 00326 00327 void Log(char *format,...) { 00328 va_list args ; 00329 va_start (args, format) ; 00330 vfprintf ((gLogFile) ? gLogFile : stderr, format, args) ; 00331 va_end (args) ; 00332 } 00333 00334 00335 00336 void LogPrintTime(FILE *f) { /* 00337 on stream f print a timestamp of the form 00338 123456789012345678901234 00339 2000-08-30_00:00:00 \0 00340 */ 00341 char ts[22] ; 00342 time_t t = time(NULL) ; 00343 strftime(ts, sizeof(ts), "%Y-%m-%d_%T ", localtime(&t)) ; 00344 fputs(ts, f) ; 00345 } 00346 00347 00348 00349 void LogT(char *format,...) { /* 00350 like Log() but prefix with timestamp 00351 (format see LogPrintTime()) 00352 */ 00353 va_list args ; 00354 LogPrintTime((gLogFile) ? gLogFile : stderr) ; 00355 va_start (args, format) ; 00356 vfprintf ((gLogFile) ? gLogFile : stderr, format, args) ; 00357 va_end (args) ; 00358 } 00359 00360 00361 00362 00363 void LogClose (void) { 00364 if (!gLogFile) 00365 return ; 00366 fclose(gLogFile) ; 00367 } 00368 00369 00370 00371 /* ------------------------------------------------------------- 00372 PART III: assertions and messages at debug time 00373 00374 00375 Assertions are meant to check for specific condtions to be true 00376 at all times. These checks can be expensive. They can be turned off 00377 (produce no code) at compile time by "#define HLR_ASSERT_OFF". 00378 00379 Debug messages should be helpful in debugging modules and programs 00380 during debug time. At production time no code should be produced. 00381 Messages can be associated with a 'detail level'. The more high level 00382 a message is, the lower the number. Level 1 is the highest 00383 abstraction level, level 9 shows even the minutest detail. 00384 The debugging code can be turned on at compile time 00385 by defining "#define HLR_DEBUG_ON". 00386 00387 */ 00388 00389 /* asserts that 'truecondition' is true; 00390 if 'truecondition' evaluates to 0, 00391 'message' is displayed and the program is aborted.; 00392 else (i.e. non-zero result) nothing happens; 00393 hlr_assert is an expression that returns 1; therefore things like 00394 while (hlr_assert(memory_check(),"msg"), i<10) { ... } 00395 are syntactically valid. 00396 #ifdef HLR_ASSERT_OFF 00397 #define hlr_assert(truecondition,message) 00398 #else 00399 #define hlr_assert(truecondition,message) ((truecondition)?1:(die(message),0)) 00400 #endif 00401 */ 00402 00403 int log_debugLevel = 9 ; 00404 /* 00405 debug message example: 00406 debugLevelSet(9) ; // see all messages (default) 00407 debugLevelSet(1) ; // see only very serious messages 00408 debugMsg(2,("error %s",var)) ; // prints nothing because detail level 00409 // was set to two 00410 // note the parenthesis surrounding the second argument of the macro 00411 // if omitted the compiler will produce messages which are hard to understand 00412 00413 implementation: 00414 #define debugLevelSet(level) (log_debugLevel=(level)) 00415 #define debugLevelGet() log_debugLevel 00416 #ifdef HLR_DEBUG_ON 00417 #define debugMsg(level,msg) {if (level<=debugLevelGet()) printf msg;} 00418 #else 00419 #define debugMsg(level,msg) 00420 #endif 00421 00422 */ 00423 00424 00425 00426 /* ------------------------------------------------------------- 00427 PART IV: buffering warnings 00428 00429 00430 00431 Exception Handling in BIOS 00432 00433 Design Concept 00434 00435 Motivation 00436 00437 When writing re-usable modules it is not forseeable in which context 00438 they will be used. It could e.g. be in a batch run or in an 00439 interactive program (WWW CGI). At runtime a module may discover 00440 things going wrong, e.g. a function is called with illegal 00441 parameters, a subsystem like Oracle denies service or the disk is 00442 full. Module (re-)usability in different application contexts is 00443 achieved by separating exception generation (where the code notices 00444 something is going wrong) from exception handling (where it is 00445 decided to inform the user, stop processing, start countermeasures, 00446 etc). Modern computer languages like Java have this separation 00447 built into their syntax (throw/catch/try). C does not have this, 00448 but it can be emulated as described below. 00449 00450 In BIOS the following exception handling policy is adopted: 00451 - the function warn() (from log.c) is NOT used in BIOS. 00452 - if something goes wrong and is clearly a 00453 programmer's error (wrong usage or wrong module code), 00454 the module calls die() and the programmer is 00455 forced to fix the bug. 00456 - if something goes wrong and it is not a programming 00457 error, the module does not print any messages anywhere, 00458 but returns some error status to the caller. 00459 To ease this task, module log.c provides a 00460 a common communication point via routines 00461 warnAdd(), warnCount(), etc., see below. 00462 - for immediate problem reporting to the end user 00463 the following functions are available: 00464 die() -- for programmers errors ("PROBLEM:") 00465 end users should be educated to report PROBLEMs. 00466 usage() -- e.g. for displaying program usage (like die(), 00467 but without "PROBLEM:")) 00468 warn() -- for immediate reporting of problems/warning 00469 to users (prints "WARNING: ...") 00470 romsg() -- like warn(), but without prefix 00471 */ 00472 00473 #define WARNMAX 30 00474 static char *gSources[WARNMAX] ; 00475 static char *gMsgs[WARNMAX] ; 00476 static int gWarnCnt = 0 ; /* # of warnceptions registered */ 00477 static int gWarnIndex = -2 ; /* for use by the iterator */ 00478 00479 00480 void warnAdd(char *source, char *msg) 00481 { /* 00482 add exception notification 00483 input: source -- name of the function issuing the warning; 00484 source must not contain a tab char 00485 msg -- message describing the warning 00486 precondition: none 00487 postcondition: warnReport() will show (source, msg) 00488 */ 00489 gWarnIndex = -2 ; 00490 ++gWarnCnt ; 00491 if (gWarnCnt > WARNMAX) { 00492 char s[80] ; 00493 free(gSources[WARNMAX-1]) ; 00494 free(gMsgs[WARNMAX-1]) ; 00495 gSources[WARNMAX-1] = strdup("warnAdd") ; 00496 sprintf(s, "Warning buffer overflow. Last %d warning(s) discarded.", gWarnCnt - WARNMAX) ; 00497 gMsgs[WARNMAX-1] = strdup(s) ; 00498 } 00499 else { 00500 if (!source || !msg) 00501 die("warnAdd() with NULL arg") ; 00502 gSources[gWarnCnt-1] = strdup(source) ; 00503 gMsgs[gWarnCnt-1] = strdup(msg) ; 00504 } 00505 } 00506 00507 00508 00509 char *warnReport(void) 00510 { /* 00511 returns: all warnings issued since process start 00512 or warnReset() as a string of the form 00513 source <tab> msg <nl> 00514 source <tab> msg <nl> 00515 ... 00516 memory managed by this routine; may be written to, 00517 but not free'd or realloc'd 00518 If warnCount(NULL,NULL) == 0, an empty string ("") 00519 is returned 00520 */ 00521 int i ; 00522 int s = 0 ; 00523 int cnt ; 00524 static char *b = NULL ; 00525 cnt = (gWarnCnt > WARNMAX) ? WARNMAX : gWarnCnt ; 00526 for (i=0; i<cnt; ++i) { 00527 s += strlen(gSources[i]) ; 00528 s += strlen(gMsgs[i]) ; 00529 } 00530 if (b) 00531 free(b) ; 00532 b = malloc(s + cnt * 2 + 1) ; 00533 b[0] = '\0' ; 00534 for (i=0; i<cnt; ++i) { 00535 strcat(b, gSources[i]) ; 00536 strcat(b, "\t") ; 00537 strcat(b, gMsgs[i]) ; 00538 strcat(b, "\n") ; 00539 } 00540 return b ; 00541 } 00542 00543 00544 00545 void warnReset(void) 00546 { /* 00547 return the expection buffer to the state at process start 00548 (no expections added) 00549 postcondition: warnCount(NULL,NULL) == 0 00550 */ 00551 int i ; 00552 int cnt ; 00553 cnt = (gWarnCnt > WARNMAX) ? WARNMAX : gWarnCnt ; 00554 for (i=0; i<cnt; ++i) { 00555 free(gSources[i]) ; 00556 free(gMsgs[i]) ; 00557 } 00558 gWarnIndex = -2 ; 00559 gWarnCnt = 0 ; 00560 } 00561 00562 00563 00564 int warnCount(char *sourceStart, char *msgStart) 00565 { /* 00566 input: sourceStart -- e.g. "seo_", 00567 NULL or "" means no restriction 00568 msgStart -- NULL or "" means no restriction 00569 returns: number of warnings whose 'source' starts with 00570 'sourceStart' and whose 'msg' starts with 00571 'msgStart', 0 else 00572 note: warnCount(NULL,NULL) is a fast way to check that 00573 no warnings have been reported since process start 00574 or the last call to warnReset() 00575 */ 00576 int i ; 00577 int cnt ; 00578 int c = 0 ; 00579 int ls = (sourceStart ? strlen(sourceStart) : 0) ; 00580 int lm = (msgStart ? strlen(msgStart) : 0) ; 00581 00582 if (!ls && !lm) 00583 return gWarnCnt ; 00584 00585 cnt = (gWarnCnt > WARNMAX) ? WARNMAX : gWarnCnt ; 00586 for (i=0; i<cnt; ++i) { 00587 if (ls && strncmp(gSources[i], sourceStart, ls)) 00588 continue ; 00589 if (lm && strncmp(gMsgs[i], msgStart, lm)) 00590 continue ; 00591 ++c ; 00592 } 00593 return c ; 00594 } 00595 00596 00597 00598 void warnIterInit(void) 00599 { /* 00600 Start iteration over all warning currently registered 00601 in chronological order. The first warning added will be 00602 the first one returned by warnIterNext(). 00603 postcondition: warnIterNext() can be called 00604 */ 00605 gWarnIndex = -1 ; 00606 } 00607 00608 00609 00610 int warnIterNext(char **source, char **msg) 00611 { /* 00612 precondition: warnIterInit() or warnIterNext() has been 00613 called since the last warnAdd() 00614 postcondition: next call to this function will return next warning 00615 returns: 1 if warning returned, 0 if end of iteration 00616 input: *source -- place to store a char* 00617 *msg -- place to store a char* 00618 output: *source -- points to source description of warning 00619 memory belongs to this routine; read-only to user 00620 *msg -- points to message text of warning 00621 memory belongs to this routine; read-only to user 00622 */ 00623 if (gWarnIndex == -2) 00624 die("warnIterNext without warnIterInit") ; 00625 if (++gWarnIndex >= gWarnCnt || gWarnIndex >= WARNMAX) 00626 return 0 ; 00627 *source = gSources[gWarnIndex] ; 00628 *msg = gMsgs[gWarnIndex] ; 00629 return 1 ; 00630 } 00631