first round of logger cleanup/migration

This commit is contained in:
Mark Harrah 2010-06-10 21:26:27 -04:00
parent 64618fe56a
commit 0d24118d93
14 changed files with 157 additions and 308 deletions

View File

@ -18,7 +18,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
val classpathSub = baseProject(utilPath / "classpath", "Classpath")
val ivySub = project("ivy", "Ivy", new IvyProject(_), interfaceSub, launchInterfaceSub)
val logSub = baseProject(utilPath / "log", "Logging", interfaceSub)
val logSub = project(utilPath / "log", "Logging", new LogProject(_), interfaceSub)
val datatypeSub = baseProject("util" /"datatype", "Datatype Generator", ioSub)
val testSub = project("scripted", "Test", new TestProject(_), ioSub)
@ -37,7 +37,6 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
val sbtSub = project(sbtPath, "Simple Build Tool", new SbtProject(_) {}, compilerSub, launchInterfaceSub)
val installerSub = project(sbtPath / "install", "Installer", new InstallerProject(_) {}, sbtSub)
lazy val dist = task { None } dependsOn(launchSub.proguard, sbtSub.publishLocal, installerSub.publishLocal)
def baseProject(path: Path, name: String, deps: Project*) = project(path, name, new Base(_), deps : _*)
@ -85,13 +84,17 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
}
trait TestDependencies extends Project
{
val sc = "org.scala-tools.testing" %% "scalacheck" % "1.6" % "test"
val sc = "org.scala-tools.testing" %% "scalacheck" % "1.7" % "test"
val sp = "org.scala-tools.testing" % "specs" % "1.6.0" % "test"
}
class StandardTaskProject(info: ProjectInfo) extends Base(info)
{
override def testClasspath = super.testClasspath +++ compilerSub.testClasspath --- compilerInterfaceClasspath
}
class LogProject(info: ProjectInfo) extends Base(info)
{
val jline = jlineDep
}
class IOProject(info: ProjectInfo) extends Base(info) with TestDependencies
class TaskProject(info: ProjectInfo) extends Base(info) with TestDependencies

View File

