From 920127728e5861de3327c6575e8f2d96bd7f0c5c Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 23 Oct 2018 17:04:32 +0200 Subject: [PATCH 01/10] Remove instrumentation since git describe issue is fixed --- .drone.yml | 17 +---------------- project/BuildPlugin.scala | 17 +---------------- project/DynVerUtils.scala | 22 ---------------------- 3 files changed, 2 insertions(+), 54 deletions(-) delete mode 100644 project/DynVerUtils.scala diff --git a/.drone.yml b/.drone.yml index 8ff9e46a55..6e1cfb62ba 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,9 +4,6 @@ matrix: TEST_BLOOP: - bin/sbt-ci.sh \ "show version" \ - "show dynverInstance" \ - "show dynverGitDescribeOutput" \ - "show dynverGitPreviousStableVersion" \ "benchmarks/compile" \ "jsonConfig210/test" \ "jsonConfig211/test" \ @@ -37,9 +34,7 @@ matrix: PUBLISH_ARTIFACTS: - bin/sbt-ci-publish.sh \ - "inspect version" \ - "show dynverGitDescribeOutput" \ - "show dynverGitPreviousStableVersion" \ + "show version" \ "set pgpPublicRing in Global := file(\"/drone/.gnupg/pubring.asc\")" \ "set pgpSecretRing in Global := file(\"/drone/.gnupg/secring.asc\")" \ "docs/ghpagesPushSite" \ @@ -102,13 +97,6 @@ pipeline: - git log --oneline --graph | head -n 20 - . bin/set-up-dodo.sh # Source it because it exports variables - . bin/detect-community-build.sh # Source it because it exports variables - - git describe - - git tag -n9 - - git fetch --tags --force - - git describe - - git tag -n9 - - git describe --tags --abbrev=0 --always 9d7c2ce8e59131f8e7db9eaae5607a958e9370fe - - git --help - ${TEST_BLOOP} run_benchmarks_scripted: @@ -166,9 +154,6 @@ pipeline: OS: linux OPS: basic commands: - - git tag -l - - git fetch --tags - - git tag -l - git log --oneline --graph | head -n 20 # I have no idea why this has to be done manually... TODO: inspect why. - export DRONE_DIR="/drone" diff --git a/project/BuildPlugin.scala b/project/BuildPlugin.scala index 601ed42f69..32fc358d18 100644 --- a/project/BuildPlugin.scala +++ b/project/BuildPlugin.scala @@ -11,7 +11,7 @@ import sbt.io.syntax.fileToRichFile import sbt.librarymanagement.syntax.stringToOrganization import sbt.util.FileFunction import sbtassembly.PathList -import sbtdynver.{DynVerUtils, GitDescribeOutput} +import sbtdynver.GitDescribeOutput import ch.epfl.scala.sbt.release.ReleaseEarlyPlugin.{autoImport => ReleaseEarlyKeys} import sbt.internal.BuildLoader import sbt.librarymanagement.MavenRepository @@ -305,7 +305,6 @@ object BuildImplementation { GitHubDev("Duhemm", "Martin Duhem", "martin.duhem@gmail.com"), GitHubDev("jvican", "Jorge Vicente Cantero", "jorge@vican.me") ), - DynVerKeys.dynverGitPreviousStableVersion := BuildDefaults.dynverGitPreviousStableVersion.value ) import sbt.{CrossVersion, compilerPlugin} @@ -383,20 +382,6 @@ object BuildImplementation { sbt.BuildPaths.getStagingDirectory(state, globalBase) } - lazy val dynverGitPreviousStableVersion: Def.Initialize[Option[sbtdynver.GitDescribeOutput]] = { - Def.setting { - val firstStable = DynVerKeys.dynverGitPreviousStableVersion.value - if (!firstStable.hasNoTags) firstStable - else { - val dynver = DynVerKeys.dynverInstance.value - val secondStable = DynVerUtils.getGitPreviousStableTag(dynver.wd) - // Only use the output of the second attempt if the stable tag contains tags - if (secondStable.hasNoTags) firstStable - else secondStable - } - } - } - import sbt.librarymanagement.Artifact import ch.epfl.scala.sbt.maven.MavenPluginKeys val mavenPluginBuildSettings: Seq[Def.Setting[_]] = List( diff --git a/project/DynVerUtils.scala b/project/DynVerUtils.scala deleted file mode 100644 index 3fe36970bf..0000000000 --- a/project/DynVerUtils.scala +++ /dev/null @@ -1,22 +0,0 @@ -package sbtdynver -import java.io.File - -object DynVerUtils { - import scala.util.Try - private final val errLogger = - scala.sys.process.ProcessLogger(stdOut => println(stdOut), stdErr => println(stdErr)) - private def execAndHandleEmptyOutput(cmd: String, wd: Option[File]): Try[String] = { - println(s"Running ${cmd}") - Try(scala.sys.process.Process(cmd, wd) !! errLogger) - .filter(_.trim.nonEmpty) - } - - def getGitPreviousStableTag(wd: Option[File]): Option[GitDescribeOutput] = { - (for { - // Find the parent of the current commit. The "^2" instructs it to pick the second parent - parent <- execAndHandleEmptyOutput(s"git --no-pager log --pretty=%H -n 1 HEAD^1", wd) - // Find the closest tag of the parent commit - tag <- execAndHandleEmptyOutput(s"git describe --tags --abbrev=0 --always $parent", wd) - } yield GitDescribeOutput.parse(tag)).toOption - } -} From 40137eb6eaa5d2cfed991dd2f25e7af7ac9049df Mon Sep 17 00:00:00 2001 From: ioleo Date: Sun, 14 Oct 2018 12:15:06 +0200 Subject: [PATCH 02/10] Debug logging in context --- .../scala/bloop/DependencyResolution.scala | 4 +- .../src/main/scala/bloop/ScalaInstance.scala | 6 +- backend/src/main/scala/bloop/io/Timer.scala | 4 +- .../scala/bloop/logging/BloopLogger.scala | 59 ++++++++++--------- .../main/scala/bloop/logging/LogContext.scala | 23 ++++++++ .../src/main/scala/bloop/logging/Logger.scala | 6 ++ .../scala/bloop/logging/ProcessLogger.scala | 1 + .../scala/bloop/logging/RecordingLogger.scala | 19 +++--- .../bloop/logging/BloopLoggerBenchmark.scala | 7 ++- .../main/scala/bloop/logging/NoopLogger.scala | 2 + .../bloop/scalanative/NativeBridge.scala | 5 +- .../main/scala/bloop/scalajs/JsBridge.scala | 5 +- .../main/scala/bloop/scalajs/JsBridge.scala | 5 +- frontend/src/main/scala/bloop/Cli.scala | 7 ++- .../scala/bloop/bsp/BloopBspServices.scala | 4 +- .../src/main/scala/bloop/bsp/BspServer.scala | 13 ++-- .../src/main/scala/bloop/cli/CliOptions.scala | 4 ++ .../src/main/scala/bloop/cli/CliParsers.scala | 11 ++++ .../src/main/scala/bloop/data/Project.scala | 5 +- .../main/scala/bloop/engine/BuildLoader.scala | 6 +- .../main/scala/bloop/engine/Interpreter.scala | 3 +- .../src/main/scala/bloop/engine/State.scala | 2 +- .../bloop/engine/caches/ResultsCache.scala | 10 ++-- .../bloop/engine/tasks/CompilationTask.scala | 5 +- .../main/scala/bloop/engine/tasks/Tasks.scala | 22 ++++--- .../engine/tasks/ToolchainCompanion.scala | 4 +- .../src/main/scala/bloop/exec/Forker.scala | 25 ++++---- .../main/scala/bloop/io/SourceWatcher.scala | 8 ++- .../scala/bloop/logging/BspClientLogger.scala | 2 + .../scala/bloop/logging/BspServerLogger.scala | 6 ++ .../scala/bloop/testing/TestInternals.scala | 8 ++- .../main/scala/bloop/testing/TestServer.scala | 10 ++-- .../scala/bloop/testing/TestSuiteEvent.scala | 4 +- .../test/scala/bloop/bsp/BspClientTest.scala | 1 - .../scala/bloop/engine/FileWatchingSpec.scala | 4 +- .../scala/bloop/engine/InterpreterSpec.scala | 4 +- .../scala/bloop/logging/BloopLoggerSpec.scala | 13 ++-- .../scala/bloop/logging/BufferedLogger.scala | 2 + .../scala/bloop/logging/PublisherLogger.scala | 14 +++-- .../scala/bloop/nailgun/NailgunTest.scala | 2 +- 40 files changed, 222 insertions(+), 123 deletions(-) create mode 100644 backend/src/main/scala/bloop/logging/LogContext.scala diff --git a/backend/src/main/scala/bloop/DependencyResolution.scala b/backend/src/main/scala/bloop/DependencyResolution.scala index a123bfbee6..63461a679f 100644 --- a/backend/src/main/scala/bloop/DependencyResolution.scala +++ b/backend/src/main/scala/bloop/DependencyResolution.scala @@ -1,6 +1,6 @@ package bloop -import bloop.logging.Logger +import bloop.logging.{ LogContext, Logger } import bloop.io.AbsolutePath import sbt.librarymanagement._ @@ -45,7 +45,7 @@ object DependencyResolution { version: String, logger: Logger, additionalRepositories: Seq[Repository] = Nil): Array[AbsolutePath] = { - logger.debug(s"Resolving $organization:$module:$version") + logger.debugInContext(s"Resolving $organization:$module:$version")(LogContext.Compilation) val dependency = Dependency(Module(organization, module), version) val start = Resolution(Set(dependency)) val repositories = { diff --git a/backend/src/main/scala/bloop/ScalaInstance.scala b/backend/src/main/scala/bloop/ScalaInstance.scala index bdee6c0b20..a3a20f26fb 100644 --- a/backend/src/main/scala/bloop/ScalaInstance.scala +++ b/backend/src/main/scala/bloop/ScalaInstance.scala @@ -7,7 +7,7 @@ import java.nio.file.attribute.BasicFileAttributes import java.util.Properties import bloop.internal.build.BloopScalaInfo -import bloop.logging.Logger +import bloop.logging.{ LogContext, Logger } import scala.util.control.NonFatal @@ -91,8 +91,8 @@ object ScalaInstance { val jarsKey = allJars.map(_.underlying).sortBy(_.toString).toList if (allJars.nonEmpty) { def newInstance = { - logger.debug(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.") - jarsKey.foreach(p => logger.debug(s" => $p")) + logger.debugInContext(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.")(LogContext.Compilation) + jarsKey.foreach(p => logger.debugInContext(s" => $p")(LogContext.Compilation)) new ScalaInstance(scalaOrg, scalaName, scalaVersion, allJars.map(_.toFile).toArray) } diff --git a/backend/src/main/scala/bloop/io/Timer.scala b/backend/src/main/scala/bloop/io/Timer.scala index 4448ec3dbc..8600da9545 100644 --- a/backend/src/main/scala/bloop/io/Timer.scala +++ b/backend/src/main/scala/bloop/io/Timer.scala @@ -1,6 +1,6 @@ package bloop.io -import bloop.logging.Logger +import bloop.logging.{ LogContext, Logger } object Timer { @inline def timed[T](logger: Logger, prefix: Option[String] = None)(op: => T): T = { @@ -8,7 +8,7 @@ object Timer { try op finally { val elapsed = (System.nanoTime() - start).toDouble / 1e6 - logger.debug(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms") + logger.debugInContext(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms")(LogContext.All) } } } diff --git a/backend/src/main/scala/bloop/logging/BloopLogger.scala b/backend/src/main/scala/bloop/logging/BloopLogger.scala index c673ca71dd..013b472ee9 100644 --- a/backend/src/main/scala/bloop/logging/BloopLogger.scala +++ b/backend/src/main/scala/bloop/logging/BloopLogger.scala @@ -9,20 +9,24 @@ import com.martiansoftware.nailgun.NGCommunicator /** * Creates a logger that writes to the given streams. * - * @param name The name of this logger. - * @param out The stream to use to write `INFO` and `WARN` level messages. - * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. - * @param colorOutput print with or without color. + * @param name The name of this logger. + * @param out The stream to use to write `INFO` and `WARN` level messages. + * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. + * @param colorOutput Print with or without color. + * @param logContext Narrows logs to specified context. */ final class BloopLogger( override val name: String, out: PrintStream, err: PrintStream, private val debugCount: Int, - colorOutput: Boolean + colorOutput: Boolean, + val logContext: LogContext ) extends Logger { override def ansiCodesSupported() = true + + override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (isVerbose && logContext.isEnabled) print(msg, printDebug) override def debug(msg: String): Unit = if (isVerbose) print(msg, printDebug) override def error(msg: String): Unit = print(msg, printError) override def warn(msg: String): Unit = print(msg, printWarning) @@ -31,8 +35,8 @@ final class BloopLogger( override def isVerbose: Boolean = debugCount > 0 override def asDiscrete: Logger = - if (debugCount > 0) new BloopLogger(name, out, err, debugCount - 1, colorOutput) else this - override def asVerbose: Logger = new BloopLogger(name, out, err, debugCount + 1, colorOutput) + if (debugCount > 0) new BloopLogger(name, out, err, debugCount - 1, colorOutput, logContext) else this + override def asVerbose: Logger = new BloopLogger(name, out, err, debugCount + 1, colorOutput, logContext) @scala.annotation.tailrec private def trace(prefix: String, exception: Throwable): Unit = { @@ -54,15 +58,14 @@ final class BloopLogger( out.println(line) } - private def colored(color: String, msg: String): String = { - if (colorOutput) - s"${RESET}${color}$msg${RESET}" - else - msg - } - + private def colored(color: String, msg: String): String = { + if (colorOutput) + s"${RESET}${color}$msg${RESET}" + else + msg + } - private def printWarning(line: String): Unit = { + private def printWarning(line: String): Unit = { out.println(s"${colored(YELLOW, "[W]")} $line") } @@ -84,25 +87,27 @@ object BloopLogger { /** * Instantiates a new `BloopLogger` using the specified streams. * - * @param name The name of the logger. - * @param out The stream to use to write `INFO` and `WARN` level messages. - * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. - * @param isVerbose Tells whether the logger is verbose or not. + * @param name The name of the logger. + * @param out The stream to use to write `INFO` and `WARN` level messages. + * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. + * @param isVerbose Tells whether the logger is verbose or not. + * @param logContext Narrows logs to specified context. * @return A `BloopLogger` whose output will be written in the specified streams. */ - def at(name: String, out: PrintStream, err: PrintStream, isVerbose: Boolean, colorOutput: Boolean): BloopLogger = - new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput ) + def at(name: String, out: PrintStream, err: PrintStream, isVerbose: Boolean, colorOutput: Boolean, logContext: LogContext): BloopLogger = + new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput, logContext) /** * Instantiates a new `BloopLogger` using the specified streams. * - * @param name The name of the logger. - * @param out The stream to use to write `INFO` and `WARN` level messages. - * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. + * @param name The name of the logger. + * @param out The stream to use to write `INFO` and `WARN` level messages. + * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. + * @param logContext Narrows logs to specified context. * @return A `BloopLogger` whose output will be written in the specified streams. */ - def at(name: String, out: PrintStream, err: PrintStream, colorOutput: Boolean): BloopLogger = - at(name, out, err, false, colorOutput) + def at(name: String, out: PrintStream, err: PrintStream, colorOutput: Boolean, logContext: LogContext): BloopLogger = + at(name, out, err, false, colorOutput, logContext) /** * Instantiates a new `BloopLogger` that writes to stdout and stderr. @@ -111,6 +116,6 @@ object BloopLogger { * @return A `BloopLogger` writing to stdout and stderr. Calling this method is equivalent to * calling `at(name, System.out, System.err)`. */ - def default(name: String): BloopLogger = at(name, System.out, System.err, false) + def default(name: String): BloopLogger = at(name, System.out, System.err, false, LogContext.All) } diff --git a/backend/src/main/scala/bloop/logging/LogContext.scala b/backend/src/main/scala/bloop/logging/LogContext.scala new file mode 100644 index 0000000000..3986f533e9 --- /dev/null +++ b/backend/src/main/scala/bloop/logging/LogContext.scala @@ -0,0 +1,23 @@ +package bloop.logging + +sealed trait LogContext { self => + + import LogContext._ + + def isEnabled(implicit other: LogContext): Boolean = + (self, other) match { + case (All, _) => true + case (_, All) => true + case (ctx1, ctx2) if ctx1 == ctx2 => true + case _ => false + } +} + +object LogContext { + + case object All extends LogContext + case object FileWatching extends LogContext + case object Compilation extends LogContext + case object Test extends LogContext + case object Bsp extends LogContext +} diff --git a/backend/src/main/scala/bloop/logging/Logger.scala b/backend/src/main/scala/bloop/logging/Logger.scala index 58213d682e..71923c27c2 100644 --- a/backend/src/main/scala/bloop/logging/Logger.scala +++ b/backend/src/main/scala/bloop/logging/Logger.scala @@ -15,6 +15,12 @@ abstract class Logger extends xsbti.Logger with sbt.testing.Logger { /** Return a logger that doesn't log verbose and debug events. */ def asDiscrete: Logger + /** Context for debug logging. */ + def logContext: LogContext + + def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) debug(msg) + def debugInContext(msg: Supplier[String])(implicit ctx: LogContext): Unit = if (logContext.isEnabled) debug(msg.get()) + override def debug(msg: Supplier[String]): Unit = debug(msg.get()) override def error(msg: Supplier[String]): Unit = error(msg.get()) override def warn(msg: Supplier[String]): Unit = warn(msg.get()) diff --git a/backend/src/main/scala/bloop/logging/ProcessLogger.scala b/backend/src/main/scala/bloop/logging/ProcessLogger.scala index cf0de103ec..632ca6a819 100644 --- a/backend/src/main/scala/bloop/logging/ProcessLogger.scala +++ b/backend/src/main/scala/bloop/logging/ProcessLogger.scala @@ -21,6 +21,7 @@ class ProcessLogger(underlying: Logger, process: Process) { private[this] val processErr = process.getErrorStream def start(): Unit = { + implicit val ctx: LogContext = LogContext.All underlying.debug("Starting to log output from process...") new StreamLogger(underlying.info, processOut).start() new StreamLogger(underlying.error, processErr).start() diff --git a/backend/src/test/scala/bloop/logging/RecordingLogger.scala b/backend/src/test/scala/bloop/logging/RecordingLogger.scala index a9edced222..0347f7a3b4 100644 --- a/backend/src/test/scala/bloop/logging/RecordingLogger.scala +++ b/backend/src/test/scala/bloop/logging/RecordingLogger.scala @@ -7,7 +7,8 @@ import scala.collection.JavaConverters.asScalaIteratorConverter final class RecordingLogger( debug: Boolean = false, - debugOut: Option[PrintStream] = None + debugOut: Option[PrintStream] = None, + val logContext: LogContext = LogContext.All ) extends Logger { private[this] val messages = new ConcurrentLinkedQueue[(String, String)] @@ -31,13 +32,13 @@ final class RecordingLogger( } messages.add((key, value)) - () } - override def debug(msg: String): Unit = { add("debug", msg); () } - override def info(msg: String): Unit = { add("info", msg); () } - override def error(msg: String): Unit = { add("error", msg); () } - override def warn(msg: String): Unit = { add("warn", msg); () } + override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) add("debug", msg) + override def debug(msg: String): Unit = add("debug", msg) + override def info(msg: String): Unit = add("info", msg) + override def error(msg: String): Unit = add("error", msg) + override def warn(msg: String): Unit = add("warn", msg) override def trace(ex: Throwable): Unit = { ex.getStackTrace.foreach(ste => trace(ste.toString)) Option(ex.getCause).foreach { cause => @@ -46,9 +47,9 @@ final class RecordingLogger( } } - def serverInfo(msg: String): Unit = { add("server-info", msg); () } - def serverError(msg: String): Unit = { add("server-error", msg); () } - private def trace(msg: String): Unit = { add("trace", msg); () } + def serverInfo(msg: String): Unit = add("server-info", msg) + def serverError(msg: String): Unit = add("server-error", msg) + private def trace(msg: String): Unit = add("trace", msg) override def isVerbose: Boolean = true override def asVerbose: Logger = this diff --git a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala index de25659bb5..c9fcd062cc 100644 --- a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala +++ b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala @@ -6,7 +6,7 @@ import org.openjdk.jmh.annotations.Benchmark object BloopLoggerBenchmark { private val devnull = new PrintStream(_ => ()) - val logger = BloopLogger.at("benchmark", devnull, devnull, false) + val logger = BloopLogger.at("benchmark", devnull, devnull, false, LogContext.All) } class BloopLoggerBenchmark { @@ -30,4 +30,9 @@ class BloopLoggerBenchmark { def logDebug(): Unit = { BloopLoggerBenchmark.logger.debug("message") } + + @Benchmark + def logDebugInContext(): Unit = { + BloopLoggerBenchmark.logger.debugInContext("message")(LogContext.All) + } } diff --git a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala index 2d1e29ee33..1fd8dfe52e 100644 --- a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala +++ b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala @@ -3,8 +3,10 @@ package bloop.logging object NoopLogger extends Logger { override def name: String = "NoopLogger" override def ansiCodesSupported(): Boolean = false + override def logContext: LogContext = LogContext.All override def debug(msg: String): Unit = () + override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = () override def error(msg: String): Unit = () override def warn(msg: String): Unit = () override def trace(exception: Throwable): Unit = () diff --git a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala index 279b3874f4..73175d2053 100644 --- a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala +++ b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala @@ -1,7 +1,7 @@ package bloop.scalanative import bloop.config.Config.{LinkerMode, NativeConfig} import bloop.io.Paths -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import java.nio.file.{Files, Path} import bloop.data.Project @@ -9,6 +9,9 @@ import bloop.data.Project import scala.scalanative.build.{Build, Config, Discover, GC, Mode, Logger => NativeLogger} object NativeBridge { + + private implicit val ctx: LogContext = LogContext.All + def nativeLink(config0: NativeConfig, project: Project, entry: String, target: Path, logger: Logger): Path = { val workdir = project.out.resolve("native") if (workdir.isDirectory) Paths.delete(workdir) diff --git a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala index e4d154c1f4..4444d7fc2a 100644 --- a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala @@ -9,18 +9,19 @@ import org.scalajs.core.tools.linker.{ModuleInitializer, StandardLinker} import org.scalajs.core.tools.logging.{Level, Logger => JsLogger} import bloop.config.Config.{JsConfig, LinkerMode, ModuleKindJS} import bloop.data.Project -import bloop.logging.{Logger => BloopLogger} +import bloop.logging.{LogContext, Logger => BloopLogger} import org.scalajs.core.tools.linker.backend.ModuleKind import org.scalajs.core.tools.sem.Semantics object JsBridge { + private class Logger(logger: BloopLogger) extends JsLogger { override def log(level: Level, message: => String): Unit = level match { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message) + case Level.Debug => logger.debugInContext(message)(LogContext.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala index d666a33891..cb3f544ba1 100644 --- a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala @@ -6,19 +6,20 @@ import org.scalajs.io.AtomicWritableFileVirtualJSFile import bloop.config.Config.{JsConfig, LinkerMode, ModuleKindJS} import bloop.data.Project import bloop.io.Paths -import bloop.logging.{Logger => BloopLogger} +import bloop.logging.{LogContext, Logger => BloopLogger} import org.scalajs.linker.irio.{FileScalaJSIRContainer, FileVirtualScalaJSIRFile, IRFileCache} import org.scalajs.linker.{ModuleInitializer, ModuleKind, Semantics, StandardLinker} import org.scalajs.logging.{Level, Logger => JsLogger} object JsBridge { + private class Logger(logger: BloopLogger) extends JsLogger { override def log(level: Level, message: => String): Unit = level match { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message) + case Level.Debug => logger.debugInContext(message)(LogContext.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/frontend/src/main/scala/bloop/Cli.scala b/frontend/src/main/scala/bloop/Cli.scala index 7d17d771c0..bff1c1da1a 100644 --- a/frontend/src/main/scala/bloop/Cli.scala +++ b/frontend/src/main/scala/bloop/Cli.scala @@ -8,7 +8,7 @@ import bloop.bsp.BspServer import bloop.cli.validation.Validate import bloop.cli.{CliOptions, CliParsers, Commands, CommonOptions, ExitStatus} import bloop.engine._ -import bloop.logging.{BloopLogger, Logger} +import bloop.logging.{BloopLogger, LogContext, Logger} import caseapp.core.{DefaultBaseCommand, Messages} import com.martiansoftware.nailgun.NGContext import _root_.monix.eval.Task @@ -269,7 +269,8 @@ object Cli { commonOpts.out, commonOpts.err, cliOptions.verbose, - !(cliOptions.noColor || commonOpts.env.containsKey("NO_COLOR")) + !(cliOptions.noColor || commonOpts.env.containsKey("NO_COLOR")), + cliOptions.debug ) val currentState = State.loadActiveStateFor(configDirectory, pool, cliOptions.common, logger) @@ -310,7 +311,7 @@ object Cli { val ngout = cliOptions.common.ngout def logElapsed(since: Long): Unit = { val elapsed = (System.nanoTime() - since).toDouble / 1e6 - logger.debug(s"Elapsed: $elapsed ms") + logger.debugInContext(s"Elapsed: $elapsed ms")(LogContext.All) } // Simulate try-catch-finally with monix tasks to time the task execution diff --git a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala index 3b7eb751e0..9e103950e7 100644 --- a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala +++ b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala @@ -11,7 +11,7 @@ import bloop.data.Project import bloop.engine.tasks.{ScalaJsToolchain, ScalaNativeToolchain, Tasks} import bloop.engine.{Action, Dag, Exit, Interpreter, Run, State} import bloop.io.{AbsolutePath, RelativePath} -import bloop.logging.BspServerLogger +import bloop.logging.{BspServerLogger, LogContext} import bloop.testing.{BspLoggingEventHandler, TestInternals} import monix.eval.Task import ch.epfl.scala.bsp.{BuildTargetIdentifier, endpoints} @@ -64,7 +64,7 @@ final class BloopBspServices( private val pool = callSiteState.pool private val defaultOpts = callSiteState.commonOptions def reloadState(config: AbsolutePath): Task[State] = { - bspForwarderLogger.debug(s"Reloading bsp state for ${config.syntax}") + bspForwarderLogger.debugInContext(s"Reloading bsp state for ${config.syntax}")(LogContext.Bsp) State.loadActiveStateFor(config, pool, defaultOpts, bspForwarderLogger).map { state0 => state0.copy(logger = bspForwarderLogger, commonOptions = latestState.commonOptions) } diff --git a/frontend/src/main/scala/bloop/bsp/BspServer.scala b/frontend/src/main/scala/bloop/bsp/BspServer.scala index dbd3233609..0f3ef8ce08 100644 --- a/frontend/src/main/scala/bloop/bsp/BspServer.scala +++ b/frontend/src/main/scala/bloop/bsp/BspServer.scala @@ -6,7 +6,7 @@ import java.util.Locale import bloop.cli.Commands import bloop.engine.State import bloop.io.{AbsolutePath, RelativePath} -import bloop.logging.{BspClientLogger, Slf4jAdapter} +import bloop.logging.{BspClientLogger, LogContext} import com.martiansoftware.nailgun.{NGUnixDomainServerSocket, NGWin32NamedPipeServerSocket} import monix.eval.Task import monix.execution.Scheduler @@ -15,6 +15,9 @@ import monix.execution.atomic.Atomic import scala.meta.jsonrpc.{BaseProtocolMessage, LanguageClient, LanguageServer} object BspServer { + + private implicit val logContext: LogContext = LogContext.Bsp + private[bloop] val isWindows: Boolean = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") private[bloop] val isMac: Boolean = @@ -34,16 +37,16 @@ object BspServer { cmd match { case Commands.WindowsLocalBsp(pipeName, _) => val server = new NGWin32NamedPipeServerSocket(pipeName) - state.logger.debug(s"Waiting for a connection at pipe $pipeName...") + state.logger.debugInContext(s"Waiting for a connection at pipe $pipeName...") Task(WindowsLocal(pipeName, server)).doOnCancel(Task(server.close())) case Commands.UnixLocalBsp(socketFile, _) => val server = new NGUnixDomainServerSocket(socketFile.toString) - state.logger.debug(s"Waiting for a connection at $socketFile...") + state.logger.debugInContext(s"Waiting for a connection at $socketFile...") Task(UnixLocal(socketFile, server)).doOnCancel(Task(server.close())) case Commands.TcpBsp(address, portNumber, _) => val socketAddress = new InetSocketAddress(address, portNumber) val server = new java.net.ServerSocket(portNumber, 10, address) - state.logger.debug(s"Waiting for a connection at $socketAddress...") + state.logger.debugInContext(s"Waiting for a connection at $socketAddress...") Task(Tcp(socketAddress, server)).doOnCancel(Task(server.close())) } } @@ -77,7 +80,7 @@ object BspServer { import state.logger def startServer(handle: ConnectionHandle): Task[State] = { val connectionURI = uri(handle) - logger.debug(s"The server is listening for incoming connections at $connectionURI...") + logger.debugInContext(s"The server is listening for incoming connections at $connectionURI...") val socket = handle.serverSocket.accept() logger.info(s"Accepted incoming BSP client connection at $connectionURI") diff --git a/frontend/src/main/scala/bloop/cli/CliOptions.scala b/frontend/src/main/scala/bloop/cli/CliOptions.scala index 171bc531bf..3e00729b0c 100644 --- a/frontend/src/main/scala/bloop/cli/CliOptions.scala +++ b/frontend/src/main/scala/bloop/cli/CliOptions.scala @@ -2,6 +2,7 @@ package bloop.cli import java.nio.file.Path +import bloop.logging.LogContext import caseapp.{ExtraName, HelpMessage, Recurse, ValueDescription} case class CliOptions( @@ -18,6 +19,9 @@ case class CliOptions( verbose: Boolean = false, @HelpMessage("If set, do not color output. Defaults to false.") noColor: Boolean = false, + @HelpMessage("Debug log context.") + @ValueDescription("file-watching") + debug: LogContext = LogContext.All, @Recurse common: CommonOptions = CommonOptions.default, ) diff --git a/frontend/src/main/scala/bloop/cli/CliParsers.scala b/frontend/src/main/scala/bloop/cli/CliParsers.scala index b3f05a05cb..58a64b2fe9 100644 --- a/frontend/src/main/scala/bloop/cli/CliParsers.scala +++ b/frontend/src/main/scala/bloop/cli/CliParsers.scala @@ -4,6 +4,7 @@ import java.io.{InputStream, PrintStream} import java.nio.file.{Path, Paths} import java.util.Properties +import bloop.logging.LogContext import caseapp.CommandParser import caseapp.core.{ArgParser, Default, DefaultBaseCommand, Parser} import caseapp.util.Implicit @@ -60,6 +61,16 @@ object CliParsers { } } + implicit val logContextParser: ArgParser[LogContext] = + ArgParser.instance[LogContext]("\"all\" | \"file-watching\" | \"compilation\" | \"test\" | \"bsp\"") { + case "all" => Right(LogContext.All) + case "file-watching" => Right(LogContext.FileWatching) + case "compilation" => Right(LogContext.Compilation) + case "test" => Right(LogContext.Test) + case "bsp" => Right(LogContext.Bsp) + case w00t => Left(s"Unrecognized log context: $w00t") + } + import shapeless.{HNil, CNil, :+:, ::, Coproduct} implicit val implicitHNil: Implicit[HNil] = Implicit.hnil implicit val implicitNone: Implicit[None.type] = Implicit.instance(None) diff --git a/frontend/src/main/scala/bloop/data/Project.scala b/frontend/src/main/scala/bloop/data/Project.scala index 35f5e2969c..2353ca807f 100644 --- a/frontend/src/main/scala/bloop/data/Project.scala +++ b/frontend/src/main/scala/bloop/data/Project.scala @@ -7,7 +7,7 @@ import java.nio.file.attribute.FileTime import scala.util.Try import bloop.exec.JavaEnv import bloop.io.AbsolutePath -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import xsbti.compile.{ClasspathOptions, CompileOrder} import bloop.ScalaInstance import bloop.bsp.ProjectUris @@ -73,6 +73,7 @@ final case class Project( } object Project { + final implicit val ps: scalaz.Show[Project] = new scalaz.Show[Project] { override def shows(f: Project): String = f.name } @@ -142,7 +143,7 @@ object Project { def fromBytesAndOrigin(bytes: Array[Byte], origin: Origin, logger: Logger): Project = { import _root_.io.circe.parser - logger.debug(s"Loading project from '${origin.path}'") + logger.debugInContext(s"Loading project from '${origin.path}'")(LogContext.All) val contents = new String(bytes, StandardCharsets.UTF_8) parser.parse(contents) match { case Left(failure) => throw failure diff --git a/frontend/src/main/scala/bloop/engine/BuildLoader.scala b/frontend/src/main/scala/bloop/engine/BuildLoader.scala index 9f0d3d2044..f9ab645371 100644 --- a/frontend/src/main/scala/bloop/engine/BuildLoader.scala +++ b/frontend/src/main/scala/bloop/engine/BuildLoader.scala @@ -3,7 +3,7 @@ package bloop.engine import bloop.data.{Origin, Project} import bloop.io.Paths.AttributedPath import bloop.io.AbsolutePath -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import bloop.util.ByteHasher import monix.eval.Task @@ -37,7 +37,7 @@ object BuildLoader { configFiles: List[Build.ReadConfiguration], logger: Logger ): Task[List[Project]] = { - logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...") + logger.debugInContext(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) val all = configFiles.map(f => Task(Project.fromBytesAndOrigin(f.bytes, f.origin, logger))) Task.gatherUnordered(all).executeOn(ExecutionContext.scheduler) } @@ -83,7 +83,7 @@ object BuildLoader { Build.ReadConfiguration(Origin(ap, hash), bytes) } - logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...") + logger.debugInContext(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) configFiles.map(f => Project.fromBytesAndOrigin(f.bytes, f.origin, logger)) } } diff --git a/frontend/src/main/scala/bloop/engine/Interpreter.scala b/frontend/src/main/scala/bloop/engine/Interpreter.scala index 2c2de388ca..47ee3776c4 100644 --- a/frontend/src/main/scala/bloop/engine/Interpreter.scala +++ b/frontend/src/main/scala/bloop/engine/Interpreter.scala @@ -7,6 +7,7 @@ import bloop.cli.CliParsers.CommandsMessages import bloop.cli.completion.{Case, Mode} import bloop.config.Config.Platform import bloop.io.{AbsolutePath, RelativePath, SourceWatcher} +import bloop.logging.LogContext import bloop.testing.{LoggingEventHandler, TestInternals} import bloop.engine.tasks.{CompilationTask, ScalaJsToolchain, ScalaNativeToolchain, Tasks} import bloop.cli.Commands.{CompilingCommand, LinkingCommand} @@ -165,7 +166,7 @@ object Interpreter { logger.info(contents) } else { val configDirectory = state.build.origin.syntax - logger.debug(s"Projects loaded from '$configDirectory':") + logger.debugInContext(s"Projects loaded from '$configDirectory':")(LogContext.All) state.build.projects.map(_.name).sorted.foreach(logger.info) } diff --git a/frontend/src/main/scala/bloop/engine/State.scala b/frontend/src/main/scala/bloop/engine/State.scala index ee004e73e4..670398ea87 100644 --- a/frontend/src/main/scala/bloop/engine/State.scala +++ b/frontend/src/main/scala/bloop/engine/State.scala @@ -5,7 +5,7 @@ import bloop.cli.{CommonOptions, ExitStatus} import bloop.data.Project import bloop.engine.caches.{ResultsCache, StateCache} import bloop.io.Paths -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import monix.eval.Task /** diff --git a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala index b065d2db65..2ed9404999 100644 --- a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala +++ b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala @@ -8,7 +8,7 @@ import bloop.Compiler.Result import bloop.engine.tasks.compilation.FinalCompileResult import bloop.engine.{Build, ExecutionContext} import bloop.io.AbsolutePath -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import bloop.reporter.Reporter import monix.eval.Task import xsbti.compile.{CompileAnalysis, MiniSetup, PreviousResult} @@ -82,6 +82,8 @@ final class ResultsCache private ( object ResultsCache { import java.util.concurrent.ConcurrentHashMap + private implicit val logContext: LogContext = LogContext.All + // TODO: Use a guava cache that stores maximum 200 analysis file private[bloop] val persisted = ConcurrentHashMap.newKeySet[PreviousResult]() @@ -107,19 +109,19 @@ object ResultsCache { val contents = FileAnalysisStore.binary(analysisFile.toFile).get().toOption contents match { case Some(res) => - logger.debug(s"Loading previous analysis for '${p.name}' from '$analysisFile'.") + logger.debugInContext(s"Loading previous analysis for '${p.name}' from '$analysisFile'.") val r = PreviousResult.of(Optional.of(res.getAnalysis), Optional.of(res.getMiniSetup)) val reporter = Reporter.fromAnalysis(res.getAnalysis, cwd, logger) Result.Success(reporter, r, 0L) case None => - logger.debug(s"Analysis '$analysisFile' for '${p.name}' is empty.") + logger.debugInContext(s"Analysis '$analysisFile' for '${p.name}' is empty.") Result.Empty } } } else { Task.now { - logger.debug(s"Missing analysis file for project '${p.name}'") + logger.debugInContext(s"Missing analysis file for project '${p.name}'") Result.Empty } } diff --git a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala index 092ab00404..3412919469 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala @@ -5,13 +5,14 @@ import bloop.data.Project import bloop.engine._ import bloop.engine.tasks.compilation._ import bloop.io.AbsolutePath -import bloop.logging.{BspServerLogger, Logger} +import bloop.logging.{BspServerLogger, LogContext, Logger} import bloop.reporter._ import bloop.{CompileInputs, CompileMode, Compiler} import monix.eval.Task import sbt.internal.inc.AnalyzingCompiler object CompilationTask { + private implicit val logContext: LogContext = LogContext.Compilation private val dateFormat = new java.text.SimpleDateFormat("HH:mm:ss.SSS") private def currentTime: String = dateFormat.format(new java.util.Date()) @@ -119,7 +120,7 @@ object CompilationTask { graphInputs.irPromise.get match { case Array() if pipeline => logger.warn(s"Project ${project.name} compiled without pipelined compilation.") - case _ => logger.debug(s"The pickle promise of ${project.name} completed in Zinc.") + case _ => logger.debugInContext(s"The pickle promise of ${project.name} completed in Zinc.") } } diff --git a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala index 9623458f24..76870203b9 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala @@ -8,6 +8,7 @@ import bloop.engine.caches.ResultsCache import bloop.engine.{Dag, State} import bloop.exec.Forker import bloop.io.AbsolutePath +import bloop.logging.LogContext import bloop.testing.{DiscoveredTests, LoggingEventHandler, TestInternals, TestSuiteEvent, TestSuiteEventHandler} import bloop.data.Project import monix.eval.Task @@ -56,7 +57,7 @@ object Tasks { case Some(instance) => val classpath = project.classpath val entries = classpath.map(_.underlying.toFile).toSeq - logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}") + logger.debugInContext(s"Setting up the console classpath with ${entries.mkString(", ")}")(LogContext.All) val loader = ClasspathUtilities.makeLoader(entries, instance) val compiler = state.compilerCache.get(instance).scalac.asInstanceOf[AnalyzingCompiler] val opts = ClasspathOptionsUtil.repl @@ -127,6 +128,8 @@ object Tasks { ): Task[State] = { import state.logger import bloop.util.JavaCompat.EnrichOptional + implicit val logContext: LogContext = LogContext.Test + def foundFrameworks(frameworks: List[Framework]) = frameworks.map(_.name).mkString(", ") // Test arguments coming after `--` can only be used if only one mapping is found @@ -138,7 +141,7 @@ object Tasks { case Nil => Nil case oneFramework :: Nil => val rawArgs = frameworkSpecificRawArgs - logger.debug(s"Test options '$rawArgs' assigned to the only found framework $cls'.") + logger.debugInContext(s"Test options '$rawArgs' assigned to the only found framework $cls'.") List(Config.TestArgument(rawArgs, Some(Config.TestFramework(cls)))) case frameworks => val frameworkNames = foundFrameworks(frameworks) @@ -163,7 +166,7 @@ object Tasks { val testLoader = forker.newClassLoader(Some(TestInternals.filteredLoader)) val frameworks = project.testFrameworks .flatMap(f => TestInternals.loadFramework(testLoader, f.names, logger)) - logger.debug(s"Found frameworks: ${foundFrameworks(frameworks)}") + logger.debugInContext(s"Found frameworks: ${foundFrameworks(frameworks)}") val frameworkArgs = considerFrameworkArgs(frameworks) val lastCompileResult = state.results.lastSuccessfulResultOrEmpty(project) @@ -189,9 +192,9 @@ object Tasks { val allNames = ungroupedTests.map(_._2.fullyQualifiedName).mkString(", ") val includedNames = includedTests.map(_._2.fullyQualifiedName).mkString(", ") val excludedNames = excludedTests.map(_._2.fullyQualifiedName).mkString(", ") - logger.debug(s"Bloop found the following tests for $projectName: $allNames") - logger.debug(s"The following tests were included by the filter: $includedNames") - logger.debug(s"The following tests were excluded by the filter: $excludedNames") + logger.debugInContext(s"Bloop found the following tests for $projectName: $allNames") + logger.debugInContext(s"The following tests were included by the filter: $includedNames") + logger.debugInContext(s"The following tests were excluded by the filter: $excludedNames") } DiscoveredTests(testLoader, includedTests.groupBy(_._1).mapValues(_.map(_._2))) @@ -214,7 +217,7 @@ object Tasks { } } - logger.debug(s"Running test suites with arguments: $args") + logger.debugInContext(s"Running test suites with arguments: $args") TestInternals.execute(cwd, forker, discovered, args, failureHandler, logger, opts) } @@ -222,7 +225,7 @@ object Tasks { Task.sequence(testTasks).map { exitCodes => // When the test execution is over report no matter what the result is testEventHandler.report() - logger.debug(s"Test suites failed: $failure") + logger.debugInContext(s"Test suites failed: $failure") val isOk = !failure && exitCodes.forall(_ == 0) if (isOk) state.mergeStatus(ExitStatus.Ok) else state.copy(status = ExitStatus.TestExecutionError) @@ -284,6 +287,7 @@ object Tasks { def findMainClasses(state: State, project: Project): List[String] = { import state.logger import bloop.util.JavaCompat.EnrichOptional + val analysis = state.results.lastSuccessfulResultOrEmpty(project).analysis().toOption match { case Some(analysis: Analysis) => analysis case _ => @@ -292,7 +296,7 @@ object Tasks { } val mainClasses = analysis.infos.allInfos.values.flatMap(_.getMainClasses).toList - logger.debug(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}") + logger.debugInContext(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}")(LogContext.All) mainClasses } diff --git a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala index a7f854b027..57d8727811 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala @@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap import bloop.DependencyResolution import bloop.config.Config import bloop.internal.build.BuildInfo -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} /** * Base class for companion objects of toolchains. @@ -68,7 +68,7 @@ abstract class ToolchainCompanion[Toolchain] { private final val BloopVersion = BuildInfo.version private final val BloopOrg = BuildInfo.organization private def resolveJars(artifactName: String, logger: Logger): List[Path] = { - logger.debug(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion") + logger.debugInContext(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion")(LogContext.Compilation) val files = DependencyResolution.resolve(BloopOrg, artifactName, BloopVersion, logger) files.iterator.map(_.underlying).filter(_.toString.endsWith(".jar")).toList } diff --git a/frontend/src/main/scala/bloop/exec/Forker.scala b/frontend/src/main/scala/bloop/exec/Forker.scala index 6af9da12bd..b33b9385e0 100644 --- a/frontend/src/main/scala/bloop/exec/Forker.scala +++ b/frontend/src/main/scala/bloop/exec/Forker.scala @@ -10,7 +10,7 @@ import java.util.concurrent.{ConcurrentHashMap, TimeUnit} import bloop.cli.{CommonOptions, ExitStatus} import bloop.engine.ExecutionContext import bloop.io.AbsolutePath -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import com.zaxxer.nuprocess.{NuAbstractProcessHandler, NuProcess} import monix.eval.Task import monix.execution.Cancelable @@ -72,7 +72,7 @@ final case class Forker(javaEnv: JavaEnv, classpath: Array[AbsolutePath]) { | classpath = '$fullClasspath' | java_home = '${javaEnv.javaHome}' | java_options = '${javaEnv.javaOptions.mkString(" ")}""".stripMargin - Task(logger.debug(debugOptions)) + Task(logger.debugInContext(debugOptions)(LogContext.All)) } else Task.unit logTask.flatMap(_ => Forker.run(cwd, cmd, logger, opts)) } @@ -81,6 +81,8 @@ final case class Forker(javaEnv: JavaEnv, classpath: Array[AbsolutePath]) { object Forker { + private implicit val logContext: LogContext = LogContext.All + /** The code returned after a successful execution. */ private final val EXIT_OK = 0 @@ -119,21 +121,22 @@ object Forker { var gobbleInput: Cancelable = null final class ProcessHandler extends NuAbstractProcessHandler { override def onStart(nuProcess: NuProcess): Unit = { - logger.debug(s"""Starting forked process: - | cwd = '$cwd' - | pid = '${nuProcess.getPID}' - | cmd = '${cmd.mkString(" ")}'""".stripMargin) + logger.debugInContext( + s"""Starting forked process: + | cwd = '$cwd' + | pid = '${nuProcess.getPID}' + | cmd = '${cmd.mkString(" ")}'""".stripMargin) } override def onExit(statusCode: Int): Unit = - logger.debug(s"Forked process exited with code: $statusCode") + logger.debugInContext(s"Forked process exited with code: $statusCode") val outBuilder = StringBuilder.newBuilder override def onStdout(buffer: ByteBuffer, closed: Boolean): Unit = { if (closed) { // Make sure that the gobbler never stays awake! if (gobbleInput != null) gobbleInput.cancel() - logger.debug("The process is closed. Emptying buffer...") + logger.debugInContext("The process is closed. Emptying buffer...") val remaining = outBuilder.mkString if (!remaining.isEmpty) logger.info(remaining) @@ -182,7 +185,7 @@ object Forker { Task { try { val exitCode = process.waitFor(0, _root_.java.util.concurrent.TimeUnit.SECONDS) - logger.debug(s"Process ${process.getPID} exited with code: $exitCode") + logger.debugInContext(s"Process ${process.getPID} exited with code: $exitCode") exitCode } finally { shutdownInput = true @@ -198,11 +201,11 @@ object Forker { if (process.isRunning) { val msg = s"The cancellation could not destroy process ${process.getPID}" opts.ngout.println(msg) - logger.debug(msg) + logger.debugInContext(msg) } else { val msg = s"The run process ${process.getPID} has been closed" opts.ngout.println(msg) - logger.debug(msg) + logger.debugInContext(msg) } } }) diff --git a/frontend/src/main/scala/bloop/io/SourceWatcher.scala b/frontend/src/main/scala/bloop/io/SourceWatcher.scala index a0a0268b7f..e94c880fe4 100644 --- a/frontend/src/main/scala/bloop/io/SourceWatcher.scala +++ b/frontend/src/main/scala/bloop/io/SourceWatcher.scala @@ -5,7 +5,7 @@ import java.nio.file.{Files, Path} import bloop.data.Project import bloop.bsp.BspServer import bloop.engine.{ExecutionContext, State} -import bloop.logging.{Logger, Slf4jAdapter} +import bloop.logging.{LogContext, Logger, Slf4jAdapter} import bloop.monix.FoldLeftAsyncConsumer import scala.collection.JavaConverters._ @@ -24,13 +24,15 @@ final class SourceWatcher private ( import java.nio.file.Files private val slf4jLogger = new Slf4jAdapter(logger) + private implicit val logContext: LogContext = LogContext.FileWatching + def watch(state0: State, action: State => Task[State]): Task[State] = { val ngout = state0.commonOptions.ngout def runAction(state: State, event: DirectoryChangeEvent): Task[State] = { // Someone that wants this to be supported by Windows will need to make it work for all terminals if (!BspServer.isWindows) logger.info("\u001b[H\u001b[2J") // Clean the terminal before acting on the file event action - logger.debug(s"A ${event.eventType()} in ${event.path()} has triggered an event") + logger.debugInContext(s"A ${event.eventType()} in ${event.path()} has triggered an event") action(state) } @@ -74,7 +76,7 @@ final class SourceWatcher private ( val watchController = Task { try watcherHandle.get() finally watcher.close() - logger.debug("File watcher was successfully closed") + logger.debugInContext("File watcher was successfully closed") } val watchCancellation = Cancelable { () => diff --git a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala index 8b78881ae0..e705b8e5bd 100644 --- a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala @@ -7,7 +7,9 @@ final class BspClientLogger[L <: Logger](val underlying: L) extends Logger with scribe.LoggerSupport { + override val logContext: LogContext = underlying.logContext override val name: String = underlying.name + override def isVerbose: Boolean = underlying.isVerbose override def asDiscrete: Logger = new BspClientLogger(underlying.asDiscrete) override def asVerbose: Logger = new BspClientLogger(underlying.asVerbose) diff --git a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala index fd8bc0728f..cd439a109a 100644 --- a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala @@ -24,6 +24,8 @@ final class BspServerLogger private ( ) extends Logger with scribe.LoggerSupport { + override def logContext: LogContext = underlying.logContext + override def isVerbose: Boolean = underlying.isVerbose override def asDiscrete: Logger = new BspServerLogger(name, underlying.asDiscrete, client, ansiSupported) @@ -31,7 +33,11 @@ final class BspServerLogger private ( new BspServerLogger(name, underlying.asVerbose, client, ansiSupported) override def ansiCodesSupported: Boolean = ansiSupported || underlying.ansiCodesSupported() + + override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = underlying.debug(msg) + override def debug(msg: String): Unit = underlying.debug(msg) + override def trace(t: Throwable): Unit = underlying.trace(t) override def error(msg: String): Unit = { diff --git a/frontend/src/main/scala/bloop/testing/TestInternals.scala b/frontend/src/main/scala/bloop/testing/TestInternals.scala index c270dcd8bd..baf52a84c9 100644 --- a/frontend/src/main/scala/bloop/testing/TestInternals.scala +++ b/frontend/src/main/scala/bloop/testing/TestInternals.scala @@ -8,7 +8,7 @@ import bloop.config.Config import bloop.engine.ExecutionContext import bloop.exec.Forker import bloop.io.AbsolutePath -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import monix.eval.Task import sbt.testing.{AnnotatedFingerprint, EventHandler, Fingerprint, SubclassFingerprint} import org.scalatools.testing.{Framework => OldFramework} @@ -28,6 +28,8 @@ object TestInternals { private final val testAgentId = "test-agent" private final val testAgentVersion = "1.0.4" + private implicit val logContext: LogContext = LogContext.Test + // Cache the resolution of test agent files since it's static (cannot be lazy because depends on logger) @volatile private var testAgentFiles: Option[Array[AbsolutePath]] = None @@ -96,7 +98,7 @@ object TestInternals { logger: Logger, opts: CommonOptions ): Task[Int] = { - logger.debug("Starting forked test execution...") + logger.debugInContext("Starting forked test execution...") // Make sure that we cache the resolution of the test agent JAR and we don't repeat it every time val agentFiles = lazyTestAgents(logger) @@ -105,7 +107,7 @@ object TestInternals { val forkMain = classOf[sbt.ForkMain].getCanonicalName val arguments = Array(server.port.toString) val testAgentJars = agentFiles.filter(_.underlying.toString.endsWith(".jar")) - logger.debug("Test agent JARs: " + testAgentJars.mkString(", ")) + logger.debugInContext("Test agent JARs: " + testAgentJars.mkString(", ")) val listener = server.listenToTests val runner = forker.runMain(cwd, forkMain, arguments, logger, opts, testAgentJars) diff --git a/frontend/src/main/scala/bloop/testing/TestServer.scala b/frontend/src/main/scala/bloop/testing/TestServer.scala index 1f90128b2c..8fef90630c 100644 --- a/frontend/src/main/scala/bloop/testing/TestServer.scala +++ b/frontend/src/main/scala/bloop/testing/TestServer.scala @@ -7,7 +7,7 @@ import bloop.cli.CommonOptions import bloop.config.Config import scala.util.control.NonFatal -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import monix.eval.Task import sbt.{ForkConfiguration, ForkTags} import sbt.testing.{Event, TaskDef} @@ -27,6 +27,8 @@ final class TestServer( opts: CommonOptions ) { + private implicit val logContext: LogContext = LogContext.Test + private val server = new ServerSocket(0) private val frameworks = discoveredTests.tests.keys private val tasks = discoveredTests.tests.values.flatten @@ -94,13 +96,13 @@ final class TestServer( val serverStarted = Promise[Unit]() val clientConnection = Task { - logger.debug(s"Firing up test server at $port. Waiting for client...") + logger.debugInContext(s"Firing up test server at $port. Waiting for client...") serverStarted.trySuccess(()) server.accept() } val testListeningTask = clientConnection.flatMap { socket => - logger.debug("Test server established connection with remote JVM.") + logger.debugInContext("Test server established connection with remote JVM.") val os = new ObjectOutputStream(socket.getOutputStream) os.flush() val is = new ObjectInputStream(socket.getInputStream) @@ -132,7 +134,7 @@ final class TestServer( server.close() // Do both just in case the logger streams have been closed by nailgun opts.ngout.println("The test execution was successfully cancelled.") - logger.debug("Test server has been successfully closed.") + logger.debugInContext("Test server has been successfully closed.") } val listener = testListeningTask diff --git a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala index 045f33a405..81ae2c08f0 100644 --- a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala +++ b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala @@ -1,6 +1,6 @@ package bloop.testing -import bloop.logging.Logger +import bloop.logging.{LogContext, Logger} import bloop.util.TimeFormat import ch.epfl.scala.bsp import ch.epfl.scala.bsp.BuildTargetIdentifier @@ -44,7 +44,7 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler { case TestSuiteEvent.Error(message) => logger.error(message) case TestSuiteEvent.Warn(message) => logger.warn(message) case TestSuiteEvent.Info(message) => logger.info(message) - case TestSuiteEvent.Debug(message) => logger.debug(message) + case TestSuiteEvent.Debug(message) => logger.debugInContext(message)(LogContext.Test) case TestSuiteEvent.Trace(throwable) => logger.error("Test suite aborted.") logger.trace(throwable) diff --git a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala index 3d85e679e8..b6ee563853 100644 --- a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala +++ b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala @@ -8,7 +8,6 @@ import bloop.logging.{BspClientLogger, RecordingLogger, Slf4jAdapter} import bloop.tasks.TestUtil import ch.epfl.scala.bsp import ch.epfl.scala.bsp.endpoints -import junit.framework.Assert import monix.execution.{ExecutionModel, Scheduler} import monix.{eval => me} import org.scalasbt.ipcsocket.Win32NamedPipeSocket diff --git a/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala b/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala index 90cf974745..e741ee1ee6 100644 --- a/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala +++ b/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala @@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit import bloop.data.Project import bloop.cli.Commands -import bloop.logging.{Logger, PublisherLogger} +import bloop.logging.{LogContext, Logger, PublisherLogger} import bloop.exec.JavaEnv import bloop.io.AbsolutePath import bloop.io.Paths.delete @@ -109,7 +109,7 @@ class FileWatchingSpec { import ExecutionContext.ioScheduler val (observer, observable) = Observable.multicast[(String, String)](MulticastStrategy.publish)(ioScheduler) - val logger = new PublisherLogger(observer, debug = debug) + val logger = new PublisherLogger(observer, debug = debug, LogContext.All) // Let's modify the project to add special sources to check the right behaviour of the watcher val (state, singleFile) = { diff --git a/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala b/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala index 4a782727e0..9ead81828c 100644 --- a/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala +++ b/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala @@ -4,7 +4,7 @@ import java.io.{ByteArrayOutputStream, PrintStream} import java.util.UUID import bloop.cli.{CliOptions, Commands} -import bloop.logging.BloopLogger +import bloop.logging.{LogContext, BloopLogger} import bloop.tasks.TestUtil import org.junit.Test import org.junit.experimental.categories.Category @@ -52,7 +52,7 @@ object InterpreterSpec { val inMemory = new ByteArrayOutputStream() val newOut = new PrintStream(inMemory) val loggerName = UUID.randomUUID().toString - val newLogger = BloopLogger.at(loggerName, newOut, newOut, false) + val newLogger = BloopLogger.at(loggerName, newOut, newOut, false, LogContext.All) val defaultCli = CliOptions.default val newCommonOptions = state.commonOptions.copy(out = newOut) val newState = state.copy(logger = newLogger, commonOptions = newCommonOptions) diff --git a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala index 8ee3414b95..9165879f3e 100644 --- a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala +++ b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala @@ -17,6 +17,9 @@ import org.junit.experimental.categories.Category @Category(Array(classOf[bloop.FastTests])) class BloopLoggerSpec { + + private implicit val ctx: LogContext = LogContext.All + @Test def infoAndWarnMessagesGoToOut = runAndCheck { logger => @@ -114,8 +117,8 @@ class BloopLoggerSpec { val bos1 = new ByteArrayOutputStream val ps1 = new PrintStream(bos1) - val l0 = BloopLogger.at("l0", ps0, ps0, false) - val l1 = BloopLogger.at("l1", ps1, ps1, false) + val l0 = BloopLogger.at("l0", ps0, ps0, false, LogContext.All) + val l1 = BloopLogger.at("l1", ps1, ps1, false, LogContext.All) l0.info("info0") l1.info("info1") @@ -135,12 +138,12 @@ class BloopLoggerSpec { val bos0 = new ByteArrayOutputStream val ps0 = new PrintStream(bos0) - val l0 = BloopLogger.at(loggerName, ps0, ps0, false) + val l0 = BloopLogger.at(loggerName, ps0, ps0, false, LogContext.All) l0.info("info0") val bos1 = new ByteArrayOutputStream val ps1 = new PrintStream(bos1) - val l1 = BloopLogger.at(loggerName, ps1, ps1, false) + val l1 = BloopLogger.at(loggerName, ps1, ps1, false, LogContext.All) l1.info("info1") val msgs0 = convertAndReadAllFrom(bos0) @@ -189,7 +192,7 @@ class BloopLoggerSpec { val err = new PrintStream(errStream) val loggerName = UUID.randomUUID().toString - val logger = BloopLogger.at(loggerName, out, err, false) + val logger = BloopLogger.at(loggerName, out, err, false, LogContext.All) op(logger) val outMessages = convertAndReadAllFrom(outStream) diff --git a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala index 5cc145d197..8aa22f51db 100644 --- a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala +++ b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala @@ -8,7 +8,9 @@ final class BufferedLogger private( ) extends Logger { override def name = underlying.name override def ansiCodesSupported() = underlying.ansiCodesSupported() + override def logContext = underlying.logContext + override def debugInContext(msg: String)(implicit ctx: LogContext) = buffer.addLast(() => underlying.debugInContext(msg)) override def debug(msg: String) = buffer.addLast(() => underlying.debug(msg)) override def error(msg: String) = buffer.addLast(() => underlying.error(msg)) override def warn(msg: String) = buffer.addLast(() => underlying.warn(msg)) diff --git a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala index 3e8a488c19..c796e73b7b 100644 --- a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala +++ b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala @@ -6,7 +6,8 @@ import monix.reactive.Observer final class PublisherLogger( observer: Observer.Sync[(String, String)], - debug: Boolean = false + debug: Boolean = false, + val logContext: LogContext ) extends Logger { private[this] val messages = new ConcurrentLinkedQueue[(String, String)] override val name: String = "PublisherLogger" @@ -31,11 +32,12 @@ final class PublisherLogger( def filterMessageByLabel(label: String): List[String] = messages.iterator.asScala.flatMap(lm => if (lm._1 == label) List(lm._2) else Nil).toList - private def trace(msg: String): Unit = { add("trace", msg); () } - override def debug(msg: String): Unit = { add("debug", msg); () } - override def info(msg: String): Unit = { add("info", msg); () } - override def error(msg: String): Unit = { add("error", msg); () } - override def warn(msg: String): Unit = { add("warn", msg); () } + private def trace(msg: String): Unit = add("trace", msg) + override def debug(msg: String): Unit = add("debug", msg) + override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) add("debug", msg) + override def info(msg: String): Unit = add("info", msg) + override def error(msg: String): Unit = add("error", msg) + override def warn(msg: String): Unit = add("warn", msg) override def trace(ex: Throwable): Unit = { ex.getStackTrace.foreach(ste => trace(ste.toString)) Option(ex.getCause).foreach { cause => diff --git a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala index 01209126c0..03c8e60e58 100644 --- a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala +++ b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit import bloop.Server import bloop.bsp.BspServer -import bloop.logging.{ProcessLogger, RecordingLogger} +import bloop.logging.{LogContext, ProcessLogger, RecordingLogger} import bloop.tasks.TestUtil import com.martiansoftware.nailgun.NGServer import monix.eval.Task From a944170df8e51a3b1921385d82dedeeceb966a4d Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 10:09:27 +0200 Subject: [PATCH 03/10] Remove warning when compiling tests --- backend/src/test/scala/bloop/logging/RecordingLogger.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/test/scala/bloop/logging/RecordingLogger.scala b/backend/src/test/scala/bloop/logging/RecordingLogger.scala index 0347f7a3b4..df96dda440 100644 --- a/backend/src/test/scala/bloop/logging/RecordingLogger.scala +++ b/backend/src/test/scala/bloop/logging/RecordingLogger.scala @@ -32,6 +32,7 @@ final class RecordingLogger( } messages.add((key, value)) + () } override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) add("debug", msg) From fccaf794b11c7863a2a32bb1848ff3deab6fe319 Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 10:10:36 +0200 Subject: [PATCH 04/10] Format the code in log context --- backend/src/main/scala/bloop/logging/Logger.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/scala/bloop/logging/Logger.scala b/backend/src/main/scala/bloop/logging/Logger.scala index 71923c27c2..a268e53bb6 100644 --- a/backend/src/main/scala/bloop/logging/Logger.scala +++ b/backend/src/main/scala/bloop/logging/Logger.scala @@ -18,8 +18,10 @@ abstract class Logger extends xsbti.Logger with sbt.testing.Logger { /** Context for debug logging. */ def logContext: LogContext - def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) debug(msg) - def debugInContext(msg: Supplier[String])(implicit ctx: LogContext): Unit = if (logContext.isEnabled) debug(msg.get()) + def debugInContext(msg: String)(implicit ctx: LogContext): Unit = + if (logContext.isEnabled) debug(msg) + def debugInContext(msg: Supplier[String])(implicit ctx: LogContext): Unit = + if (logContext.isEnabled) debug(msg.get()) override def debug(msg: Supplier[String]): Unit = debug(msg.get()) override def error(msg: Supplier[String]): Unit = error(msg.get()) From 0bd4b0fd118b909c350bcb97c6d3d149439eafe7 Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 13:32:01 +0200 Subject: [PATCH 05/10] Replace `debugInContext` by `debug` The solution required to do this change without modifying sbt.testing.Logger's interface relies on the linearization order and the check for ambiguous overloads. Typically, if two methods are defined in an interface and they have the same public interface, except that one takes an implicit and the other one doesn't, the compiler fails with an ambiguous overload error when one of the methods is used (e.g. `logger.debug(msg)` refers to `logger.debug(msg)(implicit ctx)` or `logger.debug(msg)`?). To fix this problem, we override the version that doesn't take a parameter in a super class of `BloopLogger`, which is the common logging interface across bloop and is implemented by many different types of bloop loggers. Then, we add two abstract methods that must be implemented by subclasses: 1. An implementation of a `private[logging] def printDebug`, which is used by the overridden simple `debug` in `BaseSbtLogger`. 2. An implementation of the long debug method that we want the compiler to select for every use of `logger.debug(msg)`. This trick helps us remove `debugInContext` and simplify the logger API. From now on, everyone will be forced to use `debug` and provide a context if there is not an implicit one. --- .../scala/bloop/DependencyResolution.scala | 2 +- .../src/main/scala/bloop/ScalaInstance.scala | 4 +- backend/src/main/scala/bloop/io/Timer.scala | 2 +- .../scala/bloop/logging/BloopLogger.scala | 48 ++++++++----- .../main/scala/bloop/logging/LogContext.scala | 11 ++- .../src/main/scala/bloop/logging/Logger.scala | 20 ++++-- .../scala/bloop/logging/ProcessLogger.scala | 2 +- .../scala/bloop/logging/Slf4jAdapter.scala | 1 + .../scala/bloop/logging/RecordingLogger.scala | 6 +- .../bloop/logging/BloopLoggerBenchmark.scala | 8 ++- .../main/scala/bloop/logging/NoopLogger.scala | 4 +- .../main/scala/bloop/scalajs/JsBridge.scala | 2 +- .../main/scala/bloop/scalajs/JsBridge.scala | 2 +- frontend/src/main/scala/bloop/Cli.scala | 2 +- .../scala/bloop/bsp/BloopBspServices.scala | 2 +- .../src/main/scala/bloop/bsp/BspServer.scala | 8 +-- .../src/main/scala/bloop/data/Project.scala | 2 +- .../main/scala/bloop/engine/BuildLoader.scala | 4 +- .../main/scala/bloop/engine/Interpreter.scala | 14 +++- .../src/main/scala/bloop/engine/State.scala | 2 +- .../bloop/engine/caches/ResultsCache.scala | 6 +- .../bloop/engine/tasks/CompilationTask.scala | 2 +- .../main/scala/bloop/engine/tasks/Tasks.scala | 18 ++--- .../engine/tasks/ToolchainCompanion.scala | 2 +- .../src/main/scala/bloop/exec/Forker.scala | 14 ++-- .../main/scala/bloop/io/SourceWatcher.scala | 4 +- .../scala/bloop/logging/BspClientLogger.scala | 27 ++------ .../scala/bloop/logging/BspServerLogger.scala | 28 ++------ .../scala/bloop/logging/ScribeAdapter.scala | 20 ++++++ .../scala/bloop/testing/TestInternals.scala | 4 +- .../main/scala/bloop/testing/TestServer.scala | 6 +- .../scala/bloop/testing/TestSuiteEvent.scala | 2 +- .../test/scala/bloop/bsp/BspClientTest.scala | 33 +++++---- .../scala/bloop/logging/BloopLoggerSpec.scala | 69 ++++++++++++------- .../scala/bloop/logging/BufferedLogger.scala | 22 +++--- .../scala/bloop/logging/PublisherLogger.scala | 6 +- .../scala/bloop/nailgun/NailgunTest.scala | 3 +- 37 files changed, 232 insertions(+), 180 deletions(-) create mode 100644 frontend/src/main/scala/bloop/logging/ScribeAdapter.scala diff --git a/backend/src/main/scala/bloop/DependencyResolution.scala b/backend/src/main/scala/bloop/DependencyResolution.scala index 63461a679f..0e56124a57 100644 --- a/backend/src/main/scala/bloop/DependencyResolution.scala +++ b/backend/src/main/scala/bloop/DependencyResolution.scala @@ -45,7 +45,7 @@ object DependencyResolution { version: String, logger: Logger, additionalRepositories: Seq[Repository] = Nil): Array[AbsolutePath] = { - logger.debugInContext(s"Resolving $organization:$module:$version")(LogContext.Compilation) + logger.debug(s"Resolving $organization:$module:$version")(LogContext.Compilation) val dependency = Dependency(Module(organization, module), version) val start = Resolution(Set(dependency)) val repositories = { diff --git a/backend/src/main/scala/bloop/ScalaInstance.scala b/backend/src/main/scala/bloop/ScalaInstance.scala index a3a20f26fb..ad0e2f35f2 100644 --- a/backend/src/main/scala/bloop/ScalaInstance.scala +++ b/backend/src/main/scala/bloop/ScalaInstance.scala @@ -91,8 +91,8 @@ object ScalaInstance { val jarsKey = allJars.map(_.underlying).sortBy(_.toString).toList if (allJars.nonEmpty) { def newInstance = { - logger.debugInContext(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.")(LogContext.Compilation) - jarsKey.foreach(p => logger.debugInContext(s" => $p")(LogContext.Compilation)) + logger.debug(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.")(LogContext.Compilation) + jarsKey.foreach(p => logger.debug(s" => $p")(LogContext.Compilation)) new ScalaInstance(scalaOrg, scalaName, scalaVersion, allJars.map(_.toFile).toArray) } diff --git a/backend/src/main/scala/bloop/io/Timer.scala b/backend/src/main/scala/bloop/io/Timer.scala index 8600da9545..65fbbaab12 100644 --- a/backend/src/main/scala/bloop/io/Timer.scala +++ b/backend/src/main/scala/bloop/io/Timer.scala @@ -8,7 +8,7 @@ object Timer { try op finally { val elapsed = (System.nanoTime() - start).toDouble / 1e6 - logger.debugInContext(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms")(LogContext.All) + logger.debug(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms")(LogContext.All) } } } diff --git a/backend/src/main/scala/bloop/logging/BloopLogger.scala b/backend/src/main/scala/bloop/logging/BloopLogger.scala index 013b472ee9..8336c56745 100644 --- a/backend/src/main/scala/bloop/logging/BloopLogger.scala +++ b/backend/src/main/scala/bloop/logging/BloopLogger.scala @@ -1,10 +1,8 @@ package bloop.logging import java.io.PrintStream -import java.util.concurrent.atomic.AtomicInteger import scala.Console.{CYAN, GREEN, RED, RESET, YELLOW} -import com.martiansoftware.nailgun.NGCommunicator /** * Creates a logger that writes to the given streams. @@ -23,25 +21,28 @@ final class BloopLogger( colorOutput: Boolean, val logContext: LogContext ) extends Logger { - override def ansiCodesSupported() = true - - override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (isVerbose && logContext.isEnabled) print(msg, printDebug) - override def debug(msg: String): Unit = if (isVerbose) print(msg, printDebug) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (isVerbose && logContext.isEnabledFor(ctx)) print(msg, printDebug) override def error(msg: String): Unit = print(msg, printError) override def warn(msg: String): Unit = print(msg, printWarning) override def trace(exception: Throwable): Unit = trace("", exception) override def info(msg: String): Unit = print(msg, printInfo) + override def asDiscrete: Logger = { + if (debugCount <= 0) this + else new BloopLogger(name, out, err, debugCount - 1, colorOutput, logContext) + } + override def isVerbose: Boolean = debugCount > 0 - override def asDiscrete: Logger = - if (debugCount > 0) new BloopLogger(name, out, err, debugCount - 1, colorOutput, logContext) else this - override def asVerbose: Logger = new BloopLogger(name, out, err, debugCount + 1, colorOutput, logContext) + override def asVerbose: Logger = { + new BloopLogger(name, out, err, debugCount + 1, colorOutput, logContext) + } @scala.annotation.tailrec private def trace(prefix: String, exception: Throwable): Unit = { if (isVerbose) { - print(prefix + exception.toString(), printTrace) + print(prefix + exception.toString, printTrace) exception.getStackTrace.foreach(ste => print("\t" + ste.toString, printTrace)) val cause = exception.getCause @@ -60,7 +61,7 @@ final class BloopLogger( private def colored(color: String, msg: String): String = { if (colorOutput) - s"${RESET}${color}$msg${RESET}" + s"${RESET}${color}$msg${RESET}" else msg } @@ -77,7 +78,7 @@ final class BloopLogger( err.println(s"${colored(CYAN, "[T]")} $line") } - private def printDebug(line: String): Unit = { + override private[logging] def printDebug(line: String): Unit = { err.println(s"${colored(GREEN, "[D]")} $line") } } @@ -94,8 +95,14 @@ object BloopLogger { * @param logContext Narrows logs to specified context. * @return A `BloopLogger` whose output will be written in the specified streams. */ - def at(name: String, out: PrintStream, err: PrintStream, isVerbose: Boolean, colorOutput: Boolean, logContext: LogContext): BloopLogger = - new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput, logContext) + def at( + name: String, + out: PrintStream, + err: PrintStream, + isVerbose: Boolean, + colorOutput: Boolean, + logContext: LogContext + ): BloopLogger = new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput, logContext) /** * Instantiates a new `BloopLogger` using the specified streams. @@ -106,8 +113,13 @@ object BloopLogger { * @param logContext Narrows logs to specified context. * @return A `BloopLogger` whose output will be written in the specified streams. */ - def at(name: String, out: PrintStream, err: PrintStream, colorOutput: Boolean, logContext: LogContext): BloopLogger = - at(name, out, err, false, colorOutput, logContext) + def at( + name: String, + out: PrintStream, + err: PrintStream, + colorOutput: Boolean, + logContext: LogContext + ): BloopLogger = at(name, out, err, false, colorOutput, logContext) /** * Instantiates a new `BloopLogger` that writes to stdout and stderr. @@ -116,6 +128,6 @@ object BloopLogger { * @return A `BloopLogger` writing to stdout and stderr. Calling this method is equivalent to * calling `at(name, System.out, System.err)`. */ - def default(name: String): BloopLogger = at(name, System.out, System.err, false, LogContext.All) - + def default(name: String): BloopLogger = + at(name, System.out, System.err, false, LogContext.All) } diff --git a/backend/src/main/scala/bloop/logging/LogContext.scala b/backend/src/main/scala/bloop/logging/LogContext.scala index 3986f533e9..c268317592 100644 --- a/backend/src/main/scala/bloop/logging/LogContext.scala +++ b/backend/src/main/scala/bloop/logging/LogContext.scala @@ -1,20 +1,17 @@ package bloop.logging sealed trait LogContext { self => - - import LogContext._ - - def isEnabled(implicit other: LogContext): Boolean = + private[logging] def isEnabledFor(other: LogContext): Boolean = { (self, other) match { - case (All, _) => true - case (_, All) => true + case (LogContext.All, _) => true + case (_, LogContext.All) => true case (ctx1, ctx2) if ctx1 == ctx2 => true case _ => false } + } } object LogContext { - case object All extends LogContext case object FileWatching extends LogContext case object Compilation extends LogContext diff --git a/backend/src/main/scala/bloop/logging/Logger.scala b/backend/src/main/scala/bloop/logging/Logger.scala index a268e53bb6..e36b6a104b 100644 --- a/backend/src/main/scala/bloop/logging/Logger.scala +++ b/backend/src/main/scala/bloop/logging/Logger.scala @@ -1,7 +1,7 @@ package bloop.logging import java.util.function.Supplier -abstract class Logger extends xsbti.Logger with sbt.testing.Logger { +abstract class Logger extends xsbti.Logger with BaseSbtLogger { /** The name of the logger */ def name: String @@ -18,12 +18,9 @@ abstract class Logger extends xsbti.Logger with sbt.testing.Logger { /** Context for debug logging. */ def logContext: LogContext - def debugInContext(msg: String)(implicit ctx: LogContext): Unit = - if (logContext.isEnabled) debug(msg) - def debugInContext(msg: Supplier[String])(implicit ctx: LogContext): Unit = - if (logContext.isEnabled) debug(msg.get()) + def debug(msg: String)(implicit ctx: LogContext): Unit - override def debug(msg: Supplier[String]): Unit = debug(msg.get()) + override def debug(msg: Supplier[String]): Unit = printDebug(msg.get()) override def error(msg: Supplier[String]): Unit = error(msg.get()) override def warn(msg: Supplier[String]): Unit = warn(msg.get()) override def info(msg: Supplier[String]): Unit = info(msg.get) @@ -31,3 +28,14 @@ abstract class Logger extends xsbti.Logger with sbt.testing.Logger { def report(msg: String, t: Throwable): Unit = { error(msg); trace(t) } } + +/** + * Define an intermediary trait that extends sbt's Logger and overrides + * `debug` to help the linearized lookup find the `debug` that takes an + * implicit instead of sbt's `debug` that doesn't. This technique enables + * us avoid modifying the call-sites of `debug`. + */ +private[logging] trait BaseSbtLogger extends sbt.testing.Logger { + private[logging] def printDebug(line: String): Unit + override def debug(msg: String): Unit = printDebug(msg) +} diff --git a/backend/src/main/scala/bloop/logging/ProcessLogger.scala b/backend/src/main/scala/bloop/logging/ProcessLogger.scala index 632ca6a819..58c37bd176 100644 --- a/backend/src/main/scala/bloop/logging/ProcessLogger.scala +++ b/backend/src/main/scala/bloop/logging/ProcessLogger.scala @@ -22,7 +22,7 @@ class ProcessLogger(underlying: Logger, process: Process) { def start(): Unit = { implicit val ctx: LogContext = LogContext.All - underlying.debug("Starting to log output from process...") + underlying.printDebug("Starting to log output from process...") new StreamLogger(underlying.info, processOut).start() new StreamLogger(underlying.error, processErr).start() } diff --git a/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala b/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala index 98f7383ff8..73e27fe895 100644 --- a/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala +++ b/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala @@ -13,6 +13,7 @@ import org.slf4j.{Marker, Logger => Slf4jLogger} */ final class Slf4jAdapter[L <: Logger](logger: L) extends Slf4jLogger { def underlying: L = logger + implicit val logContext: LogContext = logger.logContext override def getName: String = logger.name override def debug(msg: String): Unit = logger.debug(msg) diff --git a/backend/src/test/scala/bloop/logging/RecordingLogger.scala b/backend/src/test/scala/bloop/logging/RecordingLogger.scala index df96dda440..a620268661 100644 --- a/backend/src/test/scala/bloop/logging/RecordingLogger.scala +++ b/backend/src/test/scala/bloop/logging/RecordingLogger.scala @@ -35,8 +35,10 @@ final class RecordingLogger( () } - override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) add("debug", msg) - override def debug(msg: String): Unit = add("debug", msg) + override def printDebug(msg: String): Unit = add("debug", msg) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (logContext.isEnabledFor(ctx)) add("debug", msg) + override def info(msg: String): Unit = add("info", msg) override def error(msg: String): Unit = add("error", msg) override def warn(msg: String): Unit = add("warn", msg) diff --git a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala index c9fcd062cc..ca5f63de95 100644 --- a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala +++ b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala @@ -28,11 +28,13 @@ class BloopLoggerBenchmark { @Benchmark def logDebug(): Unit = { - BloopLoggerBenchmark.logger.debug("message") + // Debugs with `LogContext.All` + BloopLoggerBenchmark.logger.debug("message")(LogContext.All) } @Benchmark - def logDebugInContext(): Unit = { - BloopLoggerBenchmark.logger.debugInContext("message")(LogContext.All) + def logDebugWithContext(): Unit = { + implicit val logContext: LogContext = LogContext.All + BloopLoggerBenchmark.logger.debug("message")(logContext) } } diff --git a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala index 1fd8dfe52e..fc94a4c176 100644 --- a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala +++ b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala @@ -5,8 +5,8 @@ object NoopLogger extends Logger { override def ansiCodesSupported(): Boolean = false override def logContext: LogContext = LogContext.All - override def debug(msg: String): Unit = () - override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = () + override def printDebug(msg: String): Unit = () + override def debug(msg: String)(implicit ctx: LogContext): Unit = () override def error(msg: String): Unit = () override def warn(msg: String): Unit = () override def trace(exception: Throwable): Unit = () diff --git a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala index 4444d7fc2a..7ff3dabbd8 100644 --- a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala @@ -21,7 +21,7 @@ object JsBridge { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debugInContext(message)(LogContext.All) + case Level.Debug => logger.debug(message)(LogContext.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala index cb3f544ba1..ece4afd73e 100644 --- a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala @@ -19,7 +19,7 @@ object JsBridge { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debugInContext(message)(LogContext.All) + case Level.Debug => logger.debug(message)(LogContext.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/frontend/src/main/scala/bloop/Cli.scala b/frontend/src/main/scala/bloop/Cli.scala index bff1c1da1a..47c19c599d 100644 --- a/frontend/src/main/scala/bloop/Cli.scala +++ b/frontend/src/main/scala/bloop/Cli.scala @@ -311,7 +311,7 @@ object Cli { val ngout = cliOptions.common.ngout def logElapsed(since: Long): Unit = { val elapsed = (System.nanoTime() - since).toDouble / 1e6 - logger.debugInContext(s"Elapsed: $elapsed ms")(LogContext.All) + logger.debug(s"Elapsed: $elapsed ms")(LogContext.All) } // Simulate try-catch-finally with monix tasks to time the task execution diff --git a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala index 9e103950e7..f891549a2c 100644 --- a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala +++ b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala @@ -64,7 +64,7 @@ final class BloopBspServices( private val pool = callSiteState.pool private val defaultOpts = callSiteState.commonOptions def reloadState(config: AbsolutePath): Task[State] = { - bspForwarderLogger.debugInContext(s"Reloading bsp state for ${config.syntax}")(LogContext.Bsp) + bspForwarderLogger.debug(s"Reloading bsp state for ${config.syntax}")(LogContext.Bsp) State.loadActiveStateFor(config, pool, defaultOpts, bspForwarderLogger).map { state0 => state0.copy(logger = bspForwarderLogger, commonOptions = latestState.commonOptions) } diff --git a/frontend/src/main/scala/bloop/bsp/BspServer.scala b/frontend/src/main/scala/bloop/bsp/BspServer.scala index 0f3ef8ce08..2455e70312 100644 --- a/frontend/src/main/scala/bloop/bsp/BspServer.scala +++ b/frontend/src/main/scala/bloop/bsp/BspServer.scala @@ -37,16 +37,16 @@ object BspServer { cmd match { case Commands.WindowsLocalBsp(pipeName, _) => val server = new NGWin32NamedPipeServerSocket(pipeName) - state.logger.debugInContext(s"Waiting for a connection at pipe $pipeName...") + state.logger.debug(s"Waiting for a connection at pipe $pipeName...") Task(WindowsLocal(pipeName, server)).doOnCancel(Task(server.close())) case Commands.UnixLocalBsp(socketFile, _) => val server = new NGUnixDomainServerSocket(socketFile.toString) - state.logger.debugInContext(s"Waiting for a connection at $socketFile...") + state.logger.debug(s"Waiting for a connection at $socketFile...") Task(UnixLocal(socketFile, server)).doOnCancel(Task(server.close())) case Commands.TcpBsp(address, portNumber, _) => val socketAddress = new InetSocketAddress(address, portNumber) val server = new java.net.ServerSocket(portNumber, 10, address) - state.logger.debugInContext(s"Waiting for a connection at $socketAddress...") + state.logger.debug(s"Waiting for a connection at $socketAddress...") Task(Tcp(socketAddress, server)).doOnCancel(Task(server.close())) } } @@ -80,7 +80,7 @@ object BspServer { import state.logger def startServer(handle: ConnectionHandle): Task[State] = { val connectionURI = uri(handle) - logger.debugInContext(s"The server is listening for incoming connections at $connectionURI...") + logger.debug(s"The server is listening for incoming connections at $connectionURI...") val socket = handle.serverSocket.accept() logger.info(s"Accepted incoming BSP client connection at $connectionURI") diff --git a/frontend/src/main/scala/bloop/data/Project.scala b/frontend/src/main/scala/bloop/data/Project.scala index 2353ca807f..324278054d 100644 --- a/frontend/src/main/scala/bloop/data/Project.scala +++ b/frontend/src/main/scala/bloop/data/Project.scala @@ -143,7 +143,7 @@ object Project { def fromBytesAndOrigin(bytes: Array[Byte], origin: Origin, logger: Logger): Project = { import _root_.io.circe.parser - logger.debugInContext(s"Loading project from '${origin.path}'")(LogContext.All) + logger.debug(s"Loading project from '${origin.path}'")(LogContext.All) val contents = new String(bytes, StandardCharsets.UTF_8) parser.parse(contents) match { case Left(failure) => throw failure diff --git a/frontend/src/main/scala/bloop/engine/BuildLoader.scala b/frontend/src/main/scala/bloop/engine/BuildLoader.scala index f9ab645371..613e690a44 100644 --- a/frontend/src/main/scala/bloop/engine/BuildLoader.scala +++ b/frontend/src/main/scala/bloop/engine/BuildLoader.scala @@ -37,7 +37,7 @@ object BuildLoader { configFiles: List[Build.ReadConfiguration], logger: Logger ): Task[List[Project]] = { - logger.debugInContext(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) + logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) val all = configFiles.map(f => Task(Project.fromBytesAndOrigin(f.bytes, f.origin, logger))) Task.gatherUnordered(all).executeOn(ExecutionContext.scheduler) } @@ -83,7 +83,7 @@ object BuildLoader { Build.ReadConfiguration(Origin(ap, hash), bytes) } - logger.debugInContext(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) + logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) configFiles.map(f => Project.fromBytesAndOrigin(f.bytes, f.origin, logger)) } } diff --git a/frontend/src/main/scala/bloop/engine/Interpreter.scala b/frontend/src/main/scala/bloop/engine/Interpreter.scala index 47ee3776c4..26c7c7ee04 100644 --- a/frontend/src/main/scala/bloop/engine/Interpreter.scala +++ b/frontend/src/main/scala/bloop/engine/Interpreter.scala @@ -166,7 +166,7 @@ object Interpreter { logger.info(contents) } else { val configDirectory = state.build.origin.syntax - logger.debugInContext(s"Projects loaded from '$configDirectory':")(LogContext.All) + logger.debug(s"Projects loaded from '$configDirectory':")(LogContext.All) state.build.projects.map(_.name).sorted.foreach(logger.info) } @@ -183,7 +183,13 @@ object Interpreter { )(next: State => Task[State]): Task[State] = { runCompile(cmd, state, project, deduplicateFailures, excludeRoot).flatMap { compiled => if (compiled.status != ExitStatus.CompilationError) next(compiled) - else Task.now(state.withDebug(s"Failed compilation for $project. Skipping $nextAction...")) + else { + Task.now( + state.withDebug(s"Failed compilation for $project. Skipping $nextAction...")( + LogContext.Compilation + ) + ) + } } } @@ -288,7 +294,9 @@ object Interpreter { private def clean(cmd: Commands.Clean, state: State): Task[State] = { if (cmd.project.isEmpty) - Tasks.clean(state, state.build.projects, cmd.includeDependencies).map(_.mergeStatus(ExitStatus.Ok)) + Tasks + .clean(state, state.build.projects, cmd.includeDependencies) + .map(_.mergeStatus(ExitStatus.Ok)) else { val (projects, missing) = lookupProjects(cmd.project, state) if (missing.isEmpty) diff --git a/frontend/src/main/scala/bloop/engine/State.scala b/frontend/src/main/scala/bloop/engine/State.scala index 670398ea87..26fb6409c5 100644 --- a/frontend/src/main/scala/bloop/engine/State.scala +++ b/frontend/src/main/scala/bloop/engine/State.scala @@ -90,7 +90,7 @@ object State { implicit class XState(val s: State) extends AnyVal { def withTrace(t: Throwable): State = { s.logger.trace(t); s } - def withDebug(msg: String): State = { s.logger.debug(msg); s } + def withDebug(msg: String)(implicit log: LogContext): State = { s.logger.debug(msg); s } def withInfo(msg: String): State = { s.logger.info(msg); s } def withWarn(msg: String): State = { s.logger.warn(msg); s } def withError(msg: String): State = withError(msg, ExitStatus.UnexpectedError) diff --git a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala index 2ed9404999..6ac5e722c0 100644 --- a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala +++ b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala @@ -109,19 +109,19 @@ object ResultsCache { val contents = FileAnalysisStore.binary(analysisFile.toFile).get().toOption contents match { case Some(res) => - logger.debugInContext(s"Loading previous analysis for '${p.name}' from '$analysisFile'.") + logger.debug(s"Loading previous analysis for '${p.name}' from '$analysisFile'.") val r = PreviousResult.of(Optional.of(res.getAnalysis), Optional.of(res.getMiniSetup)) val reporter = Reporter.fromAnalysis(res.getAnalysis, cwd, logger) Result.Success(reporter, r, 0L) case None => - logger.debugInContext(s"Analysis '$analysisFile' for '${p.name}' is empty.") + logger.debug(s"Analysis '$analysisFile' for '${p.name}' is empty.") Result.Empty } } } else { Task.now { - logger.debugInContext(s"Missing analysis file for project '${p.name}'") + logger.debug(s"Missing analysis file for project '${p.name}'") Result.Empty } } diff --git a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala index 3412919469..01c6cf93c3 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala @@ -120,7 +120,7 @@ object CompilationTask { graphInputs.irPromise.get match { case Array() if pipeline => logger.warn(s"Project ${project.name} compiled without pipelined compilation.") - case _ => logger.debugInContext(s"The pickle promise of ${project.name} completed in Zinc.") + case _ => logger.debug(s"The pickle promise of ${project.name} completed in Zinc.") } } diff --git a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala index 76870203b9..cfe0d5f5e4 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala @@ -57,7 +57,7 @@ object Tasks { case Some(instance) => val classpath = project.classpath val entries = classpath.map(_.underlying.toFile).toSeq - logger.debugInContext(s"Setting up the console classpath with ${entries.mkString(", ")}")(LogContext.All) + logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}")(LogContext.All) val loader = ClasspathUtilities.makeLoader(entries, instance) val compiler = state.compilerCache.get(instance).scalac.asInstanceOf[AnalyzingCompiler] val opts = ClasspathOptionsUtil.repl @@ -141,7 +141,7 @@ object Tasks { case Nil => Nil case oneFramework :: Nil => val rawArgs = frameworkSpecificRawArgs - logger.debugInContext(s"Test options '$rawArgs' assigned to the only found framework $cls'.") + logger.debug(s"Test options '$rawArgs' assigned to the only found framework $cls'.") List(Config.TestArgument(rawArgs, Some(Config.TestFramework(cls)))) case frameworks => val frameworkNames = foundFrameworks(frameworks) @@ -166,7 +166,7 @@ object Tasks { val testLoader = forker.newClassLoader(Some(TestInternals.filteredLoader)) val frameworks = project.testFrameworks .flatMap(f => TestInternals.loadFramework(testLoader, f.names, logger)) - logger.debugInContext(s"Found frameworks: ${foundFrameworks(frameworks)}") + logger.debug(s"Found frameworks: ${foundFrameworks(frameworks)}") val frameworkArgs = considerFrameworkArgs(frameworks) val lastCompileResult = state.results.lastSuccessfulResultOrEmpty(project) @@ -192,9 +192,9 @@ object Tasks { val allNames = ungroupedTests.map(_._2.fullyQualifiedName).mkString(", ") val includedNames = includedTests.map(_._2.fullyQualifiedName).mkString(", ") val excludedNames = excludedTests.map(_._2.fullyQualifiedName).mkString(", ") - logger.debugInContext(s"Bloop found the following tests for $projectName: $allNames") - logger.debugInContext(s"The following tests were included by the filter: $includedNames") - logger.debugInContext(s"The following tests were excluded by the filter: $excludedNames") + logger.debug(s"Bloop found the following tests for $projectName: $allNames") + logger.debug(s"The following tests were included by the filter: $includedNames") + logger.debug(s"The following tests were excluded by the filter: $excludedNames") } DiscoveredTests(testLoader, includedTests.groupBy(_._1).mapValues(_.map(_._2))) @@ -217,7 +217,7 @@ object Tasks { } } - logger.debugInContext(s"Running test suites with arguments: $args") + logger.debug(s"Running test suites with arguments: $args") TestInternals.execute(cwd, forker, discovered, args, failureHandler, logger, opts) } @@ -225,7 +225,7 @@ object Tasks { Task.sequence(testTasks).map { exitCodes => // When the test execution is over report no matter what the result is testEventHandler.report() - logger.debugInContext(s"Test suites failed: $failure") + logger.debug(s"Test suites failed: $failure") val isOk = !failure && exitCodes.forall(_ == 0) if (isOk) state.mergeStatus(ExitStatus.Ok) else state.copy(status = ExitStatus.TestExecutionError) @@ -296,7 +296,7 @@ object Tasks { } val mainClasses = analysis.infos.allInfos.values.flatMap(_.getMainClasses).toList - logger.debugInContext(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}")(LogContext.All) + logger.debug(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}")(LogContext.All) mainClasses } diff --git a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala index 57d8727811..0123e7e8be 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala @@ -68,7 +68,7 @@ abstract class ToolchainCompanion[Toolchain] { private final val BloopVersion = BuildInfo.version private final val BloopOrg = BuildInfo.organization private def resolveJars(artifactName: String, logger: Logger): List[Path] = { - logger.debugInContext(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion")(LogContext.Compilation) + logger.debug(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion")(LogContext.Compilation) val files = DependencyResolution.resolve(BloopOrg, artifactName, BloopVersion, logger) files.iterator.map(_.underlying).filter(_.toString.endsWith(".jar")).toList } diff --git a/frontend/src/main/scala/bloop/exec/Forker.scala b/frontend/src/main/scala/bloop/exec/Forker.scala index b33b9385e0..f0eb399672 100644 --- a/frontend/src/main/scala/bloop/exec/Forker.scala +++ b/frontend/src/main/scala/bloop/exec/Forker.scala @@ -72,7 +72,7 @@ final case class Forker(javaEnv: JavaEnv, classpath: Array[AbsolutePath]) { | classpath = '$fullClasspath' | java_home = '${javaEnv.javaHome}' | java_options = '${javaEnv.javaOptions.mkString(" ")}""".stripMargin - Task(logger.debugInContext(debugOptions)(LogContext.All)) + Task(logger.debug(debugOptions)(LogContext.All)) } else Task.unit logTask.flatMap(_ => Forker.run(cwd, cmd, logger, opts)) } @@ -121,7 +121,7 @@ object Forker { var gobbleInput: Cancelable = null final class ProcessHandler extends NuAbstractProcessHandler { override def onStart(nuProcess: NuProcess): Unit = { - logger.debugInContext( + logger.debug( s"""Starting forked process: | cwd = '$cwd' | pid = '${nuProcess.getPID}' @@ -129,14 +129,14 @@ object Forker { } override def onExit(statusCode: Int): Unit = - logger.debugInContext(s"Forked process exited with code: $statusCode") + logger.debug(s"Forked process exited with code: $statusCode") val outBuilder = StringBuilder.newBuilder override def onStdout(buffer: ByteBuffer, closed: Boolean): Unit = { if (closed) { // Make sure that the gobbler never stays awake! if (gobbleInput != null) gobbleInput.cancel() - logger.debugInContext("The process is closed. Emptying buffer...") + logger.debug("The process is closed. Emptying buffer...") val remaining = outBuilder.mkString if (!remaining.isEmpty) logger.info(remaining) @@ -185,7 +185,7 @@ object Forker { Task { try { val exitCode = process.waitFor(0, _root_.java.util.concurrent.TimeUnit.SECONDS) - logger.debugInContext(s"Process ${process.getPID} exited with code: $exitCode") + logger.debug(s"Process ${process.getPID} exited with code: $exitCode") exitCode } finally { shutdownInput = true @@ -201,11 +201,11 @@ object Forker { if (process.isRunning) { val msg = s"The cancellation could not destroy process ${process.getPID}" opts.ngout.println(msg) - logger.debugInContext(msg) + logger.debug(msg) } else { val msg = s"The run process ${process.getPID} has been closed" opts.ngout.println(msg) - logger.debugInContext(msg) + logger.debug(msg) } } }) diff --git a/frontend/src/main/scala/bloop/io/SourceWatcher.scala b/frontend/src/main/scala/bloop/io/SourceWatcher.scala index e94c880fe4..6d987af80d 100644 --- a/frontend/src/main/scala/bloop/io/SourceWatcher.scala +++ b/frontend/src/main/scala/bloop/io/SourceWatcher.scala @@ -32,7 +32,7 @@ final class SourceWatcher private ( // Someone that wants this to be supported by Windows will need to make it work for all terminals if (!BspServer.isWindows) logger.info("\u001b[H\u001b[2J") // Clean the terminal before acting on the file event action - logger.debugInContext(s"A ${event.eventType()} in ${event.path()} has triggered an event") + logger.debug(s"A ${event.eventType()} in ${event.path()} has triggered an event") action(state) } @@ -76,7 +76,7 @@ final class SourceWatcher private ( val watchController = Task { try watcherHandle.get() finally watcher.close() - logger.debugInContext("File watcher was successfully closed") + logger.debug("File watcher was successfully closed") } val watchCancellation = Cancelable { () => diff --git a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala index e705b8e5bd..b4fef6fb6a 100644 --- a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala @@ -1,11 +1,7 @@ package bloop.logging -import scribe.LogRecord - /** Creates a logger that extends scribe's `LoggerSupport` for BSP's `LanguageClient`. */ -final class BspClientLogger[L <: Logger](val underlying: L) - extends Logger - with scribe.LoggerSupport { +final class BspClientLogger[L <: Logger](val underlying: L) extends Logger with ScribeAdapter { override val logContext: LogContext = underlying.logContext override val name: String = underlying.name @@ -14,26 +10,13 @@ final class BspClientLogger[L <: Logger](val underlying: L) override def asDiscrete: Logger = new BspClientLogger(underlying.asDiscrete) override def asVerbose: Logger = new BspClientLogger(underlying.asVerbose) + override def printDebug(msg: String): Unit = underlying.printDebug(msg) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (logContext.isEnabledFor(ctx)) printDebug(msg) + override def ansiCodesSupported: Boolean = underlying.ansiCodesSupported() - override def debug(msg: String): Unit = underlying.debug(msg) override def trace(t: Throwable): Unit = underlying.trace(t) override def error(msg: String): Unit = underlying.error(msg) override def warn(msg: String): Unit = underlying.warn(msg) override def info(msg: String): Unit = underlying.info(msg) - - override def log[M](record: LogRecord[M]): Unit = { - import scribe.Level - val msg = record.message - record.level match { - case Level.Info => info(msg) - case Level.Error => error(msg) - case Level.Warn => warn(msg) - case Level.Debug => debug(msg) - case Level.Trace => - record.throwable match { - case Some(t) => trace(t) - case None => debug(record.message) - } - } - } } diff --git a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala index cd439a109a..8fa42faeb0 100644 --- a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala @@ -10,7 +10,6 @@ import xsbti.Severity import scala.meta.jsonrpc.JsonRpcClient import ch.epfl.scala.bsp import ch.epfl.scala.bsp.endpoints.{Build, BuildTarget} -import scribe.LogRecord /** * Creates a logger that will forward all the messages to the underlying bsp client. @@ -22,7 +21,7 @@ final class BspServerLogger private ( implicit val client: JsonRpcClient, ansiSupported: Boolean ) extends Logger - with scribe.LoggerSupport { + with ScribeAdapter { override def logContext: LogContext = underlying.logContext @@ -34,9 +33,9 @@ final class BspServerLogger private ( override def ansiCodesSupported: Boolean = ansiSupported || underlying.ansiCodesSupported() - override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = underlying.debug(msg) - - override def debug(msg: String): Unit = underlying.debug(msg) + override private[logging] def printDebug(msg: String): Unit = underlying.printDebug(msg) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (logContext.isEnabledFor(ctx)) printDebug(msg) override def trace(t: Throwable): Unit = underlying.trace(t) @@ -101,30 +100,13 @@ final class BspServerLogger private ( ) () } - - override def log[M](record: LogRecord[M]): Unit = { - import scribe.Level - val msg = record.message - record.level match { - case Level.Info => info(msg) - case Level.Error => error(msg) - case Level.Warn => warn(msg) - case Level.Debug => debug(msg) - case Level.Trace => - record.throwable match { - case Some(t) => trace(t) - case None => error(record.message) - } - } - () - } } object BspServerLogger { private[bloop] final val counter: AtomicInteger = new AtomicInteger(0) def apply(state: State, client: JsonRpcClient, ansiCodesSupported: Boolean): BspServerLogger = { - val name: String = s"bsp-logger-${ BspServerLogger.counter.incrementAndGet()}" + val name: String = s"bsp-logger-${BspServerLogger.counter.incrementAndGet()}" new BspServerLogger(name, state.logger, client, ansiCodesSupported) } } diff --git a/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala b/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala new file mode 100644 index 0000000000..fc35bc4dc6 --- /dev/null +++ b/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala @@ -0,0 +1,20 @@ +package bloop.logging +import scribe.LogRecord + +trait ScribeAdapter extends scribe.LoggerSupport { self: Logger => + override def log[M](record: LogRecord[M]): Unit = { + import scribe.Level + val msg = record.message + record.level match { + case Level.Info => info(msg) + case Level.Error => error(msg) + case Level.Warn => warn(msg) + case Level.Debug => debug(msg)(LogContext.Bsp) + case Level.Trace => + record.throwable match { + case Some(t) => trace(t) + case None => debug(record.message)(LogContext.Bsp) + } + } + } +} diff --git a/frontend/src/main/scala/bloop/testing/TestInternals.scala b/frontend/src/main/scala/bloop/testing/TestInternals.scala index baf52a84c9..239e062133 100644 --- a/frontend/src/main/scala/bloop/testing/TestInternals.scala +++ b/frontend/src/main/scala/bloop/testing/TestInternals.scala @@ -98,7 +98,7 @@ object TestInternals { logger: Logger, opts: CommonOptions ): Task[Int] = { - logger.debugInContext("Starting forked test execution...") + logger.debug("Starting forked test execution...") // Make sure that we cache the resolution of the test agent JAR and we don't repeat it every time val agentFiles = lazyTestAgents(logger) @@ -107,7 +107,7 @@ object TestInternals { val forkMain = classOf[sbt.ForkMain].getCanonicalName val arguments = Array(server.port.toString) val testAgentJars = agentFiles.filter(_.underlying.toString.endsWith(".jar")) - logger.debugInContext("Test agent JARs: " + testAgentJars.mkString(", ")) + logger.debug("Test agent JARs: " + testAgentJars.mkString(", ")) val listener = server.listenToTests val runner = forker.runMain(cwd, forkMain, arguments, logger, opts, testAgentJars) diff --git a/frontend/src/main/scala/bloop/testing/TestServer.scala b/frontend/src/main/scala/bloop/testing/TestServer.scala index 8fef90630c..e7728cb425 100644 --- a/frontend/src/main/scala/bloop/testing/TestServer.scala +++ b/frontend/src/main/scala/bloop/testing/TestServer.scala @@ -96,13 +96,13 @@ final class TestServer( val serverStarted = Promise[Unit]() val clientConnection = Task { - logger.debugInContext(s"Firing up test server at $port. Waiting for client...") + logger.debug(s"Firing up test server at $port. Waiting for client...") serverStarted.trySuccess(()) server.accept() } val testListeningTask = clientConnection.flatMap { socket => - logger.debugInContext("Test server established connection with remote JVM.") + logger.debug("Test server established connection with remote JVM.") val os = new ObjectOutputStream(socket.getOutputStream) os.flush() val is = new ObjectInputStream(socket.getInputStream) @@ -134,7 +134,7 @@ final class TestServer( server.close() // Do both just in case the logger streams have been closed by nailgun opts.ngout.println("The test execution was successfully cancelled.") - logger.debugInContext("Test server has been successfully closed.") + logger.debug("Test server has been successfully closed.") } val listener = testListeningTask diff --git a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala index 81ae2c08f0..15becaacee 100644 --- a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala +++ b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala @@ -44,7 +44,7 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler { case TestSuiteEvent.Error(message) => logger.error(message) case TestSuiteEvent.Warn(message) => logger.warn(message) case TestSuiteEvent.Info(message) => logger.info(message) - case TestSuiteEvent.Debug(message) => logger.debugInContext(message)(LogContext.Test) + case TestSuiteEvent.Debug(message) => logger.debug(message)(LogContext.Test) case TestSuiteEvent.Trace(throwable) => logger.error("Test suite aborted.") logger.trace(throwable) diff --git a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala index b6ee563853..97f554429a 100644 --- a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala +++ b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala @@ -4,7 +4,7 @@ import java.nio.file.Files import bloop.cli.Commands import bloop.io.AbsolutePath -import bloop.logging.{BspClientLogger, RecordingLogger, Slf4jAdapter} +import bloop.logging.{BspClientLogger, LogContext, RecordingLogger, Slf4jAdapter} import bloop.tasks.TestUtil import ch.epfl.scala.bsp import ch.epfl.scala.bsp.endpoints @@ -17,6 +17,7 @@ import scala.concurrent.duration.FiniteDuration import scala.meta.jsonrpc.{BaseProtocolMessage, LanguageClient, LanguageServer, Response, Services} object BspClientTest { + private implicit val ctx: LogContext = LogContext.Bsp def cleanUpLastResources(cmd: Commands.ValidatedBsp): Unit = { cmd match { case cmd: Commands.WindowsLocalBsp => () @@ -28,9 +29,11 @@ object BspClientTest { } } - def setupBspCommand(cmd: Commands.ValidatedBsp, - cwd: AbsolutePath, - configDir: AbsolutePath): Commands.ValidatedBsp = { + def setupBspCommand( + cmd: Commands.ValidatedBsp, + cwd: AbsolutePath, + configDir: AbsolutePath + ): Commands.ValidatedBsp = { val common = cmd.cliOptions.common.copy(workingDirectory = cwd.syntax) val cliOptions = cmd.cliOptions.copy(configDir = Some(configDir.underlying), common = common) cmd match { @@ -41,11 +44,15 @@ object BspClientTest { } // We limit the scheduler on purpose so that we don't have any thread leak. - val scheduler: Scheduler = Scheduler(java.util.concurrent.Executors.newFixedThreadPool(4), - ExecutionModel.AlwaysAsyncExecution) - - def createServices(logger: BspClientLogger[_]): Services = { - Services.empty(logger) + val scheduler: Scheduler = Scheduler( + java.util.concurrent.Executors.newFixedThreadPool(4), + ExecutionModel.AlwaysAsyncExecution + ) + + def createServices(logger0: BspClientLogger[_]): Services = { + val logger: bloop.logging.Logger = logger0 + Services + .empty(logger0) .notification(endpoints.Build.showMessage) { case bsp.ShowMessageParams(bsp.MessageType.Log, _, _, msg) => logger.debug(msg) case bsp.ShowMessageParams(bsp.MessageType.Info, _, _, msg) => logger.info(msg) @@ -146,9 +153,11 @@ object BspClientTest { } // Courtesy of @olafurpg - def retryBackoff[A](source: me.Task[A], - maxRetries: Int, - firstDelay: FiniteDuration): me.Task[A] = { + def retryBackoff[A]( + source: me.Task[A], + maxRetries: Int, + firstDelay: FiniteDuration + ): me.Task[A] = { source.onErrorHandleWith { case ex: Exception => if (maxRetries > 0) diff --git a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala index 9165879f3e..eefef173bc 100644 --- a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala +++ b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala @@ -17,11 +17,8 @@ import org.junit.experimental.categories.Category @Category(Array(classOf[bloop.FastTests])) class BloopLoggerSpec { - - private implicit val ctx: LogContext = LogContext.All - @Test - def infoAndWarnMessagesGoToOut = + def infoAndWarnMessagesGoToOut(): Unit = runAndCheck { logger => logger.info("info") logger.warn("warn") @@ -34,9 +31,8 @@ class BloopLoggerSpec { } @Test - def errorMessagesGoToErr = - runAndCheck { logger => - logger.error("error") + def errorMessagesGoToErr(): Unit = + runAndCheck { logger => logger.error("error") } { (outMsgs, errMsgs) => assertEquals(0, outMsgs.length.toLong) assertEquals(1, errMsgs.length.toLong) @@ -45,9 +41,9 @@ class BloopLoggerSpec { } @Test - def debugAndTraceMessagesAreIgnoredByDefault = + def debugAndTraceMessagesAreIgnoredByDefault(): Unit = runAndCheck { logger => - logger.debug("debug") + logger.debug("debug")(LogContext.All) logger.trace(new Exception) } { (outMsgs, errMsgs) => assertEquals(0, outMsgs.length.toLong) @@ -55,7 +51,7 @@ class BloopLoggerSpec { } @Test - def debugAndTraceMessagesGoToErrInVerboseMode = + def debugAndTraceMessagesGoToErrInVerboseMode(): Unit = runAndCheck { logger => val ex0 = { val ex = new Exception("trace0") @@ -65,7 +61,7 @@ class BloopLoggerSpec { logger.error("error0") logger.warn("warn0") logger.info("info0") - logger.debug("debug0") + logger.debug("debug0")(LogContext.All) logger.trace(ex0) val verboseLogger = logger.asVerbose @@ -77,7 +73,7 @@ class BloopLoggerSpec { verboseLogger.error("error1") verboseLogger.warn("warn1") verboseLogger.info("info1") - verboseLogger.debug("debug1") + verboseLogger.debug("debug1")(LogContext.All) verboseLogger.trace(ex1) val ex2 = { @@ -88,7 +84,7 @@ class BloopLoggerSpec { logger.error("error2") logger.warn("warn2") logger.info("info2") - logger.debug("debug2") + logger.debug("debug2")(LogContext.All) logger.trace(ex2) } { (outMsgs, errMsgs) => @@ -110,7 +106,7 @@ class BloopLoggerSpec { } @Test - def multipleLoggersDontStepOnEachOtherToes = { + def multipleLoggersDontStepOnEachOtherToes(): Unit = { val bos0 = new ByteArrayOutputStream val ps0 = new PrintStream(bos0) @@ -133,7 +129,7 @@ class BloopLoggerSpec { } @Test - def multipleLoggerSameNamesDifferentOutputs = { + def multipleLoggerSameNamesDifferentOutputs(): Unit = { val loggerName = "same-name-logger" val bos0 = new ByteArrayOutputStream @@ -156,23 +152,49 @@ class BloopLoggerSpec { } @Test - def isVerbose: Unit = { + def isVerbose(): Unit = { val expectedMessage = "this-is-logged" runAndCheck { logger => - logger.debug("this-is-not-logged") + logger.debug("this-is-not-logged")(LogContext.All) assertFalse("The logger shouldn't report being in verbose mode.", logger.isVerbose) val verboseLogger = logger.asVerbose - verboseLogger.debug(expectedMessage) + verboseLogger.debug(expectedMessage)(LogContext.All) assertTrue("The logger should report being in verbose mode.", verboseLogger.isVerbose) } { (outMsgs, errMsgs) => assertTrue("Nothing should have been logged to stdout.", outMsgs.isEmpty) assertEquals(1, errMsgs.length.toLong) assertTrue("Logged message should have debug level.", isDebug(errMsgs(0))) - assertTrue(s"Logged message should contain '$expectedMessage'", - errMsgs(0).contains(expectedMessage)) + assertTrue( + s"Logged message should contain '$expectedMessage'", + errMsgs(0).contains(expectedMessage)) } + } + @Test + def isVerboseForConcreteContexts(): Unit = { + val AllDebugLog = "This is an all debug log" + val CompilationDebugLog = "This is a compilation debug log" + val CompilationDebugLog2 = "This is a compilation debug log 2" + + runAndCheck { logger0 => + val logger = logger0.asVerbose + logger.debug(AllDebugLog)(LogContext.All) + logger.debug("This is a bsp log")(LogContext.Bsp) + logger.debug("This is a file watching log")(LogContext.FileWatching) + logger.debug("This is a test log")(LogContext.Test) + logger.debug(CompilationDebugLog)(LogContext.Compilation) + + // Use a locally scope to force the use of an implicit + locally { + implicit val ctx: LogContext = LogContext.Compilation + logger.debug(CompilationDebugLog2) + } + } { (stdout, stderr) => + val successfulDebugs = List(AllDebugLog, CompilationDebugLog, CompilationDebugLog2) + val prefixedLogs = successfulDebugs.map(msg => s"[D] $msg").sorted + assertTrue(s"Unexpected ${stderr}", stderr.toList.sorted == prefixedLogs) + }(LogContext.Compilation) } private def isWarn(msg: String): Boolean = msg.contains("[W]") @@ -183,8 +205,9 @@ class BloopLoggerSpec { !(isWarn(msg) || isError(msg) || isDebug(msg) || isTrace(msg)) } - private def runAndCheck(op: BloopLogger => Unit)( - check: (Seq[String], Seq[String]) => Unit): Unit = { + private def runAndCheck( + op: BloopLogger => Unit + )(check: (Seq[String], Seq[String]) => Unit)(implicit ctx: LogContext = LogContext.All): Unit = { val outStream = new ByteArrayOutputStream val errStream = new ByteArrayOutputStream @@ -192,7 +215,7 @@ class BloopLoggerSpec { val err = new PrintStream(errStream) val loggerName = UUID.randomUUID().toString - val logger = BloopLogger.at(loggerName, out, err, false, LogContext.All) + val logger = BloopLogger.at(loggerName, out, err, false, ctx) op(logger) val outMessages = convertAndReadAllFrom(outStream) diff --git a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala index 8aa22f51db..7142eeb8bd 100644 --- a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala +++ b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala @@ -2,20 +2,22 @@ package bloop.logging import java.util.concurrent.ConcurrentLinkedDeque -final class BufferedLogger private( +final class BufferedLogger private ( underlying: Logger, private val buffer: ConcurrentLinkedDeque[() => Unit] ) extends Logger { - override def name = underlying.name - override def ansiCodesSupported() = underlying.ansiCodesSupported() - override def logContext = underlying.logContext + override def name: String = underlying.name + override def ansiCodesSupported(): Boolean = underlying.ansiCodesSupported() + override def logContext: LogContext = underlying.logContext - override def debugInContext(msg: String)(implicit ctx: LogContext) = buffer.addLast(() => underlying.debugInContext(msg)) - override def debug(msg: String) = buffer.addLast(() => underlying.debug(msg)) - override def error(msg: String) = buffer.addLast(() => underlying.error(msg)) - override def warn(msg: String) = buffer.addLast(() => underlying.warn(msg)) - override def trace(exception: Throwable) = buffer.addLast(() => underlying.trace(exception)) - override def info(msg: String) = buffer.addLast(() => underlying.info(msg)) + override def printDebug(msg: String): Unit = buffer.addLast(() => underlying.printDebug(msg)) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (isVerbose && logContext.isEnabledFor(ctx)) buffer.addLast(() => printDebug(msg)) + + override def error(msg: String): Unit = buffer.addLast(() => underlying.error(msg)) + override def warn(msg: String): Unit = buffer.addLast(() => underlying.warn(msg)) + override def trace(exception: Throwable): Unit = buffer.addLast(() => underlying.trace(exception)) + override def info(msg: String): Unit = buffer.addLast(() => underlying.info(msg)) override def isVerbose: Boolean = underlying.isVerbose override def asVerbose: Logger = new BufferedLogger(underlying.asVerbose, buffer) override def asDiscrete: Logger = new BufferedLogger(underlying.asDiscrete, buffer) diff --git a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala index c796e73b7b..274f4965bf 100644 --- a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala +++ b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala @@ -32,9 +32,11 @@ final class PublisherLogger( def filterMessageByLabel(label: String): List[String] = messages.iterator.asScala.flatMap(lm => if (lm._1 == label) List(lm._2) else Nil).toList + override def printDebug(msg: String): Unit = add("debug", msg) + override def debug(msg: String)(implicit ctx: LogContext): Unit = + if (logContext.isEnabledFor(ctx)) add("debug", msg) + private def trace(msg: String): Unit = add("trace", msg) - override def debug(msg: String): Unit = add("debug", msg) - override def debugInContext(msg: String)(implicit ctx: LogContext): Unit = if (logContext.isEnabled) add("debug", msg) override def info(msg: String): Unit = add("info", msg) override def error(msg: String): Unit = add("error", msg) override def warn(msg: String): Unit = add("warn", msg) diff --git a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala index 03c8e60e58..16c4cea480 100644 --- a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala +++ b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit import bloop.Server import bloop.bsp.BspServer -import bloop.logging.{LogContext, ProcessLogger, RecordingLogger} +import bloop.logging.{LogContext, Logger, ProcessLogger, RecordingLogger} import bloop.tasks.TestUtil import com.martiansoftware.nailgun.NGServer import monix.eval.Task @@ -38,6 +38,7 @@ abstract class NailgunTest { */ def withServerTask[T](log: RecordingLogger, config: Path)( op: (RecordingLogger, Client) => T): Task[T] = { + implicit val ctx: LogContext = LogContext.All val oldIn = System.in val oldOut = System.out val oldErr = System.err From 2c41b793fa7b99447ec457ddb7d0b5aca66d79da Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 14:17:52 +0200 Subject: [PATCH 06/10] Add guard against `isVerbose` in `printDebug` --- backend/src/main/scala/bloop/logging/BloopLogger.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/main/scala/bloop/logging/BloopLogger.scala b/backend/src/main/scala/bloop/logging/BloopLogger.scala index 8336c56745..12aabe592c 100644 --- a/backend/src/main/scala/bloop/logging/BloopLogger.scala +++ b/backend/src/main/scala/bloop/logging/BloopLogger.scala @@ -79,7 +79,8 @@ final class BloopLogger( } override private[logging] def printDebug(line: String): Unit = { - err.println(s"${colored(GREEN, "[D]")} $line") + if (!isVerbose) () + else err.println(s"${colored(GREEN, "[D]")} $line") } } From 7eec175cb12c8b3c1b972236ea4a6e283e3ff039 Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 14:43:02 +0200 Subject: [PATCH 07/10] Run doc generation to test CLI commands We didn't run this before, but now we add a way to test that all the commands in the doc generator are correctly parsed by bloop. --- .drone.yml | 1 + frontend/src/main/scala/bloop/util/CommandsDocGenerator.scala | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.drone.yml b/.drone.yml index 6e1cfb62ba..3540b26fc7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,6 +18,7 @@ matrix: "nativeBridge/test" \ "jsBridge06/test" \ "jsBridge10/test" \ + "frontend/runMain bloop.util.CommandsDocGenerator --test" \ "frontend/test" \ "gradleBloop212/test" \ "docs/makeSite" diff --git a/frontend/src/main/scala/bloop/util/CommandsDocGenerator.scala b/frontend/src/main/scala/bloop/util/CommandsDocGenerator.scala index 241dd92ef0..1caa74f999 100644 --- a/frontend/src/main/scala/bloop/util/CommandsDocGenerator.scala +++ b/frontend/src/main/scala/bloop/util/CommandsDocGenerator.scala @@ -43,6 +43,9 @@ object CommandsDocGenerator { parseExamples match { case Left(msg) => println(s"Error: $msg") + case Right(commandExamples) if (args.headOption.contains("--test")) => + val generation = generateHTML(commandExamples) + assert(!generation.isEmpty, s"Generation of HTML yielded empty map! ${generation}") case Right(commandExamples) => println(generateHTML(commandExamples).mkString(Messages.NL)) } From 17a121967ef0643ac92adcdd3f69d3302c4502ad Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 15:49:36 +0200 Subject: [PATCH 08/10] Define `DebugFilter` instead of `LogContext` A debug filter can be selected by the user by typing `--debug $FILTER` in the command-line bloop tool. Users can now select several debug filters. They are called debug filters instead of log contexts because the latter name can be misleading. A log context is currently only used for debugging purposes, so it's better to keep its semantics as precise as possible. This commit also makes `--debug` enable verbose debugging automatically, which before required the user to pass `--verbose` to see the logs. --- .../scala/bloop/DependencyResolution.scala | 4 +- .../src/main/scala/bloop/ScalaInstance.scala | 7 +- backend/src/main/scala/bloop/io/Timer.scala | 4 +- .../scala/bloop/logging/BloopLogger.scala | 26 +-- .../scala/bloop/logging/DebugFilter.scala | 55 +++++ .../main/scala/bloop/logging/LogContext.scala | 20 -- .../src/main/scala/bloop/logging/Logger.scala | 5 +- .../scala/bloop/logging/ProcessLogger.scala | 2 +- .../scala/bloop/logging/Slf4jAdapter.scala | 2 +- .../scala/bloop/logging/RecordingLogger.scala | 6 +- .../bloop/logging/BloopLoggerBenchmark.scala | 6 +- .../main/scala/bloop/logging/NoopLogger.scala | 4 +- .../bloop/scalanative/NativeBridge.scala | 4 +- .../main/scala/bloop/scalajs/JsBridge.scala | 4 +- .../main/scala/bloop/scalajs/JsBridge.scala | 4 +- frontend/src/main/scala/bloop/Cli.scala | 14 +- .../scala/bloop/bsp/BloopBspServices.scala | 4 +- .../src/main/scala/bloop/bsp/BspServer.scala | 6 +- .../src/main/scala/bloop/cli/CliOptions.scala | 7 +- .../src/main/scala/bloop/cli/CliParsers.scala | 18 +- .../src/main/scala/bloop/data/Project.scala | 4 +- .../main/scala/bloop/engine/BuildLoader.scala | 8 +- .../main/scala/bloop/engine/Interpreter.scala | 6 +- .../src/main/scala/bloop/engine/State.scala | 4 +- .../bloop/engine/caches/ResultsCache.scala | 4 +- .../bloop/engine/tasks/CompilationTask.scala | 4 +- .../main/scala/bloop/engine/tasks/Tasks.scala | 9 +- .../engine/tasks/ToolchainCompanion.scala | 5 +- .../src/main/scala/bloop/exec/Forker.scala | 6 +- .../main/scala/bloop/io/SourceWatcher.scala | 4 +- .../scala/bloop/logging/BspClientLogger.scala | 6 +- .../scala/bloop/logging/BspServerLogger.scala | 6 +- .../scala/bloop/logging/ScribeAdapter.scala | 4 +- .../scala/bloop/testing/TestInternals.scala | 4 +- .../main/scala/bloop/testing/TestServer.scala | 4 +- .../scala/bloop/testing/TestSuiteEvent.scala | 4 +- .../test/scala/bloop/bsp/BspClientTest.scala | 4 +- .../scala/bloop/engine/FileWatchingSpec.scala | 4 +- .../scala/bloop/engine/InterpreterSpec.scala | 4 +- .../scala/bloop/logging/BloopLoggerSpec.scala | 36 +-- .../scala/bloop/logging/BufferedLogger.scala | 6 +- .../scala/bloop/logging/DebugFilterSpec.scala | 207 ++++++++++++++++++ .../scala/bloop/logging/PublisherLogger.scala | 6 +- .../scala/bloop/nailgun/NailgunTest.scala | 4 +- 44 files changed, 404 insertions(+), 151 deletions(-) create mode 100644 backend/src/main/scala/bloop/logging/DebugFilter.scala delete mode 100644 backend/src/main/scala/bloop/logging/LogContext.scala create mode 100644 frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala diff --git a/backend/src/main/scala/bloop/DependencyResolution.scala b/backend/src/main/scala/bloop/DependencyResolution.scala index 0e56124a57..5d310c7bf5 100644 --- a/backend/src/main/scala/bloop/DependencyResolution.scala +++ b/backend/src/main/scala/bloop/DependencyResolution.scala @@ -1,6 +1,6 @@ package bloop -import bloop.logging.{ LogContext, Logger } +import bloop.logging.{ DebugFilter, Logger } import bloop.io.AbsolutePath import sbt.librarymanagement._ @@ -45,7 +45,7 @@ object DependencyResolution { version: String, logger: Logger, additionalRepositories: Seq[Repository] = Nil): Array[AbsolutePath] = { - logger.debug(s"Resolving $organization:$module:$version")(LogContext.Compilation) + logger.debug(s"Resolving $organization:$module:$version")(DebugFilter.Compilation) val dependency = Dependency(Module(organization, module), version) val start = Resolution(Set(dependency)) val repositories = { diff --git a/backend/src/main/scala/bloop/ScalaInstance.scala b/backend/src/main/scala/bloop/ScalaInstance.scala index ad0e2f35f2..7da3f76c9b 100644 --- a/backend/src/main/scala/bloop/ScalaInstance.scala +++ b/backend/src/main/scala/bloop/ScalaInstance.scala @@ -7,7 +7,7 @@ import java.nio.file.attribute.BasicFileAttributes import java.util.Properties import bloop.internal.build.BloopScalaInfo -import bloop.logging.{ LogContext, Logger } +import bloop.logging.{ DebugFilter, Logger } import scala.util.control.NonFatal @@ -91,8 +91,9 @@ object ScalaInstance { val jarsKey = allJars.map(_.underlying).sortBy(_.toString).toList if (allJars.nonEmpty) { def newInstance = { - logger.debug(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.")(LogContext.Compilation) - jarsKey.foreach(p => logger.debug(s" => $p")(LogContext.Compilation)) + logger.debug(s"Cache miss for scala instance ${scalaOrg}:${scalaName}:${scalaVersion}.")( + DebugFilter.Compilation) + jarsKey.foreach(p => logger.debug(s" => $p")(DebugFilter.Compilation)) new ScalaInstance(scalaOrg, scalaName, scalaVersion, allJars.map(_.toFile).toArray) } diff --git a/backend/src/main/scala/bloop/io/Timer.scala b/backend/src/main/scala/bloop/io/Timer.scala index 65fbbaab12..45a33b654f 100644 --- a/backend/src/main/scala/bloop/io/Timer.scala +++ b/backend/src/main/scala/bloop/io/Timer.scala @@ -1,6 +1,6 @@ package bloop.io -import bloop.logging.{ LogContext, Logger } +import bloop.logging.{ DebugFilter, Logger } object Timer { @inline def timed[T](logger: Logger, prefix: Option[String] = None)(op: => T): T = { @@ -8,7 +8,7 @@ object Timer { try op finally { val elapsed = (System.nanoTime() - start).toDouble / 1e6 - logger.debug(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms")(LogContext.All) + logger.debug(s"Elapsed ${prefix.map(s => s"($s)").getOrElse("")}: $elapsed ms")(DebugFilter.All) } } } diff --git a/backend/src/main/scala/bloop/logging/BloopLogger.scala b/backend/src/main/scala/bloop/logging/BloopLogger.scala index 12aabe592c..c2f90604aa 100644 --- a/backend/src/main/scala/bloop/logging/BloopLogger.scala +++ b/backend/src/main/scala/bloop/logging/BloopLogger.scala @@ -11,7 +11,7 @@ import scala.Console.{CYAN, GREEN, RED, RESET, YELLOW} * @param out The stream to use to write `INFO` and `WARN` level messages. * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. * @param colorOutput Print with or without color. - * @param logContext Narrows logs to specified context. + * @param debugFilter Narrows logs to specified context. */ final class BloopLogger( override val name: String, @@ -19,11 +19,11 @@ final class BloopLogger( err: PrintStream, private val debugCount: Int, colorOutput: Boolean, - val logContext: LogContext + val debugFilter: DebugFilter ) extends Logger { override def ansiCodesSupported() = true - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (isVerbose && logContext.isEnabledFor(ctx)) print(msg, printDebug) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (isVerbose && debugFilter.isEnabledFor(ctx)) print(msg, printDebug) override def error(msg: String): Unit = print(msg, printError) override def warn(msg: String): Unit = print(msg, printWarning) override def trace(exception: Throwable): Unit = trace("", exception) @@ -31,12 +31,12 @@ final class BloopLogger( override def asDiscrete: Logger = { if (debugCount <= 0) this - else new BloopLogger(name, out, err, debugCount - 1, colorOutput, logContext) + else new BloopLogger(name, out, err, debugCount - 1, colorOutput, debugFilter) } override def isVerbose: Boolean = debugCount > 0 override def asVerbose: Logger = { - new BloopLogger(name, out, err, debugCount + 1, colorOutput, logContext) + new BloopLogger(name, out, err, debugCount + 1, colorOutput, debugFilter) } @scala.annotation.tailrec @@ -93,7 +93,7 @@ object BloopLogger { * @param out The stream to use to write `INFO` and `WARN` level messages. * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. * @param isVerbose Tells whether the logger is verbose or not. - * @param logContext Narrows logs to specified context. + * @param filter Filters that apply to all debug messages. * @return A `BloopLogger` whose output will be written in the specified streams. */ def at( @@ -102,8 +102,8 @@ object BloopLogger { err: PrintStream, isVerbose: Boolean, colorOutput: Boolean, - logContext: LogContext - ): BloopLogger = new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput, logContext) + filter: DebugFilter + ): BloopLogger = new BloopLogger(name, out, err, if (isVerbose) 1 else 0, colorOutput, filter) /** * Instantiates a new `BloopLogger` using the specified streams. @@ -111,7 +111,7 @@ object BloopLogger { * @param name The name of the logger. * @param out The stream to use to write `INFO` and `WARN` level messages. * @param err The stream to use to write `FATAL`, `ERROR`, `DEBUG` and `TRACE` level messages. - * @param logContext Narrows logs to specified context. + * @param filter Filters that apply to all debug messages. * @return A `BloopLogger` whose output will be written in the specified streams. */ def at( @@ -119,8 +119,8 @@ object BloopLogger { out: PrintStream, err: PrintStream, colorOutput: Boolean, - logContext: LogContext - ): BloopLogger = at(name, out, err, false, colorOutput, logContext) + filter: DebugFilter + ): BloopLogger = at(name, out, err, false, colorOutput, filter) /** * Instantiates a new `BloopLogger` that writes to stdout and stderr. @@ -130,5 +130,5 @@ object BloopLogger { * calling `at(name, System.out, System.err)`. */ def default(name: String): BloopLogger = - at(name, System.out, System.err, false, LogContext.All) + at(name, System.out, System.err, false, DebugFilter.All) } diff --git a/backend/src/main/scala/bloop/logging/DebugFilter.scala b/backend/src/main/scala/bloop/logging/DebugFilter.scala new file mode 100644 index 0000000000..be7f755e7a --- /dev/null +++ b/backend/src/main/scala/bloop/logging/DebugFilter.scala @@ -0,0 +1,55 @@ +package bloop.logging + +sealed trait DebugFilter { self => + private[logging] def isEnabledFor(other: DebugFilter): Boolean = + DebugFilter.checkSubsumption(self, other) +} + +object DebugFilter { + final case object All extends DebugFilter + final case object Bsp extends DebugFilter + final case object Test extends DebugFilter + final case object Compilation extends DebugFilter + final case object FileWatching extends DebugFilter + final case class Aggregate(filters: List[DebugFilter]) extends DebugFilter + + def toUniqueFilter(filters: List[DebugFilter]): DebugFilter = { + filters match { + case Nil => DebugFilter.All + case x :: Nil => DebugFilter.All + case xs => + if (xs.contains(DebugFilter.All)) DebugFilter.All + else DebugFilter.Aggregate(xs) + } + } + + /** + * Check if `filter1` subsumes `filter2`. Note that subsumption of debug filters is + * commutative so that if the debug filter of a logger is, say, `Compile`, the use + * sites can debug normal statements tagged as `DebugFilter.All`. + * + * @param filter1 The first filter to check for subsumption. + * @param filter2 The second filter to check for subsumption. + * @return Whether filter2 is subsumed in filter1 or viceversa. + */ + private[bloop] def checkSubsumption(filter1: DebugFilter, filter2: DebugFilter): Boolean = { + def isFilterSubsumed(owner: DebugFilter, check: DebugFilter): Boolean = { + (owner, check) match { + case (DebugFilter.All, _) => true + case (_, DebugFilter.All) => true + case (ctx1, ctx2) if ctx1 == ctx2 => true + case _ => false + } + } + + (filter1, filter2) match { + case (DebugFilter.Aggregate(filters), DebugFilter.Aggregate(filters2)) => + filters.exists(filter => filters2.exists(filter2 => isFilterSubsumed(filter, filter2))) + case (DebugFilter.Aggregate(filters), target) => + filters.exists(filter => isFilterSubsumed(filter, target)) + case (target, DebugFilter.Aggregate(filters)) => + filters.exists(filter => isFilterSubsumed(target, filter)) + case (one, another) => isFilterSubsumed(one, another) + } + } +} diff --git a/backend/src/main/scala/bloop/logging/LogContext.scala b/backend/src/main/scala/bloop/logging/LogContext.scala deleted file mode 100644 index c268317592..0000000000 --- a/backend/src/main/scala/bloop/logging/LogContext.scala +++ /dev/null @@ -1,20 +0,0 @@ -package bloop.logging - -sealed trait LogContext { self => - private[logging] def isEnabledFor(other: LogContext): Boolean = { - (self, other) match { - case (LogContext.All, _) => true - case (_, LogContext.All) => true - case (ctx1, ctx2) if ctx1 == ctx2 => true - case _ => false - } - } -} - -object LogContext { - case object All extends LogContext - case object FileWatching extends LogContext - case object Compilation extends LogContext - case object Test extends LogContext - case object Bsp extends LogContext -} diff --git a/backend/src/main/scala/bloop/logging/Logger.scala b/backend/src/main/scala/bloop/logging/Logger.scala index e36b6a104b..3ff9dded24 100644 --- a/backend/src/main/scala/bloop/logging/Logger.scala +++ b/backend/src/main/scala/bloop/logging/Logger.scala @@ -16,9 +16,10 @@ abstract class Logger extends xsbti.Logger with BaseSbtLogger { def asDiscrete: Logger /** Context for debug logging. */ - def logContext: LogContext + def debugFilter: DebugFilter - def debug(msg: String)(implicit ctx: LogContext): Unit + /** Defines a debug function that takes a message and a filter from its use site. */ + def debug(msg: String)(implicit ctx: DebugFilter): Unit override def debug(msg: Supplier[String]): Unit = printDebug(msg.get()) override def error(msg: Supplier[String]): Unit = error(msg.get()) diff --git a/backend/src/main/scala/bloop/logging/ProcessLogger.scala b/backend/src/main/scala/bloop/logging/ProcessLogger.scala index 58c37bd176..9dd679fbeb 100644 --- a/backend/src/main/scala/bloop/logging/ProcessLogger.scala +++ b/backend/src/main/scala/bloop/logging/ProcessLogger.scala @@ -21,7 +21,7 @@ class ProcessLogger(underlying: Logger, process: Process) { private[this] val processErr = process.getErrorStream def start(): Unit = { - implicit val ctx: LogContext = LogContext.All + implicit val ctx: DebugFilter = DebugFilter.All underlying.printDebug("Starting to log output from process...") new StreamLogger(underlying.info, processOut).start() new StreamLogger(underlying.error, processErr).start() diff --git a/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala b/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala index 73e27fe895..063e53509b 100644 --- a/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala +++ b/backend/src/main/scala/bloop/logging/Slf4jAdapter.scala @@ -13,7 +13,7 @@ import org.slf4j.{Marker, Logger => Slf4jLogger} */ final class Slf4jAdapter[L <: Logger](logger: L) extends Slf4jLogger { def underlying: L = logger - implicit val logContext: LogContext = logger.logContext + implicit val logContext: DebugFilter = logger.debugFilter override def getName: String = logger.name override def debug(msg: String): Unit = logger.debug(msg) diff --git a/backend/src/test/scala/bloop/logging/RecordingLogger.scala b/backend/src/test/scala/bloop/logging/RecordingLogger.scala index a620268661..4437818a28 100644 --- a/backend/src/test/scala/bloop/logging/RecordingLogger.scala +++ b/backend/src/test/scala/bloop/logging/RecordingLogger.scala @@ -8,7 +8,7 @@ import scala.collection.JavaConverters.asScalaIteratorConverter final class RecordingLogger( debug: Boolean = false, debugOut: Option[PrintStream] = None, - val logContext: LogContext = LogContext.All + val debugFilter: DebugFilter = DebugFilter.All ) extends Logger { private[this] val messages = new ConcurrentLinkedQueue[(String, String)] @@ -36,8 +36,8 @@ final class RecordingLogger( } override def printDebug(msg: String): Unit = add("debug", msg) - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (logContext.isEnabledFor(ctx)) add("debug", msg) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (debugFilter.isEnabledFor(ctx)) add("debug", msg) override def info(msg: String): Unit = add("info", msg) override def error(msg: String): Unit = add("error", msg) diff --git a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala index ca5f63de95..94e663ebbe 100644 --- a/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala +++ b/benchmarks/src/main/scala/bloop/logging/BloopLoggerBenchmark.scala @@ -6,7 +6,7 @@ import org.openjdk.jmh.annotations.Benchmark object BloopLoggerBenchmark { private val devnull = new PrintStream(_ => ()) - val logger = BloopLogger.at("benchmark", devnull, devnull, false, LogContext.All) + val logger = BloopLogger.at("benchmark", devnull, devnull, false, DebugFilter.All) } class BloopLoggerBenchmark { @@ -29,12 +29,12 @@ class BloopLoggerBenchmark { @Benchmark def logDebug(): Unit = { // Debugs with `LogContext.All` - BloopLoggerBenchmark.logger.debug("message")(LogContext.All) + BloopLoggerBenchmark.logger.debug("message")(DebugFilter.All) } @Benchmark def logDebugWithContext(): Unit = { - implicit val logContext: LogContext = LogContext.All + implicit val logContext: DebugFilter = DebugFilter.All BloopLoggerBenchmark.logger.debug("message")(logContext) } } diff --git a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala index fc94a4c176..9c47d8a21c 100644 --- a/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala +++ b/benchmarks/src/main/scala/bloop/logging/NoopLogger.scala @@ -3,10 +3,10 @@ package bloop.logging object NoopLogger extends Logger { override def name: String = "NoopLogger" override def ansiCodesSupported(): Boolean = false - override def logContext: LogContext = LogContext.All + override def debugFilter: DebugFilter = DebugFilter.All override def printDebug(msg: String): Unit = () - override def debug(msg: String)(implicit ctx: LogContext): Unit = () + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = () override def error(msg: String): Unit = () override def warn(msg: String): Unit = () override def trace(exception: Throwable): Unit = () diff --git a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala index 73175d2053..543d2a4cb8 100644 --- a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala +++ b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala @@ -1,7 +1,7 @@ package bloop.scalanative import bloop.config.Config.{LinkerMode, NativeConfig} import bloop.io.Paths -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import java.nio.file.{Files, Path} import bloop.data.Project @@ -10,7 +10,7 @@ import scala.scalanative.build.{Build, Config, Discover, GC, Mode, Logger => Nat object NativeBridge { - private implicit val ctx: LogContext = LogContext.All + private implicit val ctx: DebugFilter = DebugFilter.All def nativeLink(config0: NativeConfig, project: Project, entry: String, target: Path, logger: Logger): Path = { val workdir = project.out.resolve("native") diff --git a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala index 7ff3dabbd8..6c8293b56d 100644 --- a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala @@ -9,7 +9,7 @@ import org.scalajs.core.tools.linker.{ModuleInitializer, StandardLinker} import org.scalajs.core.tools.logging.{Level, Logger => JsLogger} import bloop.config.Config.{JsConfig, LinkerMode, ModuleKindJS} import bloop.data.Project -import bloop.logging.{LogContext, Logger => BloopLogger} +import bloop.logging.{DebugFilter, Logger => BloopLogger} import org.scalajs.core.tools.linker.backend.ModuleKind import org.scalajs.core.tools.sem.Semantics @@ -21,7 +21,7 @@ object JsBridge { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message)(LogContext.All) + case Level.Debug => logger.debug(message)(DebugFilter.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala index ece4afd73e..44f2b3a413 100644 --- a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala @@ -6,7 +6,7 @@ import org.scalajs.io.AtomicWritableFileVirtualJSFile import bloop.config.Config.{JsConfig, LinkerMode, ModuleKindJS} import bloop.data.Project import bloop.io.Paths -import bloop.logging.{LogContext, Logger => BloopLogger} +import bloop.logging.{DebugFilter, Logger => BloopLogger} import org.scalajs.linker.irio.{FileScalaJSIRContainer, FileVirtualScalaJSIRFile, IRFileCache} import org.scalajs.linker.{ModuleInitializer, ModuleKind, Semantics, StandardLinker} import org.scalajs.logging.{Level, Logger => JsLogger} @@ -19,7 +19,7 @@ object JsBridge { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message)(LogContext.All) + case Level.Debug => logger.debug(message)(DebugFilter.All) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/frontend/src/main/scala/bloop/Cli.scala b/frontend/src/main/scala/bloop/Cli.scala index 47c19c599d..e92f399ebc 100644 --- a/frontend/src/main/scala/bloop/Cli.scala +++ b/frontend/src/main/scala/bloop/Cli.scala @@ -8,11 +8,12 @@ import bloop.bsp.BspServer import bloop.cli.validation.Validate import bloop.cli.{CliOptions, CliParsers, Commands, CommonOptions, ExitStatus} import bloop.engine._ -import bloop.logging.{BloopLogger, LogContext, Logger} +import bloop.logging.{BloopLogger, DebugFilter, Logger} import caseapp.core.{DefaultBaseCommand, Messages} import com.martiansoftware.nailgun.NGContext import _root_.monix.eval.Task import bloop.engine.tasks.Tasks +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag import scala.util.control.NonFatal @@ -264,13 +265,18 @@ object Cli { val commonOpts = cliOptions.common val configDirectory = getConfigDir(cliOptions) + val debugFilter: DebugFilter = DebugFilter.toUniqueFilter(cliOptions.debug) + + // We enable verbose debugging when the user either specifies `--verbose` or `--debug FILTER` + val isVerbose = cliOptions.verbose || debugFilter != DebugFilter.All + val logger = BloopLogger.at( configDirectory.syntax, commonOpts.out, commonOpts.err, - cliOptions.verbose, + isVerbose, !(cliOptions.noColor || commonOpts.env.containsKey("NO_COLOR")), - cliOptions.debug + debugFilter ) val currentState = State.loadActiveStateFor(configDirectory, pool, cliOptions.common, logger) @@ -311,7 +317,7 @@ object Cli { val ngout = cliOptions.common.ngout def logElapsed(since: Long): Unit = { val elapsed = (System.nanoTime() - since).toDouble / 1e6 - logger.debug(s"Elapsed: $elapsed ms")(LogContext.All) + logger.debug(s"Elapsed: $elapsed ms")(DebugFilter.All) } // Simulate try-catch-finally with monix tasks to time the task execution diff --git a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala index f891549a2c..248e2e3c11 100644 --- a/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala +++ b/frontend/src/main/scala/bloop/bsp/BloopBspServices.scala @@ -11,7 +11,7 @@ import bloop.data.Project import bloop.engine.tasks.{ScalaJsToolchain, ScalaNativeToolchain, Tasks} import bloop.engine.{Action, Dag, Exit, Interpreter, Run, State} import bloop.io.{AbsolutePath, RelativePath} -import bloop.logging.{BspServerLogger, LogContext} +import bloop.logging.{BspServerLogger, DebugFilter} import bloop.testing.{BspLoggingEventHandler, TestInternals} import monix.eval.Task import ch.epfl.scala.bsp.{BuildTargetIdentifier, endpoints} @@ -64,7 +64,7 @@ final class BloopBspServices( private val pool = callSiteState.pool private val defaultOpts = callSiteState.commonOptions def reloadState(config: AbsolutePath): Task[State] = { - bspForwarderLogger.debug(s"Reloading bsp state for ${config.syntax}")(LogContext.Bsp) + bspForwarderLogger.debug(s"Reloading bsp state for ${config.syntax}")(DebugFilter.Bsp) State.loadActiveStateFor(config, pool, defaultOpts, bspForwarderLogger).map { state0 => state0.copy(logger = bspForwarderLogger, commonOptions = latestState.commonOptions) } diff --git a/frontend/src/main/scala/bloop/bsp/BspServer.scala b/frontend/src/main/scala/bloop/bsp/BspServer.scala index 2455e70312..40b577615a 100644 --- a/frontend/src/main/scala/bloop/bsp/BspServer.scala +++ b/frontend/src/main/scala/bloop/bsp/BspServer.scala @@ -6,7 +6,7 @@ import java.util.Locale import bloop.cli.Commands import bloop.engine.State import bloop.io.{AbsolutePath, RelativePath} -import bloop.logging.{BspClientLogger, LogContext} +import bloop.logging.{BspClientLogger, DebugFilter} import com.martiansoftware.nailgun.{NGUnixDomainServerSocket, NGWin32NamedPipeServerSocket} import monix.eval.Task import monix.execution.Scheduler @@ -15,9 +15,7 @@ import monix.execution.atomic.Atomic import scala.meta.jsonrpc.{BaseProtocolMessage, LanguageClient, LanguageServer} object BspServer { - - private implicit val logContext: LogContext = LogContext.Bsp - + private implicit val logContext: DebugFilter = DebugFilter.Bsp private[bloop] val isWindows: Boolean = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") private[bloop] val isMac: Boolean = diff --git a/frontend/src/main/scala/bloop/cli/CliOptions.scala b/frontend/src/main/scala/bloop/cli/CliOptions.scala index 3e00729b0c..5f3688cb13 100644 --- a/frontend/src/main/scala/bloop/cli/CliOptions.scala +++ b/frontend/src/main/scala/bloop/cli/CliOptions.scala @@ -2,7 +2,7 @@ package bloop.cli import java.nio.file.Path -import bloop.logging.LogContext +import bloop.logging.DebugFilter import caseapp.{ExtraName, HelpMessage, Recurse, ValueDescription} case class CliOptions( @@ -19,9 +19,8 @@ case class CliOptions( verbose: Boolean = false, @HelpMessage("If set, do not color output. Defaults to false.") noColor: Boolean = false, - @HelpMessage("Debug log context.") - @ValueDescription("file-watching") - debug: LogContext = LogContext.All, + @HelpMessage("Debug the execution of a concrete task.") + debug: List[DebugFilter] = Nil, @Recurse common: CommonOptions = CommonOptions.default, ) diff --git a/frontend/src/main/scala/bloop/cli/CliParsers.scala b/frontend/src/main/scala/bloop/cli/CliParsers.scala index 58a64b2fe9..f52dada286 100644 --- a/frontend/src/main/scala/bloop/cli/CliParsers.scala +++ b/frontend/src/main/scala/bloop/cli/CliParsers.scala @@ -4,7 +4,7 @@ import java.io.{InputStream, PrintStream} import java.nio.file.{Path, Paths} import java.util.Properties -import bloop.logging.LogContext +import bloop.logging.DebugFilter import caseapp.CommandParser import caseapp.core.{ArgParser, Default, DefaultBaseCommand, Parser} import caseapp.util.Implicit @@ -61,15 +61,17 @@ object CliParsers { } } - implicit val logContextParser: ArgParser[LogContext] = - ArgParser.instance[LogContext]("\"all\" | \"file-watching\" | \"compilation\" | \"test\" | \"bsp\"") { - case "all" => Right(LogContext.All) - case "file-watching" => Right(LogContext.FileWatching) - case "compilation" => Right(LogContext.Compilation) - case "test" => Right(LogContext.Test) - case "bsp" => Right(LogContext.Bsp) + val DebugFilterTags = "\"all\" | \"file-watching\" | \"compilation\" | \"test\" | \"bsp\"" + implicit val debugFilterParser: ArgParser[DebugFilter] = { + ArgParser.instance[DebugFilter](DebugFilterTags) { + case "all" => Right(DebugFilter.All) + case "file-watching" => Right(DebugFilter.FileWatching) + case "compilation" => Right(DebugFilter.Compilation) + case "test" => Right(DebugFilter.Test) + case "bsp" => Right(DebugFilter.Bsp) case w00t => Left(s"Unrecognized log context: $w00t") } + } import shapeless.{HNil, CNil, :+:, ::, Coproduct} implicit val implicitHNil: Implicit[HNil] = Implicit.hnil diff --git a/frontend/src/main/scala/bloop/data/Project.scala b/frontend/src/main/scala/bloop/data/Project.scala index 324278054d..447a150174 100644 --- a/frontend/src/main/scala/bloop/data/Project.scala +++ b/frontend/src/main/scala/bloop/data/Project.scala @@ -7,7 +7,7 @@ import java.nio.file.attribute.FileTime import scala.util.Try import bloop.exec.JavaEnv import bloop.io.AbsolutePath -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import xsbti.compile.{ClasspathOptions, CompileOrder} import bloop.ScalaInstance import bloop.bsp.ProjectUris @@ -143,7 +143,7 @@ object Project { def fromBytesAndOrigin(bytes: Array[Byte], origin: Origin, logger: Logger): Project = { import _root_.io.circe.parser - logger.debug(s"Loading project from '${origin.path}'")(LogContext.All) + logger.debug(s"Loading project from '${origin.path}'")(DebugFilter.All) val contents = new String(bytes, StandardCharsets.UTF_8) parser.parse(contents) match { case Left(failure) => throw failure diff --git a/frontend/src/main/scala/bloop/engine/BuildLoader.scala b/frontend/src/main/scala/bloop/engine/BuildLoader.scala index 613e690a44..62fd1836f5 100644 --- a/frontend/src/main/scala/bloop/engine/BuildLoader.scala +++ b/frontend/src/main/scala/bloop/engine/BuildLoader.scala @@ -3,7 +3,7 @@ package bloop.engine import bloop.data.{Origin, Project} import bloop.io.Paths.AttributedPath import bloop.io.AbsolutePath -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import bloop.util.ByteHasher import monix.eval.Task @@ -37,7 +37,8 @@ object BuildLoader { configFiles: List[Build.ReadConfiguration], logger: Logger ): Task[List[Project]] = { - logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) + logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")( + DebugFilter.Compilation) val all = configFiles.map(f => Task(Project.fromBytesAndOrigin(f.bytes, f.origin, logger))) Task.gatherUnordered(all).executeOn(ExecutionContext.scheduler) } @@ -83,7 +84,8 @@ object BuildLoader { Build.ReadConfiguration(Origin(ap, hash), bytes) } - logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")(LogContext.Compilation) + logger.debug(s"Loading ${configFiles.length} projects from '${configDir.syntax}'...")( + DebugFilter.Compilation) configFiles.map(f => Project.fromBytesAndOrigin(f.bytes, f.origin, logger)) } } diff --git a/frontend/src/main/scala/bloop/engine/Interpreter.scala b/frontend/src/main/scala/bloop/engine/Interpreter.scala index 26c7c7ee04..3620e62d68 100644 --- a/frontend/src/main/scala/bloop/engine/Interpreter.scala +++ b/frontend/src/main/scala/bloop/engine/Interpreter.scala @@ -7,7 +7,7 @@ import bloop.cli.CliParsers.CommandsMessages import bloop.cli.completion.{Case, Mode} import bloop.config.Config.Platform import bloop.io.{AbsolutePath, RelativePath, SourceWatcher} -import bloop.logging.LogContext +import bloop.logging.DebugFilter import bloop.testing.{LoggingEventHandler, TestInternals} import bloop.engine.tasks.{CompilationTask, ScalaJsToolchain, ScalaNativeToolchain, Tasks} import bloop.cli.Commands.{CompilingCommand, LinkingCommand} @@ -166,7 +166,7 @@ object Interpreter { logger.info(contents) } else { val configDirectory = state.build.origin.syntax - logger.debug(s"Projects loaded from '$configDirectory':")(LogContext.All) + logger.debug(s"Projects loaded from '$configDirectory':")(DebugFilter.All) state.build.projects.map(_.name).sorted.foreach(logger.info) } @@ -186,7 +186,7 @@ object Interpreter { else { Task.now( state.withDebug(s"Failed compilation for $project. Skipping $nextAction...")( - LogContext.Compilation + DebugFilter.Compilation ) ) } diff --git a/frontend/src/main/scala/bloop/engine/State.scala b/frontend/src/main/scala/bloop/engine/State.scala index 26fb6409c5..364a8e67a1 100644 --- a/frontend/src/main/scala/bloop/engine/State.scala +++ b/frontend/src/main/scala/bloop/engine/State.scala @@ -5,7 +5,7 @@ import bloop.cli.{CommonOptions, ExitStatus} import bloop.data.Project import bloop.engine.caches.{ResultsCache, StateCache} import bloop.io.Paths -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import monix.eval.Task /** @@ -90,7 +90,7 @@ object State { implicit class XState(val s: State) extends AnyVal { def withTrace(t: Throwable): State = { s.logger.trace(t); s } - def withDebug(msg: String)(implicit log: LogContext): State = { s.logger.debug(msg); s } + def withDebug(msg: String)(implicit log: DebugFilter): State = { s.logger.debug(msg); s } def withInfo(msg: String): State = { s.logger.info(msg); s } def withWarn(msg: String): State = { s.logger.warn(msg); s } def withError(msg: String): State = withError(msg, ExitStatus.UnexpectedError) diff --git a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala index 6ac5e722c0..fcfe783e7f 100644 --- a/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala +++ b/frontend/src/main/scala/bloop/engine/caches/ResultsCache.scala @@ -8,7 +8,7 @@ import bloop.Compiler.Result import bloop.engine.tasks.compilation.FinalCompileResult import bloop.engine.{Build, ExecutionContext} import bloop.io.AbsolutePath -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import bloop.reporter.Reporter import monix.eval.Task import xsbti.compile.{CompileAnalysis, MiniSetup, PreviousResult} @@ -82,7 +82,7 @@ final class ResultsCache private ( object ResultsCache { import java.util.concurrent.ConcurrentHashMap - private implicit val logContext: LogContext = LogContext.All + private implicit val logContext: DebugFilter = DebugFilter.All // TODO: Use a guava cache that stores maximum 200 analysis file private[bloop] val persisted = ConcurrentHashMap.newKeySet[PreviousResult]() diff --git a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala index 01c6cf93c3..01b27e122e 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/CompilationTask.scala @@ -5,14 +5,14 @@ import bloop.data.Project import bloop.engine._ import bloop.engine.tasks.compilation._ import bloop.io.AbsolutePath -import bloop.logging.{BspServerLogger, LogContext, Logger} +import bloop.logging.{BspServerLogger, DebugFilter, Logger} import bloop.reporter._ import bloop.{CompileInputs, CompileMode, Compiler} import monix.eval.Task import sbt.internal.inc.AnalyzingCompiler object CompilationTask { - private implicit val logContext: LogContext = LogContext.Compilation + private implicit val logContext: DebugFilter = DebugFilter.Compilation private val dateFormat = new java.text.SimpleDateFormat("HH:mm:ss.SSS") private def currentTime: String = dateFormat.format(new java.util.Date()) diff --git a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala index cfe0d5f5e4..01da1b8692 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/Tasks.scala @@ -8,7 +8,7 @@ import bloop.engine.caches.ResultsCache import bloop.engine.{Dag, State} import bloop.exec.Forker import bloop.io.AbsolutePath -import bloop.logging.LogContext +import bloop.logging.DebugFilter import bloop.testing.{DiscoveredTests, LoggingEventHandler, TestInternals, TestSuiteEvent, TestSuiteEventHandler} import bloop.data.Project import monix.eval.Task @@ -57,7 +57,7 @@ object Tasks { case Some(instance) => val classpath = project.classpath val entries = classpath.map(_.underlying.toFile).toSeq - logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}")(LogContext.All) + logger.debug(s"Setting up the console classpath with ${entries.mkString(", ")}")(DebugFilter.All) val loader = ClasspathUtilities.makeLoader(entries, instance) val compiler = state.compilerCache.get(instance).scalac.asInstanceOf[AnalyzingCompiler] val opts = ClasspathOptionsUtil.repl @@ -128,7 +128,7 @@ object Tasks { ): Task[State] = { import state.logger import bloop.util.JavaCompat.EnrichOptional - implicit val logContext: LogContext = LogContext.Test + implicit val logContext: DebugFilter = DebugFilter.Test def foundFrameworks(frameworks: List[Framework]) = frameworks.map(_.name).mkString(", ") @@ -296,7 +296,8 @@ object Tasks { } val mainClasses = analysis.infos.allInfos.values.flatMap(_.getMainClasses).toList - logger.debug(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}")(LogContext.All) + logger.debug(s"Found ${mainClasses.size} main classes${mainClasses.mkString(": ", ", ", ".")}")( + DebugFilter.All) mainClasses } diff --git a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala index 0123e7e8be..91b460c482 100644 --- a/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala +++ b/frontend/src/main/scala/bloop/engine/tasks/ToolchainCompanion.scala @@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap import bloop.DependencyResolution import bloop.config.Config import bloop.internal.build.BuildInfo -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} /** * Base class for companion objects of toolchains. @@ -68,7 +68,8 @@ abstract class ToolchainCompanion[Toolchain] { private final val BloopVersion = BuildInfo.version private final val BloopOrg = BuildInfo.organization private def resolveJars(artifactName: String, logger: Logger): List[Path] = { - logger.debug(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion")(LogContext.Compilation) + logger.debug(s"Resolving platform bridge: $BloopOrg:$artifactName:$BloopVersion")( + DebugFilter.Compilation) val files = DependencyResolution.resolve(BloopOrg, artifactName, BloopVersion, logger) files.iterator.map(_.underlying).filter(_.toString.endsWith(".jar")).toList } diff --git a/frontend/src/main/scala/bloop/exec/Forker.scala b/frontend/src/main/scala/bloop/exec/Forker.scala index f0eb399672..f699684d6d 100644 --- a/frontend/src/main/scala/bloop/exec/Forker.scala +++ b/frontend/src/main/scala/bloop/exec/Forker.scala @@ -10,7 +10,7 @@ import java.util.concurrent.{ConcurrentHashMap, TimeUnit} import bloop.cli.{CommonOptions, ExitStatus} import bloop.engine.ExecutionContext import bloop.io.AbsolutePath -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import com.zaxxer.nuprocess.{NuAbstractProcessHandler, NuProcess} import monix.eval.Task import monix.execution.Cancelable @@ -72,7 +72,7 @@ final case class Forker(javaEnv: JavaEnv, classpath: Array[AbsolutePath]) { | classpath = '$fullClasspath' | java_home = '${javaEnv.javaHome}' | java_options = '${javaEnv.javaOptions.mkString(" ")}""".stripMargin - Task(logger.debug(debugOptions)(LogContext.All)) + Task(logger.debug(debugOptions)(DebugFilter.All)) } else Task.unit logTask.flatMap(_ => Forker.run(cwd, cmd, logger, opts)) } @@ -81,7 +81,7 @@ final case class Forker(javaEnv: JavaEnv, classpath: Array[AbsolutePath]) { object Forker { - private implicit val logContext: LogContext = LogContext.All + private implicit val logContext: DebugFilter = DebugFilter.All /** The code returned after a successful execution. */ private final val EXIT_OK = 0 diff --git a/frontend/src/main/scala/bloop/io/SourceWatcher.scala b/frontend/src/main/scala/bloop/io/SourceWatcher.scala index 6d987af80d..a191a64235 100644 --- a/frontend/src/main/scala/bloop/io/SourceWatcher.scala +++ b/frontend/src/main/scala/bloop/io/SourceWatcher.scala @@ -5,7 +5,7 @@ import java.nio.file.{Files, Path} import bloop.data.Project import bloop.bsp.BspServer import bloop.engine.{ExecutionContext, State} -import bloop.logging.{LogContext, Logger, Slf4jAdapter} +import bloop.logging.{DebugFilter, Logger, Slf4jAdapter} import bloop.monix.FoldLeftAsyncConsumer import scala.collection.JavaConverters._ @@ -24,7 +24,7 @@ final class SourceWatcher private ( import java.nio.file.Files private val slf4jLogger = new Slf4jAdapter(logger) - private implicit val logContext: LogContext = LogContext.FileWatching + private implicit val logContext: DebugFilter = DebugFilter.FileWatching def watch(state0: State, action: State => Task[State]): Task[State] = { val ngout = state0.commonOptions.ngout diff --git a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala index b4fef6fb6a..12a6567eac 100644 --- a/frontend/src/main/scala/bloop/logging/BspClientLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspClientLogger.scala @@ -3,7 +3,7 @@ package bloop.logging /** Creates a logger that extends scribe's `LoggerSupport` for BSP's `LanguageClient`. */ final class BspClientLogger[L <: Logger](val underlying: L) extends Logger with ScribeAdapter { - override val logContext: LogContext = underlying.logContext + override val debugFilter: DebugFilter = underlying.debugFilter override val name: String = underlying.name override def isVerbose: Boolean = underlying.isVerbose @@ -11,8 +11,8 @@ final class BspClientLogger[L <: Logger](val underlying: L) extends Logger with override def asVerbose: Logger = new BspClientLogger(underlying.asVerbose) override def printDebug(msg: String): Unit = underlying.printDebug(msg) - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (logContext.isEnabledFor(ctx)) printDebug(msg) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (debugFilter.isEnabledFor(ctx)) printDebug(msg) override def ansiCodesSupported: Boolean = underlying.ansiCodesSupported() override def trace(t: Throwable): Unit = underlying.trace(t) diff --git a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala index 8fa42faeb0..565a7eef56 100644 --- a/frontend/src/main/scala/bloop/logging/BspServerLogger.scala +++ b/frontend/src/main/scala/bloop/logging/BspServerLogger.scala @@ -23,7 +23,7 @@ final class BspServerLogger private ( ) extends Logger with ScribeAdapter { - override def logContext: LogContext = underlying.logContext + override def debugFilter: DebugFilter = underlying.debugFilter override def isVerbose: Boolean = underlying.isVerbose override def asDiscrete: Logger = @@ -34,8 +34,8 @@ final class BspServerLogger private ( override def ansiCodesSupported: Boolean = ansiSupported || underlying.ansiCodesSupported() override private[logging] def printDebug(msg: String): Unit = underlying.printDebug(msg) - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (logContext.isEnabledFor(ctx)) printDebug(msg) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (debugFilter.isEnabledFor(ctx)) printDebug(msg) override def trace(t: Throwable): Unit = underlying.trace(t) diff --git a/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala b/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala index fc35bc4dc6..bce42e79f2 100644 --- a/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala +++ b/frontend/src/main/scala/bloop/logging/ScribeAdapter.scala @@ -9,11 +9,11 @@ trait ScribeAdapter extends scribe.LoggerSupport { self: Logger => case Level.Info => info(msg) case Level.Error => error(msg) case Level.Warn => warn(msg) - case Level.Debug => debug(msg)(LogContext.Bsp) + case Level.Debug => debug(msg)(DebugFilter.Bsp) case Level.Trace => record.throwable match { case Some(t) => trace(t) - case None => debug(record.message)(LogContext.Bsp) + case None => debug(record.message)(DebugFilter.Bsp) } } } diff --git a/frontend/src/main/scala/bloop/testing/TestInternals.scala b/frontend/src/main/scala/bloop/testing/TestInternals.scala index 239e062133..8fc64d68ac 100644 --- a/frontend/src/main/scala/bloop/testing/TestInternals.scala +++ b/frontend/src/main/scala/bloop/testing/TestInternals.scala @@ -8,7 +8,7 @@ import bloop.config.Config import bloop.engine.ExecutionContext import bloop.exec.Forker import bloop.io.AbsolutePath -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import monix.eval.Task import sbt.testing.{AnnotatedFingerprint, EventHandler, Fingerprint, SubclassFingerprint} import org.scalatools.testing.{Framework => OldFramework} @@ -28,7 +28,7 @@ object TestInternals { private final val testAgentId = "test-agent" private final val testAgentVersion = "1.0.4" - private implicit val logContext: LogContext = LogContext.Test + private implicit val logContext: DebugFilter = DebugFilter.Test // Cache the resolution of test agent files since it's static (cannot be lazy because depends on logger) @volatile private var testAgentFiles: Option[Array[AbsolutePath]] = None diff --git a/frontend/src/main/scala/bloop/testing/TestServer.scala b/frontend/src/main/scala/bloop/testing/TestServer.scala index e7728cb425..70cebeb0f2 100644 --- a/frontend/src/main/scala/bloop/testing/TestServer.scala +++ b/frontend/src/main/scala/bloop/testing/TestServer.scala @@ -7,7 +7,7 @@ import bloop.cli.CommonOptions import bloop.config.Config import scala.util.control.NonFatal -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import monix.eval.Task import sbt.{ForkConfiguration, ForkTags} import sbt.testing.{Event, TaskDef} @@ -27,7 +27,7 @@ final class TestServer( opts: CommonOptions ) { - private implicit val logContext: LogContext = LogContext.Test + private implicit val logContext: DebugFilter = DebugFilter.Test private val server = new ServerSocket(0) private val frameworks = discoveredTests.tests.keys diff --git a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala index 15becaacee..4e9d0db857 100644 --- a/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala +++ b/frontend/src/main/scala/bloop/testing/TestSuiteEvent.scala @@ -1,6 +1,6 @@ package bloop.testing -import bloop.logging.{LogContext, Logger} +import bloop.logging.{DebugFilter, Logger} import bloop.util.TimeFormat import ch.epfl.scala.bsp import ch.epfl.scala.bsp.BuildTargetIdentifier @@ -44,7 +44,7 @@ class LoggingEventHandler(logger: Logger) extends TestSuiteEventHandler { case TestSuiteEvent.Error(message) => logger.error(message) case TestSuiteEvent.Warn(message) => logger.warn(message) case TestSuiteEvent.Info(message) => logger.info(message) - case TestSuiteEvent.Debug(message) => logger.debug(message)(LogContext.Test) + case TestSuiteEvent.Debug(message) => logger.debug(message)(DebugFilter.Test) case TestSuiteEvent.Trace(throwable) => logger.error("Test suite aborted.") logger.trace(throwable) diff --git a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala index 97f554429a..0cfd02c567 100644 --- a/frontend/src/test/scala/bloop/bsp/BspClientTest.scala +++ b/frontend/src/test/scala/bloop/bsp/BspClientTest.scala @@ -4,7 +4,7 @@ import java.nio.file.Files import bloop.cli.Commands import bloop.io.AbsolutePath -import bloop.logging.{BspClientLogger, LogContext, RecordingLogger, Slf4jAdapter} +import bloop.logging.{BspClientLogger, DebugFilter, RecordingLogger, Slf4jAdapter} import bloop.tasks.TestUtil import ch.epfl.scala.bsp import ch.epfl.scala.bsp.endpoints @@ -17,7 +17,7 @@ import scala.concurrent.duration.FiniteDuration import scala.meta.jsonrpc.{BaseProtocolMessage, LanguageClient, LanguageServer, Response, Services} object BspClientTest { - private implicit val ctx: LogContext = LogContext.Bsp + private implicit val ctx: DebugFilter = DebugFilter.Bsp def cleanUpLastResources(cmd: Commands.ValidatedBsp): Unit = { cmd match { case cmd: Commands.WindowsLocalBsp => () diff --git a/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala b/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala index e741ee1ee6..36be80da8e 100644 --- a/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala +++ b/frontend/src/test/scala/bloop/engine/FileWatchingSpec.scala @@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit import bloop.data.Project import bloop.cli.Commands -import bloop.logging.{LogContext, Logger, PublisherLogger} +import bloop.logging.{DebugFilter, Logger, PublisherLogger} import bloop.exec.JavaEnv import bloop.io.AbsolutePath import bloop.io.Paths.delete @@ -109,7 +109,7 @@ class FileWatchingSpec { import ExecutionContext.ioScheduler val (observer, observable) = Observable.multicast[(String, String)](MulticastStrategy.publish)(ioScheduler) - val logger = new PublisherLogger(observer, debug = debug, LogContext.All) + val logger = new PublisherLogger(observer, debug = debug, DebugFilter.All) // Let's modify the project to add special sources to check the right behaviour of the watcher val (state, singleFile) = { diff --git a/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala b/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala index 9ead81828c..a9c66c462b 100644 --- a/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala +++ b/frontend/src/test/scala/bloop/engine/InterpreterSpec.scala @@ -4,7 +4,7 @@ import java.io.{ByteArrayOutputStream, PrintStream} import java.util.UUID import bloop.cli.{CliOptions, Commands} -import bloop.logging.{LogContext, BloopLogger} +import bloop.logging.{DebugFilter, BloopLogger} import bloop.tasks.TestUtil import org.junit.Test import org.junit.experimental.categories.Category @@ -52,7 +52,7 @@ object InterpreterSpec { val inMemory = new ByteArrayOutputStream() val newOut = new PrintStream(inMemory) val loggerName = UUID.randomUUID().toString - val newLogger = BloopLogger.at(loggerName, newOut, newOut, false, LogContext.All) + val newLogger = BloopLogger.at(loggerName, newOut, newOut, false, DebugFilter.All) val defaultCli = CliOptions.default val newCommonOptions = state.commonOptions.copy(out = newOut) val newState = state.copy(logger = newLogger, commonOptions = newCommonOptions) diff --git a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala index eefef173bc..ef7b671052 100644 --- a/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala +++ b/frontend/src/test/scala/bloop/logging/BloopLoggerSpec.scala @@ -43,7 +43,7 @@ class BloopLoggerSpec { @Test def debugAndTraceMessagesAreIgnoredByDefault(): Unit = runAndCheck { logger => - logger.debug("debug")(LogContext.All) + logger.debug("debug")(DebugFilter.All) logger.trace(new Exception) } { (outMsgs, errMsgs) => assertEquals(0, outMsgs.length.toLong) @@ -61,7 +61,7 @@ class BloopLoggerSpec { logger.error("error0") logger.warn("warn0") logger.info("info0") - logger.debug("debug0")(LogContext.All) + logger.debug("debug0")(DebugFilter.All) logger.trace(ex0) val verboseLogger = logger.asVerbose @@ -73,7 +73,7 @@ class BloopLoggerSpec { verboseLogger.error("error1") verboseLogger.warn("warn1") verboseLogger.info("info1") - verboseLogger.debug("debug1")(LogContext.All) + verboseLogger.debug("debug1")(DebugFilter.All) verboseLogger.trace(ex1) val ex2 = { @@ -84,7 +84,7 @@ class BloopLoggerSpec { logger.error("error2") logger.warn("warn2") logger.info("info2") - logger.debug("debug2")(LogContext.All) + logger.debug("debug2")(DebugFilter.All) logger.trace(ex2) } { (outMsgs, errMsgs) => @@ -113,8 +113,8 @@ class BloopLoggerSpec { val bos1 = new ByteArrayOutputStream val ps1 = new PrintStream(bos1) - val l0 = BloopLogger.at("l0", ps0, ps0, false, LogContext.All) - val l1 = BloopLogger.at("l1", ps1, ps1, false, LogContext.All) + val l0 = BloopLogger.at("l0", ps0, ps0, false, DebugFilter.All) + val l1 = BloopLogger.at("l1", ps1, ps1, false, DebugFilter.All) l0.info("info0") l1.info("info1") @@ -134,12 +134,12 @@ class BloopLoggerSpec { val bos0 = new ByteArrayOutputStream val ps0 = new PrintStream(bos0) - val l0 = BloopLogger.at(loggerName, ps0, ps0, false, LogContext.All) + val l0 = BloopLogger.at(loggerName, ps0, ps0, false, DebugFilter.All) l0.info("info0") val bos1 = new ByteArrayOutputStream val ps1 = new PrintStream(bos1) - val l1 = BloopLogger.at(loggerName, ps1, ps1, false, LogContext.All) + val l1 = BloopLogger.at(loggerName, ps1, ps1, false, DebugFilter.All) l1.info("info1") val msgs0 = convertAndReadAllFrom(bos0) @@ -155,11 +155,11 @@ class BloopLoggerSpec { def isVerbose(): Unit = { val expectedMessage = "this-is-logged" runAndCheck { logger => - logger.debug("this-is-not-logged")(LogContext.All) + logger.debug("this-is-not-logged")(DebugFilter.All) assertFalse("The logger shouldn't report being in verbose mode.", logger.isVerbose) val verboseLogger = logger.asVerbose - verboseLogger.debug(expectedMessage)(LogContext.All) + verboseLogger.debug(expectedMessage)(DebugFilter.All) assertTrue("The logger should report being in verbose mode.", verboseLogger.isVerbose) } { (outMsgs, errMsgs) => assertTrue("Nothing should have been logged to stdout.", outMsgs.isEmpty) @@ -179,22 +179,22 @@ class BloopLoggerSpec { runAndCheck { logger0 => val logger = logger0.asVerbose - logger.debug(AllDebugLog)(LogContext.All) - logger.debug("This is a bsp log")(LogContext.Bsp) - logger.debug("This is a file watching log")(LogContext.FileWatching) - logger.debug("This is a test log")(LogContext.Test) - logger.debug(CompilationDebugLog)(LogContext.Compilation) + logger.debug(AllDebugLog)(DebugFilter.All) + logger.debug("This is a bsp log")(DebugFilter.Bsp) + logger.debug("This is a file watching log")(DebugFilter.FileWatching) + logger.debug("This is a test log")(DebugFilter.Test) + logger.debug(CompilationDebugLog)(DebugFilter.Compilation) // Use a locally scope to force the use of an implicit locally { - implicit val ctx: LogContext = LogContext.Compilation + implicit val ctx: DebugFilter = DebugFilter.Compilation logger.debug(CompilationDebugLog2) } } { (stdout, stderr) => val successfulDebugs = List(AllDebugLog, CompilationDebugLog, CompilationDebugLog2) val prefixedLogs = successfulDebugs.map(msg => s"[D] $msg").sorted assertTrue(s"Unexpected ${stderr}", stderr.toList.sorted == prefixedLogs) - }(LogContext.Compilation) + }(DebugFilter.Compilation) } private def isWarn(msg: String): Boolean = msg.contains("[W]") @@ -207,7 +207,7 @@ class BloopLoggerSpec { private def runAndCheck( op: BloopLogger => Unit - )(check: (Seq[String], Seq[String]) => Unit)(implicit ctx: LogContext = LogContext.All): Unit = { + )(check: (Seq[String], Seq[String]) => Unit)(implicit ctx: DebugFilter = DebugFilter.All): Unit = { val outStream = new ByteArrayOutputStream val errStream = new ByteArrayOutputStream diff --git a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala index 7142eeb8bd..8f1b3b1c39 100644 --- a/frontend/src/test/scala/bloop/logging/BufferedLogger.scala +++ b/frontend/src/test/scala/bloop/logging/BufferedLogger.scala @@ -8,11 +8,11 @@ final class BufferedLogger private ( ) extends Logger { override def name: String = underlying.name override def ansiCodesSupported(): Boolean = underlying.ansiCodesSupported() - override def logContext: LogContext = underlying.logContext + override def debugFilter: DebugFilter = underlying.debugFilter override def printDebug(msg: String): Unit = buffer.addLast(() => underlying.printDebug(msg)) - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (isVerbose && logContext.isEnabledFor(ctx)) buffer.addLast(() => printDebug(msg)) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (isVerbose && debugFilter.isEnabledFor(ctx)) buffer.addLast(() => printDebug(msg)) override def error(msg: String): Unit = buffer.addLast(() => underlying.error(msg)) override def warn(msg: String): Unit = buffer.addLast(() => underlying.warn(msg)) diff --git a/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala b/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala new file mode 100644 index 0000000000..af8a68e776 --- /dev/null +++ b/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala @@ -0,0 +1,207 @@ +package bloop.logging + +import bloop.Cli +import bloop.cli.{Commands, CommonOptions, ExitStatus} +import bloop.engine.{Exit, Print, Run} +import org.junit.{Assert, Test} + +class DebugFilterSpec { + @Test + def parseDebugFilterAll(): Unit = { + val args = Array("compile", "foo", "--debug", "all") + Cli.parse(args, CommonOptions.default) match { + case Run(c: Commands.Compile, _) if c.cliOptions.debug == List(DebugFilter.All) => () + case r => Assert.fail(s"Expected `Compile` command with debug filter `All`, got $r") + } + } + + @Test + def parseDebugAndSimplifyToAll(): Unit = { + val args = Array("compile", "foo", "--debug", "all", "--debug", "compilation") + Cli.parse(args, CommonOptions.default) match { + case Run(c: Commands.Compile, _) => + val uniqueFilter = DebugFilter.toUniqueFilter(c.cliOptions.debug) + Assert.assertTrue( + s"Expected only All, got ${uniqueFilter}", + uniqueFilter == DebugFilter.All + ) + case r => Assert.fail(s"Expected `Compile` command with debug filter `All`, got $r") + } + } + + @Test + def parseDebugFilters(): Unit = { + val args = Array("compile", "foo", "--debug", "bsp", "--debug", "compilation") + Cli.parse(args, CommonOptions.default) match { + case Run(c: Commands.Compile, _) => + DebugFilter.toUniqueFilter(c.cliOptions.debug) match { + case DebugFilter.Aggregate(obtained) => + Assert.assertTrue( + s"Expected Debug and Bsp, got ${obtained}", + obtained.toSet == Set(DebugFilter.Bsp, DebugFilter.Compilation) + ) + case r => Assert.fail(s"Expected aggregate of Bsp and Compilation, got $r") + } + case r => Assert.fail(s"Expected `Compile` command with debug filter `All`, got $r") + } + } + + @Test + def parseAllDebugFilters(): Unit = { + val args = Array( + "compile", + "foo", + "--debug", + "test", + "--debug", + "compilation", + "--debug", + "file-watching", + "--debug", + "bsp" + ) + + Cli.parse(args, CommonOptions.default) match { + case Run(c: Commands.Compile, _) => + DebugFilter.toUniqueFilter(c.cliOptions.debug) match { + case DebugFilter.Aggregate(obtained) => + Assert.assertTrue( + s"Expected all debug filters, got ${obtained}", + obtained.toSet == Set( + DebugFilter.FileWatching, + DebugFilter.Test, + DebugFilter.Bsp, + DebugFilter.Compilation + ) + ) + case r => Assert.fail(s"Expected aggregate of Bsp and Compilation, got $r") + } + case r => Assert.fail(s"Expected `Compile` command with debug filter `All`, got $r") + } + } + + @Test + def failWhenParsingOnlyDebugHeader(): Unit = { + val args = Array("compile", "foo", "--debug") + Cli.parse(args, CommonOptions.default) match { + case Print(msg, _, Exit(ExitStatus.InvalidCommandLineOption)) if msg == "argument missing" => + case r => Assert.fail(s"Expected `Print` with argument missing followed by exit, got $r") + } + } + + @Test + def checkSubsumption(): Unit = { + def checkTrue( + check: Boolean + )(implicit path: sourcecode.Enclosing, line: sourcecode.Line): Unit = { + Assert.assertTrue(s"Unexpected false at ${path.value}:${line.value}", check) + } + + checkTrue(DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.All)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.Compilation)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.Test)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.FileWatching)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.Bsp)) + checkTrue( + DebugFilter.checkSubsumption(DebugFilter.All, DebugFilter.Aggregate(List(DebugFilter.All))) + ) + + checkTrue(DebugFilter.checkSubsumption(DebugFilter.Compilation, DebugFilter.All)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.Test, DebugFilter.All)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.FileWatching, DebugFilter.All)) + checkTrue(DebugFilter.checkSubsumption(DebugFilter.Bsp, DebugFilter.All)) + checkTrue( + DebugFilter.checkSubsumption(DebugFilter.Aggregate(List(DebugFilter.All)), DebugFilter.All) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.All)), + DebugFilter.Aggregate(List(DebugFilter.All)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.All)), + DebugFilter.Aggregate(List(DebugFilter.Compilation)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation)), + DebugFilter.Aggregate(List(DebugFilter.All)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.All)), + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test, DebugFilter.Bsp)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.All)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.All, DebugFilter.Test, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test, DebugFilter.Bsp)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.All, DebugFilter.Compilation, DebugFilter.Test)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Test, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test, DebugFilter.Bsp)) + ) + ) + + checkTrue( + DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test)) + ) + ) + + // Only test a few, could be improved with property testing in the future + checkTrue(!DebugFilter.checkSubsumption(DebugFilter.Compilation, DebugFilter.Test)) + checkTrue(!DebugFilter.checkSubsumption(DebugFilter.Test, DebugFilter.FileWatching)) + checkTrue(!DebugFilter.checkSubsumption(DebugFilter.Test, DebugFilter.Bsp)) + checkTrue(!DebugFilter.checkSubsumption(DebugFilter.Bsp, DebugFilter.Compilation)) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Test, DebugFilter.Bsp)) + ) + ) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation)), + DebugFilter.Aggregate(List(DebugFilter.Test)) + ) + ) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test)) + ) + ) + } +} diff --git a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala index 274f4965bf..9bb1a5bf88 100644 --- a/frontend/src/test/scala/bloop/logging/PublisherLogger.scala +++ b/frontend/src/test/scala/bloop/logging/PublisherLogger.scala @@ -7,7 +7,7 @@ import monix.reactive.Observer final class PublisherLogger( observer: Observer.Sync[(String, String)], debug: Boolean = false, - val logContext: LogContext + val debugFilter: DebugFilter ) extends Logger { private[this] val messages = new ConcurrentLinkedQueue[(String, String)] override val name: String = "PublisherLogger" @@ -33,8 +33,8 @@ final class PublisherLogger( messages.iterator.asScala.flatMap(lm => if (lm._1 == label) List(lm._2) else Nil).toList override def printDebug(msg: String): Unit = add("debug", msg) - override def debug(msg: String)(implicit ctx: LogContext): Unit = - if (logContext.isEnabledFor(ctx)) add("debug", msg) + override def debug(msg: String)(implicit ctx: DebugFilter): Unit = + if (debugFilter.isEnabledFor(ctx)) add("debug", msg) private def trace(msg: String): Unit = add("trace", msg) override def info(msg: String): Unit = add("info", msg) diff --git a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala index 16c4cea480..24cd7d0586 100644 --- a/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala +++ b/frontend/src/test/scala/bloop/nailgun/NailgunTest.scala @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit import bloop.Server import bloop.bsp.BspServer -import bloop.logging.{LogContext, Logger, ProcessLogger, RecordingLogger} +import bloop.logging.{DebugFilter, Logger, ProcessLogger, RecordingLogger} import bloop.tasks.TestUtil import com.martiansoftware.nailgun.NGServer import monix.eval.Task @@ -38,7 +38,7 @@ abstract class NailgunTest { */ def withServerTask[T](log: RecordingLogger, config: Path)( op: (RecordingLogger, Client) => T): Task[T] = { - implicit val ctx: LogContext = LogContext.All + implicit val ctx: DebugFilter = DebugFilter.All val oldIn = System.in val oldOut = System.out val oldErr = System.err From 2ca081f46b1197e4b69e2ed5eac184939c367590 Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 15:59:51 +0200 Subject: [PATCH 09/10] Add `link` to debug filters and change the context in linkers --- .../scala/bloop/logging/DebugFilter.scala | 1 + .../bloop/scalanative/NativeBridge.scala | 4 +--- .../main/scala/bloop/scalajs/JsBridge.scala | 2 +- .../main/scala/bloop/scalajs/JsBridge.scala | 3 +-- .../src/main/scala/bloop/cli/CliParsers.scala | 4 +++- .../scala/bloop/logging/DebugFilterSpec.scala | 21 +++++++++++++++++++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/backend/src/main/scala/bloop/logging/DebugFilter.scala b/backend/src/main/scala/bloop/logging/DebugFilter.scala index be7f755e7a..9fe4ceb297 100644 --- a/backend/src/main/scala/bloop/logging/DebugFilter.scala +++ b/backend/src/main/scala/bloop/logging/DebugFilter.scala @@ -7,6 +7,7 @@ sealed trait DebugFilter { self => object DebugFilter { final case object All extends DebugFilter + final case object Link extends DebugFilter final case object Bsp extends DebugFilter final case object Test extends DebugFilter final case object Compilation extends DebugFilter diff --git a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala index 543d2a4cb8..0059241d50 100644 --- a/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala +++ b/bridges/scala-native/src/main/scala/bloop/scalanative/NativeBridge.scala @@ -9,9 +9,7 @@ import bloop.data.Project import scala.scalanative.build.{Build, Config, Discover, GC, Mode, Logger => NativeLogger} object NativeBridge { - - private implicit val ctx: DebugFilter = DebugFilter.All - + private implicit val ctx: DebugFilter = DebugFilter.Link def nativeLink(config0: NativeConfig, project: Project, entry: String, target: Path, logger: Logger): Path = { val workdir = project.out.resolve("native") if (workdir.isDirectory) Paths.delete(workdir) diff --git a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala index 6c8293b56d..7d9c04ec06 100644 --- a/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-0.6/src/main/scala/bloop/scalajs/JsBridge.scala @@ -21,7 +21,7 @@ object JsBridge { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message)(DebugFilter.All) + case Level.Debug => logger.debug(message)(DebugFilter.Link) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala index 44f2b3a413..23f3abd338 100644 --- a/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala +++ b/bridges/scalajs-1.0/src/main/scala/bloop/scalajs/JsBridge.scala @@ -12,14 +12,13 @@ import org.scalajs.linker.{ModuleInitializer, ModuleKind, Semantics, StandardLin import org.scalajs.logging.{Level, Logger => JsLogger} object JsBridge { - private class Logger(logger: BloopLogger) extends JsLogger { override def log(level: Level, message: => String): Unit = level match { case Level.Error => logger.error(message) case Level.Warn => logger.warn(message) case Level.Info => logger.info(message) - case Level.Debug => logger.debug(message)(DebugFilter.All) + case Level.Debug => logger.debug(message)(DebugFilter.Link) } override def success(message: => String): Unit = logger.info(message) override def trace(t: => Throwable): Unit = logger.trace(t) diff --git a/frontend/src/main/scala/bloop/cli/CliParsers.scala b/frontend/src/main/scala/bloop/cli/CliParsers.scala index f52dada286..774c9cd21b 100644 --- a/frontend/src/main/scala/bloop/cli/CliParsers.scala +++ b/frontend/src/main/scala/bloop/cli/CliParsers.scala @@ -61,7 +61,8 @@ object CliParsers { } } - val DebugFilterTags = "\"all\" | \"file-watching\" | \"compilation\" | \"test\" | \"bsp\"" + val DebugFilterTags = + "\"all\" | \"file-watching\" | \"compilation\" | \"test\" | \"bsp\" | \"link\"" implicit val debugFilterParser: ArgParser[DebugFilter] = { ArgParser.instance[DebugFilter](DebugFilterTags) { case "all" => Right(DebugFilter.All) @@ -69,6 +70,7 @@ object CliParsers { case "compilation" => Right(DebugFilter.Compilation) case "test" => Right(DebugFilter.Test) case "bsp" => Right(DebugFilter.Bsp) + case "link" => Right(DebugFilter.Link) case w00t => Left(s"Unrecognized log context: $w00t") } } diff --git a/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala b/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala index af8a68e776..aca2541a20 100644 --- a/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala +++ b/frontend/src/test/scala/bloop/logging/DebugFilterSpec.scala @@ -203,5 +203,26 @@ class DebugFilterSpec { DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Test)) ) ) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation, DebugFilter.Link)), + DebugFilter.Aggregate(List(DebugFilter.Test, DebugFilter.Bsp)) + ) + ) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.Compilation)), + DebugFilter.Aggregate(List(DebugFilter.Link)) + ) + ) + + checkTrue( + !DebugFilter.checkSubsumption( + DebugFilter.Aggregate(List(DebugFilter.FileWatching)), + DebugFilter.Aggregate(List(DebugFilter.Link, DebugFilter.Test)) + ) + ) } } From 96a4648fa5e4f5839ded04043c95ffadef4d2dba Mon Sep 17 00:00:00 2001 From: jvican Date: Wed, 24 Oct 2018 16:01:10 +0200 Subject: [PATCH 10/10] Remove unnecessary dump in `CompilationTaskTest` --- frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala b/frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala index c21669f1d6..bba39888de 100644 --- a/frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala +++ b/frontend/src/test/scala/bloop/tasks/CompilationTaskTest.scala @@ -107,7 +107,6 @@ class CompilationTaskTest { )(_ => ()) val errors = TestUtil.errorsFromLogger(logger) - logger.dump() assert(errors.size == 3) assert(errors.exists(_.contains("cannot find symbol"))) }