Merge pull request #2907 from eed3si9n/topic/logging

Split log output per channel
This commit is contained in:
eugene yokota 2017-01-14 08:34:44 -05:00 committed by GitHub
commit fd36a20183
22 changed files with 297 additions and 111 deletions

View File

@ -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
}

View File

@ -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 =

View File

@ -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
}

View File

@ -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

View File

@ -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)))

View File

@ -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

View File

@ -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
}
}
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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 =>

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 =
{

View File

@ -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

View File

@ -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
}

View File

@ -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
)

View File

@ -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

View File

@ -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")

View File

@ -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 {

View File

@ -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]

View File

@ -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)))
}
}