@ -5,140 +5,8 @@ package sbt
import scala.collection.mutable.{Buffer, HashMap, ListBuffer}
sealed trait LogEvent extends NotNull
final class Success(val msg: String) extends LogEvent
final class Log(val level: Level.Value, val msg: String) extends LogEvent
final class Trace(val exception: Throwable) extends LogEvent
final class SetLevel(val newLevel: Level.Value) extends LogEvent
final class SetTrace(val level: Int) extends LogEvent
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
object ControlEvent extends Enumeration
{
val Start, Header, Finish = Value
}
abstract class Logger extends xsbt.CompileLogger with IvyLogger
{
def getLevel: Level.Value
def setLevel(newLevel: Level.Value)
def setTrace(flag: Int)
def getTrace: Int
final def traceEnabled = getTrace >= 0
def ansiCodesSupported = false
def atLevel(level: Level.Value) = level.id >= getLevel.id
def trace(t: => Throwable): Unit
final def verbose(message: => String): Unit = debug(message)
final def debug(message: => String): Unit = log(Level.Debug, message)
final def info(message: => String): Unit = log(Level.Info, message)
final def warn(message: => String): Unit = log(Level.Warn, message)
final def error(message: => String): Unit = log(Level.Error, message)
def success(message: => String): Unit
def log(level: Level.Value, message: => String): Unit
def control(event: ControlEvent.Value, message: => String): Unit
def logAll(events: Seq[LogEvent]): Unit
/** Defined in terms of other methods in Logger and should not be called from them. */
final def log(event: LogEvent)
{
event match
{
case s: Success => success(s.msg)
case l: Log => log(l.level, l.msg)
case t: Trace => trace(t.exception)
case setL: SetLevel => setLevel(setL.newLevel)
case setT: SetTrace => setTrace(setT.level)
case c: ControlEvent => control(c.event, c.msg)
}
}
import xsbti.F0
def debug(msg: F0[String]): Unit = log(Level.Debug, msg)
def warn(msg: F0[String]): Unit = log(Level.Warn, msg)
def info(msg: F0[String]): Unit = log(Level.Info, msg)
def error(msg: F0[String]): Unit = log(Level.Error, msg)
def trace(msg: F0[Throwable]) = trace(msg.apply)
def log(level: Level.Value, msg: F0[String]): Unit = log(level, msg.apply)
}
/** Implements the level-setting methods of Logger.*/
abstract class BasicLogger extends Logger
{
private var traceEnabledVar = java.lang.Integer.MAX_VALUE
private var level: Level.Value = Level.Info
def getLevel = level
def setLevel(newLevel: Level.Value) { level = newLevel }
def setTrace(level: Int) { traceEnabledVar = level }
def getTrace = traceEnabledVar
}
final class SynchronizedLogger(delegate: Logger) extends Logger
{
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
def getLevel = { synchronized { delegate.getLevel } }
def setLevel(newLevel: Level.Value) { synchronized { delegate.setLevel(newLevel) } }
def setTrace(level: Int) { synchronized { delegate.setTrace(level) } }
def getTrace: Int = { synchronized { delegate.getTrace } }
def trace(t: => Throwable) { synchronized { delegate.trace(t) } }
def log(level: Level.Value, message: => String) { synchronized { delegate.log(level, message) } }
def success(message: => String) { synchronized { delegate.success(message) } }
def control(event: ControlEvent.Value, message: => String) { synchronized { delegate.control(event, message) } }
def logAll(events: Seq[LogEvent]) { synchronized { delegate.logAll(events) } }
}
final class MultiLogger(delegates: List[Logger]) extends BasicLogger
{
override lazy val ansiCodesSupported = delegates.forall(_.ansiCodesSupported)
override def setLevel(newLevel: Level.Value)
{
super.setLevel(newLevel)
dispatch(new SetLevel(newLevel))
}
override def setTrace(level: Int)
{
super.setTrace(level)
dispatch(new SetTrace(level))
}
def trace(t: => Throwable) { dispatch(new Trace(t)) }
def log(level: Level.Value, message: => String) { dispatch(new Log(level, message)) }
def success(message: => String) { dispatch(new Success(message)) }
def logAll(events: Seq[LogEvent]) { delegates.foreach(_.logAll(events)) }
def control(event: ControlEvent.Value, message: => String) { delegates.foreach(_.control(event, message)) }
private def dispatch(event: LogEvent) { delegates.foreach(_.log(event)) }
}
/** A filter logger is used to delegate messages but not the logging level to another logger. This means
* that messages are logged at the higher of the two levels set by this logger and its delegate.
* */
final class FilterLogger(delegate: Logger) extends BasicLogger
{
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
def trace(t: => Throwable)
{
if(traceEnabled)
delegate.trace(t)
}
override def setTrace(level: Int) { delegate.setTrace(level) }
override def getTrace = delegate.getTrace
def log(level: Level.Value, message: => String)
{
if(atLevel(level))
delegate.log(level, message)
}
def success(message: => String)
{
if(atLevel(Level.Info))
delegate.success(message)
}
def control(event: ControlEvent.Value, message: => String)
{
if(atLevel(Level.Info))
delegate.control(event, message)
}
def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events)
}
trait Logger extends AbstractLogger with xsbt.CompileLogger with IvyLogger
/** A logger that can buffer the logging done on it by currently executing Thread and
* then can flush the buffer to the delegate logger provided in the constructor. Use
@ -151,7 +19,7 @@ final class FilterLogger(delegate: Logger) extends BasicLogger
*
* This logger is thread-safe.
* */
final class BufferedLogger(delegate: Logger) extends Logger
final class BufferedLogger(delegate: AbstractLogger) extends Logger
{
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
private[this] val buffers = wrap.Wrappers.weakMap[Thread, Buffer[LogEvent]]
@ -244,9 +112,9 @@ final class BufferedLogger(delegate: Logger) extends Logger
}
def control(event: ControlEvent.Value, message: => String): Unit =
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
synchronized
{
if(condition)
@ -259,134 +127,3 @@ final class BufferedLogger(delegate: Logger) extends Logger
}
}
}
object ConsoleLogger
{
private val formatEnabled = ansiSupported && !formatExplicitlyDisabled
private[this] def formatExplicitlyDisabled = java.lang.Boolean.getBoolean("sbt.log.noformat")
private[this] def ansiSupported =
try { jline.Terminal.getTerminal.isANSISupported }
catch { case e: Exception => !isWindows }
private[this] def os = System.getProperty("os.name")
private[this] def isWindows = os.toLowerCase.indexOf("windows") >= 0
}
/** A logger that logs to the console. On supported systems, the level labels are
* colored.
*
* This logger is not thread-safe.*/
class ConsoleLogger extends BasicLogger
{
override def ansiCodesSupported = ConsoleLogger.formatEnabled
def messageColor(level: Level.Value) = Console.RESET
def labelColor(level: Level.Value) =
level match
{
case Level.Error => Console.RED
case Level.Warn => Console.YELLOW
case _ => Console.RESET
}
def successLabelColor = Console.GREEN
def successMessageColor = Console.RESET
override def success(message: => String)
{
if(atLevel(Level.Info))
log(successLabelColor, Level.SuccessLabel, successMessageColor, message)
}
def trace(t: => Throwable): Unit =
System.out.synchronized
{
val traceLevel = getTrace
if(traceLevel >= 0)
System.out.synchronized { System.out.print(StackTrace.trimmed(t, traceLevel)) }
}
def log(level: Level.Value, message: => String)
{
if(atLevel(level))
log(labelColor(level), level.toString, messageColor(level), message)
}
private def setColor(color: String)
{
if(ansiCodesSupported)
System.out.synchronized { System.out.print(color) }
}
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =
System.out.synchronized
{
for(line <- message.split("""\n"""))
{
setColor(Console.RESET)
System.out.print('[')
setColor(labelColor)
System.out.print(label)
setColor(Console.RESET)
System.out.print("] ")
setColor(messageColor)
System.out.print(line)
setColor(Console.RESET)
System.out.println()
}
}
def logAll(events: Seq[LogEvent]) = System.out.synchronized { events.foreach(log) }
def control(event: ControlEvent.Value, message: => String)
{ log(labelColor(Level.Info), Level.Info.toString, Console.BLUE, message) }
}
/** An enumeration defining the levels available for logging. A level includes all of the levels
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).*/
object Level extends Enumeration with NotNull
{
val Debug = Value(1, "debug")
val Info = Value(2, "info")
val Warn = Value(3, "warn")
val Error = Value(4, "error")
/** Defines the label to use for success messages. A success message is logged at the info level but
* uses this label. Because the label for levels is defined in this module, the success
* label is also defined here. */
val SuccessLabel = "success"
// added because elements was renamed to iterator in 2.8.0 nightly
def levels = Debug :: Info :: Warn :: Error :: Nil
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
def apply(s: String) = levels.find(s == _.toString)
/** Same as apply, defined for use in pattern matching. */
private[sbt] def unapply(s: String) = apply(s)
}
/** Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`.
* A line is delimited by `nl`, which is by default the platform line separator.*/
final class LoggerWriter(delegate: Logger, level: Level.Value, nl: String) extends java.io.Writer
{
def this(delegate: Logger, level: Level.Value) = this(delegate, level, FileUtilities.Newline)
private[this] val buffer = new StringBuilder
override def close() = flush()
override def flush(): Unit =
synchronized {
if(buffer.length > 0)
{
log(buffer.toString)
buffer.clear()
}
}
override def write(content: Array[Char], offset: Int, length: Int): Unit =
synchronized {
buffer.append(content, offset, length)
process()
}
private[this] def process()
{
val i = buffer.indexOf(nl)
if(i >= 0)
{
log(buffer.substring(0, i))
buffer.delete(0, i + nl.length)
process()
}
}
private[this] def log(s: String): Unit = delegate.log(level, s)
}

