/********************************************************************* * Copyright 2018, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. * $Header$ *********************************************************************/ #include "config.h" #ifdef _MSC_VER #include #endif #include #include #include #include #include #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "netcdf.h" #include "nclog.h" #define PREFIXLEN 8 #define MAXTAGS 256 #define NCTAGDFALT "Log"; #define NC_MAX_FRAMES 1024 static int nclogginginitialized = 0; static struct NCLOGGLOBAL { int loglevel; int tracelevel; FILE* nclogstream; int depth; struct Frame { const char* fcn; int level; int depth; } frames[NC_MAX_FRAMES]; } nclog_global = {0,-1,NULL}; static const char* nctagset[] = {"OFF","ERR","WARN","NOTE","DEBUG",NULL}; /* Forward */ static const char* nctagname(int tag); static int nctagforname(const char* tag); /*!\defgroup NClog NClog Management @{*/ /*!\internal */ void ncloginit(void) { const char* envv = NULL; if(nclogginginitialized) return; nclogginginitialized = 1; memset(&nclog_global,0,sizeof(nclog_global)); ncsetloglevel(NCLOGOFF); nclog_global.tracelevel = -1; nclog_global.nclogstream = stderr; /* Use environment variables to preset nclogging state*/ envv = getenv(NCENVLOGGING); if(envv != NULL) { int level = nctagforname(envv); if(level < 0) ncsetloglevel(level); } envv = getenv(NCENVTRACING); if(envv != NULL) nctracelevel(atoi(envv)); } /*! Enable logging messages to a given level. Set to NCLOGOFF to disable all messages, NCLOGERR for errors only, NCLOGWARN for warnings and errors, and so on \param[in] level Messages above this level are ignored \return The previous value of the logging flag. */ int ncsetloglevel(int level) { int was; if(!nclogginginitialized) ncloginit(); was = nclog_global.loglevel; if(level >= 0 && level <= NCLOGDEBUG) nclog_global.loglevel = level; if(nclog_global.nclogstream == NULL) nclogopen(NULL); return was; } int nclogopen(FILE* stream) { if(!nclogginginitialized) ncloginit(); if(stream == NULL) stream = stderr; nclog_global.nclogstream = stream; return 1; } /*! Send logging messages. This uses a variable number of arguments and operates like the stdio printf function. \param[in] tag Indicate the kind of this log message. \param[in] format Format specification as with printf. */ void nclog(int tag, const char* fmt, ...) { if(fmt != NULL) { va_list args; va_start(args, fmt); ncvlog(tag,fmt,args); va_end(args); } } void ncvlog(int level, const char* fmt, va_list ap) { const char* prefix; if(!nclogginginitialized) ncloginit(); if(nclog_global.loglevel < level || nclog_global.nclogstream == NULL) { return; } prefix = nctagname(level); fprintf(nclog_global.nclogstream,"%s: ",prefix); if(fmt != NULL) { vfprintf(nclog_global.nclogstream, fmt, ap); } fprintf(nclog_global.nclogstream, "\n" ); fflush(nclog_global.nclogstream); } void nclogtext(int tag, const char* text) { nclogtextn(tag,text,strlen(text)); } /*! Send arbitrarily long text as a logging message. Each line will be sent using nclog with the specified tag. \param[in] tag Indicate the kind of this log message. \param[in] text Arbitrary text to send as a logging message. */ void nclogtextn(int level, const char* text, size_t count) { if(nclog_global.loglevel > level || nclog_global.nclogstream == NULL) return; fwrite(text,1,count,nclog_global.nclogstream); fflush(nclog_global.nclogstream); } static const char* nctagname(int tag) { if(tag < NCLOGOFF || tag >= NCLOGDEBUG) return "unknown"; return nctagset[tag]; } static int nctagforname(const char* tag) { int level; const char** p = NULL; for(level=0,p=nctagset;*p;p++,level++) { if(strcasecmp(*p,tag)==0) return level; } return -1; } /*! Send trace messages. \param[in] level Indicate the level of trace \param[in] format Format specification as with printf. */ int nctracelevel(int level) { int oldlevel; if(!nclogginginitialized) ncloginit(); oldlevel = nclog_global.tracelevel; if(level < 0) { nclog_global.tracelevel = level; } else { /*(level >= 0)*/ nclog_global.tracelevel = level; nclogopen(NULL); /* use stderr */ } return oldlevel; } void nctrace(int level, const char* fcn, const char* fmt, ...) { va_list args; va_start(args, fmt); ncvtrace(level,fcn,fmt,args); va_end(args); } void nctracemore(int level, const char* fmt, ...) { va_list args; va_start(args, fmt); ncvtrace(level,NULL,fmt,args); va_end(args); } void ncvtrace(int level, const char* fcn, const char* fmt, va_list ap) { struct Frame* frame; if(!nclogginginitialized) ncloginit(); if(fcn != NULL) { frame = &nclog_global.frames[nclog_global.depth]; frame->fcn = fcn; frame->level = level; frame->depth = nclog_global.depth; } if(level <= nclog_global.tracelevel) { if(fcn != NULL) fprintf(nclog_global.nclogstream,"%s: (%d): %s:","Enter",level,fcn); if(fmt != NULL) vfprintf(nclog_global.nclogstream, fmt, ap); fprintf(nclog_global.nclogstream, "\n" ); fflush(nclog_global.nclogstream); } if(fcn != NULL) nclog_global.depth++; } int ncuntrace(const char* fcn, int err, const char* fmt, ...) { va_list args; struct Frame* frame; va_start(args, fmt); if(nclog_global.depth == 0) { fprintf(nclog_global.nclogstream,"*** Unmatched untrace: %s: depth==0\n",fcn); goto done; } nclog_global.depth--; frame = &nclog_global.frames[nclog_global.depth]; if(frame->depth != nclog_global.depth || strcmp(frame->fcn,fcn) != 0) { fprintf(nclog_global.nclogstream,"*** Unmatched untrace: fcn=%s expected=%s\n",frame->fcn,fcn); goto done; } if(frame->level <= nclog_global.tracelevel) { fprintf(nclog_global.nclogstream,"%s: (%d): %s: ","Exit",frame->level,frame->fcn); if(err) fprintf(nclog_global.nclogstream,"err=(%d) '%s':",err,nc_strerror(err)); if(fmt != NULL) vfprintf(nclog_global.nclogstream, fmt, args); fprintf(nclog_global.nclogstream, "\n" ); fflush(nclog_global.nclogstream); } done: va_end(args); if(err != 0) return ncbreakpoint(err); else return err; } int ncthrow(int err,const char* file,int line) { if(err == 0) return err; return ncbreakpoint(err); } int ncbreakpoint(int err) { return err; } /**@}*/