mirror of https://github.com/sbt/sbt.git
commit
b6cad50327
|
|
@ -99,12 +99,12 @@ lazy val utilComplete = (project in internalPath / "util-complete").
|
||||||
// logging
|
// logging
|
||||||
lazy val utilLogging = (project in internalPath / "util-logging").
|
lazy val utilLogging = (project in internalPath / "util-logging").
|
||||||
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
|
enablePlugins(ContrabandPlugin, JsonCodecPlugin).
|
||||||
dependsOn(utilInterface, utilTesting % Test).
|
dependsOn(utilInterface, utilCollection, utilTesting % Test).
|
||||||
settings(
|
settings(
|
||||||
commonSettings,
|
commonSettings,
|
||||||
crossScalaVersions := Seq(scala210, scala211, scala212),
|
crossScalaVersions := Seq(scala210, scala211, scala212),
|
||||||
name := "Util Logging",
|
name := "Util Logging",
|
||||||
libraryDependencies ++= Seq(jline, log4jApi, log4jCore, disruptor, sjsonnewScalaJson),
|
libraryDependencies ++= Seq(jline, log4jApi, log4jCore, disruptor, sjsonnewScalaJson, scalaReflect.value),
|
||||||
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala"
|
sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ abstract class EvaluateSettings[Scope] {
|
||||||
keyString
|
keyString
|
||||||
|
|
||||||
private[this] def keyString =
|
private[this] def keyString =
|
||||||
(static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey(key) :: Nil else Nil }).headOption getOrElse "non-static"
|
(static.toSeq.flatMap { case (key, value) => if (value eq this) init.showFullKey.show(key) :: Nil else Nil }).headOption getOrElse "non-static"
|
||||||
|
|
||||||
final def get: T = synchronized {
|
final def get: T = synchronized {
|
||||||
assert(value != null, toString + " not evaluated")
|
assert(value != null, toString + " not evaluated")
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ package sbt.internal.util
|
||||||
import scala.language.existentials
|
import scala.language.existentials
|
||||||
|
|
||||||
import Types._
|
import Types._
|
||||||
|
import sbt.util.Show
|
||||||
|
|
||||||
sealed trait Settings[Scope] {
|
sealed trait Settings[Scope] {
|
||||||
def data: Map[Scope, AttributeMap]
|
def data: Map[Scope, AttributeMap]
|
||||||
|
|
@ -119,7 +120,7 @@ trait Init[Scope] {
|
||||||
def mapScope(f: Scope => Scope): MapScoped = new MapScoped {
|
def mapScope(f: Scope => Scope): MapScoped = new MapScoped {
|
||||||
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
|
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
|
||||||
}
|
}
|
||||||
private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey(key))
|
private final class InvalidReference(val key: ScopedKey[_]) extends RuntimeException("Internal settings error: invalid reference to " + showFullKey.show(key))
|
||||||
|
|
||||||
private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] =
|
||||||
{
|
{
|
||||||
|
|
@ -215,11 +216,11 @@ trait Init[Scope] {
|
||||||
{
|
{
|
||||||
val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey)
|
val guessed = guessIntendedScope(validKeys, delegates, u.referencedKey)
|
||||||
val derived = u.defining.isDerived
|
val derived = u.defining.isDerived
|
||||||
val refString = display(u.defining.key)
|
val refString = display.show(u.defining.key)
|
||||||
val sourceString = if (derived) "" else parenPosString(u.defining)
|
val sourceString = if (derived) "" else parenPosString(u.defining)
|
||||||
val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display(g) + " ?").toList.mkString
|
val guessedString = if (derived) "" else guessed.map(g => "\n Did you mean " + display.show(g) + " ?").toList.mkString
|
||||||
val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else ""
|
val derivedString = if (derived) ", which is a derived setting that needs this key to be defined in this scope." else ""
|
||||||
display(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString
|
display.show(u.referencedKey) + " from " + refString + sourceString + derivedString + guessedString
|
||||||
}
|
}
|
||||||
private[this] def parenPosString(s: Setting[_]): String =
|
private[this] def parenPosString(s: Setting[_]): String =
|
||||||
s.positionString match { case None => ""; case Some(s) => " (" + s + ")" }
|
s.positionString match { case None => ""; case Some(s) => " (" + s + ")" }
|
||||||
|
|
@ -255,7 +256,7 @@ trait Init[Scope] {
|
||||||
new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ")
|
new Uninitialized(keys, prefix + suffix + " to undefined setting" + suffix + ": " + keysString + "\n ")
|
||||||
}
|
}
|
||||||
final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) {
|
final class Compiled[T](val key: ScopedKey[T], val dependencies: Iterable[ScopedKey[_]], val settings: Seq[Setting[T]]) {
|
||||||
override def toString = showFullKey(key)
|
override def toString = showFullKey.show(key)
|
||||||
}
|
}
|
||||||
final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]])
|
final class Flattened(val key: ScopedKey[_], val dependencies: Iterable[ScopedKey[_]])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package sbt.internal.util
|
|
||||||
|
|
||||||
trait Show[T] {
|
|
||||||
def apply(t: T): String
|
|
||||||
}
|
|
||||||
object Show {
|
|
||||||
def apply[T](f: T => String): Show[T] = new Show[T] { def apply(t: T): String = f(t) }
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package sbt.util
|
||||||
|
|
||||||
|
trait Show[A] {
|
||||||
|
def show(a: A): String
|
||||||
|
}
|
||||||
|
object Show {
|
||||||
|
def apply[A](f: A => String): Show[A] = new Show[A] { def show(a: A): String = f(a) }
|
||||||
|
|
||||||
|
def fromToString[A]: Show[A] = new Show[A] {
|
||||||
|
def show(a: A): String = a.toString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package sbt.internal.util
|
package sbt.util
|
||||||
|
|
||||||
trait ShowLines[A] {
|
trait ShowLines[A] {
|
||||||
def showLines(a: A): Seq[String]
|
def showLines(a: A): Seq[String]
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package sbt.internal.util
|
package sbt.internal.util
|
||||||
|
|
||||||
|
import sbt.util.Show
|
||||||
|
|
||||||
/** Define our settings system */
|
/** Define our settings system */
|
||||||
|
|
||||||
// A basic scope indexed by an integer.
|
// A basic scope indexed by an integer.
|
||||||
|
|
@ -12,9 +14,10 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0)
|
||||||
// That would be a general pain.)
|
// That would be a general pain.)
|
||||||
case class SettingsExample() extends Init[Scope] {
|
case class SettingsExample() extends Init[Scope] {
|
||||||
// Provides a way of showing a Scope+AttributeKey[_]
|
// Provides a way of showing a Scope+AttributeKey[_]
|
||||||
val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
|
val showFullKey: Show[ScopedKey[_]] = Show[ScopedKey[_]]((key: ScopedKey[_]) =>
|
||||||
def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
{
|
||||||
}
|
s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
||||||
|
})
|
||||||
|
|
||||||
// A sample delegation function that delegates to a Scope with a lower index.
|
// A sample delegation function that delegates to a Scope with a lower index.
|
||||||
val delegates: Scope => Seq[Scope] = {
|
val delegates: Scope => Seq[Scope] = {
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,19 @@
|
||||||
*/
|
*/
|
||||||
package xsbti;
|
package xsbti;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface Position
|
public interface Position
|
||||||
{
|
{
|
||||||
Maybe<Integer> line();
|
Optional<Integer> line();
|
||||||
String lineContent();
|
String lineContent();
|
||||||
Maybe<Integer> offset();
|
Optional<Integer> offset();
|
||||||
|
|
||||||
// pointer to the column position of the error/warning
|
// pointer to the column position of the error/warning
|
||||||
Maybe<Integer> pointer();
|
Optional<Integer> pointer();
|
||||||
Maybe<String> pointerSpace();
|
Optional<String> pointerSpace();
|
||||||
|
|
||||||
Maybe<String> sourcePath();
|
Optional<String> sourcePath();
|
||||||
Maybe<java.io.File> sourceFile();
|
Optional<File> sourceFile();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This code is generated using sbt-datatype.
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
* This code is generated using sbt-datatype.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// DO NOT EDIT MANUALLY
|
|
||||||
package sbt.internal.util
|
|
||||||
final class ChannelLogEntry private (
|
|
||||||
val level: String,
|
|
||||||
val message: String,
|
|
||||||
channelName: Option[String],
|
|
||||||
execId: Option[String]) extends sbt.internal.util.AbstractEntry(channelName, execId) with Serializable {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override def equals(o: Any): Boolean = o match {
|
|
||||||
case x: ChannelLogEntry => (this.level == x.level) && (this.message == x.message) && (this.channelName == x.channelName) && (this.execId == x.execId)
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
override def hashCode: Int = {
|
|
||||||
37 * (37 * (37 * (37 * (17 + level.##) + message.##) + channelName.##) + execId.##)
|
|
||||||
}
|
|
||||||
override def toString: String = {
|
|
||||||
"ChannelLogEntry(" + level + ", " + message + ", " + channelName + ", " + execId + ")"
|
|
||||||
}
|
|
||||||
protected[this] def copy(level: String = level, message: String = message, channelName: Option[String] = channelName, execId: Option[String] = execId): ChannelLogEntry = {
|
|
||||||
new ChannelLogEntry(level, message, channelName, execId)
|
|
||||||
}
|
|
||||||
def withLevel(level: String): ChannelLogEntry = {
|
|
||||||
copy(level = level)
|
|
||||||
}
|
|
||||||
def withMessage(message: String): ChannelLogEntry = {
|
|
||||||
copy(message = message)
|
|
||||||
}
|
|
||||||
def withChannelName(channelName: Option[String]): ChannelLogEntry = {
|
|
||||||
copy(channelName = channelName)
|
|
||||||
}
|
|
||||||
def withChannelName(channelName: String): ChannelLogEntry = {
|
|
||||||
copy(channelName = Option(channelName))
|
|
||||||
}
|
|
||||||
def withExecId(execId: Option[String]): ChannelLogEntry = {
|
|
||||||
copy(execId = execId)
|
|
||||||
}
|
|
||||||
def withExecId(execId: String): ChannelLogEntry = {
|
|
||||||
copy(execId = Option(execId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
object ChannelLogEntry {
|
|
||||||
|
|
||||||
def apply(level: String, message: String, channelName: Option[String], execId: Option[String]): ChannelLogEntry = new ChannelLogEntry(level, message, channelName, execId)
|
|
||||||
def apply(level: String, message: String, channelName: String, execId: String): ChannelLogEntry = new ChannelLogEntry(level, message, Option(channelName), Option(execId))
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
// DO NOT EDIT MANUALLY
|
||||||
|
package sbt.internal.util
|
||||||
|
final class StringEvent private (
|
||||||
|
val level: String,
|
||||||
|
val message: String,
|
||||||
|
channelName: Option[String],
|
||||||
|
execId: Option[String]) extends sbt.internal.util.AbstractEntry(channelName, execId) with Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override def equals(o: Any): Boolean = o match {
|
||||||
|
case x: StringEvent => (this.level == x.level) && (this.message == x.message) && (this.channelName == x.channelName) && (this.execId == x.execId)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
override def hashCode: Int = {
|
||||||
|
37 * (37 * (37 * (37 * (17 + level.##) + message.##) + channelName.##) + execId.##)
|
||||||
|
}
|
||||||
|
override def toString: String = {
|
||||||
|
"StringEvent(" + level + ", " + message + ", " + channelName + ", " + execId + ")"
|
||||||
|
}
|
||||||
|
protected[this] def copy(level: String = level, message: String = message, channelName: Option[String] = channelName, execId: Option[String] = execId): StringEvent = {
|
||||||
|
new StringEvent(level, message, channelName, execId)
|
||||||
|
}
|
||||||
|
def withLevel(level: String): StringEvent = {
|
||||||
|
copy(level = level)
|
||||||
|
}
|
||||||
|
def withMessage(message: String): StringEvent = {
|
||||||
|
copy(message = message)
|
||||||
|
}
|
||||||
|
def withChannelName(channelName: Option[String]): StringEvent = {
|
||||||
|
copy(channelName = channelName)
|
||||||
|
}
|
||||||
|
def withChannelName(channelName: String): StringEvent = {
|
||||||
|
copy(channelName = Option(channelName))
|
||||||
|
}
|
||||||
|
def withExecId(execId: Option[String]): StringEvent = {
|
||||||
|
copy(execId = execId)
|
||||||
|
}
|
||||||
|
def withExecId(execId: String): StringEvent = {
|
||||||
|
copy(execId = Option(execId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object StringEvent {
|
||||||
|
|
||||||
|
def apply(level: String, message: String, channelName: Option[String], execId: Option[String]): StringEvent = new StringEvent(level, message, channelName, execId)
|
||||||
|
def apply(level: String, message: String, channelName: String, execId: String): StringEvent = new StringEvent(level, message, Option(channelName), Option(execId))
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
/**
|
/**
|
||||||
* This code is generated using sbt-datatype.
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
package sbt.internal.util.codec
|
package sbt.internal.util.codec
|
||||||
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
|
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
|
||||||
trait AbstractEntryFormats { self: sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.ChannelLogEntryFormats =>
|
trait AbstractEntryFormats { self: sjsonnew.BasicJsonProtocol with sbt.internal.util.codec.StringEventFormats =>
|
||||||
implicit lazy val AbstractEntryFormat: JsonFormat[sbt.internal.util.AbstractEntry] = flatUnionFormat1[sbt.internal.util.AbstractEntry, sbt.internal.util.ChannelLogEntry]("type")
|
implicit lazy val AbstractEntryFormat: JsonFormat[sbt.internal.util.AbstractEntry] = flatUnionFormat1[sbt.internal.util.AbstractEntry, sbt.internal.util.StringEvent]("type")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
/**
|
/**
|
||||||
* This code is generated using sbt-datatype.
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
package sbt.internal.util.codec
|
package sbt.internal.util.codec
|
||||||
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||||
with sbt.internal.util.codec.ChannelLogEntryFormats
|
with sbt.internal.util.codec.StringEventFormats
|
||||||
with sbt.internal.util.codec.AbstractEntryFormats
|
with sbt.internal.util.codec.AbstractEntryFormats
|
||||||
object JsonProtocol extends JsonProtocol
|
object JsonProtocol extends JsonProtocol
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
/**
|
/**
|
||||||
* This code is generated using sbt-datatype.
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// DO NOT EDIT MANUALLY
|
// DO NOT EDIT MANUALLY
|
||||||
package sbt.internal.util.codec
|
package sbt.internal.util.codec
|
||||||
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
|
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
|
||||||
trait ChannelLogEntryFormats { self: sjsonnew.BasicJsonProtocol =>
|
trait StringEventFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||||
implicit lazy val ChannelLogEntryFormat: JsonFormat[sbt.internal.util.ChannelLogEntry] = new JsonFormat[sbt.internal.util.ChannelLogEntry] {
|
implicit lazy val StringEventFormat: JsonFormat[sbt.internal.util.StringEvent] = new JsonFormat[sbt.internal.util.StringEvent] {
|
||||||
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.ChannelLogEntry = {
|
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.util.StringEvent = {
|
||||||
jsOpt match {
|
jsOpt match {
|
||||||
case Some(js) =>
|
case Some(js) =>
|
||||||
unbuilder.beginObject(js)
|
unbuilder.beginObject(js)
|
||||||
|
|
@ -16,12 +16,12 @@ implicit lazy val ChannelLogEntryFormat: JsonFormat[sbt.internal.util.ChannelLog
|
||||||
val channelName = unbuilder.readField[Option[String]]("channelName")
|
val channelName = unbuilder.readField[Option[String]]("channelName")
|
||||||
val execId = unbuilder.readField[Option[String]]("execId")
|
val execId = unbuilder.readField[Option[String]]("execId")
|
||||||
unbuilder.endObject()
|
unbuilder.endObject()
|
||||||
sbt.internal.util.ChannelLogEntry(level, message, channelName, execId)
|
sbt.internal.util.StringEvent(level, message, channelName, execId)
|
||||||
case None =>
|
case None =>
|
||||||
deserializationError("Expected JsObject but found None")
|
deserializationError("Expected JsObject but found None")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override def write[J](obj: sbt.internal.util.ChannelLogEntry, builder: Builder[J]): Unit = {
|
override def write[J](obj: sbt.internal.util.StringEvent, builder: Builder[J]): Unit = {
|
||||||
builder.beginObject()
|
builder.beginObject()
|
||||||
builder.addField("level", obj.level)
|
builder.addField("level", obj.level)
|
||||||
builder.addField("message", obj.message)
|
builder.addField("message", obj.message)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package sbt.internal.util
|
||||||
|
@target(Java)
|
||||||
|
@codecPackage("sbt.internal.util.codec")
|
||||||
|
@fullCodec("JsonProtocol")
|
||||||
|
|
||||||
|
enum Severity
|
||||||
|
{
|
||||||
|
Info, Warn, Error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Position {
|
||||||
|
line: Int
|
||||||
|
lineContent: String!
|
||||||
|
offset: Int
|
||||||
|
pointer: Int
|
||||||
|
pointerSpace: String
|
||||||
|
sourcePath: String
|
||||||
|
sourceFile: java.io.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type Problem {
|
||||||
|
category: String!
|
||||||
|
severity: Severity!
|
||||||
|
message: String!
|
||||||
|
position: Position!
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ interface AbstractEntry {
|
||||||
execId: String
|
execId: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelLogEntry implements sbt.internal.util.AbstractEntry {
|
type StringEvent implements sbt.internal.util.AbstractEntry {
|
||||||
level: String!
|
level: String!
|
||||||
message: String!
|
message: String!
|
||||||
channelName: String
|
channelName: String
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,76 @@ package sbt.internal.util
|
||||||
|
|
||||||
import sbt.util._
|
import sbt.util._
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent, Appender }
|
||||||
|
import org.apache.logging.log4j.core.appender.AbstractAppender
|
||||||
|
import org.apache.logging.log4j.core.layout.PatternLayout
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
object BufferedAppender {
|
||||||
|
def generateName: String =
|
||||||
|
"buffered-" + generateId.incrementAndGet
|
||||||
|
private val generateId: AtomicInteger = new AtomicInteger
|
||||||
|
def apply(delegate: Appender): BufferedAppender =
|
||||||
|
apply(generateName, delegate)
|
||||||
|
def apply(name: String, delegate: Appender): BufferedAppender =
|
||||||
|
{
|
||||||
|
val appender = new BufferedAppender(name, delegate)
|
||||||
|
appender.start
|
||||||
|
appender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Am appender that can buffer the logging done on it and then can flush the buffer
|
||||||
|
* to the delegate appender provided in the constructor. Use 'record()' to
|
||||||
|
* start buffering and then 'play' to flush the buffer to the backing appender.
|
||||||
|
* The logging level set at the time a message is originally logged is used, not
|
||||||
|
* the level at the time 'play' is called.
|
||||||
|
*/
|
||||||
|
class BufferedAppender private[BufferedAppender] (name: String, delegate: Appender) extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) {
|
||||||
|
private[this] val buffer = new ListBuffer[XLogEvent]
|
||||||
|
private[this] var recording = false
|
||||||
|
|
||||||
|
def append(event: XLogEvent): Unit =
|
||||||
|
{
|
||||||
|
if (recording) {
|
||||||
|
buffer += event
|
||||||
|
} else delegate.append(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enables buffering. */
|
||||||
|
def record() = synchronized { recording = true }
|
||||||
|
def buffer[T](f: => T): T = {
|
||||||
|
record()
|
||||||
|
try { f }
|
||||||
|
finally { stopQuietly() }
|
||||||
|
}
|
||||||
|
def bufferQuietly[T](f: => T): T = {
|
||||||
|
record()
|
||||||
|
try {
|
||||||
|
val result = f
|
||||||
|
clearBuffer()
|
||||||
|
result
|
||||||
|
} catch { case e: Throwable => stopQuietly(); throw e }
|
||||||
|
}
|
||||||
|
def stopQuietly() = synchronized { try { stopBuffer() } catch { case e: Exception => () } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the buffer to the delegate logger. This method calls logAll on the delegate
|
||||||
|
* so that the messages are written consecutively. The buffer is cleared in the process.
|
||||||
|
*/
|
||||||
|
def play(): Unit =
|
||||||
|
synchronized {
|
||||||
|
buffer.toList foreach {
|
||||||
|
delegate.append
|
||||||
|
}
|
||||||
|
buffer.clear()
|
||||||
|
}
|
||||||
|
/** Clears buffered events and disables buffering. */
|
||||||
|
def clearBuffer(): Unit = synchronized { buffer.clear(); recording = false }
|
||||||
|
/** Plays buffered events and disables buffering. */
|
||||||
|
def stopBuffer(): Unit = synchronized { play(); clearBuffer() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logger that can buffer the logging done on it and then can flush the buffer
|
* A logger that can buffer the logging done on it and then can flush the buffer
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ import java.io.{ PrintStream, PrintWriter }
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import org.apache.logging.log4j.{ Level => XLevel }
|
import org.apache.logging.log4j.{ Level => XLevel }
|
||||||
import org.apache.logging.log4j.message.{ Message, ParameterizedMessage, ObjectMessage, ReusableObjectMessage }
|
import org.apache.logging.log4j.message.{ Message, ObjectMessage, ReusableObjectMessage }
|
||||||
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent }
|
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent }
|
||||||
import org.apache.logging.log4j.core.appender.AbstractAppender
|
import org.apache.logging.log4j.core.appender.AbstractAppender
|
||||||
import org.apache.logging.log4j.core.layout.PatternLayout
|
import org.apache.logging.log4j.core.layout.PatternLayout
|
||||||
import org.apache.logging.log4j.core.async.RingBufferLogEvent
|
|
||||||
|
|
||||||
import ConsoleAppender._
|
import ConsoleAppender._
|
||||||
|
|
||||||
|
|
@ -149,7 +148,7 @@ object ConsoleAppender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val formatEnabled =
|
val formatEnabled: Boolean =
|
||||||
{
|
{
|
||||||
import java.lang.Boolean.{ getBoolean, parseBoolean }
|
import java.lang.Boolean.{ getBoolean, parseBoolean }
|
||||||
val value = System.getProperty("sbt.log.format")
|
val value = System.getProperty("sbt.log.format")
|
||||||
|
|
@ -180,8 +179,16 @@ object ConsoleAppender {
|
||||||
|
|
||||||
def apply(out: PrintStream): ConsoleAppender = apply(generateName, ConsoleOut.printStreamOut(out))
|
def apply(out: PrintStream): ConsoleAppender = apply(generateName, ConsoleOut.printStreamOut(out))
|
||||||
def apply(out: PrintWriter): ConsoleAppender = apply(generateName, ConsoleOut.printWriterOut(out))
|
def apply(out: PrintWriter): ConsoleAppender = apply(generateName, ConsoleOut.printWriterOut(out))
|
||||||
def apply(name: String = generateName, out: ConsoleOut = ConsoleOut.systemOut, ansiCodesSupported: Boolean = formatEnabled,
|
def apply(): ConsoleAppender = apply(generateName, ConsoleOut.systemOut)
|
||||||
useColor: Boolean = formatEnabled, suppressedMessage: SuppressedTraceContext => Option[String] = noSuppressedMessage): ConsoleAppender =
|
def apply(name: String): ConsoleAppender = apply(name, ConsoleOut.systemOut, formatEnabled, formatEnabled, noSuppressedMessage)
|
||||||
|
def apply(out: ConsoleOut): ConsoleAppender = apply(generateName, out, formatEnabled, formatEnabled, noSuppressedMessage)
|
||||||
|
def apply(name: String, out: ConsoleOut): ConsoleAppender = apply(name, out, formatEnabled, formatEnabled, noSuppressedMessage)
|
||||||
|
def apply(name: String, out: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): ConsoleAppender =
|
||||||
|
apply(name, out, formatEnabled, formatEnabled, suppressedMessage)
|
||||||
|
def apply(name: String, out: ConsoleOut, useColor: Boolean): ConsoleAppender =
|
||||||
|
apply(name, out, formatEnabled, useColor, noSuppressedMessage)
|
||||||
|
def apply(name: String, out: ConsoleOut, ansiCodesSupported: Boolean,
|
||||||
|
useColor: Boolean, suppressedMessage: SuppressedTraceContext => Option[String]): ConsoleAppender =
|
||||||
{
|
{
|
||||||
val appender = new ConsoleAppender(name, out, ansiCodesSupported, useColor, suppressedMessage)
|
val appender = new ConsoleAppender(name, out, ansiCodesSupported, useColor, suppressedMessage)
|
||||||
appender.start
|
appender.start
|
||||||
|
|
@ -238,24 +245,30 @@ class ConsoleAppender private[ConsoleAppender] (
|
||||||
{
|
{
|
||||||
val level = ConsoleAppender.toLevel(event.getLevel)
|
val level = ConsoleAppender.toLevel(event.getLevel)
|
||||||
val message = event.getMessage
|
val message = event.getMessage
|
||||||
val str = messageToString(message)
|
// val str = messageToString(message)
|
||||||
appendLog(level, str)
|
appendMessage(level, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
def messageToString(msg: Message): String =
|
def appendMessage(level: Level.Value, msg: Message): Unit =
|
||||||
msg match {
|
msg match {
|
||||||
case p: ParameterizedMessage => p.getFormattedMessage
|
case o: ObjectMessage => objectToLines(o.getParameter) foreach { appendLog(level, _) }
|
||||||
case r: RingBufferLogEvent => r.getFormattedMessage
|
case o: ReusableObjectMessage => objectToLines(o.getParameter) foreach { appendLog(level, _) }
|
||||||
case o: ObjectMessage => objectToString(o.getParameter)
|
case _ => appendLog(level, msg.getFormattedMessage)
|
||||||
case o: ReusableObjectMessage => objectToString(o.getParameter)
|
|
||||||
case _ => msg.getFormattedMessage
|
|
||||||
}
|
}
|
||||||
def objectToString(o: AnyRef): String =
|
def objectToLines(o: AnyRef): Vector[String] =
|
||||||
o match {
|
o match {
|
||||||
case x: ChannelLogEntry => x.message
|
case x: StringEvent => Vector(x.message)
|
||||||
case _ => o.toString
|
case x: ObjectEvent[_] => objectEventToLines(x)
|
||||||
|
case _ => Vector(o.toString)
|
||||||
|
}
|
||||||
|
def objectEventToLines(oe: ObjectEvent[_]): Vector[String] =
|
||||||
|
{
|
||||||
|
val tag = oe.tag
|
||||||
|
LogExchange.stringCodec[AnyRef](tag) match {
|
||||||
|
case Some(codec) => codec.showLines(oe.message.asInstanceOf[AnyRef]).toVector
|
||||||
|
case _ => Vector(oe.message.toString)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def messageColor(level: Level.Value) = RESET
|
def messageColor(level: Level.Value) = RESET
|
||||||
def labelColor(level: Level.Value) =
|
def labelColor(level: Level.Value) =
|
||||||
level match {
|
level match {
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,15 @@ object MainAppender {
|
||||||
def defaultScreen(name: String, console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): Appender =
|
def defaultScreen(name: String, console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String]): Appender =
|
||||||
ConsoleAppender(name, console, suppressedMessage = suppressedMessage)
|
ConsoleAppender(name, console, suppressedMessage = suppressedMessage)
|
||||||
|
|
||||||
def defaultBacked(
|
def defaultBacked: PrintWriter => Appender = defaultBacked(generateGlobalBackingName, ConsoleAppender.formatEnabled)
|
||||||
loggerName: String = generateGlobalBackingName,
|
def defaultBacked(loggerName: String): PrintWriter => Appender = defaultBacked(loggerName, ConsoleAppender.formatEnabled)
|
||||||
useColor: Boolean = ConsoleAppender.formatEnabled
|
def defaultBacked(useColor: Boolean): PrintWriter => Appender = defaultBacked(generateGlobalBackingName, useColor)
|
||||||
): PrintWriter => Appender =
|
def defaultBacked(loggerName: String, useColor: Boolean): PrintWriter => Appender =
|
||||||
to => {
|
to => {
|
||||||
ConsoleAppender(
|
ConsoleAppender(
|
||||||
ConsoleAppender.generateName,
|
ConsoleAppender.generateName,
|
||||||
ConsoleOut.printWriterOut(to), useColor = useColor
|
ConsoleOut.printWriterOut(to),
|
||||||
|
useColor = useColor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package sbt.internal.util
|
||||||
import sbt.util._
|
import sbt.util._
|
||||||
import org.apache.logging.log4j.{ Logger => XLogger }
|
import org.apache.logging.log4j.{ Logger => XLogger }
|
||||||
import org.apache.logging.log4j.message.ObjectMessage
|
import org.apache.logging.log4j.message.ObjectMessage
|
||||||
|
import sjsonnew.JsonFormat
|
||||||
|
import scala.reflect.runtime.universe.TypeTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates log events to the associated LogExchange.
|
* Delegates log events to the associated LogExchange.
|
||||||
|
|
@ -18,8 +20,32 @@ class ManagedLogger(
|
||||||
{
|
{
|
||||||
xlogger.log(
|
xlogger.log(
|
||||||
ConsoleAppender.toXLevel(level),
|
ConsoleAppender.toXLevel(level),
|
||||||
new ObjectMessage(ChannelLogEntry(level.toString, message, channelName, execId))
|
new ObjectMessage(StringEvent(level.toString, message, channelName, execId))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
override def success(message: => String): Unit = xlogger.info(message)
|
override def success(message: => String): Unit = xlogger.info(message)
|
||||||
|
|
||||||
|
def registerStringCodec[A: ShowLines: TypeTag]: Unit =
|
||||||
|
{
|
||||||
|
val tag = StringTypeTag[A]
|
||||||
|
val ev = implicitly[ShowLines[A]]
|
||||||
|
// println(s"registerStringCodec ${tag.key}")
|
||||||
|
val _ = LogExchange.getOrElseUpdateStringCodec(tag.key, ev)
|
||||||
|
}
|
||||||
|
final def debugEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Debug, event)
|
||||||
|
final def infoEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Info, event)
|
||||||
|
final def warnEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Warn, event)
|
||||||
|
final def errorEvent[A: JsonFormat: TypeTag](event: => A): Unit = logEvent(Level.Error, event)
|
||||||
|
def logEvent[A: JsonFormat: TypeTag](level: Level.Value, event: => A): Unit =
|
||||||
|
{
|
||||||
|
val v: A = event
|
||||||
|
val tag = StringTypeTag[A]
|
||||||
|
LogExchange.getOrElseUpdateJsonCodec(tag.key, implicitly[JsonFormat[A]])
|
||||||
|
// println("logEvent " + tag.key)
|
||||||
|
val entry: ObjectEvent[A] = new ObjectEvent(level, v, channelName, execId, tag.key)
|
||||||
|
xlogger.log(
|
||||||
|
ConsoleAppender.toXLevel(level),
|
||||||
|
new ObjectMessage(entry)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package sbt
|
||||||
|
package internal
|
||||||
|
package util
|
||||||
|
|
||||||
|
import sbt.util.Level
|
||||||
|
import sjsonnew.JsonFormat
|
||||||
|
|
||||||
|
final class ObjectEvent[A](
|
||||||
|
val level: Level.Value,
|
||||||
|
val message: A,
|
||||||
|
val channelName: Option[String],
|
||||||
|
val execId: Option[String],
|
||||||
|
val tag: String
|
||||||
|
) extends Serializable {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package sbt.internal.util
|
||||||
|
|
||||||
|
import scala.reflect.runtime.universe._
|
||||||
|
|
||||||
|
/** This is used to carry type information in JSON. */
|
||||||
|
final case class StringTypeTag[A](key: String) {
|
||||||
|
override def toString: String = key
|
||||||
|
}
|
||||||
|
|
||||||
|
object StringTypeTag {
|
||||||
|
def apply[A: TypeTag]: StringTypeTag[A] =
|
||||||
|
{
|
||||||
|
val tag = implicitly[TypeTag[A]]
|
||||||
|
val tpe = tag.tpe
|
||||||
|
val k = typeToString(tpe)
|
||||||
|
// println(tpe.getClass.toString + " " + k)
|
||||||
|
StringTypeTag[A](k)
|
||||||
|
}
|
||||||
|
def typeToString(tpe: Type): String =
|
||||||
|
tpe match {
|
||||||
|
case ref: TypeRef =>
|
||||||
|
if (ref.args.nonEmpty) {
|
||||||
|
val typeCon = ref.typeConstructor.typeSymbol.asType.fullName
|
||||||
|
val typeArgs = ref.typeArgs map typeToString
|
||||||
|
s"""$typeCon[${typeArgs.mkString(",")}]"""
|
||||||
|
} else tpe.toString
|
||||||
|
case _ => tpe.toString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal.util.codec
|
||||||
|
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
|
||||||
|
import xsbti.Position
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
trait PositionFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val PositionFormat: JsonFormat[Position] = new JsonFormat[Position] {
|
||||||
|
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Position = {
|
||||||
|
jsOpt match {
|
||||||
|
case Some(js) =>
|
||||||
|
unbuilder.beginObject(js)
|
||||||
|
val line0 = unbuilder.readField[Optional[java.lang.Integer]]("line")
|
||||||
|
val lineContent0 = unbuilder.readField[String]("lineContent")
|
||||||
|
val offset0 = unbuilder.readField[Optional[java.lang.Integer]]("offset")
|
||||||
|
val pointer0 = unbuilder.readField[Optional[java.lang.Integer]]("pointer")
|
||||||
|
val pointerSpace0 = unbuilder.readField[Optional[String]]("pointerSpace")
|
||||||
|
val sourcePath0 = unbuilder.readField[Optional[String]]("sourcePath")
|
||||||
|
val sourceFile0 = unbuilder.readField[Optional[java.io.File]]("sourceFile")
|
||||||
|
unbuilder.endObject()
|
||||||
|
new Position() {
|
||||||
|
override val line = line0
|
||||||
|
override val lineContent = lineContent0
|
||||||
|
override val offset = offset0
|
||||||
|
override val pointer = pointer0
|
||||||
|
override val pointerSpace = pointerSpace0
|
||||||
|
override val sourcePath = sourcePath0
|
||||||
|
override val sourceFile = sourceFile0
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsObject but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: Position, builder: Builder[J]): Unit = {
|
||||||
|
builder.beginObject()
|
||||||
|
builder.addField("line", obj.line)
|
||||||
|
builder.addField("lineContent", obj.lineContent)
|
||||||
|
builder.addField("offset", obj.offset)
|
||||||
|
builder.addField("pointer", obj.pointer)
|
||||||
|
builder.addField("pointerSpace", obj.pointerSpace)
|
||||||
|
builder.addField("sourcePath", obj.sourcePath)
|
||||||
|
builder.addField("sourceFile", obj.sourceFile)
|
||||||
|
builder.endObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal.util.codec
|
||||||
|
|
||||||
|
import xsbti.{ Problem, Severity, Position }
|
||||||
|
import sbt.util.InterfaceUtil.problem
|
||||||
|
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
|
||||||
|
|
||||||
|
trait ProblemFormats { self: SeverityFormats with PositionFormats with sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val ProblemFormat: JsonFormat[Problem] = new JsonFormat[Problem] {
|
||||||
|
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Problem = {
|
||||||
|
jsOpt match {
|
||||||
|
case Some(js) =>
|
||||||
|
unbuilder.beginObject(js)
|
||||||
|
val category = unbuilder.readField[String]("category")
|
||||||
|
val severity = unbuilder.readField[Severity]("severity")
|
||||||
|
val message = unbuilder.readField[String]("message")
|
||||||
|
val position = unbuilder.readField[Position]("position")
|
||||||
|
unbuilder.endObject()
|
||||||
|
problem(category, position, message, severity)
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsObject but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: Problem, builder: Builder[J]): Unit = {
|
||||||
|
builder.beginObject()
|
||||||
|
builder.addField("category", obj.category)
|
||||||
|
builder.addField("severity", obj.severity)
|
||||||
|
builder.addField("message", obj.message)
|
||||||
|
builder.addField("position", obj.position)
|
||||||
|
builder.endObject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.internal.util.codec
|
||||||
|
|
||||||
|
import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder }
|
||||||
|
import xsbti.Severity;
|
||||||
|
|
||||||
|
trait SeverityFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||||
|
implicit lazy val SeverityFormat: JsonFormat[Severity] = new JsonFormat[Severity] {
|
||||||
|
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Severity = {
|
||||||
|
jsOpt match {
|
||||||
|
case Some(js) =>
|
||||||
|
unbuilder.readString(js) match {
|
||||||
|
case "Info" => Severity.Info
|
||||||
|
case "Warn" => Severity.Warn
|
||||||
|
case "Error" => Severity.Error
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
deserializationError("Expected JsString but found None")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def write[J](obj: Severity, builder: Builder[J]): Unit = {
|
||||||
|
val str = obj match {
|
||||||
|
case Severity.Info => "Info"
|
||||||
|
case Severity.Warn => "Warn"
|
||||||
|
case Severity.Error => "Error"
|
||||||
|
}
|
||||||
|
builder.writeString(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package sbt.util
|
||||||
|
|
||||||
import xsbti.{ Maybe, F0, F1, T2, Position, Problem, Severity }
|
import xsbti.{ Maybe, F0, F1, T2, Position, Problem, Severity }
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
object InterfaceUtil {
|
object InterfaceUtil {
|
||||||
def f0[A](a: => A): F0[A] = new ConcreteF0[A](a)
|
def f0[A](a: => A): F0[A] = new ConcreteF0[A](a)
|
||||||
|
|
@ -18,6 +19,16 @@ object InterfaceUtil {
|
||||||
case None => Maybe.nothing()
|
case None => Maybe.nothing()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def jo2o[A](o: Optional[A]): Option[A] =
|
||||||
|
if (o.isPresent) Some(o.get)
|
||||||
|
else None
|
||||||
|
|
||||||
|
def o2jo[A](o: Option[A]): Optional[A] =
|
||||||
|
o match {
|
||||||
|
case Some(v) => Optional.ofNullable(v)
|
||||||
|
case None => Optional.empty[A]()
|
||||||
|
}
|
||||||
|
|
||||||
def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer],
|
def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer],
|
||||||
pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position =
|
pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position =
|
||||||
new ConcretePosition(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0)
|
new ConcretePosition(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0)
|
||||||
|
|
@ -61,13 +72,13 @@ object InterfaceUtil {
|
||||||
sourcePath0: Option[String],
|
sourcePath0: Option[String],
|
||||||
sourceFile0: Option[File]
|
sourceFile0: Option[File]
|
||||||
) extends Position {
|
) extends Position {
|
||||||
val line = o2m(line0)
|
val line = o2jo(line0)
|
||||||
val lineContent = content
|
val lineContent = content
|
||||||
val offset = o2m(offset0)
|
val offset = o2jo(offset0)
|
||||||
val pointer = o2m(pointer0)
|
val pointer = o2jo(pointer0)
|
||||||
val pointerSpace = o2m(pointerSpace0)
|
val pointerSpace = o2jo(pointerSpace0)
|
||||||
val sourcePath = o2m(sourcePath0)
|
val sourcePath = o2jo(sourcePath0)
|
||||||
val sourceFile = o2m(sourceFile0)
|
val sourceFile = o2jo(sourceFile0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ConcreteProblem(
|
private final class ConcreteProblem(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import org.apache.logging.log4j.core._
|
||||||
import org.apache.logging.log4j.core.appender.AsyncAppender
|
import org.apache.logging.log4j.core.appender.AsyncAppender
|
||||||
import org.apache.logging.log4j.core.config.{ AppenderRef, LoggerConfig }
|
import org.apache.logging.log4j.core.config.{ AppenderRef, LoggerConfig }
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
import scala.collection.concurrent
|
||||||
|
import sjsonnew.JsonFormat
|
||||||
|
|
||||||
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
|
// http://logging.apache.org/log4j/2.x/manual/customconfig.html
|
||||||
// https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html
|
// https://logging.apache.org/log4j/2.x/log4j-core/apidocs/index.html
|
||||||
|
|
@ -13,6 +15,8 @@ import scala.collection.JavaConverters._
|
||||||
sealed abstract class LogExchange {
|
sealed abstract class LogExchange {
|
||||||
private[sbt] lazy val context: LoggerContext = init()
|
private[sbt] lazy val context: LoggerContext = init()
|
||||||
private[sbt] lazy val asyncStdout: AsyncAppender = buildAsyncStdout
|
private[sbt] lazy val asyncStdout: AsyncAppender = buildAsyncStdout
|
||||||
|
private[sbt] val jsonCodecs: concurrent.Map[String, JsonFormat[_]] = concurrent.TrieMap()
|
||||||
|
private[sbt] val stringCodecs: concurrent.Map[String, ShowLines[_]] = concurrent.TrieMap()
|
||||||
|
|
||||||
def logger(name: String): ManagedLogger = logger(name, None, None)
|
def logger(name: String): ManagedLogger = logger(name, None, None)
|
||||||
def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = {
|
def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = {
|
||||||
|
|
@ -43,6 +47,19 @@ sealed abstract class LogExchange {
|
||||||
val config = ctx.getConfiguration
|
val config = ctx.getConfiguration
|
||||||
config.getLoggerConfig(loggerName)
|
config.getLoggerConfig(loggerName)
|
||||||
}
|
}
|
||||||
|
def jsonCodec[A](tag: String): Option[JsonFormat[A]] =
|
||||||
|
jsonCodecs.get(tag) map { _.asInstanceOf[JsonFormat[A]] }
|
||||||
|
def hasJsonCodec(tag: String): Boolean =
|
||||||
|
jsonCodecs.contains(tag)
|
||||||
|
def getOrElseUpdateJsonCodec[A](tag: String, v: JsonFormat[A]): JsonFormat[A] =
|
||||||
|
jsonCodecs.getOrElseUpdate(tag, v).asInstanceOf[JsonFormat[A]]
|
||||||
|
def stringCodec[A](tag: String): Option[ShowLines[A]] =
|
||||||
|
stringCodecs.get(tag) map { _.asInstanceOf[ShowLines[A]] }
|
||||||
|
def hasStringCodec(tag: String): Boolean =
|
||||||
|
stringCodecs.contains(tag)
|
||||||
|
def getOrElseUpdateStringCodec[A](tag: String, v: ShowLines[A]): ShowLines[A] =
|
||||||
|
stringCodecs.getOrElseUpdate(tag, v).asInstanceOf[ShowLines[A]]
|
||||||
|
|
||||||
private[sbt] def buildAsyncStdout: AsyncAppender = {
|
private[sbt] def buildAsyncStdout: AsyncAppender = {
|
||||||
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
|
val ctx = XLogManager.getContext(false) match { case x: LoggerContext => x }
|
||||||
val config = ctx.getConfiguration
|
val config = ctx.getConfiguration
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import sys.process.ProcessLogger
|
||||||
import sbt.internal.util.{ BufferedLogger, FullLogger }
|
import sbt.internal.util.{ BufferedLogger, FullLogger }
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is intended to be the simplest logging interface for use by code that wants to log.
|
* This is intended to be the simplest logging interface for use by code that wants to log.
|
||||||
|
|
@ -88,6 +89,8 @@ object Logger {
|
||||||
def f0[A](a: => A): F0[A] = InterfaceUtil.f0[A](a)
|
def f0[A](a: => A): F0[A] = InterfaceUtil.f0[A](a)
|
||||||
def m2o[A](m: Maybe[A]): Option[A] = InterfaceUtil.m2o(m)
|
def m2o[A](m: Maybe[A]): Option[A] = InterfaceUtil.m2o(m)
|
||||||
def o2m[A](o: Option[A]): Maybe[A] = InterfaceUtil.o2m(o)
|
def o2m[A](o: Option[A]): Maybe[A] = InterfaceUtil.o2m(o)
|
||||||
|
def jo2o[A](o: Optional[A]): Option[A] = InterfaceUtil.jo2o(o)
|
||||||
|
def o2jo[A](o: Option[A]): Optional[A] = InterfaceUtil.o2jo(o)
|
||||||
def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer],
|
def position(line0: Option[Integer], content: String, offset0: Option[Integer], pointer0: Option[Integer],
|
||||||
pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position =
|
pointerSpace0: Option[String], sourcePath0: Option[String], sourceFile0: Option[File]): Position =
|
||||||
InterfaceUtil.position(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0)
|
InterfaceUtil.position(line0, content, offset0, pointer0, pointerSpace0, sourcePath0, sourceFile0)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,40 @@ class ManagedLoggerSpec extends FlatSpec with Matchers {
|
||||||
log.debug("test")
|
log.debug("test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "support event logging" in {
|
||||||
|
import sjsonnew.BasicJsonProtocol._
|
||||||
|
val log = LogExchange.logger("foo")
|
||||||
|
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
|
||||||
|
log.infoEvent(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "allow registering Show[Int]" in {
|
||||||
|
import sjsonnew.BasicJsonProtocol._
|
||||||
|
val log = LogExchange.logger("foo")
|
||||||
|
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
|
||||||
|
implicit val intShow: ShowLines[Int] = ShowLines({ (x: Int) => Vector(s"String representation of $x") })
|
||||||
|
log.registerStringCodec[Int]
|
||||||
|
log.infoEvent(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "allow registering Show[Array[Int]]" in {
|
||||||
|
import sjsonnew.BasicJsonProtocol._
|
||||||
|
val log = LogExchange.logger("foo")
|
||||||
|
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
|
||||||
|
implicit val intArrayShow: ShowLines[Array[Int]] = ShowLines({ (x: Array[Int]) => Vector(s"String representation of ${x.mkString}") })
|
||||||
|
log.registerStringCodec[Array[Int]]
|
||||||
|
log.infoEvent(Array(1, 2, 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "allow registering Show[Vector[Vector[Int]]]" in {
|
||||||
|
import sjsonnew.BasicJsonProtocol._
|
||||||
|
val log = LogExchange.logger("foo")
|
||||||
|
LogExchange.bindLoggerAppenders("foo", List(LogExchange.asyncStdout -> Level.Info))
|
||||||
|
implicit val intVectorShow: ShowLines[Vector[Vector[Int]]] = ShowLines({ (xss: Vector[Vector[Int]]) => Vector(s"String representation of $xss") })
|
||||||
|
log.registerStringCodec[Vector[Vector[Int]]]
|
||||||
|
log.infoEvent(Vector(Vector(1, 2, 3)))
|
||||||
|
}
|
||||||
|
|
||||||
"global logging" should "log immediately after initialization" in {
|
"global logging" should "log immediately after initialization" in {
|
||||||
// this is passed into State normally
|
// this is passed into State normally
|
||||||
val global0 = initialGlobalLogging
|
val global0 = initialGlobalLogging
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,18 @@ package internal
|
||||||
package scripted
|
package scripted
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import sbt.util.Logger
|
import sbt.util.{ Logger, LogExchange, Level }
|
||||||
import sbt.internal.util.{ ConsoleLogger, BufferedLogger, FullLogger }
|
import sbt.internal.util.{ ManagedLogger, ConsoleOut, MainAppender, ConsoleAppender, BufferedAppender }
|
||||||
import sbt.io.IO.wrapNull
|
import sbt.io.IO.wrapNull
|
||||||
import sbt.io.{ DirectoryFilter, HiddenFileFilter }
|
import sbt.io.{ DirectoryFilter, HiddenFileFilter }
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.internal.io.Resources
|
import sbt.internal.io.Resources
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
object ScriptedRunnerImpl {
|
object ScriptedRunnerImpl {
|
||||||
def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], handlersProvider: HandlersProvider): Unit = {
|
def run(resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], handlersProvider: HandlersProvider): Unit = {
|
||||||
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider)
|
val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider)
|
||||||
val logger = ConsoleLogger()
|
val logger = newLogger
|
||||||
logger.setLevel(sbt.util.Level.Debug)
|
|
||||||
val allTests = get(tests, resourceBaseDirectory, logger) flatMap {
|
val allTests = get(tests, resourceBaseDirectory, logger) flatMap {
|
||||||
case ScriptedTest(group, name) =>
|
case ScriptedTest(group, name) =>
|
||||||
runner.scriptedTest(group, name, logger)
|
runner.scriptedTest(group, name, logger)
|
||||||
|
|
@ -26,29 +26,36 @@ object ScriptedRunnerImpl {
|
||||||
if (errors.nonEmpty)
|
if (errors.nonEmpty)
|
||||||
sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n"))
|
sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n"))
|
||||||
}
|
}
|
||||||
def get(tests: Seq[String], baseDirectory: File, log: Logger): Seq[ScriptedTest] =
|
def get(tests: Seq[String], baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] =
|
||||||
if (tests.isEmpty) listTests(baseDirectory, log) else parseTests(tests)
|
if (tests.isEmpty) listTests(baseDirectory, log) else parseTests(tests)
|
||||||
def listTests(baseDirectory: File, log: Logger): Seq[ScriptedTest] =
|
def listTests(baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] =
|
||||||
(new ListTests(baseDirectory, _ => true, log)).listTests
|
(new ListTests(baseDirectory, _ => true, log)).listTests
|
||||||
def parseTests(in: Seq[String]): Seq[ScriptedTest] =
|
def parseTests(in: Seq[String]): Seq[ScriptedTest] =
|
||||||
for (testString <- in) yield {
|
for (testString <- in) yield {
|
||||||
val Array(group, name) = testString.split("/").map(_.trim)
|
val Array(group, name) = testString.split("/").map(_.trim)
|
||||||
ScriptedTest(group, name)
|
ScriptedTest(group, name)
|
||||||
}
|
}
|
||||||
|
private[sbt] val generateId: AtomicInteger = new AtomicInteger
|
||||||
|
private[sbt] def newLogger: ManagedLogger =
|
||||||
|
{
|
||||||
|
val loggerName = "scripted-" + generateId.incrementAndGet
|
||||||
|
val x = LogExchange.logger(loggerName)
|
||||||
|
x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider) {
|
final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider) {
|
||||||
// import ScriptedTests._
|
|
||||||
private val testResources = new Resources(resourceBaseDirectory)
|
private val testResources = new Resources(resourceBaseDirectory)
|
||||||
|
private val consoleAppender: ConsoleAppender = ConsoleAppender()
|
||||||
|
|
||||||
val ScriptFilename = "test"
|
val ScriptFilename = "test"
|
||||||
val PendingScriptFilename = "pending"
|
val PendingScriptFilename = "pending"
|
||||||
|
|
||||||
def scriptedTest(group: String, name: String, log: xsbti.Logger): Seq[() => Option[String]] =
|
def scriptedTest(group: String, name: String, log: xsbti.Logger): Seq[() => Option[String]] =
|
||||||
scriptedTest(group, name, Logger.xlog2Log(log))
|
scriptedTest(group, name, Logger.xlog2Log(log))
|
||||||
def scriptedTest(group: String, name: String, log: Logger): Seq[() => Option[String]] =
|
def scriptedTest(group: String, name: String, log: ManagedLogger): Seq[() => Option[String]] =
|
||||||
scriptedTest(group, name, { _ => () }, log)
|
scriptedTest(group, name, { _ => () }, log)
|
||||||
def scriptedTest(group: String, name: String, prescripted: File => Unit, log: Logger): Seq[() => Option[String]] = {
|
def scriptedTest(group: String, name: String, prescripted: File => Unit, log: ManagedLogger): Seq[() => Option[String]] = {
|
||||||
for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield {
|
for (groupDir <- (resourceBaseDirectory * group).get; nme <- (groupDir * name).get) yield {
|
||||||
val g = groupDir.getName
|
val g = groupDir.getName
|
||||||
val n = nme.getName
|
val n = nme.getName
|
||||||
|
|
@ -69,19 +76,20 @@ final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def scriptedTest(label: String, testDirectory: File, prescripted: File => Unit, log: Logger): Unit =
|
private def scriptedTest(label: String, testDirectory: File, prescripted: File => Unit, log: ManagedLogger): Unit =
|
||||||
{
|
{
|
||||||
val buffered = new BufferedLogger(new FullLogger(log))
|
val buffered = BufferedAppender(consoleAppender)
|
||||||
buffered.setLevel(sbt.util.Level.Debug)
|
LogExchange.unbindLoggerAppenders(log.name)
|
||||||
if (bufferLog)
|
LogExchange.bindLoggerAppenders(log.name, (buffered -> Level.Debug) :: Nil)
|
||||||
|
if (bufferLog) {
|
||||||
buffered.record()
|
buffered.record()
|
||||||
|
}
|
||||||
def createParser() =
|
def createParser() =
|
||||||
{
|
{
|
||||||
// val fileHandler = new FileCommands(testDirectory)
|
// val fileHandler = new FileCommands(testDirectory)
|
||||||
// // val sbtHandler = new SbtHandler(testDirectory, launcher, buffered, launchOpts)
|
// // val sbtHandler = new SbtHandler(testDirectory, launcher, buffered, launchOpts)
|
||||||
// new TestScriptParser(Map('$' -> fileHandler, /* '>' -> sbtHandler, */ '#' -> CommentHandler))
|
// new TestScriptParser(Map('$' -> fileHandler, /* '>' -> sbtHandler, */ '#' -> CommentHandler))
|
||||||
val scriptConfig = new ScriptConfig(label, testDirectory, buffered)
|
val scriptConfig = new ScriptConfig(label, testDirectory, log)
|
||||||
new TestScriptParser(handlersProvider getHandlers scriptConfig)
|
new TestScriptParser(handlersProvider getHandlers scriptConfig)
|
||||||
}
|
}
|
||||||
val (file, pending) = {
|
val (file, pending) = {
|
||||||
|
|
@ -98,31 +106,31 @@ final class ScriptedTests(resourceBaseDirectory: File, bufferLog: Boolean, handl
|
||||||
run(parser.parse(file))
|
run(parser.parse(file))
|
||||||
}
|
}
|
||||||
def testFailed(): Unit = {
|
def testFailed(): Unit = {
|
||||||
if (pending) buffered.clear() else buffered.stop()
|
if (pending) buffered.clearBuffer() else buffered.stopBuffer()
|
||||||
buffered.error("x " + label + pendingString)
|
log.error("x " + label + pendingString)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
prescripted(testDirectory)
|
prescripted(testDirectory)
|
||||||
runTest()
|
runTest()
|
||||||
buffered.info("+ " + label + pendingString)
|
log.info("+ " + label + pendingString)
|
||||||
if (pending) throw new PendingTestSuccessException(label)
|
if (pending) throw new PendingTestSuccessException(label)
|
||||||
} catch {
|
} catch {
|
||||||
case e: TestException =>
|
case e: TestException =>
|
||||||
testFailed()
|
testFailed()
|
||||||
e.getCause match {
|
e.getCause match {
|
||||||
case null | _: java.net.SocketException => buffered.error(" " + e.getMessage)
|
case null | _: java.net.SocketException => log.error(" " + e.getMessage)
|
||||||
case _ => if (!pending) e.printStackTrace
|
case _ => if (!pending) e.printStackTrace
|
||||||
}
|
}
|
||||||
if (!pending) throw e
|
if (!pending) throw e
|
||||||
case e: PendingTestSuccessException =>
|
case e: PendingTestSuccessException =>
|
||||||
testFailed()
|
testFailed()
|
||||||
buffered.error(" Mark as passing to remove this failure.")
|
log.error(" Mark as passing to remove this failure.")
|
||||||
throw e
|
throw e
|
||||||
case e: Exception =>
|
case e: Exception =>
|
||||||
testFailed()
|
testFailed()
|
||||||
if (!pending) throw e
|
if (!pending) throw e
|
||||||
} finally { buffered.clear() }
|
} finally { buffered.clearBuffer() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0-M3")
|
addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.0-M4")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue