TIP 390: A Logging API for Tcl

Author:         Jeff Rogers <[email protected]>
Author:         Donal K. Fellows <[email protected]>
State:          Draft
Type:           Project
Tcl-Version:    9.1
Created:        27-Oct-2011
Vote:           Pending


This TIP proposes a standard API for logging in Tcl applications, both at the Tcl and C level.


Logging is needed in most applications. There is no standard, documented logging API in Tcl, for either Tcl or C code, leading most people to roll their own. The TIP proposes a standard Tcl and C API that handles the most common logging functions.

The default implementation of the logger should just print the message (with substitutions performed as if with format) to the standard error channel.


There shall be a logger associated with each Tcl interpreter, and a separate global logger. As the global logger may be used by multiple threads at once, it shall be protected by an appropriate mutex; it may only be used from C code. Interpreter-specific loggers will be exposed within those interpreters.


  1. void Tcl_Log(Tcl_Interp *interp, Tcl_LogLevel level, const char *message, ...)

    This is the main C api call; it logs the formatted message at the specified log level. If interp is specified, it operates within the context of that interp; if NULL then it logs in a maner not associated with any interpreter.

  2. void Tcl_SetLogLevel(Tcl_Interp *interp, Tcl_LogLevel level)

    Instructs the logging system to set the logging level to the given value for the logger associated with the given interp (or the global logger when interp is NULL). Legal levels (from lowest to highest) are TCL_LOG_DEV, TCL_LOG_DEBUG, TCL_LOG_INFO, TCL_LOG_NOTICE, TCL_LOG_WARNING, TCL_LOG_ERROR, TCL_LOG_FATAL, TCL_LOG_BUG.

  3. Tcl_LogLevel Tcl_GetLogLevel(Tcl_Interp *interp)

    Retrieves the current logging level for the logger associated with the given interp (or the global logger when interp is NULL). The result will be one of the values documented above.

  4. int Tcl_LogLevelEnabled(Tcl_Interp *interp, Tcl_LogLevel level)

    Returns a boolean value that says whether logging at level is enabled for the logger associated with the given interp (or the global logger when interp is NULL). This is useful when the calculation of the arguments to Tcl_Log is expensive.

  5. void Tcl_SetLogHandler(Tcl_Interp *interp, const Tcl_LogHandler *logHandler, ClientData clientData)

    Where Tcl_LogHandler is defined as (approximately):

    typedef struct Tcl_LogHandler {
        Tcl_LogHandlerProc *logHandlerProc;
        Tcl_SetLogLevelProc *setLogLevelProc;
        Tcl_FreeLogHandlerProc *freeLogHandlerProc;
    } Tcl_LogHandler;
    int Tcl_LogHandlerProc(ClientData clientData, Tcl_Interp *interp,
            Tcl_LogLevel level, Tcl_Obj *message);
    void Tcl_SetLogLevelProc(ClientData clientData, Tcl_Interp *interp,
            Tcl_LogLevel level);
    void Tcl_FreeLogHandlerProc(ClientData clientData);

    Installs a new log handler for a specified interp or globally. If no log handler is installed, the default action is to print the formatted message on standard error if the message level is equal to or greater than the loglevel; in the context of a particular interpreter the default action is to call the configured log handler (which does the same thing by default). The log handler callback must be non-NULL; it returns a standard Tcl result code, but if the global log handler returns non-TCL_OK then Tcl will panic. The set log level callback can be NULL if the log handler wishes to be notified of the current logging level.

    The clientData is an arbitrary pointer that is passed through by Tcl unexamined; if the free log handler callback is non-NULL, it will be called to release whatever the clientData points to when the log handler is changed.

    Note that when a logging event happens and is of a level high enough to be processed, the message and subsequent arguments will be formatted into a Tcl_Obj* value prior to delivery to the log handler (as if with Tcl_ObjPrintf).

Tcl Script API

The corresponding Tcl API is:

  1. interp logger ?loggerCommandFragment?

    Gets or sets the log handler for the executing interpreter. The log handler (which will be a list of at least one element) will have these arguments appended on invoke: level message args

  2. interp loglevel ?level?

    Gets or sets the current logging level for the executing interpreter. The level, if provided, must be one of: dev, debug, info, notice, warning, error, fatal, or bug; they correspond to the levels defined above for Tcl_SetLogLevel.

  3. log level message ...

    Tcl equivalent of Tcl_Log(); if level is at or above the log level of the current inerpreter, message will be formatted with the remaining arguments (as in format) and then sends them to the interpreter's configured logger.


This document has been placed in the public domain.