mirror of https://github.com/sbt/sbt.git
Merge pull request #2907 from eed3si9n/topic/logging
Split log output per channel
This commit is contained in:
commit
fd36a20183
|
|
@ -196,10 +196,10 @@ object BasicCommands {
|
|||
def server = Command.command(Server, Help.more(Server, ServerDetailed)) { s0 =>
|
||||
val exchange = State.exchange
|
||||
val s1 = exchange.run(s0)
|
||||
exchange.publishEvent(ConsolePromptEvent(s0))
|
||||
exchange.publishEventMessage(ConsolePromptEvent(s0))
|
||||
val exec: Exec = exchange.blockUntilNextExec
|
||||
val newState = s1.copy(onFailure = Some(Exec(Server, None)), remainingCommands = exec +: Exec(Server, None) +: s1.remainingCommands).setInteractive(true)
|
||||
exchange.publishEvent(ConsoleUnpromptEvent(exec.source))
|
||||
exchange.publishEventMessage(ConsoleUnpromptEvent(exec.source))
|
||||
if (exec.commandLine.trim.isEmpty) newState
|
||||
else newState.clearGlobalLog
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ object Command {
|
|||
def process(exec: Exec, state: State): State =
|
||||
{
|
||||
val channelName = exec.source map { _.channelName }
|
||||
State.exchange.publishEvent(ExecStatusEvent("Processing", channelName, exec.execId, Vector()))
|
||||
State.exchange.publishEventMessage(ExecStatusEvent("Processing", channelName, exec.execId, Vector()))
|
||||
val parser = combine(state.definedCommands)
|
||||
val newState = parse(exec.commandLine, parser(state)) match {
|
||||
case Right(s) => s() // apply command. command side effects happen here
|
||||
|
|
@ -100,7 +100,7 @@ object Command {
|
|||
state.log.error(errMsg)
|
||||
state.fail
|
||||
}
|
||||
State.exchange.publishEvent(ExecStatusEvent("Done", channelName, exec.execId, newState.remainingCommands.toVector map { _.commandLine }))
|
||||
State.exchange.publishEventMessage(ExecStatusEvent("Done", channelName, exec.execId, newState.remainingCommands.toVector map { _.commandLine }))
|
||||
newState
|
||||
}
|
||||
def invalidValue(label: String, allowed: Iterable[String])(value: String): String =
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ package sbt
|
|||
import java.util.regex.Pattern
|
||||
import scala.Console.{ BOLD, RESET }
|
||||
|
||||
import sbt.internal.util.ConsoleLogger
|
||||
import sbt.internal.util.ConsoleAppender
|
||||
|
||||
object Highlight {
|
||||
|
||||
def showMatches(pattern: Pattern)(line: String): Option[String] =
|
||||
{
|
||||
val matcher = pattern.matcher(line)
|
||||
if (ConsoleLogger.formatEnabled) {
|
||||
if (ConsoleAppender.formatEnabled) {
|
||||
// ANSI codes like \033[39m (normal text color) don't work on Windows
|
||||
val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + RESET)
|
||||
if (highlighted == line) None else Some(highlighted)
|
||||
|
|
@ -19,5 +19,5 @@ object Highlight {
|
|||
else
|
||||
None
|
||||
}
|
||||
def bold(s: String) = if (ConsoleLogger.formatEnabled) BOLD + s.replace(RESET, RESET + BOLD) + RESET else s
|
||||
def bold(s: String) = if (ConsoleAppender.formatEnabled) BOLD + s.replace(RESET, RESET + BOLD) + RESET else s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,21 +66,22 @@ object MainLoop {
|
|||
def runWithNewLog(state: State, logBacking: GlobalLogBacking): RunNext =
|
||||
Using.fileWriter(append = true)(logBacking.file) { writer =>
|
||||
val out = new java.io.PrintWriter(writer)
|
||||
val newLogging = state.globalLogging.newLogger(out, logBacking)
|
||||
transferLevels(state, newLogging)
|
||||
val full = state.globalLogging.full
|
||||
val newLogging = state.globalLogging.newAppender(full, out, logBacking)
|
||||
// transferLevels(state, newLogging)
|
||||
val loggedState = state.copy(globalLogging = newLogging)
|
||||
try run(loggedState) finally out.close()
|
||||
}
|
||||
|
||||
/** Transfers logging and trace levels from the old global loggers to the new ones. */
|
||||
private[this] def transferLevels(state: State, logging: GlobalLogging): Unit = {
|
||||
val old = state.globalLogging
|
||||
Logger.transferLevels(old.backed, logging.backed)
|
||||
(old.full, logging.full) match { // well, this is a hack
|
||||
case (oldLog: AbstractLogger, newLog: AbstractLogger) => Logger.transferLevels(oldLog, newLog)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
// /** Transfers logging and trace levels from the old global loggers to the new ones. */
|
||||
// private[this] def transferLevels(state: State, logging: GlobalLogging): Unit = {
|
||||
// val old = state.globalLogging
|
||||
// Logger.transferLevels(old.backed, logging.backed)
|
||||
// (old.full, logging.full) match { // well, this is a hack
|
||||
// case (oldLog: AbstractLogger, newLog: AbstractLogger) => Logger.transferLevels(oldLog, newLog)
|
||||
// case _ => ()
|
||||
// }
|
||||
// }
|
||||
|
||||
sealed trait RunNext
|
||||
final class ClearGlobalLog(val state: State) extends RunNext
|
||||
|
|
|
|||
|
|
@ -32,10 +32,16 @@ final case class State(
|
|||
history: State.History,
|
||||
attributes: AttributeMap,
|
||||
globalLogging: GlobalLogging,
|
||||
source: Option[CommandSource],
|
||||
currentCommand: Option[Exec],
|
||||
next: State.Next
|
||||
) extends Identity {
|
||||
lazy val combinedParser = Command.combine(definedCommands)(this)
|
||||
|
||||
def source: Option[CommandSource] =
|
||||
currentCommand match {
|
||||
case Some(x) => x.source
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
trait Identity {
|
||||
|
|
@ -76,11 +82,6 @@ trait StateOps {
|
|||
/** Sets the next command processing action to do.*/
|
||||
def setNext(n: State.Next): State
|
||||
|
||||
/** Sets the current command source channel.*/
|
||||
def setSource(source: CommandSource): State
|
||||
|
||||
@deprecated("Use setNext", "0.11.0") def setResult(ro: Option[xsbti.MainResult]): State
|
||||
|
||||
/**
|
||||
* Restarts sbt without dropping loaded Scala classes. It is a shallower restart than `reboot`.
|
||||
* This method takes a snapshot of the remaining commands and will resume executing those commands after reload.
|
||||
|
|
@ -203,7 +204,7 @@ object State {
|
|||
case List() => exit(true)
|
||||
case x :: xs =>
|
||||
log.debug(s"> $x")
|
||||
f(x, s.copy(remainingCommands = xs, history = x :: s.history))
|
||||
f(x, s.copy(remainingCommands = xs, currentCommand = Some(x), history = x :: s.history))
|
||||
}
|
||||
|
||||
def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) })
|
||||
|
|
@ -214,8 +215,6 @@ object State {
|
|||
def +(newCommand: Command): State = this ++ (newCommand :: Nil)
|
||||
def baseDir: File = s.configuration.baseDirectory
|
||||
def setNext(n: Next) = s.copy(next = n)
|
||||
def setSource(x: CommandSource): State = s.copy(source = Some(x))
|
||||
def setResult(ro: Option[xsbti.MainResult]) = ro match { case None => continue; case Some(r) => setNext(new Return(r)) }
|
||||
def continue = setNext(Continue)
|
||||
def reboot(full: Boolean) = { runExitHooks(); throw new xsbti.FullReload((s.remainingCommands map { case e: Exec => e.commandLine }).toArray, full) }
|
||||
def reload = runExitHooks().setNext(new Return(defaultReload(s)))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package internal
|
|||
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import sbt.protocol.EventMessage
|
||||
import sjsonnew.JsonFormat
|
||||
|
||||
/**
|
||||
* A command channel represents an IO device such as network socket or human
|
||||
|
|
@ -15,7 +16,8 @@ abstract class CommandChannel {
|
|||
commandQueue.add(exec)
|
||||
def poll: Option[Exec] = Option(commandQueue.poll)
|
||||
|
||||
def publishEvent(event: EventMessage): Unit
|
||||
def publishEvent[A: JsonFormat](event: A): Unit
|
||||
def publishEventMessage(event: EventMessage): Unit
|
||||
def publishBytes(bytes: Array[Byte]): Unit
|
||||
def shutdown(): Unit
|
||||
def name: String
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import java.net.SocketException
|
|||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import sbt.internal.server._
|
||||
import sbt.internal.util.ChannelLogEntry
|
||||
import sbt.protocol.{ EventMessage, Serialization, ChannelAcceptedEvent }
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.annotation.tailrec
|
||||
import BasicKeys.serverPort
|
||||
import java.net.Socket
|
||||
import sjsonnew.JsonFormat
|
||||
|
||||
/**
|
||||
* The command exchange merges multiple command channels (e.g. network and console),
|
||||
|
|
@ -74,7 +76,7 @@ private[sbt] final class CommandExchange {
|
|||
s.log.info(s"new client connected from: ${socket.getPort}")
|
||||
val channel = new NetworkChannel(newChannelName, socket)
|
||||
subscribe(channel)
|
||||
channel.publishEvent(ChannelAcceptedEvent(channel.name))
|
||||
channel.publishEventMessage(ChannelAcceptedEvent(channel.name))
|
||||
}
|
||||
server match {
|
||||
case Some(x) => // do nothing
|
||||
|
|
@ -94,23 +96,28 @@ private[sbt] final class CommandExchange {
|
|||
server = None
|
||||
}
|
||||
|
||||
// fanout publisEvent
|
||||
def publishEvent(event: EventMessage): Unit =
|
||||
def publishEvent[A: JsonFormat](event: A): Unit =
|
||||
{
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
val bytes = Serialization.serializeEvent(event)
|
||||
event match {
|
||||
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
|
||||
case e: ConsolePromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEvent(e)
|
||||
}
|
||||
case e: ConsoleUnpromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEvent(e)
|
||||
case entry: ChannelLogEntry =>
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
|
||||
c.publishEvent(event)
|
||||
}
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
if (entry.channelName == Some(c.name)) {
|
||||
c.publishBytes(bytes)
|
||||
}
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
// TODO do not do this on the calling thread
|
||||
val bytes = Serialization.serializeEvent(event)
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEvent(event)
|
||||
|
|
@ -131,4 +138,42 @@ private[sbt] final class CommandExchange {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fanout publisEvent
|
||||
def publishEventMessage(event: EventMessage): Unit =
|
||||
{
|
||||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
event match {
|
||||
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
|
||||
case e: ConsolePromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case e: ConsoleUnpromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
}
|
||||
case _ =>
|
||||
// TODO do not do this on the calling thread
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
channels.foreach {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEventMessage(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishBytes(bytes)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
}
|
||||
toDel.toList match {
|
||||
case Nil => // do nothing
|
||||
case xs =>
|
||||
lock.synchronized {
|
||||
channelBuffer --= xs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import sbt.internal.util._
|
|||
import BasicKeys._
|
||||
import java.io.File
|
||||
import sbt.protocol.EventMessage
|
||||
import sjsonnew.JsonFormat
|
||||
|
||||
private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel {
|
||||
private var askUserThread: Option[Thread] = None
|
||||
|
|
@ -30,7 +31,9 @@ private[sbt] final class ConsoleChannel(val name: String) extends CommandChannel
|
|||
|
||||
def publishBytes(bytes: Array[Byte]): Unit = ()
|
||||
|
||||
def publishEvent(event: EventMessage): Unit =
|
||||
def publishEvent[A: JsonFormat](event: A): Unit = ()
|
||||
|
||||
def publishEventMessage(event: EventMessage): Unit =
|
||||
event match {
|
||||
case e: ConsolePromptEvent =>
|
||||
askUserThread match {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package sbt
|
||||
package internal
|
||||
|
||||
import org.apache.logging.log4j.message._
|
||||
import org.apache.logging.log4j.core.{ LogEvent => XLogEvent }
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout
|
||||
import org.apache.logging.log4j.core.async.RingBufferLogEvent
|
||||
import sbt.util.Level
|
||||
import sbt.internal.util._
|
||||
import sbt.protocol.LogEvent
|
||||
import sbt.internal.util.codec._
|
||||
|
||||
class RelayAppender(name: String) extends AbstractAppender(name, null, PatternLayout.createDefaultLayout(), true) {
|
||||
import JsonProtocol._
|
||||
|
||||
def append(event: XLogEvent): Unit =
|
||||
{
|
||||
val level = ConsoleAppender.toLevel(event.getLevel)
|
||||
val message = event.getMessage
|
||||
message match {
|
||||
case o: ObjectMessage => appendEvent(level, o.getParameter)
|
||||
case p: ParameterizedMessage => appendLog(level, p.getFormattedMessage)
|
||||
case r: RingBufferLogEvent => appendLog(level, r.getFormattedMessage)
|
||||
case _ => appendLog(level, message.toString)
|
||||
}
|
||||
}
|
||||
def appendLog(level: Level.Value, message: => String): Unit = {
|
||||
State.exchange.publishEventMessage(LogEvent(level.toString, message))
|
||||
}
|
||||
def appendEvent(level: Level.Value, event: AnyRef): Unit =
|
||||
event match {
|
||||
case x: ChannelLogEntry => State.exchange.publishEvent(x: AbstractEntry)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,11 @@ package client
|
|||
import java.net.{ URI, Socket, InetAddress, SocketException }
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference }
|
||||
import sbt.protocol._
|
||||
import sbt.internal.util.JLine
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.control.NonFatal
|
||||
import sbt.protocol._
|
||||
import sbt.internal.util.{ JLine, ChannelLogEntry, ConsoleAppender }
|
||||
import sbt.util.Level
|
||||
|
||||
class NetworkClient(arguments: List[String]) { self =>
|
||||
private val channelName = new AtomicReference("_")
|
||||
|
|
@ -20,6 +21,8 @@ class NetworkClient(arguments: List[String]) { self =>
|
|||
private val running = new AtomicBoolean(true)
|
||||
private val pendingExecIds = ListBuffer.empty[String]
|
||||
|
||||
private val console = ConsoleAppender("thin1")
|
||||
|
||||
def usageError = sys.error("Expecting: sbt client 127.0.0.1:port")
|
||||
val connection = init()
|
||||
start()
|
||||
|
|
@ -44,6 +47,7 @@ class NetworkClient(arguments: List[String]) { self =>
|
|||
val socket = new Socket(InetAddress.getByName(host), port)
|
||||
new ServerConnection(socket) {
|
||||
override def onEvent(event: EventMessage): Unit = self.onEvent(event)
|
||||
override def onLogEntry(event: ChannelLogEntry): Unit = self.onLogEntry(event)
|
||||
override def onShutdown(): Unit =
|
||||
{
|
||||
running.set(false)
|
||||
|
|
@ -51,6 +55,17 @@ class NetworkClient(arguments: List[String]) { self =>
|
|||
}
|
||||
}
|
||||
|
||||
def onLogEntry(event: ChannelLogEntry): Unit =
|
||||
{
|
||||
val level = event.level match {
|
||||
case "debug" => Level.Debug
|
||||
case "info" => Level.Info
|
||||
case "warn" => Level.Warn
|
||||
case "error" => Level.Error
|
||||
}
|
||||
console.appendLog(level, event.message)
|
||||
}
|
||||
|
||||
def onEvent(event: EventMessage): Unit =
|
||||
event match {
|
||||
case e: ChannelAcceptedEvent =>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package client
|
|||
import java.net.{ SocketTimeoutException, Socket }
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import sbt.protocol._
|
||||
import sbt.internal.util.ChannelLogEntry
|
||||
|
||||
abstract class ServerConnection(connection: Socket) {
|
||||
|
||||
|
|
@ -39,7 +40,10 @@ abstract class ServerConnection(connection: Socket) {
|
|||
val s = new String(chunk.toArray, "UTF-8")
|
||||
println(s"Got invalid chunk from server: $s \n" + errorDesc)
|
||||
},
|
||||
onEvent
|
||||
_ match {
|
||||
case event: EventMessage => onEvent(event)
|
||||
case event: ChannelLogEntry => onLogEntry(event)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +65,7 @@ abstract class ServerConnection(connection: Socket) {
|
|||
}
|
||||
|
||||
def onEvent(event: EventMessage): Unit
|
||||
def onLogEntry(event: ChannelLogEntry): Unit
|
||||
|
||||
def onShutdown(): Unit
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package server
|
|||
import java.net.{ Socket, SocketTimeoutException }
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage }
|
||||
import sjsonnew.JsonFormat
|
||||
|
||||
final class NetworkChannel(val name: String, connection: Socket) extends CommandChannel {
|
||||
private val running = new AtomicBoolean(true)
|
||||
|
|
@ -50,12 +51,18 @@ final class NetworkChannel(val name: String, connection: Socket) extends Command
|
|||
}
|
||||
thread.start()
|
||||
|
||||
def publishEvent(event: EventMessage): Unit =
|
||||
def publishEvent[A: JsonFormat](event: A): Unit =
|
||||
{
|
||||
val bytes = Serialization.serializeEvent(event)
|
||||
publishBytes(bytes)
|
||||
}
|
||||
|
||||
def publishEventMessage(event: EventMessage): Unit =
|
||||
{
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
publishBytes(bytes)
|
||||
}
|
||||
|
||||
def publishBytes(event: Array[Byte]): Unit =
|
||||
{
|
||||
out.write(event)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import descriptor.ModuleDescriptor, id.ModuleRevisionId
|
|||
import testing.Framework
|
||||
import KeyRanks._
|
||||
|
||||
import sbt.internal.{ BuildStructure, LoadedBuild, PluginDiscovery, BuildDependencies, SessionSettings }
|
||||
import sbt.internal.{ BuildStructure, LoadedBuild, PluginDiscovery, BuildDependencies, SessionSettings, LogManager }
|
||||
import sbt.io.FileFilter
|
||||
import sbt.internal.io.WatchState
|
||||
import sbt.internal.util.{ AttributeKey, CacheStore, SourcePosition }
|
||||
|
|
@ -73,6 +73,7 @@ import sbt.internal.librarymanagement.{
|
|||
UnresolvedWarningConfiguration
|
||||
}
|
||||
import sbt.util.{ AbstractLogger, Level, Logger }
|
||||
import org.apache.logging.log4j.core.Appender
|
||||
|
||||
object Keys {
|
||||
val TraceValues = "-1 to disable, 0 for up to the first sbt frame, or a positive number to set the maximum number of frames shown."
|
||||
|
|
@ -87,7 +88,7 @@ object Keys {
|
|||
val showSuccess = SettingKey[Boolean]("show-success", "If true, displays a success message after running a command successfully.", CSetting)
|
||||
val showTiming = SettingKey[Boolean]("show-timing", "If true, the command success message includes the completion time.", CSetting)
|
||||
val timingFormat = SettingKey[java.text.DateFormat]("timing-format", "The format used for displaying the completion time.", CSetting)
|
||||
val extraLoggers = SettingKey[ScopedKey[_] => Seq[AbstractLogger]]("extra-loggers", "A function that provides additional loggers for a given setting.", DSetting)
|
||||
val extraLoggers = SettingKey[ScopedKey[_] => Seq[Appender]]("extra-loggers", "A function that provides additional loggers for a given setting.", DSetting)
|
||||
val logManager = SettingKey[LogManager]("log-manager", "The log manager, which creates Loggers for different contexts.", DSetting)
|
||||
val logBuffered = SettingKey[Boolean]("log-buffered", "True if logging should be buffered until work completes.", CSetting)
|
||||
val sLog = SettingKey[Logger]("setting-logger", "Logger usable by settings during project loading.", CSetting)
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ import sbt.internal.{
|
|||
ProjectNavigation,
|
||||
Script,
|
||||
SessionSettings,
|
||||
SettingCompletions
|
||||
SettingCompletions,
|
||||
LogManager
|
||||
}
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, complete, ConsoleOut, GlobalLogging, LineRange, MainLogging, SimpleReader, Types }
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, complete, ConsoleOut, GlobalLogging, LineRange, MainAppender, SimpleReader, Types }
|
||||
import sbt.util.{ Level, Logger }
|
||||
|
||||
import complete.{ DefaultParsers, Parser }
|
||||
|
|
@ -87,7 +88,7 @@ object StandardMain {
|
|||
/** The common interface to standard output, used for all built-in ConsoleLoggers. */
|
||||
val console = ConsoleOut.systemOutOverwrite(ConsoleOut.overwriteContaining("Resolving "))
|
||||
|
||||
def initialGlobalLogging: GlobalLogging = GlobalLogging.initial(MainLogging.globalDefault(console), File.createTempFile("sbt", ".log"), console)
|
||||
def initialGlobalLogging: GlobalLogging = GlobalLogging.initial(MainAppender.globalDefault(console), File.createTempFile("sbt", ".log"), console)
|
||||
|
||||
def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Project._
|
|||
import Keys.{ appConfiguration, stateBuildStructure, commands, configuration, historyPath, projectCommand, sessionSettings, shellPrompt, serverPort, thisProject, thisProjectRef, watch }
|
||||
import Scope.{ GlobalScope, ThisScope }
|
||||
import Def.{ Flattened, Initialize, ScopedKey, Setting }
|
||||
import sbt.internal.{ Load, BuildStructure, LoadedBuild, LoadedBuildUnit, SettingGraph, SettingCompletions, AddSettings, SessionSettings }
|
||||
import sbt.internal.{ Load, BuildStructure, LoadedBuild, LoadedBuildUnit, SettingGraph, SettingCompletions, AddSettings, SessionSettings, LogManager }
|
||||
import sbt.internal.util.{ AttributeKey, AttributeMap, Dag, Relation, Settings, Show, ~> }
|
||||
import sbt.internal.util.Types.{ const, idFun }
|
||||
import sbt.internal.util.complete.DefaultParsers
|
||||
|
|
@ -398,7 +398,8 @@ object Project extends ProjectExtra {
|
|||
val (onLoad, onUnload) = getHooks(structure.data)
|
||||
val newAttrs = unloaded.attributes.put(stateBuildStructure, structure).put(sessionSettings, session).put(Keys.onUnload.key, onUnload)
|
||||
val newState = unloaded.copy(attributes = newAttrs)
|
||||
onLoad(LogManager.setGlobalLogLevels(updateCurrent(newState), structure.data))
|
||||
// TODO: Fix this
|
||||
onLoad(updateCurrent(newState) /*LogManager.setGlobalLogLevels(updateCurrent(newState), structure.data)*/ )
|
||||
}
|
||||
|
||||
def orIdentity[T](opt: Option[T => T]): T => T = opt getOrElse idFun
|
||||
|
|
|
|||
|
|
@ -2,46 +2,67 @@
|
|||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package internal
|
||||
|
||||
import java.io.PrintWriter
|
||||
import Def.ScopedKey
|
||||
import Scope.GlobalScope
|
||||
import Keys.{ logLevel, logManager, persistLogLevel, persistTraceLevel, sLog, traceLevel }
|
||||
import scala.Console.{ BLUE, RESET }
|
||||
import sbt.internal.util.{ AttributeKey, ConsoleOut, MultiLoggerConfig, Settings, SuppressedTraceContext }
|
||||
import sbt.internal.util.MainLogging._
|
||||
import sbt.util.{ AbstractLogger, Level, Logger }
|
||||
import sbt.internal.util.{ AttributeKey, ConsoleOut, Settings, SuppressedTraceContext, MainAppender }
|
||||
import MainAppender._
|
||||
import sbt.util.{ AbstractLogger, Level, Logger, LogExchange }
|
||||
import org.apache.logging.log4j.core.Appender
|
||||
|
||||
sealed abstract class LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
||||
}
|
||||
|
||||
object LogManager {
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
private val generateId: AtomicInteger = new AtomicInteger
|
||||
|
||||
// This is called by mkStreams
|
||||
def construct(data: Settings[Scope], state: State) = (task: ScopedKey[_], to: PrintWriter) =>
|
||||
{
|
||||
val manager = logManager in task.scope get data getOrElse defaultManager(state.globalLogging.console)
|
||||
manager(data, state, task, to)
|
||||
}
|
||||
@deprecated("Use defaultManager to explicitly specify standard out.", "0.13.0")
|
||||
lazy val default: LogManager = defaultManager(StandardMain.console)
|
||||
|
||||
def defaultManager(console: ConsoleOut): LogManager = withLoggers((sk, s) => defaultScreen(console))
|
||||
|
||||
@deprecated("Explicitly specify standard out.", "0.13.0")
|
||||
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger]): LogManager = defaults(extra, StandardMain.console)
|
||||
|
||||
def defaults(extra: ScopedKey[_] => Seq[AbstractLogger], console: ConsoleOut): LogManager =
|
||||
// This is called by Defaults.
|
||||
def defaults(extra: ScopedKey[_] => Seq[Appender], console: ConsoleOut): LogManager =
|
||||
withLoggers((task, state) => defaultScreen(console, suppressedMessage(task, state)), extra = extra)
|
||||
|
||||
def withScreenLogger(mk: (ScopedKey[_], State) => AbstractLogger): LogManager = withLoggers(screen = mk)
|
||||
def withScreenLogger(mk: (ScopedKey[_], State) => Appender): LogManager = withLoggers(screen = mk)
|
||||
|
||||
def withLoggers(
|
||||
screen: (ScopedKey[_], State) => AbstractLogger = (sk, s) => defaultScreen(s.globalLogging.console),
|
||||
backed: PrintWriter => AbstractLogger = defaultBacked(),
|
||||
extra: ScopedKey[_] => Seq[AbstractLogger] = _ => Nil
|
||||
): LogManager = new LogManager {
|
||||
screen: (ScopedKey[_], State) => Appender = (sk, s) => defaultScreen(s.globalLogging.console),
|
||||
backed: PrintWriter => Appender = defaultBacked(),
|
||||
relay: Unit => Appender = defaultRelay,
|
||||
extra: ScopedKey[_] => Seq[Appender] = _ => Nil
|
||||
): LogManager = new DefaultLogManager(screen, backed, relay, extra)
|
||||
|
||||
private class DefaultLogManager(
|
||||
screen: (ScopedKey[_], State) => Appender,
|
||||
backed: PrintWriter => Appender,
|
||||
relay: Unit => Appender,
|
||||
extra: ScopedKey[_] => Seq[Appender]
|
||||
) extends LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], to: PrintWriter): Logger =
|
||||
defaultLogger(data, state, task, screen(task, state), backed(to), extra(task).toList)
|
||||
defaultLogger(data, state, task, screen(task, state), backed(to), relay(()), extra(task).toList)
|
||||
}
|
||||
|
||||
def defaultLogger(data: Settings[Scope], state: State, task: ScopedKey[_], console: AbstractLogger, backed: AbstractLogger, extra: List[AbstractLogger]): Logger =
|
||||
// This is the main function that is used to generat the logger for tasks.
|
||||
def defaultLogger(data: Settings[Scope], state: State, task: ScopedKey[_],
|
||||
console: Appender, backed: Appender, relay: Appender, extra: List[Appender]): Logger =
|
||||
{
|
||||
val execOpt = state.currentCommand
|
||||
val loggerName: String = s"${task.key.label}-${generateId.incrementAndGet}"
|
||||
val channelName: Option[String] = execOpt flatMap { e => e.source map { _.channelName } }
|
||||
val execId: Option[String] = execOpt flatMap { _.execId }
|
||||
val log = LogExchange.logger(loggerName, channelName, execId)
|
||||
val scope = task.scope
|
||||
// to change from global being the default to overriding, switch the order of state.get and data.get
|
||||
def getOr[T](key: AttributeKey[T], default: T): T = data.get(scope, key) orElse state.get(key) getOrElse default
|
||||
|
|
@ -49,8 +70,20 @@ object LogManager {
|
|||
val backingLevel = getOr(persistLogLevel.key, Level.Debug)
|
||||
val screenTrace = getOr(traceLevel.key, defaultTraceLevel(state))
|
||||
val backingTrace = getOr(persistTraceLevel.key, Int.MaxValue)
|
||||
val extraBacked = state.globalLogging.backed :: Nil
|
||||
multiLogger(MultiLoggerConfig(console, backed, extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace))
|
||||
val extraBacked = state.globalLogging.backed :: relay :: Nil
|
||||
val consoleOpt =
|
||||
execOpt match {
|
||||
case Some(x: Exec) =>
|
||||
x.source match {
|
||||
// TODO: Fix this stringliness
|
||||
case Some(x: CommandSource) if x.channelName == "console0" => Option(console)
|
||||
case Some(x: CommandSource) => None
|
||||
case _ => Option(console)
|
||||
}
|
||||
case _ => Option(console)
|
||||
}
|
||||
multiLogger(log, MainAppender.MainAppenderConfig(consoleOpt, backed,
|
||||
extraBacked ::: extra, screenLevel, backingLevel, screenTrace, backingTrace))
|
||||
}
|
||||
def defaultTraceLevel(state: State): Int =
|
||||
if (state.interactive) -1 else Int.MaxValue
|
||||
|
|
@ -66,24 +99,25 @@ object LogManager {
|
|||
case _ => key // should never get here
|
||||
}
|
||||
|
||||
// TODO: Fix this
|
||||
// if global logging levels are not explicitly set, set them from project settings
|
||||
private[sbt] def setGlobalLogLevels(s: State, data: Settings[Scope]): State =
|
||||
if (hasExplicitGlobalLogLevels(s))
|
||||
s
|
||||
else {
|
||||
val logging = s.globalLogging
|
||||
def get[T](key: SettingKey[T]) = key in GlobalScope get data
|
||||
def transfer(l: AbstractLogger, traceKey: SettingKey[Int], levelKey: SettingKey[Level.Value]): Unit = {
|
||||
get(traceKey).foreach(l.setTrace)
|
||||
get(levelKey).foreach(l.setLevel)
|
||||
}
|
||||
logging.full match {
|
||||
case a: AbstractLogger => transfer(a, traceLevel, logLevel)
|
||||
case _ => ()
|
||||
}
|
||||
transfer(logging.backed, persistTraceLevel, persistLogLevel)
|
||||
s
|
||||
}
|
||||
// private[sbt] def setGlobalLogLevels(s: State, data: Settings[Scope]): State =
|
||||
// if (hasExplicitGlobalLogLevels(s))
|
||||
// s
|
||||
// else {
|
||||
// val logging = s.globalLogging
|
||||
// def get[T](key: SettingKey[T]) = key in GlobalScope get data
|
||||
// def transfer(l: AbstractLogger, traceKey: SettingKey[Int], levelKey: SettingKey[Level.Value]): Unit = {
|
||||
// get(traceKey).foreach(l.setTrace)
|
||||
// get(levelKey).foreach(l.setLevel)
|
||||
// }
|
||||
// logging.full match {
|
||||
// case a: AbstractLogger => transfer(a, traceLevel, logLevel)
|
||||
// case _ => ()
|
||||
// }
|
||||
// transfer(logging.backed, persistTraceLevel, persistLogLevel)
|
||||
// s
|
||||
// }
|
||||
|
||||
def setGlobalLogLevel(s: State, level: Level.Value): State = {
|
||||
s.globalLogging.full match {
|
||||
|
|
@ -93,6 +127,10 @@ object LogManager {
|
|||
s.put(BasicKeys.explicitGlobalLogLevels, true).put(Keys.logLevel.key, level)
|
||||
}
|
||||
|
||||
// This is the default implementation for the relay appender
|
||||
val defaultRelay: Unit => Appender = _ => defaultRelayImpl
|
||||
private[this] lazy val defaultRelayImpl: RelayAppender = new RelayAppender("Relay0")
|
||||
|
||||
private[this] def hasExplicitGlobalLogLevels(s: State): Boolean =
|
||||
State.getBoolean(s, BasicKeys.explicitGlobalLogLevels, default = false)
|
||||
|
||||
|
|
@ -114,7 +152,3 @@ object LogManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait LogManager {
|
||||
def apply(data: Settings[Scope], state: State, task: ScopedKey[_], writer: PrintWriter): Logger
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import java.io._
|
|||
import org.specs2.mutable.Specification
|
||||
|
||||
import sbt.internal._
|
||||
import sbt.internal.util.{ AttributeEntry, AttributeMap, ConsoleOut, GlobalLogging, MainLogging, Settings }
|
||||
import sbt.internal.util.{ AttributeEntry, AttributeMap, ConsoleOut, GlobalLogging, MainAppender, Settings }
|
||||
|
||||
object PluginCommandTestPlugin0 extends AutoPlugin { override def requires = empty }
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ object FakeState {
|
|||
List(),
|
||||
State.newHistory,
|
||||
attributes,
|
||||
GlobalLogging.initial(MainLogging.globalDefault(ConsoleOut.systemOut), File.createTempFile("sbt", ".log"), ConsoleOut.systemOut),
|
||||
GlobalLogging.initial(MainAppender.globalDefault(ConsoleOut.systemOut), File.createTempFile("sbt", ".log"), ConsoleOut.systemOut),
|
||||
None,
|
||||
State.Continue
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ object Dependencies {
|
|||
|
||||
// sbt modules
|
||||
private val ioVersion = "1.0.0-M9"
|
||||
private val utilVersion = "1.0.0-M17"
|
||||
private val lmVersion = "0.1.0-X3"
|
||||
private val zincVersion = "1.0.0-X7"
|
||||
private val utilVersion = "1.0.0-M18"
|
||||
private val lmVersion = "1.0.0-X4"
|
||||
private val zincVersion = "1.0.0-X8"
|
||||
|
||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
scalaVersion := "2.10.6"
|
||||
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")
|
||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.8")
|
||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.11")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.4")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.5")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-javaversioncheck" % "0.1.0")
|
||||
|
|
|
|||
|
|
@ -4,13 +4,20 @@
|
|||
package sbt
|
||||
package protocol
|
||||
|
||||
import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter }
|
||||
import scala.json.ast.unsafe.JValue
|
||||
import sjsonnew.support.scalajson.unsafe.Parser
|
||||
import sjsonnew.JsonFormat
|
||||
import sjsonnew.support.scalajson.unsafe.{ Parser, Converter, CompactPrinter }
|
||||
import scala.json.ast.unsafe.{ JValue, JObject, JString }
|
||||
import java.nio.ByteBuffer
|
||||
import scala.util.{ Success, Failure }
|
||||
import sbt.internal.util.ChannelLogEntry
|
||||
|
||||
object Serialization {
|
||||
def serializeEvent[A: JsonFormat](event: A): Array[Byte] =
|
||||
{
|
||||
val json: JValue = Converter.toJson[A](event).get
|
||||
CompactPrinter(json).getBytes("UTF-8")
|
||||
}
|
||||
|
||||
def serializeCommand(command: CommandMessage): Array[Byte] =
|
||||
{
|
||||
import codec.JsonProtocol._
|
||||
|
|
@ -18,7 +25,7 @@ object Serialization {
|
|||
CompactPrinter(json).getBytes("UTF-8")
|
||||
}
|
||||
|
||||
def serializeEvent(event: EventMessage): Array[Byte] =
|
||||
def serializeEventMessage(event: EventMessage): Array[Byte] =
|
||||
{
|
||||
import codec.JsonProtocol._
|
||||
val json: JValue = Converter.toJson[EventMessage](event).get
|
||||
|
|
@ -46,7 +53,44 @@ object Serialization {
|
|||
/**
|
||||
* @return A command or an invalid input description
|
||||
*/
|
||||
def deserializeEvent(bytes: Seq[Byte]): Either[String, EventMessage] =
|
||||
def deserializeEvent(bytes: Seq[Byte]): Either[String, Any] =
|
||||
{
|
||||
val buffer = ByteBuffer.wrap(bytes.toArray)
|
||||
Parser.parseFromByteBuffer(buffer) match {
|
||||
case Success(json) =>
|
||||
detectType(json) match {
|
||||
case Some("ChannelLogEntry") =>
|
||||
import sbt.internal.util.codec.JsonProtocol._
|
||||
Converter.fromJson[ChannelLogEntry](json) match {
|
||||
case Success(event) => Right(event)
|
||||
case Failure(e) => Left(e.getMessage)
|
||||
}
|
||||
case _ =>
|
||||
import codec.JsonProtocol._
|
||||
Converter.fromJson[EventMessage](json) match {
|
||||
case Success(event) => Right(event)
|
||||
case Failure(e) => Left(e.getMessage)
|
||||
}
|
||||
}
|
||||
case Failure(e) =>
|
||||
Left(s"Parse error: ${e.getMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
def detectType(json: JValue): Option[String] =
|
||||
json match {
|
||||
case JObject(fields) =>
|
||||
(fields find { _.field == "type" } map { _.value }) match {
|
||||
case Some(JString(value)) => Some(value)
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A command or an invalid input description
|
||||
*/
|
||||
def deserializeEventMessage(bytes: Seq[Byte]): Either[String, EventMessage] =
|
||||
{
|
||||
val buffer = ByteBuffer.wrap(bytes.toArray)
|
||||
Parser.parseFromByteBuffer(buffer) match {
|
||||
|
|
|
|||
|
|
@ -98,10 +98,6 @@ trait Import {
|
|||
type FullLogger = sbt.internal.util.FullLogger
|
||||
val FullReader = sbt.internal.util.FullReader
|
||||
type FullReader = sbt.internal.util.FullReader
|
||||
val GlobalLogBacking = sbt.internal.util.GlobalLogBacking
|
||||
type GlobalLogBacking = sbt.internal.util.GlobalLogBacking
|
||||
val GlobalLogging = sbt.internal.util.GlobalLogging
|
||||
type GlobalLogging = sbt.internal.util.GlobalLogging
|
||||
val HCons = sbt.internal.util.HCons
|
||||
type HCons[H, T <: HList] = sbt.internal.util.HCons[H, T]
|
||||
val HList = sbt.internal.util.HList
|
||||
|
|
@ -128,12 +124,9 @@ trait Import {
|
|||
type LineReader = sbt.internal.util.LineReader
|
||||
val LoggerWriter = sbt.internal.util.LoggerWriter
|
||||
type LoggerWriter = sbt.internal.util.LoggerWriter
|
||||
val MainLogging = sbt.internal.util.MainLogging
|
||||
type MessageOnlyException = sbt.internal.util.MessageOnlyException
|
||||
type ModifiedFileInfo = sbt.internal.util.ModifiedFileInfo
|
||||
type MultiLogger = sbt.internal.util.MultiLogger
|
||||
val MultiLoggerConfig = sbt.internal.util.MultiLoggerConfig
|
||||
type MultiLoggerConfig = sbt.internal.util.MultiLoggerConfig
|
||||
val NoPosition = sbt.internal.util.NoPosition
|
||||
val PMap = sbt.internal.util.PMap
|
||||
type PMap[K[_], V[_]] = sbt.internal.util.PMap[K, V]
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ def addExtra2(s: State, extra: Seq[File]): State =
|
|||
else
|
||||
{
|
||||
val newID = ApplicationID(currentID).copy(extra = extra)
|
||||
s.setResult(Some(reload.copy(app = newID)))
|
||||
s.setNext(new State.Return(reload.copy(app = newID)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue