src/lib/log.c 1.10
1.10 2010/01/12 09:06:24 markd
added support for setting minimum priority to log
Index: src/lib/log.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/lib/log.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -b -B -U 1000000 -r1.9 -r1.10
--- src/lib/log.c 17 Sep 2008 17:56:37 -0000 1.9
+++ src/lib/log.c 12 Jan 2010 09:06:24 -0000 1.10
@@ -1,268 +1,344 @@
/* log.c - logging for servers, can log to a file and/or syslog. Compile with
* -DNO_SYSLOG for systems without syslog. */
#include "common.h"
#include "log.h"
#include "errabort.h"
#include "dystring.h"
#include "options.h"
#include "portable.h"
#ifndef NO_SYSLOG
#include <syslog.h>
#endif
#include <time.h>
static char const rcsid[] = "$Id$";
static char *gProgram = "unknown"; /* name of program */
static boolean gSysLogOn = FALSE; /* syslog logging enabled? */
static FILE *gLogFh = NULL; /* logging file */
+struct nameVal
+/* pair of string name and integer value */
+{
+ char *name;
+ int val;
+};
+
#ifndef NO_SYSLOG
-static struct {
- char *name; /* string name for facility */
- int fac; /* integer value */
-} facilityNameTbl[] =
+static struct nameVal facilityNameTbl[] =
/* not all version of syslog have the facilitynames table, so define our own */
{
{"auth", LOG_AUTH},
#ifdef LOG_AUTHPRIV
{"authpriv", LOG_AUTHPRIV},
#endif
{"cron", LOG_CRON},
{"daemon", LOG_DAEMON},
#ifdef LOG_FTP
{"ftp", LOG_FTP},
#endif
{"kern", LOG_KERN},
{"lpr", LOG_LPR},
{"mail", LOG_MAIL},
{"news", LOG_NEWS},
{"syslog", LOG_SYSLOG},
{"user", LOG_USER},
#ifdef LOG_UUCP
{"uucp", LOG_UUCP},
#endif
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7},
{NULL, 0}
};
#endif
+/* Priority numbers and names used for setting minimum priority to log. This
+ * is kept independent of syslog, so it works on file logging too. */
+#define PRI_EMERG 0
+#define PRI_ALERT 1
+#define PRI_CRIT 2
+#define PRI_ERR 3
+#define PRI_WARNING 4
+#define PRI_NOTICE 5
+#define PRI_INFO 6
+#define PRI_DEBUG 7
+
+static struct nameVal priorityNameTbl[] = {
+ {"panic", PRI_EMERG},
+ {"emerg", PRI_EMERG},
+ {"alert", PRI_ALERT},
+ {"crit", PRI_CRIT},
+ {"err", PRI_ERR},
+ {"error", PRI_ERR},
+ {"warn", PRI_WARNING},
+ {"warning", PRI_WARNING},
+ {"notice", PRI_NOTICE},
+ {"info", PRI_INFO},
+ {"debug", PRI_DEBUG},
+ {NULL, -1}
+ };
+
+static int gMinPriority = PRI_INFO; // minimum priority to log (reverse numbering)
+
+static int nameValTblFind(struct nameVal *tbl, char *name)
+/* search a nameVal table, return -1 if not found */
+{
+int i;
+for (i = 0; tbl[i].name != NULL; i++)
+ {
+ if (sameString(tbl[i].name, name))
+ return tbl[i].val;
+ }
+return -1;
+}
+
+static char *nameValTblMsg(struct nameVal *tbl)
+/* generate a message for values in table */
+{
+struct dyString *msg = dyStringNew(256);
+int i;
+for (i = 0; tbl[i].name != NULL; i++)
+ {
+ if (i > 0)
+ dyStringAppend(msg, ", ");
+ dyStringAppend(msg, tbl[i].name);
+ }
+return dyStringCannibalize(&msg);
+}
+
static void logWarnHander(char *format, va_list args)
/* Warn handler that logs message. */
{
-/* use logError, since errAbort and warn all print through warn handler */
-logErrorVa(format, args);
+if (errAbortInProgress)
+ logErrorVa(format, args);
+else
+ logWarnVa(format, args);
}
static void logAbortHandler()
/* abort handler that logs this fact and exits. */
{
logError("%s aborted", gProgram);
fprintf(stderr, "aborted");
exit(1);
}
static void setProgram(char* program)
/* set the program name, removing leading directories from file */
{
char name[128], ext[64];
int len;
splitPath(program, NULL, name, ext);
len = strlen(name) + strlen(ext) + 1;
gProgram = needMem(len);
strcpy(gProgram, name);
if (ext[0] != '\0')
strcat(gProgram, ext); /* includes dot */
}
#ifndef NO_SYSLOG
static int parseFacility(char *facility)
/* parse a facility name into a number, or use default if NULL. */
{
-int i;
-struct dyString *msg;
if (facility == NULL)
return LOG_LOCAL0;
-for (i = 0; facilityNameTbl[i].name != NULL; i++)
- {
- if (sameString(facilityNameTbl[i].name, facility))
- return facilityNameTbl[i].fac;
- }
-msg = dyStringNew(256);
-
-for (i = 0; facilityNameTbl[i].name != NULL; i++)
- {
- if (i > 0)
- dyStringAppend(msg, ", ");
- dyStringAppend(msg, facilityNameTbl[i].name);
- }
-
-errAbort("invalid log facility: %s, expected one of: %s", facility, msg->string);
-return 0; /* never reached */
+int val = nameValTblFind(facilityNameTbl, facility);
+if (val < 0)
+ errAbort("invalid log facility: %s, expected one of: %s", facility, nameValTblMsg(facilityNameTbl));
+return val;
}
#endif
+static int parsePriority(char *pri)
+/* parse a priority name into a number, or use default if NULL. */
+{
+if (pri == NULL)
+ return PRI_INFO;
+int val = nameValTblFind(priorityNameTbl, pri);
+if (val < 0)
+ errAbort("invalid log priority: %s, expected one of: %s", pri, nameValTblMsg(priorityNameTbl));
+return val;
+}
+
void logOpenSyslog(char* program, char *facility)
/* Initialize syslog using the specified facility. Facility is the syslog
* facility as specified in syslog.conf. If facility is NULL, local0 is used.
* This adds a warn and errAbort handlers that do logging. If custom handlers
* are added, they should call logErrorVa().
*/
{
#ifndef NO_SYSLOG
setProgram(program);
openlog(program, LOG_PID, parseFacility(facility));
pushWarnHandler(logWarnHander);
pushAbortHandler(logAbortHandler);
gSysLogOn = TRUE;
#else
errAbort("syslog support was not compiled into %s", __FILE__);
#endif
}
void logOpenFile(char* program, char *logFile)
/* Initialize logging to the specified file. Append to the file if it exists.
* This adds a warn and errAbort handlers that do logging. If custom handlers
* are added, they should call logErrorVa().
*/
{
setProgram(program);
gLogFh = mustOpen(logFile, "a");
pushWarnHandler(logWarnHander);
pushAbortHandler(logAbortHandler);
}
+void logSetMinPriority(char *minPriority)
+/* set minimum priority to log, which is one of the syslog priority names,
+ * even when logging to a file */
+{
+gMinPriority = parsePriority(minPriority);
+}
+
FILE *logGetFile()
/* Returns the log FILE object if file logging is enabled, or NULL if it
* isn't. This is useful for logging debugging data that doesn't fit the log
* message paradigm, For example, logging fasta records. */
{
return gLogFh;
}
static void logFilePrint(char* level, char *format, va_list args)
/* write a message to the log file */
{
static char *timeFmt = "%Y/%m/%d %H:%M:%S";
char timeBuf[128];
time_t curTime = time(NULL);
strftime(timeBuf, sizeof(timeBuf), timeFmt, localtime(&curTime));
fprintf(gLogFh, "%s: %s: ", timeBuf, level);
vfprintf(gLogFh, format, args);
fputc('\n', gLogFh);
fflush(gLogFh);
}
void logErrorVa(char *format, va_list args)
/* Variable args logError. */
{
+if (gMinPriority >= PRI_ERR)
+ {
#ifndef NO_SYSLOG
-if (gSysLogOn)
+ if (gSysLogOn)
vsyslog(LOG_ERR, format, args);
#endif
-if (gLogFh != NULL)
+ if (gLogFh != NULL)
logFilePrint("error", format, args);
+ }
}
void logError(char *format, ...)
/* Log an error message. */
{
va_list args;
va_start(args, format);
logErrorVa(format, args);
va_end(args);
}
void logWarnVa(char *format, va_list args)
/* Variable args logWarn. */
{
+if (gMinPriority >= PRI_WARNING)
+ {
#ifndef NO_SYSLOG
-if (gSysLogOn)
+ if (gSysLogOn)
vsyslog(LOG_WARNING, format, args);
#endif
-if (gLogFh != NULL)
+ if (gLogFh != NULL)
logFilePrint("warn", format, args);
+ }
}
void logWarn(char *format, ...)
/* Log a warn message. */
{
va_list args;
va_start(args, format);
logWarnVa(format, args);
va_end(args);
}
void logInfoVa(char *format, va_list args)
/* Variable args logInfo. */
{
+if (gMinPriority >= PRI_INFO)
+ {
#ifndef NO_SYSLOG
-if (gSysLogOn)
+ if (gSysLogOn)
vsyslog(LOG_INFO, format, args);
#endif
-if (gLogFh != NULL)
+ if (gLogFh != NULL)
logFilePrint("info", format, args);
+ }
}
void logInfo(char *format, ...)
/* Log an info message. */
{
va_list args;
va_start(args, format);
logInfoVa(format, args);
va_end(args);
}
void logDebugVa(char *format, va_list args)
/* Variable args logDebug. */
{
+if (gMinPriority >= PRI_DEBUG)
+ {
#ifndef NO_SYSLOG
-if (gSysLogOn)
+ if (gSysLogOn)
vsyslog(LOG_DEBUG, format, args);
#endif
-if (gLogFh != NULL)
+ if (gLogFh != NULL)
logFilePrint("debug", format, args);
+ }
}
void logDebug(char *format, ...)
/* Log a debug message. */
{
va_list args;
va_start(args, format);
logDebugVa(format, args);
va_end(args);
}
void logDaemonize(char *progName)
/* daemonize parasol server process, closing open file descriptors and
* starting logging based on the -logFacility and -log command line options .
* if -debug is supplied , don't fork. */
{
if (!optionExists("debug"))
{
int i, maxFiles = getdtablesize();
if (mustFork() != 0)
exit(0); /* parent goes away */
/* Close all open files first (before logging) */
for (i = 0; i < maxFiles; i++)
close(i);
}
/* Set up log handler. */
if (optionExists("log"))
logOpenFile(progName, optionVal("log", NULL));
else
logOpenSyslog(progName, optionVal("logFacility", NULL));
}