I would guess this has already been written, but I got tired of writing and then commenting out trace / debugging comments in my ChucK code, so I put together this simple class modeled loosely after similar packages in Java other languages.  It comprises all static functions, so you can call it from anywhere.  If someone wants to extend the log() function to write to a file or stream, be my guest -- currently it simply prints to <<< >>> (stderr).

Here is the code in its entirety.  Share and enjoy.

- Rob

// File: log.ck
// Log: A simple logging facility for ChucK
// 
// Loosely modeled after logging packages in other languages, Log
// allows you to sprinkle your code with print messages tagged with
// different severity levels.  What gets printed at run time depends
// on the setting of the log severity level.  The levels (from lowest
// to highest severity) are:
//
// TRACE 
// DEBUG 
// INFO 
// WARN 
// ERROR
// FATAL
// 
// A simple example of usage:
// ================
// class ArrayFns {
//   fun int iterate(Object things[]) {
//     Log.debug(this, "entering iterate()");
//     if (things == null) {
//       Log.warn(this, "null array, returning 0");
//       return 0;
//     }
//     for (0 => int i; i<things.size(); i++) {
//       if (Log.severity(Log.TRACE)) Log.log(this, "iterate(), processing " + i);
//       process(things[i]);
//     }
//     things.size() => int retval;
//     if (Log.severity(Log.DEBUG)) Log.log(this, "exiting iterate() with " + retval);
//     return retval;
//   }
// }
// ================
// The explanation:
//
// The line:
//     Log.debug(this, "entering iterate()");
// will print the message if Log.set_severity() is DEBUG or TRACE.
//
// The line:
//     Log.warn(this, "null array, returning 0");
// will print the message if Log.set_severity() is WARN or lower
// (WARN, INFO, DEBUG or TRACE).
//
// The line:
//     if (Log.severity(TRACE)) Log.log(this, "iterate(), count=" + i);
// is equivalent to:
//     Log.trace(this, "iterate(), count=" + i);
// except that the latter form will always evaluate its arguments,
// which in this case causes string catenation.  You can avoid the
// extra evaluation by using the first form, which will call Log.log()
// iff the current severity level is TRACE.  This is especially useful
// in inner loops that are called often, or if the construction of the
// error message itself is particularly costly.
// 
// ==== Authors:
// Robert Poor <r@alum.mit.edu>
// ==== Revision History:
// 20100321_214041: Initial Version
// ====

public class Log {
    
    static int TRACE;
    static int DEBUG;
    static int INFO;
    static int WARN;
    static int ERROR;
    static int FATAL;

    static int _severity;

    fun static void init() {
0 => TRACE;
1 => DEBUG;
2 => INFO;
3 => WARN;
4 => ERROR;
5 => FATAL;
set_severity(WARN);
    }

    // get/set the severity level
    fun static int get_severity() { return _severity; }
    fun static void set_severity(int severity) { severity => _severity; }

    // Return true if Log is at severity or lower
    fun static int severity(int level) { return level >= _severity; }

    fun static void trace(string msg) { log_if(TRACE, msg); }
    fun static void trace(Object o, string msg) { log_if(TRACE, o, msg); }
    fun static void debug(string msg) { log_if(DEBUG, msg); }
    fun static void debug(Object o, string msg) { log_if(DEBUG, o, msg); }
    fun static void info(string msg) { log_if(INFO, msg); }
    fun static void info(Object o, string msg) { log_if(INFO, o, msg); }
    fun static void warn(string msg) { log_if(WARN, msg); }
    fun static void warn(Object o, string msg) { log_if(WARN, o, msg); }
    fun static void error(string msg) { log_if(ERROR, msg); }
    fun static void error(Object o, string msg) { log_if(ERROR, o, msg); }
    fun static void fatal(string msg) { log_if(FATAL, msg); }
    fun static void fatal(Object o, string msg) { log_if(FATAL, o, msg); }

    // Conditionally log
    fun static void log_if(int level, string msg) { if (severity(level)) log(msg);  }
    fun static void log_if(int level, Object o, string msg) { if (severity(level)) log(o, msg); }

    // Unconditionally log
    fun static void log(string msg) { log("[toplevel]", msg); }
    fun static void log(Object o, string msg) { <<< now, me, o.toString(), msg >>>; }

}

// setup static constants and defaults at load time.
Log.init();
// ===== EOF =====