diff --git a/util/control/MessageOnlyException.scala b/util/control/MessageOnlyException.scala index 7fa43746d..75b7737d8 100644 --- a/util/control/MessageOnlyException.scala +++ b/util/control/MessageOnlyException.scala @@ -7,8 +7,12 @@ final class MessageOnlyException(override val toString: String) extends RuntimeE /** A dummy exception for the top-level exception handler to know that an exception * has been handled, but is being passed further up to indicate general failure. */ -final class AlreadyHandledException extends RuntimeException +final class AlreadyHandledException(val underlying: Throwable) extends RuntimeException /** A marker trait for a top-level exception handler to know that this exception * doesn't make sense to display. */ -trait UnprintableException extends Throwable \ No newline at end of file +trait UnprintableException extends Throwable + +/** A marker trait that refines UnprintableException to indicate to a top-level exception handler +* that the code throwing this exception has already provided feedback to the user about the error condition. */ +trait FeedbackProvidedException extends UnprintableException diff --git a/util/log/ConsoleLogger.scala b/util/log/ConsoleLogger.scala index ad626336f..d8ce50dd0 100644 --- a/util/log/ConsoleLogger.scala +++ b/util/log/ConsoleLogger.scala @@ -42,15 +42,16 @@ object ConsoleLogger } catch { case e: Exception => !isWindows } + val noSuppressedMessage = (_: SuppressedTraceContext) => None private[this] def os = System.getProperty("os.name") private[this] def isWindows = os.toLowerCase.indexOf("windows") >= 0 - def apply(): ConsoleLogger = apply(systemOut) def apply(out: PrintStream): ConsoleLogger = apply(printStreamOut(out)) def apply(out: PrintWriter): ConsoleLogger = apply(printWriterOut(out)) - def apply(out: ConsoleOut, ansiCodesSupported: Boolean = formatEnabled, useColor: Boolean = formatEnabled): ConsoleLogger = - new ConsoleLogger(out, ansiCodesSupported, useColor) + def apply(out: ConsoleOut = systemOut, ansiCodesSupported: Boolean = formatEnabled, + useColor: Boolean = formatEnabled, suppressedMessage: SuppressedTraceContext => Option[String] = noSuppressedMessage): ConsoleLogger = + new ConsoleLogger(out, ansiCodesSupported, useColor, suppressedMessage) private[this] val EscapeSequence = (27.toChar + "[^@-~]*[@-~]").r def stripEscapeSequences(s: String): String = @@ -61,7 +62,7 @@ object ConsoleLogger * colored. * * This logger is not thread-safe.*/ -class ConsoleLogger private[ConsoleLogger](val out: ConsoleOut, override val ansiCodesSupported: Boolean, val useColor: Boolean) extends BasicLogger +class ConsoleLogger private[ConsoleLogger](val out: ConsoleOut, override val ansiCodesSupported: Boolean, val useColor: Boolean, val suppressedMessage: SuppressedTraceContext => Option[String]) extends BasicLogger { import scala.Console.{BLUE, GREEN, RED, RESET, YELLOW} def messageColor(level: Level.Value) = RESET @@ -85,6 +86,9 @@ class ConsoleLogger private[ConsoleLogger](val out: ConsoleOut, override val ans val traceLevel = getTrace if(traceLevel >= 0) out.print(StackTrace.trimmed(t, traceLevel)) + if(traceLevel <= 2) + for(msg <- suppressedMessage(new SuppressedTraceContext(traceLevel, ansiCodesSupported && useColor))) + printLabeledLine(labelColor(Level.Error), "trace", messageColor(Level.Error), msg) } def log(level: Level.Value, message: => String) { @@ -102,24 +106,27 @@ class ConsoleLogger private[ConsoleLogger](val out: ConsoleOut, override val ans out.lockObject.synchronized { for(line <- message.split("""\n""")) - { - reset() - out.print("[") - setColor(labelColor) - out.print(label) - reset() - out.print("] ") - setColor(messageColor) - out.print(line) - reset() - out.println() - } + printLabeledLine(labelColor, label, messageColor, line) } + private def printLabeledLine(labelColor: String, label: String, messageColor: String, line: String): Unit = + { + reset() + out.print("[") + setColor(labelColor) + out.print(label) + reset() + out.print("] ") + setColor(messageColor) + out.print(line) + reset() + out.println() + } def logAll(events: Seq[LogEvent]) = out.lockObject.synchronized { events.foreach(log) } def control(event: ControlEvent.Value, message: => String) { log(labelColor(Level.Info), Level.Info.toString, BLUE, message) } } +final class SuppressedTraceContext(val traceLevel: Int, val useColor: Boolean) sealed trait ConsoleOut { val lockObject: AnyRef diff --git a/util/log/MainLogging.scala b/util/log/MainLogging.scala index b07abf4e3..25f42a6af 100644 --- a/util/log/MainLogging.scala +++ b/util/log/MainLogging.scala @@ -25,12 +25,14 @@ object MainLogging } def defaultMultiConfig(backing: AbstractLogger): MultiLoggerConfig = - new MultiLoggerConfig(defaultScreen, backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue) + new MultiLoggerConfig(defaultScreen(ConsoleLogger.noSuppressedMessage), backing, Nil, Level.Info, Level.Debug, -1, Int.MaxValue) - def defaultScreen: AbstractLogger = ConsoleLogger() + def defaultScreen(): AbstractLogger = ConsoleLogger() + def defaultScreen(suppressedMessage: SuppressedTraceContext => Option[String]): AbstractLogger = ConsoleLogger(suppressedMessage = suppressedMessage) def defaultBacked(useColor: Boolean = ConsoleLogger.formatEnabled): PrintWriter => ConsoleLogger = to => ConsoleLogger(ConsoleLogger.printWriterOut(to), useColor = useColor) // TODO: should probably filter ANSI codes when useColor=false } -final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int) \ No newline at end of file +final case class MultiLoggerConfig(console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger], + screenLevel: Level.Value, backingLevel: Level.Value, screenTrace: Int, backingTrace: Int) \ No newline at end of file