View File

@ -416,7 +416,7 @@ class xMain extends xsbti.AppMain
val ContinuousCompilePollDelaySeconds = 1
/** The list of logging levels.*/
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.levels.map(_.toString)
private def logLevels: Iterable[String] = TreeSet.empty[String] ++ Level.values.map(_.toString)
/** The list of all interactive commands other than logging level.*/
private def basicCommands: Iterable[String] = TreeSet(ShowProjectsAction, ShowActions, ShowCurrent, HelpAction,
RebootCommand, TraceCommand, ContinuousCompileCommand, ProjectConsoleAction, BuilderCommand) ++
@ -464,7 +464,7 @@ class xMain extends xsbti.AppMain
printCmd(RebootCommand, "Reloads sbt, picking up modifications to sbt.version or scala.version and recompiling modified project definitions.")
printCmd(HelpAction, "Displays this help message.")
printCmd(ShowCurrent, "Shows the current project, Scala version, and logging level.")
printCmd(Level.levels.mkString(", "), "Set logging for the current project to the specified level.")
printCmd(Level.values.mkString(", "), "Set logging for the current project to the specified level.")
printCmd(TraceCommand + " " + validTraceArguments, "Configures stack trace logging. " + traceExplanation)
printCmd(ProjectAction + " <project name>", "Sets the currently active project.")
printCmd(ShowProjectsAction, "Shows all available projects.")

