add: * NsTclLogObjCmd -- * *	Implements ns_log as obj command. * * Results: *	Tcl result. * * Side effects: *	See docs. * *---------------------------------------------------------------------- */  int NsTclLogObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Ns_LogSeverity severity; Ns_DString ds; int i; char *cmd = Tcl_GetString(objv[1]);  if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "severity string ?string ...?"); return TCL_ERROR; } if (STRIEQ(cmd, "notice")) { severity = Notice; } else if (STRIEQ(cmd, "warning")) { severity = Warning; } else if (STRIEQ(cmd, "error")) { severity = Error; } else if (STRIEQ(cmd, "fatal")) { severity = Fatal; } else if (STRIEQ(cmd, "bug")) { severity = Bug; } else if (STRIEQ(cmd, "debug")) { severity = Debug; } else if (Tcl_GetIntFromObj(interp, objv[1], &i) == TCL_OK) { severity = i; } else { Tcl_Obj *result = Tcl_NewObj(); Tcl_AppendStringsToObj(result, "unknown severity \"", Tcl_GetString(objv[1]), "\":  should be one of: ", "fatal, error, warning, bug, notice, or debug.", NULL); Tcl_SetObjResult(interp, result); return TCL_ERROR; } Ns_DStringInit(&ds); if (LogStart(&ds, severity)) { for (i = 2; i < objc; ++i) { Ns_DStringVarAppend(&ds, Tcl_GetString(objv[i]), i > 2 ? " " : NULL, NULL); } LogEnd(&ds); } Ns_DStringFree(&ds); return TCL_OK; }
context: static Ns_Callback LogFlush;static char *LogTime(int gmtoff);/* * Static variables defined in this file */static Ns_Mutex lock;static Ns_Cond cond;static Ns_DString buffer;static int buffered = 0;static int flushing = 0;static int nbuffered = 0;static Ns_Tls tls;/* *---------------------------------------------------------------------- * * NsInitLog -- * *	Initialize the log API and TLS slot. * * Results: *	None. * * Side effects: *	None.  * *---------------------------------------------------------------------- */voidNsInitLog(void){    Ns_MutexInit(&lock);    Ns_CondInit(&cond);    Ns_MutexSetName(&lock, "ns:log");    Ns_DStringInit(&buffer);    Ns_TlsAlloc(&tls, ns_free);}/* *---------------------------------------------------------------------- * * Ns_InfoErrorLog -- * *	Returns the filename of the log file.  * * Results: *	Log file name or NULL if none.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */char *Ns_InfoErrorLog(void){    return nsconf.log.file;}/* *---------------------------------------------------------------------- * * Ns_LogRoll -- * *	Signal handler for SIG_HUP which will roll the files. Also a  *	tasty snack from Stuckey's.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Will rename the log file and reopen it.  * *---------------------------------------------------------------------- */intNs_LogRoll(void){    if (nsconf.log.file != NULL) {        if (access(nsconf.log.file, F_OK) == 0) {            Ns_RollFile(nsconf.log.file, nsconf.log.maxback);        }        Ns_Log(Notice, "log: re-opening log file '%s'", nsconf.log.file);        if (LogReOpen() != NS_OK) {	    return NS_ERROR;	}    }    return NS_OK;}/* *---------------------------------------------------------------------- * * Ns_Log -- * *	Spit a message out to the server log.  * * Results: *	None.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */voidNs_Log(Ns_LogSeverity severity, char *fmt, ...){    va_list ap;    va_start(ap, fmt);    Log(severity, fmt, ap);    va_end(ap);}/* NB: For binary compatibility with previous releases. */voidns_serverLog(Ns_LogSeverity severity, char *fmt, va_list *vaPtr){    Log(severity, fmt, *vaPtr);}/* *---------------------------------------------------------------------- * * Ns_Fatal -- * *	Spit a message out to the server log with severity level of  *	Fatal, and then terminate the nsd process.  * * Results: *	None.  * * Side effects: *	WILL CAUSE AOLSERVER TO EXIT!  * *---------------------------------------------------------------------- */voidNs_Fatal(char *fmt, ...){    va_list ap;    va_start(ap, fmt);    Log(Fatal, fmt, ap);    va_end(ap);    _exit(1);}/* *---------------------------------------------------------------------- * * Ns_LogTime -- * *	Construct local date and time for log file.  * * Results: *	A string time and date.  * * Side effects: *	Will put data into timeBuf, which must be at least 41 bytes  *	long.  * *---------------------------------------------------------------------- */char *Ns_LogTime(char *timeBuf){    strcpy(timeBuf, LogTime(1));    return timeBuf;}/* *---------------------------------------------------------------------- * * NsLogOpen -- * *	Open the log file. Adjust configurable parameters, too.  * * Results: *	None.  * * Side effects: *	Configures this module to use the newly opened log file. *	If LogRoll is turned on in the config file, then it registers *	a signal callback. * *---------------------------------------------------------------------- */voidNsLogOpen(void){    /*     * Initialize log buffering.     */    if (nsconf.log.flags & LOG_BUFFER) {	buffered = 1;	Ns_RegisterAtShutdown(LogFlush, (void *) 1);	if (nsconf.log.flushint > 0) {	    Ns_ScheduleProc(LogFlush, (void *) 0, 0, nsconf.log.flushint);	}    }    /*     * Open the log and schedule the signal roll.     */    if (LogReOpen() != NS_OK) {	Ns_Fatal("log: failed to open server log '%s': '%s'", 		 nsconf.log.file, strerror(errno));    }    if (nsconf.log.flags & LOG_ROLL) {	Ns_RegisterAtSignal((Ns_Callback *) Ns_LogRoll, NULL);    }}/* *---------------------------------------------------------------------- * * NsTclLogRollCmd -- * *	Implements ns_logroll.  * * Results: *	Tcl result.  * * Side effects: *	See docs.  * *---------------------------------------------------------------------- */intNsTclLogRollCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv){    if (Ns_LogRoll() != NS_OK) {	Tcl_SetResult(interp, "could not roll server log", TCL_STATIC);    }    return TCL_OK;}/* *---------------------------------------------------------------------- * * NsTclLogCmd -- * *	Implements ns_log. * * Results: *	Tcl result.  * * Side effects: *	See docs.  * *---------------------------------------------------------------------- */intNsTclLogCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv){    Ns_LogSeverity severity;    Ns_DString ds;    int i;    if (argc < 3) {        Tcl_AppendResult(interp, "wrong # of args:  should be \"",                         argv[0], " severity string ?string ...?\"", NULL);    	return TCL_ERROR;    }    if (STRIEQ(argv[1], "notice")) {	severity = Notice;    } else if (STRIEQ(argv[1], "warning")) {	severity = Warning;    } else if (STRIEQ(argv[1], "error")) {	severity = Error;    } else if (STRIEQ(argv[1], "fatal")) {	severity = Fatal;    } else if (STRIEQ(argv[1], "bug")) {	severity = Bug;    } else if (STRIEQ(argv[1], "debug")) {	severity = Debug;    } else if (Tcl_GetInt(interp, argv[1], &i) == TCL_OK) {	severity = i;    } else {        Tcl_AppendResult(interp, "unknown severity \"",                         argv[1], "\":  should be one of: ",			 "fatal, error, warning, bug, notice, or debug.",			 NULL);    	return TCL_ERROR;    }    Ns_DStringInit(&ds);    if (LogStart(&ds, severity)) {	for (i = 2; i < argc; ++i) {	    Ns_DStringVarAppend(&ds, argv[i], i > 2 ? " " : NULL, NULL);	}	LogEnd(&ds);    }    Ns_DStringFree(&ds);    return TCL_OK;}/* *---------------------------------------------------------------------- * * Log -- * *	Add an entry to the log file if the severity is not surpressed. * * Results: *	None.  * * Side effects: *	May write immediately or later through buffer. * *---------------------------------------------------------------------- */static voidLog(Ns_LogSeverity severity, char *fmt, va_list ap){    Ns_DString ds;    Ns_DStringInit(&ds);    if (LogStart(&ds, severity)) {	Ns_DStringVPrintf(&ds, fmt, ap);	LogEnd(&ds);    }    Ns_DStringFree(&ds);}/* *---------------------------------------------------------------------- * * LogStart -- * *	Start a log entry. * * Results: *	1 if log started and should be written, 0 if given severity *	is surpressed. * * Side effects: *	May append log header to given dstring. * *---------------------------------------------------------------------- */static intLogStart(Ns_DString *dsPtr, Ns_LogSeverity severity){    char *severityStr, buf[10];    switch (severity) {	case Notice:	    if (nsconf.log.flags & LOG_NONOTICE) {		return 0;	    }	    severityStr = "Notice";	    break;        case Warning:	    severityStr = "Warning";	    break;	case Error:	    severityStr = "Error";	    break;	case Fatal:	    severityStr = "Fatal";	    break;	case Bug:	    severityStr = "Bug";	    break;	case Debug:	    if (!(nsconf.log.flags & LOG_DEBUG)) {		return 0;	    }	    severityStr = "Debug";	    break;	case Dev:	    if (!(nsconf.log.flags & LOG_DEV)) {		return 0;	    }	    severityStr = "Dev";	    break;	default:	    if (severity > nsconf.log.maxlevel) {		return 0;	    }	    sprintf(buf, "L%d", severity);	    severityStr = buf;	    break;    }    Ns_DStringPrintf(dsPtr, "%s[%d.%d][%s] %s: ", LogTime(0),		Ns_InfoPid(), Ns_ThreadId(), Ns_ThreadGetName(), severityStr);    if (nsconf.log.flags & LOG_EXPAND) {	Ns_DStringAppend(dsPtr, "\n    ");    }    return 1;}/* *---------------------------------------------------------------------- * * LogEnd -- * *	Complete a log entry and either append to buffer or write *	immediately. * * Results: *	None. * * Side effects: *	May flush log. * *---------------------------------------------------------------------- */static voidLogEnd(Ns_DString *dsPtr){    Ns_DStringNAppend(dsPtr, "\n", 1);    if (nsconf.log.flags & LOG_EXPAND) {	Ns_DStringNAppend(dsPtr, "\n", 1);    }    Ns_MutexLock(&lock);    if (!buffered) {	Ns_MutexUnlock(&lock);	(void) write(2, dsPtr->string, dsPtr->length);    } else {	while (flushing) {	    Ns_CondWait(&cond, &lock);	}	Ns_DStringNAppend(&buffer, dsPtr->string, dsPtr->length);	if (nbuffered++ > nsconf.log.maxbuffer) {	    LogWrite();	}	Ns_MutexUnlock(&lock);    }}/* *---------------------------------------------------------------------- * * LogFlush -- * *	Flush the buffered log, either from the scheduled proc timeout *	or at shutdown. * * Results: *	None. * * Side effects: *	May write to log and disables buffering at shutdown. * *---------------------------------------------------------------------- */static voidLogFlush(void *arg){    int exit = (int) arg;    Ns_MutexLock(&lock);    /*     * Wait for current flushing, if any, to complete.     */    while (flushing) {	Ns_CondWait(&cond, &lock);    }    if (nbuffered > 0) {	LogWrite();    }    /*     * Disable further buffering at exit time.     */    if (exit) {	buffered = 0;    }    Ns_MutexUnlock(&lock);}