* limit stack trace length: trace 'on' | 'off' | 'nosbt' | 1 | 2 | ...

* updating license/copyright
This commit is contained in:
Mark Harrah 2010-01-14 00:15:21 -05:00
parent 791c7a2a14
commit b6cb89fea4
10 changed files with 144 additions and 82 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2008, 2009 Steven Blundy, Mark Harrah, David MacIver, Mikko Peltonen
Copyright (c) 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, David MacIver, Mikko Peltonen, Tony Sloane, Vesa Vilhonen
All rights reserved.
Redistribution and use in source and binary forms, with or without

29
NOTICE
View File

@ -1,5 +1,6 @@
Simple Build Tool (sbt)
Copyright 2008, 2009 Steven Blundy, Mark Harrah, David MacIver, Mikko Peltonen
Copyright 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, David MacIver, Mikko Peltonen, Tony Sloane, Vesa Vilhonen
Licensed under BSD-style license (see LICENSE)
Portions based on code by Mike Clark in JDepend
@ -14,30 +15,12 @@ Portions based on code from the Scala compiler
Copyright 2002-2008 EPFL, Lausanne
Licensed under BSD-style license (see licenses/LICENSE_Scala)
Portions based on code from specs
Copyright 2007-2008 Eric Torreborre
Licensed under MIT license (see licenses/LICENSE_specs)
Portions based on code from ScalaTest
Copyright 2001-2008 Artima, Inc.
Licensed under the Apache License, Version 2.0(see licenses/LICENSE_Apache)
Portions based on code from ScalaCheck
Copyright 2007, Rickard Nilsson
Licensed under BSD-style license (see licenses/LICENSE_ScalaCheck)
Jetty is licensed under the Apache License, Version 2.0 (see licenses/LICENSE_Apache).
ScalaTest is distributed with sbt (in the subversion repository)
and requires the following notice:
This product includes software developed by
Artima, Inc. (http://www.artima.com/).
JLine is distributed with the sbt launcher.
It is licensed under a BSD-style license (see licenses/LICENSE_JLine)
Apache Ivy, licensed under the Apache License, Version 2.0
(see licenses/LICENSE_Apache) is distributed with sbt and
requires the following notice:
(see licenses/LICENSE_Apache) is distributed with the sbt launcher.
It requires the following notice:
This product includes software developed by
The Apache Software Foundation (http://www.apache.org/).

View File

@ -1,4 +1,4 @@
Copyright (c) 2008 Mark Harrah, David MacIver
Copyright (c) 2008, 2009, 2010 Steven Blundy, Josh Cough, Nathan Hamblen, Mark Harrah, David MacIver, Mikko Peltonen, Tony Sloane, Vesa Vilhonen
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -22,4 +22,4 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -10,7 +10,7 @@ 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
@ -22,8 +22,9 @@ abstract class Logger extends xsbt.CompileLogger with xsbt.IvyLogger
{
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
@ -47,7 +48,7 @@ abstract class Logger extends xsbt.CompileLogger with xsbt.IvyLogger
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)
}
}
@ -64,12 +65,12 @@ abstract class Logger extends xsbt.CompileLogger with xsbt.IvyLogger
/** Implements the level-setting methods of Logger.*/
abstract class BasicLogger extends Logger
{
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
}
final class SynchronizedLogger(delegate: Logger) extends Logger
@ -77,8 +78,8 @@ 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 enableTrace(enabled: Boolean) { synchronized { delegate.enableTrace(enabled) } }
def traceEnabled: Boolean = { synchronized { delegate.traceEnabled } }
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) } }
@ -95,10 +96,10 @@ final class MultiLogger(delegates: List[Logger]) extends BasicLogger
super.setLevel(newLevel)
dispatch(new SetLevel(newLevel))
}
override def enableTrace(enabled: Boolean)
override def setTrace(level: Int)
{
super.enableTrace(enabled)
dispatch(new SetTrace(enabled))
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)) }
@ -119,6 +120,8 @@ final class FilterLogger(delegate: Logger) extends BasicLogger
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))
@ -216,12 +219,12 @@ final class BufferedLogger(delegate: Logger) extends Logger
delegate.setLevel(newLevel)
}
def getLevel = synchronized { delegate.getLevel }
def traceEnabled = synchronized { delegate.traceEnabled }
def enableTrace(flag: Boolean): Unit =
def getTrace = synchronized { delegate.getTrace }
def setTrace(level: Int): Unit =
synchronized
{
buffer.foreach{_ += new SetTrace(flag) }
delegate.enableTrace(flag)
buffer.foreach{_ += new SetTrace(level) }
delegate.setTrace(level)
}
def trace(t: => Throwable): Unit =
@ -295,8 +298,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)
{

View File

@ -29,6 +29,7 @@ object Main
val UsageErrorExitCode = 4
val BuildErrorExitCode = 5
val ProgramErrorExitCode = 6
val MaxInt = java.lang.Integer.MAX_VALUE
}
import Main._
@ -385,6 +386,8 @@ class xMain extends xsbti.AppMain
setProperty(currentProject, action.substring(SetAction.length + 1))
else if(action.startsWith(GetAction + " "))
getProperty(currentProject, action.substring(GetAction.length + 1))
else if(action.startsWith(TraceCommand + " "))
setTrace(currentProject, action.substring(TraceCommand.length + 1))
else
handleCommand(currentProject, action)
}
@ -432,6 +435,7 @@ class xMain extends xsbti.AppMain
case GetAction => getArgumentError(project.log)
case SetAction => setArgumentError(project.log)
case ProjectAction => setProjectError(project.log)
case TraceCommand => setTraceError(project.log); true
case ShowCurrent =>
printProject("Current project is ", project)
Console.println("Current Scala version is " + project.buildScalaVersion)
@ -439,7 +443,6 @@ class xMain extends xsbti.AppMain
printTraceEnabled(project)
true
case ShowActions => showActions(project); true
case TraceCommand => toggleTrace(project); true
case Level(level) => setLevel(project, level); true
case ContinuousCompileCommand => compileContinuously(project)
case action if action.startsWith(ContinuousExecutePrefix) => executeContinuously(project, action.substring(ContinuousExecutePrefix.length).trim)
@ -493,15 +496,21 @@ class xMain extends xsbti.AppMain
}
/** Toggles whether stack traces are enabled.*/
private def toggleTrace(project: Project)
private def setTrace(project: Project, value: String): Boolean =
{
val newValue = !project.log.traceEnabled
project.projectClosure.foreach(_.log.enableTrace(newValue))
printTraceEnabled(project)
try
{
val newValue = if(value == "on") MaxInt else if(value == "off") -1 else if(value == "nosbt") 0 else value.toInt
project.projectClosure.foreach(_.log.setTrace(newValue))
printTraceEnabled(project)
true
}
catch { case _: NumberFormatException => setTraceError(project.log) }
}
private def printTraceEnabled(project: Project)
{
Console.println("Stack traces are " + (if(project.log.traceEnabled) "enabled" else "disabled"))
def traceLevel(level: Int) = if(level == 0) " (no stack elements)" else if(level == MaxInt) "" else " (maximum " + level + " stack elements per exception)"
Console.println("Stack traces are " + (if(project.log.traceEnabled) "enabled" + traceLevel(project.log.getTrace) else "disabled"))
}
/** Sets the logging level on the given project.*/
private def setLevel(project: Project, level: Level.Value)
@ -688,6 +697,7 @@ class xMain extends xsbti.AppMain
}
private def isTerminateAction(s: String) = TerminateActions.elements.contains(s.toLowerCase)
private def setTraceError(log: Logger) = logError(log)("Invalid arguments for 'trace': expected 'on', 'off', or integer number of stack elements to show per exception.")
private def setArgumentError(log: Logger) = logError(log)("Invalid arguments for 'set': expected property name and new value.")
private def getArgumentError(log: Logger) = logError(log)("Invalid arguments for 'get': expected property name.")
private def setProjectError(log: Logger) = logError(log)("Invalid arguments for 'project': expected project name.")