View File

@ -93,7 +93,7 @@ object LogWriterTest extends Properties("Log Writer")
for(ls <- arbList[List[ToLog]].arbitrary; lv <- genLevel) yield
new Output(ls, lv)
def levelsGen: Seq[Gen[Level.Value]] = Level.elements.toList.map(x => value(x))
def levelsGen: Seq[Gen[Level.Value]] = Level.values.toList.map(x => value(x))
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
def addNewline(l: ToLog): ToLog =

View File

@ -1,7 +1,8 @@
/* sbt -- Simple Build Tool
* Copyright 2009, 2010 Mark Harrah
*/
package xsbt.api
package xsbt
package api
import java.io.File
import xsbt.FileUtilities

View File

@ -1,15 +1,15 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package xsbt
package sbt
/** Implements the level-setting methods of Logger.*/
abstract class BasicLogger extends Logger
abstract class BasicLogger extends AbstractLogger
{
private var traceEnabledVar = true
private var traceEnabledVar = java.lang.Integer.MAX_VALUE
private var level: Level.Value = Level.Info
def getLevel = level
def setLevel(newLevel: Level.Value) { level = newLevel }
def enableTrace(flag: Boolean) { traceEnabledVar = flag }
def traceEnabled = traceEnabledVar
}
def setTrace(level: Int) { traceEnabledVar = level }
def getTrace = traceEnabledVar
}

View File

