/*
* tclLogging.c --
*
* Contains the logging facilities for TCL.
*
* Copyright (c) 2019 Donal K. Fellows
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "tclInt.h"
static const char *levelNames = {
"DEV",
"DEBUG",
"INFO",
"NOTICE",
"WARNING",
"ERROR",
"FATAL",
"BUG"
};
Tcl_LogLevel
Tcl_GetLogLevel(
Tcl_Interp *interp)
{
Interp *iPtr = (Interp *) interp;
return iPtr->log.level;
}
void
Tcl_SetLogLevel(
Tcl_Interp *interp,
Tcl_LogLevel level)
{
Interp *iPtr = (Interp *) interp;
if (TCL_LOG_DEV > (int) level || TCL_LOG_BUG < (int) level) {
Tcl_Panic("bad log level: %d", level);
}
iPtr->log.level = level;
if (iPtr->log.handler && iPtr->log.handler->setLogLevelProc) {
iPtr->log.handler->setLogLevelProc(iPtr->log.clientData,
interp, level);
}
}
int
Tcl_LogLevelEnabled(
Tcl_Interp *interp,
Tcl_LogLevel level)
{
Interp *iPtr = (Interp *) interp;
return level >= iPtr->log.level;
}
void
Tcl_Log(
Tcl_Interp *interp,
Tcl_LogLevel level,
const char *message,
...)
{
Interp *iPtr = (Interp *) interp;
va_list argList;
Tcl_Obj *objPtr;
/*
* Check the logging level is legal.
*/
if (TCL_LOG_DEV > (int) level || TCL_LOG_BUG < (int) level) {
Tcl_Panic("bad log level: %d", level);
}
/*
* Are we allowed to log this at the moment?
*/
if (level < iPtr->log.level) {
return;
}
/*
* Is there a handler registered?
*/
if (!iPtr->log.handler || !iPtr->log.handler->logProc) {
return;
}
/*
* Format the log detail message. The log handler itself may do more.
*/
objPtr = Tcl_NewObj();
va_start(argList, format);
TclAppendPrintfToObjVA(objPtr, format, argList);
va_end(argList);
/*
* Dispatch to the log handler. Note that logging does not report errors
* directly.
*/
Tcl_IncrRefCount(objPtr);
iPtr->log.handler->logProc(iPtr->log.clientData,
interp, level, objPtr);
Tcl_DecrRefCount(objPtr);
}
void
Tcl_SetLogHandler(
Tcl_Interp *interp,
const Tcl_LogHandler *logHandler,
ClientData clientData)
{
Interp *iPtr = (Interp *) interp;
/*
* Free any old handler.
*/
if (iPtr->log.handler && iPtr->log.handler->freeProc) {
iPtr->log.handler->freeProc(iPtr->log.clientData);
}
/*
* Install the new log handler.
*/
iPtr->log.handler = logHandler;
iPtr->log.clientData = clientData;
/*
* Inform the log handler what the current logging level is.
*/
if (iPtr->log.handler->setLevelProc) {
iPtr->log.handler->setLevelProc(iPtr->log.clientData,
interp, iPtr->log.level);
}
}
static Tcl_LogProc LogToStdout;
static Tcl_SetLogLevelProc StdoutLogLevelSet;
static Tcl_FreeLogHandlerProc StdoutLogFree;
static const Tcl_LogHandler simpleStdoutLog = {
LogToStdout,
StdoutLogLevelSet,
StdoutLogFree
};
struct StdoutLog {
Tcl_LogLevel level;
};
void
TclInstallStdoutLogger(
Tcl_Interp *interp)
{
struct StdoutLog *levelStore = Tcl_Alloc(sizeof(struct StdoutLog));
Tcl_SetLogHandler(interp, &simpleStdoutLog, levelStore);
}
static void
StdoutLogLevelSet(
ClientData clientData,
Tcl_Interp *interp,
Tcl_LogLevel level)
{
struct StdoutLog *levelStore = clientData;
levelStore->level = level;
}
static void
StdoutLogFree(
ClientData clientData)
{
struct StdoutLog *levelStore = clientData;
Tcl_Free(levelStore);
}
static int
LogToStdout(
ClientData clientData,
Tcl_Interp *interp,
Tcl_LogLevel level,
Tcl_Obj *message)
{
fprintf(stdout, "[%s] %s\n", levelNames[level], TclGetString(message));
return TCL_OK;
}
/*
* Local Variables:
* mode: c
* c-basic-offset: 4
* fill-column: 78
* tab-width: 8
* End:
*/