View File

@ -311,7 +311,6 @@ object Project
{
val log = new ConsoleLogger
log.setLevel(Level.Debug)
log.enableTrace(true)
log
}

View File

@ -82,7 +82,6 @@ class Resources(val baseDirectory: File, additional: ClassLoader, app: AppProvid
{
val buffered = new BufferedLogger(log)
buffered.setLevel(Level.Debug)
buffered.enableTrace(true)
def error(msg: String) =
{
buffered.stopAll()

View File

@ -0,0 +1,63 @@
/* sbt -- Simple Build Tool
* Copyright 2010 Tony Sloane
*/
package sbt
object StackTrace
{
def isSbtClass(name: String) = name.startsWith("sbt") || name.startsWith("xsbt")
/**
* Return a printable representation of the stack trace associated
* with t. Information about t and its Throwable causes is included.
* The number of lines to be included for each Throwable is configured
* via d which should be greater than or equal to zero. If d is zero,
* then all elements are included up to (but not including) the first
* element that comes from sbt. If d is greater than zero, then up to
* that many lines are included, where the line for the Throwable is
* counted plus one line for each stack element. Less lines will be
* included if there are not enough stack elements.
*/
def trimmed(t : Throwable, d : Int) : String = {
require(d >= 0)
val b = new StringBuilder ()
def appendStackTrace (t : Throwable, first : Boolean) {
val include : StackTraceElement => Boolean =
if (d == 0)
element => !isSbtClass(element.getClassName)
else {
var count = d - 1
(_ => { count -= 1; count >= 0 })
}
def appendElement (e : StackTraceElement) {
b.append ("\tat ")
b.append (e)
b.append ('\n')
}
if (!first)
b.append ("Caused by: ")
b.append (t)
b.append ('\n')
val els = t.getStackTrace ()
var i = 0
while ((i < els.size) && include (els (i))) {
appendElement (els (i))
i += 1
}
}
appendStackTrace (t, true)
var c = t
while (c.getCause () != null) {
c = c.getCause ()
appendStackTrace (c, false)
}
b.toString ()
}
}

View File

@ -1,5 +1,5 @@
/* sbt -- Simple Build Tool
* Copyright 2008 Mark Harrah
* Copyright 2008, 2009, 2010 Mark Harrah
*/
package sbt
@ -82,9 +82,6 @@ private trait JettyRun
}
sealed trait JettyConfiguration extends NotNull
{
def war: Path
def scanDirectories: Seq[File]
def scanInterval: Int
/** The classpath to get Jetty from. */
def jettyClasspath: PathFinder
def classpathName: String
@ -92,6 +89,10 @@ sealed trait JettyConfiguration extends NotNull
}
trait DefaultJettyConfiguration extends JettyConfiguration
{
def war: Path
def scanDirectories: Seq[File]
def scanInterval: Int
def contextPath: String
def port: Int
/** The classpath containing the classes, jars, and resources for the web application. */
@ -129,7 +130,27 @@ private object LazyJettyRun extends JettyRun
Log.setLog(new JettyLogger(configuration.log))
val server = new Server
val listener =
def configureScanner(listener: Scanner.BulkListener, scanDirectories: Seq[File], scanInterval: Int) =
{
if(scanDirectories.isEmpty)
None
else
{
configuration.log.debug("Scanning for changes to: " + scanDirectories.mkString(", "))
val scanner = new Scanner
val list = new java.util.ArrayList[File]
scanDirectories.foreach(x => list.add(x))
scanner.setScanDirs(list)
scanner.setRecursive(true)
scanner.setScanInterval(scanInterval)
scanner.setReportExistingFilesOnStartup(false)
scanner.addListener(listener)
scanner.start()
Some(new WeakReference(scanner))
}
}
val (listener, scanner) =
configuration match
{
case c: DefaultJettyConfiguration =>
@ -144,43 +165,23 @@ private object LazyJettyRun extends JettyRun
setLoader()
server.setHandler(webapp)
Some(new Scanner.BulkListener with Reload {
val listener = new Scanner.BulkListener with Reload {
def reloadApp() = reload(server, setLoader(), log)
def filesChanged(files: java.util.List[_]) { reloadApp() }
})
}
(Some(listener), configureScanner(listener, c.scanDirectories, c.scanInterval))
case c: CustomJettyConfiguration =>
for(x <- c.jettyConfigurationXML)
(new XmlConfiguration(x.toString)).configure(server)
for(file <- c.jettyConfigurationFiles)
(new XmlConfiguration(file.toURI.toURL)).configure(server)
None
(None, None)
}
def configureScanner() =
{
val scanDirectories = configuration.scanDirectories
if(listener.isEmpty || scanDirectories.isEmpty)
None
else
{
configuration.log.debug("Scanning for changes to: " + scanDirectories.mkString(", "))
val scanner = new Scanner
val list = new java.util.ArrayList[File]
scanDirectories.foreach(x => list.add(x))
scanner.setScanDirs(list)
scanner.setRecursive(true)
scanner.setScanInterval(configuration.scanInterval)
scanner.setReportExistingFilesOnStartup(false)
scanner.addListener(listener.get)
scanner.start()
Some(new WeakReference(scanner))
}
}
try
{
server.start()
new StopServer(new WeakReference(server), listener.map(new WeakReference(_)), configureScanner(), oldLog)
new StopServer(new WeakReference(server), listener.map(new WeakReference(_)), scanner, oldLog)
}
catch { case e => server.stop(); throw e }
}

View File

@ -0,0 +1,3 @@
import sbt._
class BlankProject(info: ProjectInfo) extends DefaultProject(info)