@ -1,8 +1,9 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package xsbt
import sbt.{AbstractLogger, ControlEvent, Level, Log, LogEvent, SetLevel, SetTrace, Success, Trace}
import scala.collection.mutable.ListBuffer
/** A logger that can buffer the logging done on it and then can flush the buffer
@ -13,7 +14,7 @@
*
* This class assumes that it is the only client of the delegate logger.
* */
class BufferedLogger(delegate: Logger) extends Logger
class BufferedLogger(delegate: AbstractLogger) extends AbstractLogger
{
private[this] val buffer = new ListBuffer[LogEvent]
private[this] var recording = false
@ -54,10 +55,10 @@ class BufferedLogger(delegate: Logger) extends Logger
}
def getLevel = delegate.getLevel
def traceEnabled = delegate.traceEnabled
def enableTrace(flag: Boolean)
def setTrace(level: Int)
{
buffer += new SetTrace(flag)
delegate.enableTrace(flag)
buffer += new SetTrace(level)
delegate.setTrace(level)
}
def trace(t: => Throwable): Unit =
@ -73,9 +74,9 @@ class BufferedLogger(delegate: Logger) extends Logger
delegate.logAll(events)
def control(event: ControlEvent.Value, message: => String): Unit =
doBufferable(Level.Info, new ControlEvent(event, message), _.control(event, message))
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
private def doBufferable(level: Level.Value, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
doBufferableIf(atLevel(level), appendIfBuffered, doUnbuffered)
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: Logger => Unit): Unit =
private def doBufferableIf(condition: => Boolean, appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit): Unit =
if(condition)
{
if(recording)

View File

@ -1,7 +1,7 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package xsbt
package sbt
object ConsoleLogger
{
@ -17,10 +17,12 @@ object ConsoleLogger
}
/** A logger that logs to the console. On supported systems, the level labels are
* colored. */
* colored.
*
* This logger is not thread-safe.*/
class ConsoleLogger extends BasicLogger
{
import ConsoleLogger.formatEnabled
override def ansiCodesSupported = ConsoleLogger.formatEnabled
def messageColor(level: Level.Value) = Console.RESET
def labelColor(level: Level.Value) =
level match
@ -39,8 +41,9 @@ class ConsoleLogger extends BasicLogger
def trace(t: => Throwable): Unit =
System.out.synchronized
{
if(traceEnabled)
t.printStackTrace
val traceLevel = getTrace
if(traceLevel >= 0)
System.out.synchronized { System.out.print(StackTrace.trimmed(t, traceLevel)) }
}
def log(level: Level.Value, message: => String)
{
@ -49,7 +52,7 @@ class ConsoleLogger extends BasicLogger
}
private def setColor(color: String)
{
if(formatEnabled)
if(ansiCodesSupported)
System.out.synchronized { System.out.print(color) }
}
private def log(labelColor: String, label: String, messageColor: String, message: String): Unit =

View File

@ -0,0 +1,35 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
/** A filter logger is used to delegate messages but not the logging level to another logger. This means
* that messages are logged at the higher of the two levels set by this logger and its delegate.
* */
class FilterLogger(delegate: AbstractLogger) extends BasicLogger
{
override lazy val ansiCodesSupported = delegate.ansiCodesSupported
def trace(t: => Throwable)
{
if(traceEnabled)
delegate.trace(t)
}
override def setTrace(level: Int) { delegate.setTrace(level) }
override def getTrace = delegate.getTrace
def log(level: Level.Value, message: => String)
{
if(atLevel(level))
delegate.log(level, message)
}
def success(message: => String)
{
if(atLevel(Level.Info))
delegate.success(message)
}
def control(event: ControlEvent.Value, message: => String)
{
if(atLevel(Level.Info))
delegate.control(event, message)
}
def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events)
}

View File

@ -1,11 +1,11 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
package sbt
/** An enumeration defining the levels available for logging. A level includes all of the levels
* with id larger than its own id. For example, Warn (id=3) includes Error (id=4).*/
object Level extends Enumeration with NotNull
object Level extends Enumeration
{
val Debug = Value(1, "debug")
val Info = Value(2, "info")
@ -16,10 +16,8 @@ object Level extends Enumeration with NotNull
* label is also defined here. */
val SuccessLabel = "success"
// added because elements was renamed to iterator in 2.8.0 nightly
def levels = Debug :: Info :: Warn :: Error :: Nil
/** Returns the level with the given name wrapped in Some, or None if no level exists for that name. */
def apply(s: String) = levels.find(s == _.toString)
def apply(s: String) = values.find(s == _.toString)
/** Same as apply, defined for use in pattern matching. */
private[xsbt] def unapply(s: String) = apply(s)
private[sbt] def unapply(s: String) = apply(s)
}

View File

@ -1,14 +1,14 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
package sbt
sealed trait LogEvent extends NotNull
final class Success(val msg: String) extends LogEvent
final class Log(val level: Level.Value, val msg: String) extends LogEvent
final class Trace(val exception: Throwable) extends LogEvent
final class SetLevel(val newLevel: Level.Value) extends LogEvent
final class SetTrace(val enabled: Boolean) extends LogEvent
final class SetTrace(val level: Int) extends LogEvent
final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent
object ControlEvent extends Enumeration

View File

@ -1,18 +1,22 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009 Mark Harrah
*/
package xsbt
package sbt
import xsbti.{Logger => xLogger, F0}
abstract class Logger extends xLogger with NotNull
abstract class AbstractLogger extends xLogger with NotNull
{
def getLevel: Level.Value
def setLevel(newLevel: Level.Value)
def enableTrace(flag: Boolean)
def traceEnabled: Boolean
def setTrace(flag: Int)
def getTrace: Int
final def traceEnabled = getTrace >= 0
def ansiCodesSupported = false
def atLevel(level: Level.Value) = level.id >= getLevel.id
def trace(t: => Throwable): Unit
final def verbose(message: => String): Unit = debug(message)
final def debug(message: => String): Unit = log(Level.Debug, message)
final def info(message: => String): Unit = log(Level.Info, message)
final def warn(message: => String): Unit = log(Level.Warn, message)
@ -31,7 +35,7 @@ abstract class Logger extends xLogger with NotNull
case l: Log => log(l.level, l.msg)
case t: Trace => trace(t.exception)
case setL: SetLevel => setLevel(setL.newLevel)
case setT: SetTrace => enableTrace(setT.enabled)
case setT: SetTrace => setTrace(setT.level)
case c: ControlEvent => control(c.event, c.msg)
}
}

