// NOTE: I actually made a mistake in this implementation // Logging needs to have a redirect with type Logging, not Logger // this is so that if a->b and b gets redirected, things logged via a.log() // actually get forwarded along (regardless of what came first: redirecting // b somewhere else or the call to a.log()). // the only thing to worry about is loops, but i'm willing to take that risk for now. // i have an implementation of this in Parma called Logging2 import util.Random // TODO in a real implementation, this would be a trait class Logger { private[this] val id = Random.nextInt def log(msg: String) { println("from logger %d, message: %s".format(id, msg)) } } trait Logging { // TODO include ability to tee? thats a bit more than i need... private[Logging] val defaultLogger = new Logger private[Logging] var logger = defaultLogger def log(msg: String) { logger.log(this + "\t" + msg) } def redirectLogTo(l: Logging) { logger = l.logger } def undoLogRedirection { logger = defaultLogger } def logTo(l: Logger)(block: => Any) { val temp = logger logger = l block logger = temp } } class Foo extends Logging { val specialLogger = new Logger def foo { log("hello") } def specialFun { logTo(specialLogger) { log("special!") } log("not-so-special") } } class Bar extends Logging { def bar { log("hello") } // namespace is not polluted by things defined as private[Logging] // error: value defaultLogger in trait Logging cannot be accessed in Bar //def baz { defaultLogger.log("polluted namespace!") } } object Run { def main(args: Array[String]) { val f = new Foo val b = new Bar val b2 = new Bar f.foo b.bar b2.bar f.redirectLogTo(b) f.foo // prints from b f.specialFun // prints from its own log b2.bar // prints from b2's original logger f.foo // prints from b f.undoLogRedirection f.foo // prints from f's original logger } }