View File

@ -0,0 +1,40 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
/** Provides a `java.io.Writer` interface to a `Logger`. Content is line-buffered and logged at `level`.
* A line is delimited by `nl`, which is by default the platform line separator.*/
class LoggerWriter(delegate: AbstractLogger, level: Level.Value, nl: String) extends java.io.Writer
{
def this(delegate: AbstractLogger, level: Level.Value) = this(delegate, level, System.getProperty("line.separator"))
private[this] val buffer = new StringBuilder
override def close() = flush()
override def flush(): Unit =
synchronized {
if(buffer.length > 0)
{
log(buffer.toString)
buffer.clear()
}
}
override def write(content: Array[Char], offset: Int, length: Int): Unit =
synchronized {
buffer.append(content, offset, length)
process()
}
private[this] def process()
{
val i = buffer.indexOf(nl)
if(i >= 0)
{
log(buffer.substring(0, i))
buffer.delete(0, i + nl.length)
process()
}
}
private[this] def log(s: String): Unit = delegate.log(level, s)
}

View File

@ -0,0 +1,27 @@
/* sbt -- Simple Build Tool
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger
{
override lazy val ansiCodesSupported = delegates.forall(_.ansiCodesSupported)
override def setLevel(newLevel: Level.Value)
{
super.setLevel(newLevel)
dispatch(new SetLevel(newLevel))
}
override def setTrace(level: Int)
{
super.setTrace(level)
dispatch(new SetTrace(level))
}
def trace(t: => Throwable) { dispatch(new Trace(t)) }
def log(level: Level.Value, message: => String) { dispatch(new Log(level, message)) }
def success(message: => String) { dispatch(new Success(message)) }
def logAll(events: Seq[LogEvent]) { delegates.foreach(_.logAll(events)) }
def control(event: ControlEvent.Value, message: => String) { delegates.foreach(_.control(event, message)) }
private def dispatch(event: LogEvent) { delegates.foreach(_.log(event)) }
}