From 59764e88a2168db8eeb61ecc0f10a171f4c160e4 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Wed, 5 Mar 2025 21:46:22 +0800 Subject: [PATCH] Cleanup logger (#4653) A step towards https://github.com/com-lihaoyi/mill/issues/3603. For now just doing relatively shallow cleanups, but hopefully that paves the way to bigger refactorings The main external-facing change is renaming `inStream`/`outputStream`/`errorStream` to `streams.in`/`out`/`err`, though the internal refactorings are more substantial --- bsp/src/mill/bsp/BSP.scala | 4 +- bsp/src/mill/bsp/BspContext.scala | 19 ++- .../src/mill/bsp/worker/MillBspLogger.scala | 4 +- .../src/mill/bsp/worker/MillBuildServer.scala | 6 +- ci/mill-bootstrap.patch | 64 +++++++- .../mill/contrib/flyway/FlywayModule.scala | 2 +- core/api/src/mill/api/ColorLogger.scala | 7 - core/api/src/mill/api/Logger.scala | 97 ++++++------ core/define/src/mill/define/Evaluator.scala | 6 +- core/exec/src/mill/exec/Execution.scala | 30 ++-- .../src/mill/exec/ExecutionContexts.scala | 4 +- core/exec/src/mill/exec/GroupExecution.scala | 37 +++-- .../src/mill/internal/DummyLogger.scala | 6 +- .../src/mill/internal/FileLogger.scala | 24 +-- .../src/mill/internal/MultiLogger.scala | 117 +++++++------- .../src/mill/internal/PrefixLogger.scala | 146 +++++++----------- .../src/mill/internal/PrintLogger.scala | 91 +++-------- .../src/mill/internal/PromptLogger.scala | 94 +++++------ .../src/mill/internal/ProxyLogger.scala | 28 +--- .../src/mill/internal/PromptLoggerTests.scala | 40 ++--- core/src/mill/eval/EvaluatorImpl.scala | 6 +- core/src/mill/eval/EvaluatorProxy.scala | 4 +- .../worker/impl/KotlinWorkerImpl.scala | 2 +- main/init/src/mill/init/InitModule.scala | 2 +- main/src/mill/main/MainModule.scala | 4 +- .../src/mill/runner/MillBuildBootstrap.scala | 4 +- runner/src/mill/runner/MillMain.scala | 10 +- scalalib/src/mill/scalalib/JavaModule.scala | 2 +- scalalib/src/mill/scalalib/ScalaModule.scala | 2 +- .../scalalib/scalafmt/ScalafmtWorker.scala | 2 +- .../mill/scalalib/worker/ZincWorkerImpl.scala | 2 +- .../src/mill/testrunner/TestRunnerUtils.scala | 14 +- 32 files changed, 416 insertions(+), 464 deletions(-) delete mode 100644 core/api/src/mill/api/ColorLogger.scala diff --git a/bsp/src/mill/bsp/BSP.scala b/bsp/src/mill/bsp/BSP.scala index 94572bab487..7024a5c5805 100644 --- a/bsp/src/mill/bsp/BSP.scala +++ b/bsp/src/mill/bsp/BSP.scala @@ -50,9 +50,9 @@ object BSP extends ExternalModule with CoursierModule { */ def startSession(allBootstrapEvaluators: Evaluator.AllBootstrapEvaluators) : Command[BspServerResult] = Task.Command(exclusive = true) { - Task.log.errorStream.println("BSP/startSession: Starting BSP session") + Task.log.streams.err.println("BSP/startSession: Starting BSP session") val res = BspContext.bspServerHandle.runSession(allBootstrapEvaluators.value) - Task.log.errorStream.println(s"BSP/startSession: Finished BSP session, result: ${res}") + Task.log.streams.err.println(s"BSP/startSession: Finished BSP session, result: ${res}") res } diff --git a/bsp/src/mill/bsp/BspContext.scala b/bsp/src/mill/bsp/BspContext.scala index 4197ae51227..5126dc1c780 100644 --- a/bsp/src/mill/bsp/BspContext.scala +++ b/bsp/src/mill/bsp/BspContext.scala @@ -24,7 +24,7 @@ private[mill] class BspContext( BspContext.bspServerHandle = try { startBspServer( - streams = streams, + streams0 = streams, logStream = bspLogStream, canReload = true ).get @@ -37,27 +37,26 @@ private[mill] class BspContext( streams.err.println("BSP server started") def startBspServer( - streams: SystemStreams, + streams0: SystemStreams, logStream: Option[PrintStream], canReload: Boolean ): Result[BspServerHandle] = { val log: Logger = new Logger { override def colored: Boolean = false - override def systemStreams: SystemStreams = new SystemStreams( - out = streams.out, - err = streams.err, + override def streams: SystemStreams = new SystemStreams( + out = streams0.out, + err = streams0.err, in = DummyInputStream ) + def prompt = new Logger.Prompt.NoOp { + override def setPromptDetail(key: Seq[String], s: String): Unit = streams.err.println(s) + } override def info(s: String): Unit = streams.err.println(s) override def error(s: String): Unit = streams.err.println(s) override def ticker(s: String): Unit = streams.err.println(s) - override def setPromptDetail(key: Seq[String], s: String): Unit = streams.err.println(s) - override def debug(s: String): Unit = streams.err.println(s) - - override def debugEnabled: Boolean = true - override def rawOutputStream: PrintStream = systemStreams.out + override def debug(s: String): Unit = streams.err.println(s) } BspWorker(mill.api.WorkspaceRoot.workspaceRoot, home, log).flatMap { worker => diff --git a/bsp/worker/src/mill/bsp/worker/MillBspLogger.scala b/bsp/worker/src/mill/bsp/worker/MillBspLogger.scala index 2e4b564eb01..b96470b7d9a 100644 --- a/bsp/worker/src/mill/bsp/worker/MillBspLogger.scala +++ b/bsp/worker/src/mill/bsp/worker/MillBspLogger.scala @@ -1,7 +1,7 @@ package mill.bsp.worker import ch.epfl.scala.bsp4j._ -import mill.api.{ColorLogger, Logger} +import mill.api.{Logger} import mill.internal.ProxyLogger /** @@ -19,7 +19,7 @@ import mill.internal.ProxyLogger */ private class MillBspLogger(client: BuildClient, taskId: Int, logger: Logger) extends ProxyLogger(logger) - with ColorLogger { + with Logger { override def infoColor = fansi.Color.Blue override def errorColor = fansi.Color.Red diff --git a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala index 3ad926fe499..62d7a404e6e 100644 --- a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala +++ b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala @@ -4,7 +4,7 @@ import ch.epfl.scala.bsp4j import ch.epfl.scala.bsp4j.* import com.google.gson.JsonObject import mill.api.ExecResult -import mill.api.{ColorLogger, CompileProblemReporter, DummyTestReporter, Result, TestReporter} +import mill.api.{Logger, CompileProblemReporter, DummyTestReporter, Result, TestReporter} import mill.bsp.{BspServerResult, Constants} import mill.bsp.worker.Utils.{makeBuildTarget, outputPaths, sanitizeUri} import mill.define.Segment.Label @@ -801,7 +801,7 @@ private class MillBuildServer( goals: Seq[Task[?]], reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, - logger: ColorLogger = null + logger: Logger = null ): ExecutionResults = { val logger0 = Option(logger).getOrElse(evaluator.baseLogger) mill.runner.MillMain.withOutLock( @@ -812,7 +812,7 @@ private class MillBuildServer( case n: NamedTask[_] => n.label case t => t.toString }, - streams = logger0.systemStreams + streams = logger0.streams ) { evaluator.execute( goals, diff --git a/ci/mill-bootstrap.patch b/ci/mill-bootstrap.patch index d25f0038ca2..eb573314876 100644 --- a/ci/mill-bootstrap.patch +++ b/ci/mill-bootstrap.patch @@ -1,5 +1,5 @@ diff --git a/build.mill b/build.mill -index 5c48e1e4e43..538e4b86e0c 100644 +index 42be8622781..ecb84c31089 100644 --- a/build.mill +++ b/build.mill @@ -1,16 +1,16 @@ @@ -189,7 +189,7 @@ index e49f218f4be..9d03aa073f3 100644 def ivyDeps = Task { if (!caseName.contains("realistic") && !caseName.contains("sourcecode")) super.ivyDeps() diff --git a/dist/package.mill b/dist/package.mill -index fbbeacbc843..fb06c486273 100644 +index fbbeacbc843..b3e00f8db5f 100644 --- a/dist/package.mill +++ b/dist/package.mill @@ -2,15 +2,14 @@ package build.dist @@ -210,6 +210,15 @@ index fbbeacbc843..fb06c486273 100644 case m: PublishModule if (m ne build.dist) && (m ne build.dist.native) => m } def moduleDeps = Seq(build.runner, build.idea, build.main.init) +@@ -44,7 +43,7 @@ trait InstallModule extends build.MillPublishJavaModule { + (os.home / ".cache/mill/download" / (build.millVersion() + batExt)).toString() + ) + )() +- Task.log.outputStream.println(path.toString()) ++ Task.log.streams.out.println(path.toString()) + PathRef(path) + } + @@ -185,10 +184,10 @@ object `package` extends RootModule with InstallModule { val wd = os.Path(wd0, Task.workspace) os.makeDir.all(wd) @@ -650,7 +659,7 @@ index 3b577e29c65..7f45dcaeab6 100644 def forkEnv = super.forkEnv() ++ Map("MILL_EXECUTABLE_PATH" -> build.dist.launcher().path.toString()) diff --git a/website/package.mill b/website/package.mill -index f88dc3fe8f6..eb1916dae76 100644 +index f88dc3fe8f6..2e2d2be3716 100644 --- a/website/package.mill +++ b/website/package.mill @@ -26,7 +26,7 @@ object `package` extends RootModule { @@ -753,3 +762,52 @@ index f88dc3fe8f6..eb1916dae76 100644 def blogFolder = Task { os.copy(blogFolder0().path, Task.dest, mergeFolders = true) expandDiagramsInDirectoryAdocFile(Task.dest, mill.main.VisualizeModule.classpath().map(_.path)) +@@ -332,21 +332,21 @@ object `package` extends RootModule { + + def localPages: T[PathRef] = Task { + val pages = generatePages(authorMode = true).apply().apply(oldDocSources().map(_.path)) +- Task.log.outputStream.println( ++ Task.log.streams.out.println( + s"You can browse the pages at: ${(pages.path / "index.html").toNIO.toUri()}" + ) + pages + } + def fastPages: T[PathRef] = Task { + val pages = generatePages(authorMode = true).apply().apply(Nil) +- Task.log.outputStream.println( ++ Task.log.streams.out.println( + s"You can browse the pages at: ${(pages.path / "index.html").toNIO.toUri()}" + ) + pages + } + + def generatePages(authorMode: Boolean) = Task.Anon { (extraSources: Seq[os.Path]) => +- Task.log.errorStream.println("Creating Antora playbook ...") ++ Task.log.streams.err.println("Creating Antora playbook ...") + // dependency to sources + source() + val docSite = Task.dest +@@ -357,7 +357,7 @@ object `package` extends RootModule { + data = githubPagesPlaybookText(authorMode).apply().apply(extraSources), + createFolders = true + ) +- Task.log.errorStream.println("Running Antora ...") ++ Task.log.streams.err.println("Running Antora ...") + runAntora( + npmDir = npmBase(), + workDir = docSite, +@@ -373,12 +373,12 @@ object `package` extends RootModule { + os.write(siteDir / ".nojekyll", "") + + // sanitize devAntora source URLs +- Task.log.errorStream.println("Sanitizing links ...") ++ Task.log.streams.err.println("Sanitizing links ...") + sanitizeDevUrls(siteDir, devAntoraSources().path, build.baseDir / "docs", build.baseDir) + + // only copy the "api" sub-dir; api docs contains a top-level index.html with we don't want + val unidocSrc = if (authorMode) site.unidocLocal().path else site.unidocSite().path +- Task.log.errorStream.println(s"Copying API docs from ${unidocSrc} ...") ++ Task.log.streams.err.println(s"Copying API docs from ${unidocSrc} ...") + os.copy(unidocSrc, siteDir / "api/latest", createFolders = true) + + PathRef(siteDir) diff --git a/contrib/flyway/src/mill/contrib/flyway/FlywayModule.scala b/contrib/flyway/src/mill/contrib/flyway/FlywayModule.scala index 5a0a7a9fad3..5ac5ba84313 100644 --- a/contrib/flyway/src/mill/contrib/flyway/FlywayModule.scala +++ b/contrib/flyway/src/mill/contrib/flyway/FlywayModule.scala @@ -74,7 +74,7 @@ trait FlywayModule extends JavaModule { val out = s"""Schema version: ${currentSchemaVersion} |${MigrationInfoDumper.dumpToAsciiTable(info.all)}""".stripMargin - Task.log.outputStream.println(out) + Task.log.streams.out.println(out) out } } diff --git a/core/api/src/mill/api/ColorLogger.scala b/core/api/src/mill/api/ColorLogger.scala deleted file mode 100644 index 2fb3d0f6346..00000000000 --- a/core/api/src/mill/api/ColorLogger.scala +++ /dev/null @@ -1,7 +0,0 @@ -package mill.api - -import java.io.PrintStream - -trait ColorLogger extends Logger { - override def withOutStream(outStream: PrintStream): ColorLogger = this -} diff --git a/core/api/src/mill/api/Logger.scala b/core/api/src/mill/api/Logger.scala index 50cdb977936..f15776a62ca 100644 --- a/core/api/src/mill/api/Logger.scala +++ b/core/api/src/mill/api/Logger.scala @@ -1,6 +1,6 @@ package mill.api -import java.io.{InputStream, PrintStream} +import java.io.PrintStream /** * The standard logging interface of the Mill build tool. @@ -24,66 +24,73 @@ import java.io.{InputStream, PrintStream} * but when `show` is used both are forwarded to stderr and stdout is only * used to display the final `show` output for easy piping. */ -trait Logger extends AutoCloseable { +trait Logger { def infoColor: fansi.Attrs = fansi.Attrs.Empty def errorColor: fansi.Attrs = fansi.Attrs.Empty def colored: Boolean - private[mill] def unprefixedSystemStreams: SystemStreams = systemStreams - def systemStreams: SystemStreams - - def errorStream: PrintStream = systemStreams.err - def outputStream: PrintStream = systemStreams.out - - /** - * [[rawOutputStream]] is intended to be a version of [[outputStream]] - * without decoration: colors, prefixes, timestamps, etc. It is intended - * for the use of tasks like `show` which output data in a way that is - * easily readable by downstream programs. - */ - def rawOutputStream: PrintStream = systemStreams.out - def inStream: InputStream = systemStreams.in + private[mill] def unprefixedStreams: SystemStreams = streams + def streams: SystemStreams def info(s: String): Unit def debug(s: String): Unit def error(s: String): Unit def ticker(s: String): Unit - private[mill] def setPromptDetail(key: Seq[String], s: String): Unit = ticker(s) - private[mill] def reportKey(key: Seq[String]): Unit = () - private[mill] def setPromptLine( - key: Seq[String], - verboseKeySuffix: String, - message: String - ): Unit = - ticker(s"${key.mkString("-")} $message") - private[mill] def setPromptLine(): Unit = () - private[mill] def setPromptHeaderPrefix(s: String): Unit = () - private[mill] def clearPromptStatuses(): Unit = () - private[mill] def removePromptLine(key: Seq[String]): Unit = () - private[mill] def removePromptLine(): Unit = () - private[mill] def withPromptPaused[T](t: => T): T = t - private[mill] def withPromptUnpaused[T](t: => T): T = t - - /** - * @since Mill 0.10.5 - */ - // We only default-implement it to keep binary compatibility in 0.10.x - def debugEnabled: Boolean = false - - def close(): Unit = () + private[mill] def prompt: Logger.Prompt - def enableTicker: Boolean = false - - private[mill] def subLogger(path: os.Path, verboseKeySuffix: String, message: String): Logger = + private[mill] def subLogger(path: os.Path, keySuffix: String, message: String): Logger = this - private[mill] def withPrompt[T](t: => T): T = { - setPromptLine() + private[mill] def withPromptLine[T](t: => T): T = { + prompt.setPromptLine(logPrefixKey, keySuffix, message) try t - finally removePromptLine() + finally prompt.removePromptLine(logPrefixKey) } def withOutStream(outStream: PrintStream): Logger = this + private[mill] def message: String = "" + private[mill] def keySuffix: String = "" private[mill] def logPrefixKey: Seq[String] = Nil + final def debugEnabled = prompt.debugEnabled +} + +object Logger { + + /** + * APIs that allow a logger to interact with the global prompt: setting and unsetting + * lines, enabling or disabling the prompt, etc. Normally passed through from logger + * to logger unchanged without any customization. + */ + trait Prompt { + private[mill] def setPromptDetail(key: Seq[String], s: String): Unit + private[mill] def reportKey(key: Seq[String]): Unit + private[mill] def setPromptLine(key: Seq[String], keySuffix: String, message: String): Unit + private[mill] def setPromptHeaderPrefix(s: String): Unit + private[mill] def clearPromptStatuses(): Unit + private[mill] def removePromptLine(key: Seq[String]): Unit + private[mill] def withPromptPaused[T](t: => T): T + private[mill] def withPromptUnpaused[T](t: => T): T + + def debugEnabled: Boolean + + def enableTicker: Boolean + } + object Prompt { + class NoOp extends Prompt { + private[mill] def setPromptDetail(key: Seq[String], s: String): Unit = () + private[mill] def reportKey(key: Seq[String]): Unit = () + private[mill] def setPromptLine(key: Seq[String], keySuffix: String, message: String): Unit = + () + private[mill] def setPromptHeaderPrefix(s: String): Unit = () + private[mill] def clearPromptStatuses(): Unit = () + private[mill] def removePromptLine(key: Seq[String]): Unit = () + private[mill] def withPromptPaused[T](t: => T): T = t + private[mill] def withPromptUnpaused[T](t: => T): T = t + + def debugEnabled: Boolean = false + + def enableTicker: Boolean = false + } + } } diff --git a/core/define/src/mill/define/Evaluator.scala b/core/define/src/mill/define/Evaluator.scala index 2eef957daa7..f053ba43c2f 100644 --- a/core/define/src/mill/define/Evaluator.scala +++ b/core/define/src/mill/define/Evaluator.scala @@ -10,14 +10,14 @@ trait Evaluator extends AutoCloseable { private[mill] def allowPositionalCommandArgs: Boolean private[mill] def selectiveExecution: Boolean private[mill] def workspace: os.Path - private[mill] def baseLogger: ColorLogger + private[mill] def baseLogger: Logger private[mill] def outPath: os.Path private[mill] def codeSignatures: Map[String, Int] private[mill] def rootModule: BaseModule private[mill] def workerCache: mutable.Map[Segments, (Int, Val)] private[mill] def env: Map[String, String] - def withBaseLogger(newBaseLogger: ColorLogger): Evaluator + def withBaseLogger(newBaseLogger: Logger): Evaluator def resolveSegments( scriptArgs: Seq[String], @@ -38,7 +38,7 @@ trait Evaluator extends AutoCloseable { targets: Seq[Task[T]], reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, - logger: ColorLogger = baseLogger, + logger: Logger = baseLogger, serialCommandExec: Boolean = false, selectiveExecution: Boolean = false ): Evaluator.Result[T] diff --git a/core/exec/src/mill/exec/Execution.scala b/core/exec/src/mill/exec/Execution.scala index c67c975d643..c972c69c37d 100644 --- a/core/exec/src/mill/exec/Execution.scala +++ b/core/exec/src/mill/exec/Execution.scala @@ -16,7 +16,7 @@ import scala.concurrent._ * Core logic of evaluating tasks, without any user-facing helper methods */ private[mill] case class Execution( - baseLogger: ColorLogger, + baseLogger: Logger, chromeProfileLogger: JsonArrayLogger.ChromeProfile, profileLogger: JsonArrayLogger.Profile, home: os.Path, @@ -36,7 +36,7 @@ private[mill] case class Execution( getEvaluator: () => Evaluator ) extends GroupExecution with AutoCloseable { - def withBaseLogger(newBaseLogger: ColorLogger) = this.copy(baseLogger = newBaseLogger) + def withBaseLogger(newBaseLogger: Logger) = this.copy(baseLogger = newBaseLogger) /** * @param goals The tasks that need to be evaluated @@ -47,9 +47,9 @@ private[mill] case class Execution( goals: Seq[Task[?]], reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, - logger: ColorLogger = baseLogger, + logger: Logger = baseLogger, serialCommandExec: Boolean = false - ): Execution.Results = logger.withPromptUnpaused { + ): Execution.Results = logger.prompt.withPromptUnpaused { os.makeDir.all(outPath) PathRef.validatedPaths.withValue(new PathRef.ValidatedPaths()) { @@ -79,7 +79,7 @@ private[mill] case class Execution( private def execute0( goals: Seq[Task[?]], - logger: ColorLogger, + logger: Logger, reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, ec: mill.api.Ctx.Fork.Impl, @@ -109,8 +109,8 @@ private[mill] case class Execution( val futures = mutable.Map.empty[Task[?], Future[Option[GroupExecution.Results]]] - def formatHeaderPrefix(countMsg: String, verboseKeySuffix: String) = - s"$countMsg$verboseKeySuffix${Execution.formatFailedCount(rootFailedCount.get())}" + def formatHeaderPrefix(countMsg: String, keySuffix: String) = + s"$countMsg$keySuffix${Execution.formatFailedCount(rootFailedCount.get())}" def evaluateTerminals( terminals: Seq[Task[?]], @@ -151,8 +151,8 @@ private[mill] case class Execution( '0' ) - val verboseKeySuffix = s"/${terminals0.size}" - logger.setPromptHeaderPrefix(formatHeaderPrefix(countMsg, verboseKeySuffix)) + val keySuffix = s"/${terminals0.size}" + logger.prompt.setPromptHeaderPrefix(formatHeaderPrefix(countMsg, keySuffix)) if (failed.get()) None else { val upstreamResults = upstreamValues @@ -169,12 +169,13 @@ private[mill] case class Execution( } yield upstreamResults(item).map(_._1) val logRun = inputResults.forall(_.isInstanceOf[ExecResult.Success[?]]) - val tickerPrefix = if (logRun && logger.enableTicker) terminal.toString else "" + val tickerPrefix = + if (logRun && logger.prompt.enableTicker) terminal.toString else "" val contextLogger = new PrefixLogger( logger0 = logger, - key0 = if (!logger.enableTicker) Nil else Seq(countMsg), - verboseKeySuffix = verboseKeySuffix, + key0 = if (!logger.prompt.enableTicker) Nil else Seq(countMsg), + keySuffix = keySuffix, message = tickerPrefix, noPrefix = exclusive ) @@ -184,7 +185,6 @@ private[mill] case class Execution( group = plan.sortedGroups.lookupKey(terminal).toSeq, results = upstreamResults, countMsg = countMsg, - verboseKeySuffix = verboseKeySuffix, zincProblemReporter = reporter, testReporter = testReporter, logger = contextLogger, @@ -201,7 +201,7 @@ private[mill] case class Execution( rootFailedCount.addAndGet(newFailures) // Always show failed count in header if there are failures - logger.setPromptHeaderPrefix(formatHeaderPrefix(countMsg, verboseKeySuffix)) + logger.prompt.setPromptHeaderPrefix(formatHeaderPrefix(countMsg, keySuffix)) if (failFast && res.newResults.values.exists(_.asSuccess.isEmpty)) failed.set(true) @@ -249,7 +249,7 @@ private[mill] case class Execution( val exclusiveResults = evaluateTerminals(leafExclusiveCommands, ec, exclusive = true) - logger.clearPromptStatuses() + logger.prompt.clearPromptStatuses() val finishedOptsMap = (nonExclusiveResults ++ exclusiveResults).toMap diff --git a/core/exec/src/mill/exec/ExecutionContexts.scala b/core/exec/src/mill/exec/ExecutionContexts.scala index 1d46ca324fd..9817a2b5f68 100644 --- a/core/exec/src/mill/exec/ExecutionContexts.scala +++ b/core/exec/src/mill/exec/ExecutionContexts.scala @@ -96,9 +96,9 @@ private object ExecutionContexts { dest } Future { - logger.withPrompt { + logger.withPromptLine { os.dynamicPwdFunction.withValue(() => makeDest()) { - mill.api.SystemStreams.withStreams(logger.systemStreams) { + mill.api.SystemStreams.withStreams(logger.streams) { t } } diff --git a/core/exec/src/mill/exec/GroupExecution.scala b/core/exec/src/mill/exec/GroupExecution.scala index 54baf1add11..4dbf7dfffa1 100644 --- a/core/exec/src/mill/exec/GroupExecution.scala +++ b/core/exec/src/mill/exec/GroupExecution.scala @@ -44,17 +44,16 @@ private trait GroupExecution { group: Seq[Task[?]], results: Map[Task[?], ExecResult[(Val, Int)]], countMsg: String, - verboseKeySuffix: String, zincProblemReporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, - logger: ColorLogger, + logger: Logger, deps: Seq[Task[?]], classToTransitiveClasses: Map[Class[?], IndexedSeq[Class[?]]], allTransitiveClassMethods: Map[Class[?], Map[String, Method]], executionContext: mill.api.Ctx.Fork.Api, exclusive: Boolean ): GroupExecution.Results = { - logger.withPrompt { + logger.withPromptLine { val externalInputsHash = MurmurHash3.orderedHash( group.flatMap(_.inputs).filter(!group.contains(_)) .flatMap(results(_).asSuccess.map(_.value._2)) @@ -206,7 +205,7 @@ private trait GroupExecution { val newResults = mutable.Map.empty[Task[?], ExecResult[(Val, Int)]] val nonEvaluatedTargets = group.toIndexedSeq.filterNot(results.contains) - val multiLogger = resolveLogger(paths.map(_.log), logger) + val (multiLogger, fileLoggerOpt) = resolveLogger(paths.map(_.log), logger) var usedDest = Option.empty[os.Path] for (task <- nonEvaluatedTargets) { @@ -264,7 +263,7 @@ private trait GroupExecution { def wrap[T](t: => T): T = { val (streams, destFunc) = if (exclusive) (exclusiveSystemStreams, () => workspace) - else (multiLogger.systemStreams, () => makeDest()) + else (multiLogger.streams, () => makeDest()) os.dynamicPwdFunction.withValue(destFunc) { os.checker.withValue(executionChecker) { @@ -273,8 +272,8 @@ private trait GroupExecution { Evaluator.currentEvaluator0.withValue(exposedEvaluator) { if (!exclusive) t else { - logger.reportKey(Seq(counterMsg)) - logger.withPromptPaused { t } + logger.prompt.reportKey(Seq(counterMsg)) + logger.prompt.withPromptPaused { t } } } } @@ -305,7 +304,7 @@ private trait GroupExecution { newResults(task) = for (v <- res) yield (v, getValueHash(v, task, inputsHash)) } - multiLogger.close() + fileLoggerOpt.foreach(_.close()) if (!failFast) maybeTargetLabel.foreach { targetLabel => val taskFailed = newResults.exists(task => !task._2.isInstanceOf[Success[?]]) @@ -371,21 +370,25 @@ private trait GroupExecution { } } - def resolveLogger(logPath: Option[os.Path], logger: mill.api.Logger): mill.api.Logger = + def resolveLogger( + logPath: Option[os.Path], + logger: mill.api.Logger + ): (mill.api.Logger, Option[AutoCloseable]) = logPath match { - case None => logger - case Some(path) => new MultiLogger( + case None => (logger, None) + case Some(path) => + val fileLogger = new FileLogger(logger.colored, path) + val multiLogger = new MultiLogger( logger.colored, logger, - // we always enable debug here, to get some more context in log files - new FileLogger(logger.colored, path, debugEnabled = true), - logger.systemStreams.in, - debugEnabled = logger.debugEnabled + fileLogger, + logger.streams.in ) + (multiLogger, Some(fileLogger)) } private def loadCachedJson( - logger: ColorLogger, + logger: Logger, inputsHash: Int, labelled: NamedTask[?], paths: ExecutionPaths @@ -420,7 +423,7 @@ private trait GroupExecution { if (task.isInstanceOf[Worker[?]]) inputsHash else v.## } private def loadUpToDateWorker( - logger: ColorLogger, + logger: Logger, inputsHash: Int, labelled: NamedTask[?], forceDiscard: Boolean diff --git a/core/internal/src/mill/internal/DummyLogger.scala b/core/internal/src/mill/internal/DummyLogger.scala index 295b6861314..f3ea93f8afa 100644 --- a/core/internal/src/mill/internal/DummyLogger.scala +++ b/core/internal/src/mill/internal/DummyLogger.scala @@ -7,17 +7,15 @@ import java.io.{ByteArrayInputStream, PrintStream} private[mill] object DummyLogger extends Logger { def colored = false - val systemStreams = new SystemStreams( + val streams = new SystemStreams( new PrintStream(_ => ()), new PrintStream(_ => ()), new ByteArrayInputStream(Array()) ) - override def rawOutputStream = systemStreams.out def info(s: String) = () def error(s: String) = () def ticker(s: String) = () def debug(s: String) = () - override val debugEnabled: Boolean = false - + def prompt = new Logger.Prompt.NoOp } diff --git a/core/internal/src/mill/internal/FileLogger.scala b/core/internal/src/mill/internal/FileLogger.scala index f54ee667396..610a1fbf794 100644 --- a/core/internal/src/mill/internal/FileLogger.scala +++ b/core/internal/src/mill/internal/FileLogger.scala @@ -8,9 +8,8 @@ import java.nio.file.{Files, StandardOpenOption} private[mill] class FileLogger( override val colored: Boolean, file: os.Path, - override val debugEnabled: Boolean, append: Boolean = false -) extends Logger { +) extends Logger with AutoCloseable { override def toString: String = s"FileLogger($file)" private var outputStreamUsed: Boolean = false @@ -44,17 +43,18 @@ private[mill] class FileLogger( }) } - val systemStreams = new SystemStreams(fileStream, fileStream, mill.api.DummyInputStream) - def info(s: String): Unit = outputStream.println(s) - def error(s: String): Unit = outputStream.println(s) - def ticker(s: String): Unit = outputStream.println(s) - def debug(s: String): Unit = if (debugEnabled) outputStream.println(s) - override def close(): Unit = { + val streams = new SystemStreams(fileStream, fileStream, mill.api.DummyInputStream) + def info(s: String): Unit = streams.out.println(s) + def error(s: String): Unit = streams.out.println(s) + def ticker(s: String): Unit = streams.out.println(s) + def debug(s: String): Unit = if (prompt.debugEnabled) streams.out.println(s) + def close(): Unit = { if (outputStreamUsed) - outputStream.close() + streams.out.close() } - override def rawOutputStream: PrintStream = outputStream - override def subLogger(path: os.Path, verboseKeySuffix: String, message: String): Logger = { - new FileLogger(colored, path, debugEnabled, append) + def enableTicker = false + def prompt = new Logger.Prompt.NoOp + override def subLogger(path: os.Path, keySuffix: String, message: String): Logger = { + new FileLogger(colored, path, append) } } diff --git a/core/internal/src/mill/internal/MultiLogger.scala b/core/internal/src/mill/internal/MultiLogger.scala index a561d9a3ac1..825714e3256 100644 --- a/core/internal/src/mill/internal/MultiLogger.scala +++ b/core/internal/src/mill/internal/MultiLogger.scala @@ -1,7 +1,7 @@ package mill.internal import fansi.Attrs -import mill.api.{ColorLogger, Logger, SystemStreams} +import mill.api.{Logger, SystemStreams} import java.io.{InputStream, PrintStream} @@ -9,19 +9,18 @@ private[mill] class MultiLogger( val colored: Boolean, val logger1: Logger, val logger2: Logger, - val inStream0: InputStream, - override val debugEnabled: Boolean -) extends ColorLogger { + val inStream0: InputStream +) extends Logger { override def toString: String = s"MultiLogger($logger1, $logger2)" - lazy val systemStreams = new SystemStreams( - new MultiStream(logger1.systemStreams.out, logger2.systemStreams.out), - new MultiStream(logger1.systemStreams.err, logger2.systemStreams.err), + lazy val streams = new SystemStreams( + new MultiStream(logger1.streams.out, logger2.streams.out), + new MultiStream(logger1.streams.err, logger2.streams.err), inStream0 ) - private[mill] override lazy val unprefixedSystemStreams: SystemStreams = new SystemStreams( - new MultiStream(logger1.unprefixedSystemStreams.out, logger2.unprefixedSystemStreams.out), - new MultiStream(logger1.unprefixedSystemStreams.err, logger2.unprefixedSystemStreams.err), + private[mill] override lazy val unprefixedStreams: SystemStreams = new SystemStreams( + new MultiStream(logger1.unprefixedStreams.out, logger2.unprefixedStreams.out), + new MultiStream(logger1.unprefixedStreams.err, logger2.unprefixedStreams.err), inStream0 ) @@ -38,70 +37,65 @@ private[mill] class MultiLogger( logger2.ticker(s) } - override def setPromptDetail(key: Seq[String], s: String): Unit = { - logger1.setPromptDetail(key, s) - logger2.setPromptDetail(key, s) - } + def prompt: Logger.Prompt = new Logger.Prompt { - private[mill] override def setPromptLine( - key: Seq[String], - verboseKeySuffix: String, - message: String - ): Unit = { - logger1.setPromptLine(key, verboseKeySuffix, message) - logger2.setPromptLine(key, verboseKeySuffix, message) - } + override def setPromptDetail(key: Seq[String], s: String): Unit = { + logger1.prompt.setPromptDetail(key, s) + logger2.prompt.setPromptDetail(key, s) + } - private[mill] override def setPromptLine(): Unit = { - logger1.setPromptLine() - logger2.setPromptLine() - } + private[mill] override def setPromptLine( + key: Seq[String], + keySuffix: String, + message: String + ): Unit = { + logger1.prompt.setPromptLine(key, keySuffix, message) + logger2.prompt.setPromptLine(key, keySuffix, message) + } - def debug(s: String): Unit = { - logger1.debug(s) - logger2.debug(s) - } + private[mill] override def reportKey(key: Seq[String]): Unit = { + logger1.prompt.reportKey(key) + logger2.prompt.reportKey(key) + } - override def close(): Unit = { - logger1.close() - logger2.close() - } - private[mill] override def reportKey(key: Seq[String]): Unit = { - logger1.reportKey(key) - logger2.reportKey(key) - } + private[mill] override def clearPromptStatuses(): Unit = { + logger1.prompt.clearPromptStatuses() + logger2.prompt.clearPromptStatuses() + } - override def rawOutputStream: PrintStream = systemStreams.out + private[mill] override def removePromptLine(key: Seq[String]): Unit = { + logger1.prompt.removePromptLine(key) + logger2.prompt.removePromptLine(key) + } - private[mill] override def removePromptLine(key: Seq[String]): Unit = { - logger1.removePromptLine(key) - logger2.removePromptLine(key) - } - private[mill] override def removePromptLine(): Unit = { - logger1.removePromptLine() - logger2.removePromptLine() - } - private[mill] override def setPromptHeaderPrefix(s: String): Unit = { - logger1.setPromptHeaderPrefix(s) - logger2.setPromptHeaderPrefix(s) - } + private[mill] override def setPromptHeaderPrefix(s: String): Unit = { + logger1.prompt.setPromptHeaderPrefix(s) + logger2.prompt.setPromptHeaderPrefix(s) + } + + private[mill] override def withPromptPaused[T](t: => T): T = { + logger1.prompt.withPromptPaused(logger2.prompt.withPromptPaused(t)) + } + + private[mill] override def withPromptUnpaused[T](t: => T): T = { + logger1.prompt.withPromptUnpaused(logger2.prompt.withPromptUnpaused(t)) + } + + override def enableTicker: Boolean = logger1.prompt.enableTicker || logger2.prompt.enableTicker - private[mill] override def withPromptPaused[T](t: => T): T = { - logger1.withPromptPaused(logger2.withPromptPaused(t)) + override def debugEnabled: Boolean = logger1.prompt.debugEnabled || logger2.prompt.debugEnabled } - private[mill] override def withPromptUnpaused[T](t: => T): T = { - logger1.withPromptUnpaused(logger2.withPromptUnpaused(t)) + def debug(s: String): Unit = { + logger1.debug(s) + logger2.debug(s) } - override def enableTicker: Boolean = logger1.enableTicker || logger2.enableTicker - private[mill] override def subLogger(path: os.Path, key: String, message: String): Logger = { new MultiLogger( colored, logger1.subLogger(path, key, message), logger2.subLogger(path, key, message), - inStream0, - debugEnabled + inStream0 ) } @@ -109,13 +103,12 @@ private[mill] class MultiLogger( override def errorColor: Attrs = logger1.errorColor ++ logger2.errorColor private[mill] override def logPrefixKey = logger1.logPrefixKey ++ logger2.logPrefixKey - override def withOutStream(outStream: PrintStream): ColorLogger = { + override def withOutStream(outStream: PrintStream): Logger = { new MultiLogger( colored, logger1.withOutStream(outStream), logger2.withOutStream(outStream), - inStream0, - debugEnabled + inStream0 ) } } diff --git a/core/internal/src/mill/internal/PrefixLogger.scala b/core/internal/src/mill/internal/PrefixLogger.scala index f32257f7da4..63efab85722 100644 --- a/core/internal/src/mill/internal/PrefixLogger.scala +++ b/core/internal/src/mill/internal/PrefixLogger.scala @@ -1,136 +1,96 @@ package mill.internal -import mill.api.{ColorLogger, Logger, SystemStreams} +import mill.api.{Logger, SystemStreams} import java.io.PrintStream +/** + * Configures a logger that prefixes lines of logs. + * + * Generates log lines of the form + * + * [$logPrefixKey/$keySuffix] $message + * [$logPrefixKey] ...logs... + * [$logPrefixKey] ...logs... + * [$logPrefixKey] ...logs... + * + * And a prompt line of the form + * + * [$logPrefixKey] $message + */ private[mill] class PrefixLogger( - val logger0: ColorLogger, + val logger0: Logger, key0: Seq[String], - tickerContext: String = "", - outStream0: Option[PrintStream] = None, - errStream0: Option[PrintStream] = None, - verboseKeySuffix: String = "", - message: String = "", + override val keySuffix: String = "", + override val message: String = "", // Disable printing the prefix, but continue reporting the `key` to `reportKey`. Used // for `exclusive` commands where we don't want the prefix, but we do want the header // above the output of every command that gets run so we can see who the output belongs to noPrefix: Boolean = false -) extends ColorLogger { +) extends Logger { private[mill] override val logPrefixKey = logger0.logPrefixKey ++ key0 + assert(key0.forall(_.nonEmpty)) val linePrefix: String = if (noPrefix || logPrefixKey.isEmpty) "" else s"[${logPrefixKey.mkString("-")}] " override def toString: String = s"PrefixLogger($logger0, $key0)" - def this(logger0: ColorLogger, context: String, tickerContext: String) = - this(logger0, Seq(context), tickerContext, None, None) - def this( - logger0: ColorLogger, - context: String, - tickerContext: String, - outStream0: Option[PrintStream], - errStream0: Option[PrintStream] - ) = - this(logger0, Seq(context), tickerContext, outStream0, errStream0, "", "") override def colored = logger0.colored override def infoColor = logger0.infoColor override def errorColor = logger0.errorColor - val systemStreams = new SystemStreams( - out = outStream0.getOrElse( - new PrintStream(new LinePrefixOutputStream( - infoColor(linePrefix).render, - logger0.unprefixedSystemStreams.out, - () => reportKey(logPrefixKey) - )) - ), - err = errStream0.getOrElse( - new PrintStream(new LinePrefixOutputStream( - infoColor(linePrefix).render, - logger0.unprefixedSystemStreams.err, - () => reportKey(logPrefixKey) - )) - ), - logger0.systemStreams.in + def prefixPrintStream(stream: java.io.OutputStream) = { + new PrintStream(new LinePrefixOutputStream( + infoColor(linePrefix).render, + stream, + () => prompt.reportKey(logPrefixKey) + )) + } + val streams = new SystemStreams( + out = prefixPrintStream(logger0.unprefixedStreams.out), + err = prefixPrintStream(logger0.unprefixedStreams.err), + logger0.streams.in ) - private[mill] override val unprefixedSystemStreams = new SystemStreams( - outStream0.getOrElse(logger0.unprefixedSystemStreams.out), - errStream0.getOrElse(logger0.unprefixedSystemStreams.err), - logger0.unprefixedSystemStreams.in + private[mill] override val unprefixedStreams = new SystemStreams( + logger0.unprefixedStreams.out, + logger0.unprefixedStreams.err, + logger0.unprefixedStreams.in ) - override def rawOutputStream = logger0.rawOutputStream - override def info(s: String): Unit = { - reportKey(logPrefixKey) + prompt.reportKey(logPrefixKey) logger0.info("" + infoColor(linePrefix) + s) } override def error(s: String): Unit = { - reportKey(logPrefixKey) + prompt.reportKey(logPrefixKey) logger0.error("" + infoColor(linePrefix) + s) } - override def ticker(s: String): Unit = setPromptDetail(logPrefixKey, s) - override def setPromptDetail(key: Seq[String], s: String): Unit = logger0.setPromptDetail(key, s) - - private[mill] override def setPromptLine( - callKey: Seq[String], - verboseKeySuffix: String, - message: String - ): Unit = { - - logger0.setPromptLine(callKey, verboseKeySuffix, message) - } + override def ticker(s: String): Unit = prompt.setPromptDetail(logPrefixKey, s) - private[mill] override def setPromptLine(): Unit = - setPromptLine(logPrefixKey, verboseKeySuffix, message) + def prompt = logger0.prompt override def debug(s: String): Unit = { - if (debugEnabled) reportKey(logPrefixKey) + if (prompt.debugEnabled) prompt.reportKey(logPrefixKey) logger0.debug("" + infoColor(linePrefix) + s) } - override def debugEnabled: Boolean = logger0.debugEnabled - - override def withOutStream(outStream: PrintStream): PrefixLogger = new PrefixLogger( - logger0.withOutStream(outStream), - logPrefixKey, - infoColor(tickerContext).toString(), - outStream0 = Some(outStream), - errStream0 = Some(systemStreams.err) - ) - - private[mill] override def reportKey(callKey: Seq[String]): Unit = - logger0.reportKey(callKey) - private[mill] override def removePromptLine(callKey: Seq[String]): Unit = - logger0.removePromptLine(callKey) - private[mill] override def removePromptLine(): Unit = removePromptLine(logPrefixKey) - private[mill] override def setPromptHeaderPrefix(s: String): Unit = - logger0.setPromptHeaderPrefix(s) - override def enableTicker = logger0.enableTicker + override def withOutStream(outStream: PrintStream): Logger = new ProxyLogger(this) with Logger { + override lazy val unprefixedStreams = new SystemStreams( + outStream, + PrefixLogger.this.unprefixedStreams.err, + PrefixLogger.this.unprefixedStreams.in + ) - private[mill] override def subLogger( - path: os.Path, - subKeySuffix: String, - message: String - ): Logger = { - new PrefixLogger( - this, - Seq(subKeySuffix), - tickerContext, - outStream0, - errStream0, - verboseKeySuffix, - message + override lazy val streams = new SystemStreams( + outStream, + PrefixLogger.this.streams.err, + PrefixLogger.this.streams.in ) } - private[mill] override def withPromptPaused[T](t: => T): T = logger0.withPromptPaused(t) - private[mill] override def withPromptUnpaused[T](t: => T): T = logger0.withPromptUnpaused(t) -} -private[mill] object PrefixLogger { - def apply(out: ColorLogger, context: String, tickerContext: String = ""): PrefixLogger = - new PrefixLogger(out, context, tickerContext) + private[mill] override def subLogger(path: os.Path, subKey: String, message: String): Logger = { + new PrefixLogger(this, Seq(subKey), keySuffix, message) + } } diff --git a/core/internal/src/mill/internal/PrintLogger.scala b/core/internal/src/mill/internal/PrintLogger.scala index 40ba208712d..947d5a7627d 100644 --- a/core/internal/src/mill/internal/PrintLogger.scala +++ b/core/internal/src/mill/internal/PrintLogger.scala @@ -1,41 +1,48 @@ package mill.internal -import mill.api.{ColorLogger, SystemStreams} +import mill.api.{Logger, SystemStreams} import java.io.* private[mill] class PrintLogger( override val colored: Boolean, - override val enableTicker: Boolean, + enableTicker: Boolean, override val infoColor: fansi.Attrs, override val errorColor: fansi.Attrs, - val systemStreams: SystemStreams, - override val debugEnabled: Boolean, + val streams: SystemStreams, + debugEnabled: Boolean, val context: String, printLoggerState: PrintLogger.State -) extends ColorLogger { +) extends Logger with AutoCloseable { + def close() = () // do nothing override def toString: String = s"PrintLogger($colored, $enableTicker)" def info(s: String): Unit = synchronized { printLoggerState.value = PrintLogger.State.Newline - systemStreams.err.println(infoColor(context + s)) + streams.err.println(infoColor(context + s)) } def error(s: String): Unit = synchronized { printLoggerState.value = PrintLogger.State.Newline - systemStreams.err.println((infoColor(context) ++ errorColor(s)).render) + streams.err.println((infoColor(context) ++ errorColor(s)).render) } - override def setPromptDetail(key: Seq[String], s: String): Unit = synchronized { ticker(s) } + def prompt = new Logger.Prompt.NoOp { + override def setPromptDetail(key: Seq[String], s: String): Unit = synchronized { + ticker(s) + } + + override def enableTicker: Boolean = PrintLogger.this.enableTicker + } def ticker(s: String): Unit = synchronized { if (enableTicker) { printLoggerState.value match { case PrintLogger.State.Newline => - systemStreams.err.println(infoColor(s)) + streams.err.println(infoColor(s)) case PrintLogger.State.Middle => - systemStreams.err.println() - systemStreams.err.println(infoColor(s)) + streams.err.println() + streams.err.println(infoColor(s)) case PrintLogger.State.Ticker => - val p = new PrintWriter(systemStreams.err) + val p = new PrintWriter(streams.err) // Need to make this more "atomic" val nav = new AnsiNav(p) nav.up(1) @@ -43,30 +50,18 @@ private[mill] class PrintLogger( nav.left(9999) p.flush() - systemStreams.err.println(infoColor(s)) + streams.err.println(infoColor(s)) } printLoggerState.value = PrintLogger.State.Ticker } } - override def withOutStream(outStream: PrintStream): PrintLogger = - copy(systemStreams = new SystemStreams(outStream, systemStreams.err, systemStreams.in)) - - private def copy( - colored: Boolean = colored, - enableTicker: Boolean = enableTicker, - infoColor: fansi.Attrs = infoColor, - errorColor: fansi.Attrs = errorColor, - systemStreams: SystemStreams = systemStreams, - debugEnabled: Boolean = debugEnabled, - context: String = context, - printLoggerState: PrintLogger.State = printLoggerState - ): PrintLogger = new PrintLogger( + override def withOutStream(outStream: PrintStream): PrintLogger = new PrintLogger( colored, enableTicker, infoColor, errorColor, - systemStreams, + new SystemStreams(outStream, streams.err, streams.in), debugEnabled, context, printLoggerState @@ -75,53 +70,13 @@ private[mill] class PrintLogger( def debug(s: String): Unit = synchronized { if (debugEnabled) { printLoggerState.value = PrintLogger.State.Newline - systemStreams.err.println(context + s) + streams.err.println(context + s) } } - - override def rawOutputStream: PrintStream = systemStreams.out } private[mill] object PrintLogger { - def wrapSystemStreams(systemStreams0: SystemStreams, printLoggerState: State): SystemStreams = { - new SystemStreams( - new PrintStream(new PrintLogger.StateStream(systemStreams0.out, printLoggerState.value = _)), - new PrintStream(new PrintLogger.StateStream(systemStreams0.err, printLoggerState.value = _)), - systemStreams0.in - ) - } - class StateStream(wrapped: OutputStream, setprintLoggerState0: State.Value => Unit) - extends OutputStream { - - private def setprintLoggerState(c: Char) = setprintLoggerState0( - c match { - case '\n' => State.Newline - case '\r' => State.Newline - case _ => State.Middle - } - ) - - override def write(b: Array[Byte]): Unit = synchronized { - if (b.nonEmpty) setprintLoggerState(b(b.length - 1).toChar) - wrapped.write(b) - } - - override def write(b: Array[Byte], off: Int, len: Int): Unit = synchronized { - if (len != 0) setprintLoggerState(b(off + len - 1).toChar) - wrapped.write(b, off, len) - } - - override def write(b: Int): Unit = synchronized { - setprintLoggerState(b.toChar) - wrapped.write(b) - } - - override def flush(): Unit = synchronized { - wrapped.flush() - } - } - class State { var value: State.Value = State.Newline } diff --git a/core/internal/src/mill/internal/PromptLogger.scala b/core/internal/src/mill/internal/PromptLogger.scala index 78de04c7443..0389fcda8d9 100644 --- a/core/internal/src/mill/internal/PromptLogger.scala +++ b/core/internal/src/mill/internal/PromptLogger.scala @@ -1,6 +1,6 @@ package mill.internal -import mill.api.{SystemStreams, ColorLogger} +import mill.api.{SystemStreams, Logger} import mill.constants.ProxyStream import mill.internal.PromptLoggerUtil.* import pprint.Util.literalize @@ -13,21 +13,21 @@ import java.io.* * * Most operations that update mutable state *or* writes to parent [[systemStreams0]] is * synchronized under the [[PromptLogger]] object. Notably, child writes to - * [[systemStreams]] are *not* synchronized, and instead goes into a [[PipeStreams]] + * [[streams]] are *not* synchronized, and instead goes into a [[PipeStreams]] * buffer to be read out and handled asynchronously. */ private[mill] class PromptLogger( override val colored: Boolean, - override val enableTicker: Boolean, + enableTicker: Boolean, override val infoColor: fansi.Attrs, override val errorColor: fansi.Attrs, systemStreams0: SystemStreams, - override val debugEnabled: Boolean, + debugEnabled: Boolean, titleText: String, terminfoPath: os.Path, currentTimeMillis: () => Long, autoUpdate: Boolean = true -) extends ColorLogger with AutoCloseable { +) extends Logger with AutoCloseable { override def toString: String = s"PromptLogger(${literalize(titleText)})" import PromptLogger.* @@ -94,52 +94,65 @@ private[mill] class PromptLogger( if (enableTicker && autoUpdate) promptUpdaterThread.start() - def info(s: String): Unit = systemStreams.err.println(s) + def info(s: String): Unit = streams.err.println(s) - def error(s: String): Unit = systemStreams.err.println(s) + def error(s: String): Unit = streams.err.println(s) - override def setPromptHeaderPrefix(s: String): Unit = synchronized { - promptLineState.setHeaderPrefix(s) - } + object prompt extends Logger.Prompt { + override def setPromptHeaderPrefix(s: String): Unit = synchronized { + promptLineState.setHeaderPrefix(s) + } - override def clearPromptStatuses(): Unit = synchronized { promptLineState.clearStatuses() } - override def removePromptLine(key: Seq[String]): Unit = synchronized { - promptLineState.setCurrent(key, None) - } + override def clearPromptStatuses(): Unit = synchronized { + promptLineState.clearStatuses() + } - def ticker(s: String): Unit = () - override def setPromptDetail(key: Seq[String], s: String): Unit = synchronized { - promptLineState.setDetail(key, s) - } + override def removePromptLine(key: Seq[String]): Unit = synchronized { + promptLineState.setCurrent(key, None) + } - override def reportKey(key: Seq[String]): Unit = { - val res = synchronized { - if (reportedIdentifiers(key)) None - else { - reportedIdentifiers.add(key) - seenIdentifiers.get(key) - } + override def setPromptDetail(key: Seq[String], s: String): Unit = synchronized { + promptLineState.setDetail(key, s) } - for ((verboseKeySuffix, message) <- res) { - if (enableTicker) { - systemStreams.err.println(infoColor(s"[${key.mkString("-")}$verboseKeySuffix] $message")) - streamManager.awaitPumperEmpty() + + override def reportKey(key: Seq[String]): Unit = { + val res = synchronized { + if (reportedIdentifiers(key)) None + else { + reportedIdentifiers.add(key) + seenIdentifiers.get(key) + } + } + for ((keySuffix, message) <- res) { + if (prompt.enableTicker) { + streams.err.println(infoColor(s"[${key.mkString("-")}$keySuffix] $message")) + streamManager.awaitPumperEmpty() + } } } + + override def setPromptLine(key: Seq[String], keySuffix: String, message: String): Unit = + synchronized { + promptLineState.setCurrent(key, Some(s"[${key.mkString("-")}] $message")) + seenIdentifiers(key) = (keySuffix, message) + } + + private[mill] override def withPromptPaused[T](t: => T): T = + runningState.withPromptPaused0(true, t) + + private[mill] override def withPromptUnpaused[T](t: => T): T = + runningState.withPromptPaused0(false, t) + + def enableTicker = PromptLogger.this.enableTicker + def debugEnabled = PromptLogger.this.debugEnabled } + def ticker(s: String): Unit = () def streamsAwaitPumperEmpty(): Unit = streamManager.awaitPumperEmpty() private val seenIdentifiers = collection.mutable.Map.empty[Seq[String], (String, String)] private val reportedIdentifiers = collection.mutable.Set.empty[Seq[String]] - override def setPromptLine(key: Seq[String], verboseKeySuffix: String, message: String): Unit = - synchronized { - promptLineState.setCurrent(key, Some(s"[${key.mkString("-")}] $message")) - seenIdentifiers(key) = (verboseKeySuffix, message) - } - def debug(s: String): Unit = if (debugEnabled) systemStreams.err.println(s) - - override def rawOutputStream: PrintStream = systemStreams0.out + def debug(s: String): Unit = if (debugEnabled) streams.err.println(s) override def close(): Unit = { synchronized { @@ -159,12 +172,7 @@ private[mill] class PromptLogger( promptUpdaterThread.join() } - def systemStreams = streamManager.proxySystemStreams - - private[mill] override def withPromptPaused[T](t: => T): T = - runningState.withPromptPaused0(true, t) - private[mill] override def withPromptUnpaused[T](t: => T): T = - runningState.withPromptPaused0(false, t) + def streams = streamManager.proxySystemStreams } private[mill] object PromptLogger { diff --git a/core/internal/src/mill/internal/ProxyLogger.scala b/core/internal/src/mill/internal/ProxyLogger.scala index 253ade1fd5d..1777ff13cc4 100644 --- a/core/internal/src/mill/internal/ProxyLogger.scala +++ b/core/internal/src/mill/internal/ProxyLogger.scala @@ -2,8 +2,6 @@ package mill.internal import mill.api.{Logger, SystemStreams} -import java.io.PrintStream - /** * A Logger that forwards all logging to another Logger. Intended to be * used as a base class for wrappers that modify logging behavior. @@ -12,37 +10,17 @@ private[mill] class ProxyLogger(logger: Logger) extends Logger { override def toString: String = s"ProxyLogger($logger)" def colored = logger.colored - lazy val systemStreams = logger.systemStreams + lazy val streams = logger.streams def info(s: String): Unit = logger.info(s) def error(s: String): Unit = logger.error(s) def ticker(s: String): Unit = logger.ticker(s) - override def setPromptDetail(key: Seq[String], s: String): Unit = logger.setPromptDetail(key, s) - private[mill] override def setPromptLine(): Unit = logger.setPromptLine() - private[mill] override def setPromptLine( - key: Seq[String], - verboseKeySuffix: String, - message: String - ): Unit = - logger.setPromptLine(key, verboseKeySuffix, message) def debug(s: String): Unit = logger.debug(s) - override def debugEnabled: Boolean = logger.debugEnabled - - override def close(): Unit = logger.close() - private[mill] override def reportKey(key: Seq[String]): Unit = logger.reportKey(key) - - override def rawOutputStream: PrintStream = logger.rawOutputStream - private[mill] override def removePromptLine(key: Seq[String]): Unit = logger.removePromptLine(key) - private[mill] override def removePromptLine(): Unit = logger.removePromptLine() - private[mill] override def setPromptHeaderPrefix(s: String): Unit = - logger.setPromptHeaderPrefix(s) - private[mill] override def withPromptPaused[T](t: => T): T = logger.withPromptPaused(t) - private[mill] override def withPromptUnpaused[T](t: => T): T = logger.withPromptUnpaused(t) + def prompt = logger.prompt - override def enableTicker = logger.enableTicker override def infoColor: fansi.Attrs = logger.infoColor override def errorColor: fansi.Attrs = logger.errorColor private[mill] override def logPrefixKey: Seq[String] = logger.logPrefixKey - private[mill] override def unprefixedSystemStreams: SystemStreams = logger.unprefixedSystemStreams + private[mill] override def unprefixedStreams: SystemStreams = logger.unprefixedStreams } diff --git a/core/internal/test/src/mill/internal/PromptLoggerTests.scala b/core/internal/test/src/mill/internal/PromptLoggerTests.scala index 98e0e15355d..51517fc03f5 100644 --- a/core/internal/test/src/mill/internal/PromptLoggerTests.scala +++ b/core/internal/test/src/mill/internal/PromptLoggerTests.scala @@ -57,18 +57,18 @@ object PromptLoggerTests extends TestSuite { val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp()) - promptLogger.setPromptHeaderPrefix("123/456") - promptLogger.setPromptLine(Seq("1"), "/456", "my-task") + promptLogger.prompt.setPromptHeaderPrefix("123/456") + promptLogger.prompt.setPromptLine(Seq("1"), "/456", "my-task") now += 10000 - prefixLogger.outputStream.println("HELLO") + prefixLogger.streams.out.println("HELLO") promptLogger.refreshPrompt() - prefixLogger.outputStream.println("WORLD") + prefixLogger.streams.out.println("WORLD") - promptLogger.removePromptLine(Seq("1")) + promptLogger.prompt.removePromptLine(Seq("1")) now += 10000 promptLogger.refreshPrompt() @@ -104,16 +104,16 @@ object PromptLoggerTests extends TestSuite { var now = 0L val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp("80 40")) - promptLogger.setPromptHeaderPrefix("123/456") + promptLogger.prompt.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() check(promptLogger, baos)( "[123/456] ============================== TITLE ==============================" ) - promptLogger.setPromptLine(Seq("1"), "/456", "my-task") + promptLogger.prompt.setPromptLine(Seq("1"), "/456", "my-task") now += 10000 - prefixLogger.outputStream.println("HELLO") + prefixLogger.streams.out.println("HELLO") promptLogger.refreshPrompt() // Need to call `refreshPrompt()` for prompt to change // First time we log with the prefix `[1]`, make sure we print out the title line @@ -125,7 +125,7 @@ object PromptLoggerTests extends TestSuite { "[1] my-task 10s" ) - prefixLogger.outputStream.println("WORLD") + prefixLogger.streams.out.println("WORLD") // Prompt doesn't change, no need to call `refreshPrompt()` for it to be // re-rendered below the latest prefixed output. Subsequent log line with `[1]` // prefix does not re-render title line `[1/456] ...` @@ -141,16 +141,16 @@ object PromptLoggerTests extends TestSuite { // Only after some time has passed do we start displaying the new ticker entry, // to ensure it is meaningful to read and not just something that will flash and disappear val newPrefixLogger2 = new PrefixLogger(promptLogger, Seq("2")) - newPrefixLogger2.setPromptLine(Seq("2"), "/456", "my-task-new") - newPrefixLogger2.errorStream.println("I AM COW") - newPrefixLogger2.errorStream.println("HEAR ME MOO") + newPrefixLogger2.prompt.setPromptLine(Seq("2"), "/456", "my-task-new") + newPrefixLogger2.streams.err.println("I AM COW") + newPrefixLogger2.streams.err.println("HEAR ME MOO") // For short-lived ticker entries that are removed quickly, they never // appear in the prompt at all even though they can run and generate logs val newPrefixLogger3 = new PrefixLogger(promptLogger, Seq("3")) - newPrefixLogger3.setPromptLine(Seq("3"), "/456", "my-task-short-lived") - newPrefixLogger3.errorStream.println("hello short lived") - newPrefixLogger3.errorStream.println("goodbye short lived") + newPrefixLogger3.prompt.setPromptLine(Seq("3"), "/456", "my-task-short-lived") + newPrefixLogger3.streams.err.println("hello short lived") + newPrefixLogger3.streams.err.println("goodbye short lived") // my-task-new does not appear yet because it is too new promptLogger.refreshPrompt() @@ -168,7 +168,7 @@ object PromptLoggerTests extends TestSuite { "[1] my-task 10s" ) - newPrefixLogger3.removePromptLine(Seq("3")) + newPrefixLogger3.prompt.removePromptLine(Seq("3")) now += 1000 @@ -189,7 +189,7 @@ object PromptLoggerTests extends TestSuite { "[2] my-task-new 1s" ) - promptLogger.removePromptLine(Seq("1")) + promptLogger.prompt.removePromptLine(Seq("1")) now += 10 @@ -272,10 +272,10 @@ object PromptLoggerTests extends TestSuite { @volatile var now = 0L val (baos, promptLogger, prefixLogger) = setup(() => now, os.temp("80 40")) - promptLogger.setPromptHeaderPrefix("123/456") + promptLogger.prompt.setPromptHeaderPrefix("123/456") promptLogger.refreshPrompt() - promptLogger.setPromptLine(Seq("1"), "/456", "my-task") + promptLogger.prompt.setPromptLine(Seq("1"), "/456", "my-task") prefixLogger.ticker("detail") now += 1000 promptLogger.refreshPrompt() @@ -289,7 +289,7 @@ object PromptLoggerTests extends TestSuite { "[123/456] ============================== TITLE ============================= 1s", "[1] my-task 1s detail-too-long-gets-truncated...fghijklmnopqrstuvwxyz1234567890" ) - promptLogger.removePromptLine(Seq("1")) + promptLogger.prompt.removePromptLine(Seq("1")) now += 10000 promptLogger.refreshPrompt() check(promptLogger, baos)( diff --git a/core/src/mill/eval/EvaluatorImpl.scala b/core/src/mill/eval/EvaluatorImpl.scala index 048af5e22dc..1034ac3c9c5 100644 --- a/core/src/mill/eval/EvaluatorImpl.scala +++ b/core/src/mill/eval/EvaluatorImpl.scala @@ -1,7 +1,7 @@ package mill.eval import mill.api.{ - ColorLogger, + Logger, CompileProblemReporter, DummyTestReporter, ExecResult, @@ -41,7 +41,7 @@ final class EvaluatorImpl private[mill] ( private[mill] def workerCache = execution.workerCache private[mill] def env = execution.env - def withBaseLogger(newBaseLogger: ColorLogger): Evaluator = new EvaluatorImpl( + def withBaseLogger(newBaseLogger: Logger): Evaluator = new EvaluatorImpl( allowPositionalCommandArgs, selectiveExecution, execution.withBaseLogger(newBaseLogger) @@ -106,7 +106,7 @@ final class EvaluatorImpl private[mill] ( targets: Seq[Task[T]], reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, - logger: ColorLogger = baseLogger, + logger: Logger = baseLogger, serialCommandExec: Boolean = false, selectiveExecution: Boolean = false ): Evaluator.Result[T] = { diff --git a/core/src/mill/eval/EvaluatorProxy.scala b/core/src/mill/eval/EvaluatorProxy.scala index 59928ce5a84..17bfb8cd36d 100644 --- a/core/src/mill/eval/EvaluatorProxy.scala +++ b/core/src/mill/eval/EvaluatorProxy.scala @@ -12,7 +12,7 @@ final class EvaluatorProxy(delegate: => Evaluator) extends Evaluator { override def workerCache = delegate.workerCache override def env = delegate.env - def withBaseLogger(newBaseLogger: ColorLogger): Evaluator = delegate.withBaseLogger(newBaseLogger) + def withBaseLogger(newBaseLogger: Logger): Evaluator = delegate.withBaseLogger(newBaseLogger) def resolveSegments( scriptArgs: Seq[String], @@ -42,7 +42,7 @@ final class EvaluatorProxy(delegate: => Evaluator) extends Evaluator { targets: Seq[Task[T]], reporter: Int => Option[CompileProblemReporter] = _ => Option.empty[CompileProblemReporter], testReporter: TestReporter = DummyTestReporter, - logger: ColorLogger = baseLogger, + logger: Logger = baseLogger, serialCommandExec: Boolean = false, selectiveExecution: Boolean = false ): Evaluator.Result[T] = { diff --git a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala index d0f9d678420..d7f533ef464 100644 --- a/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala +++ b/kotlinlib/worker/impl/src/mill/kotlinlib/worker/impl/KotlinWorkerImpl.scala @@ -22,7 +22,7 @@ class KotlinWorkerImpl extends KotlinWorker { case KotlinWorkerTarget.Jvm => new K2JVMCompiler() case KotlinWorkerTarget.Js => new K2JSCompiler() } - val exitCode = compiler.exec(ctx.log.errorStream, args*) + val exitCode = compiler.exec(ctx.log.streams.err, args*) if (exitCode.getCode != 0) { Result.Failure(s"Kotlin compiler failed with exit code ${exitCode.getCode} ($exitCode)") } else { diff --git a/main/init/src/mill/init/InitModule.scala b/main/init/src/mill/init/InitModule.scala index a1f6383e1c0..617727f8032 100644 --- a/main/init/src/mill/init/InitModule.scala +++ b/main/init/src/mill/init/InitModule.scala @@ -87,7 +87,7 @@ trait InitModule extends Module { } } match { case Success((ret, msg)) => - Task.log.outputStream.println(msg) + Task.log.streams.out.println(msg) ret case Failure(exception) => Task.log.error(exception.getMessage) diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index e972e6e9572..076a0e1f7c2 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -322,8 +322,8 @@ object MainModule { // When using `show`, redirect all stdout of the evaluated tasks so the // printed JSON is the only thing printed to stdout. val redirectLogger = log - .withOutStream(evaluator.baseLogger.errorStream) - .asInstanceOf[ColorLogger] + .withOutStream(evaluator.baseLogger.streams.err) + .asInstanceOf[Logger] evaluator.withBaseLogger(redirectLogger) .evaluate( diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 61f90147c06..c36480d7be7 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -4,7 +4,7 @@ import mill.internal.PrefixLogger import mill.define.internal.Watchable import mill.main.{BuildInfo, RootModule} import mill.constants.CodeGenConstants.* -import mill.api.{ColorLogger, PathRef, Result, SystemStreams, Val, WorkspaceRoot, internal} +import mill.api.{Logger, PathRef, Result, SystemStreams, Val, WorkspaceRoot, internal} import mill.define.{BaseModule, Evaluator, Segments, SelectMode} import mill.exec.JsonArrayLogger import mill.constants.OutFiles.{millBuild, millChromeProfile, millProfile, millRunnerState} @@ -43,7 +43,7 @@ class MillBuildBootstrap( threadCount: Option[Int], targetsAndParams: Seq[String], prevRunnerState: RunnerState, - logger: ColorLogger, + logger: Logger, needBuildFile: Boolean, requestedMetaLevel: Option[Int], allowPositionalCommandArgs: Boolean, diff --git a/runner/src/mill/runner/MillMain.scala b/runner/src/mill/runner/MillMain.scala index 2da51a62382..fbc58a503c5 100644 --- a/runner/src/mill/runner/MillMain.scala +++ b/runner/src/mill/runner/MillMain.scala @@ -8,7 +8,7 @@ import java.nio.file.StandardOpenOption import java.util.Locale import scala.jdk.CollectionConverters.* import scala.util.Properties -import mill.api.{ColorLogger, MillException, Result, SystemStreams, WorkspaceRoot, internal} +import mill.api.{Logger, MillException, Result, SystemStreams, WorkspaceRoot, internal} import mill.bsp.{BspContext, BspServerResult} import mill.constants.{OutFiles, ServerFiles, Util} import mill.client.lock.Lock @@ -274,8 +274,8 @@ object MillMain { // Enter key pressed, removing mill-selective-execution.json to // ensure all tasks re-run even though no inputs may have changed if (enterKeyPressed) os.remove(out / OutFiles.millSelectiveExecution) - SystemStreams.withStreams(logger.systemStreams) { - tailManager.withOutErr(logger.outputStream, logger.errorStream) { + SystemStreams.withStreams(logger.streams) { + tailManager.withOutErr(logger.streams.out, logger.streams.err) { new MillBuildBootstrap( projectRoot = WorkspaceRoot.workspaceRoot, output = out, @@ -372,7 +372,7 @@ object MillMain { serverDir: os.Path, colored: Boolean, colors: Colors - ): ColorLogger = { + ): Logger with AutoCloseable = { val logger = if (config.disablePrompt.value) { new mill.internal.PrintLogger( @@ -380,7 +380,7 @@ object MillMain { enableTicker = enableTicker.getOrElse(mainInteractive), infoColor = colors.info, errorColor = colors.error, - systemStreams = streams, + streams = streams, debugEnabled = config.debugLog.value, context = "", printLoggerState diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index c6dac7e4e7f..eadcefb5f73 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -455,7 +455,7 @@ trait JavaModule // This is exclusive to avoid scrambled output Task.Command(exclusive = true) { val asString = formatModuleDeps(recursive, true)() - Task.log.outputStream.println(asString) + Task.log.streams.out.println(asString) } } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 707f7de161d..fa82a82057e 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -506,7 +506,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => * Use [[ammoniteVersion]] to customize the Ammonite version to use. */ def repl(replOptions: String*): Command[Unit] = Task.Command(exclusive = true) { - if (Task.log.inStream == DummyInputStream) { + if (Task.log.streams.in == DummyInputStream) { Result.Failure("repl needs to be run with the -i/--interactive flag") } else { val mainClass = ammoniteMainClass() diff --git a/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala b/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala index 9bcb4be2062..5f79603e534 100644 --- a/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala +++ b/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala @@ -28,7 +28,7 @@ private[scalafmt] class ScalafmtWorker extends AutoCloseable { if (misformatted.isEmpty) { Result.Success(()) } else { - val out = ctx.log.outputStream + val out = ctx.log.streams.out for (u <- misformatted) { out.println(u.path.toString) } diff --git a/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala b/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala index 0b302766f09..ccac46935c5 100644 --- a/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala +++ b/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala @@ -537,7 +537,7 @@ class ZincWorkerImpl( val consoleAppender = ConsoleAppender( "ZincLogAppender", - ConsoleOut.printStreamOut(ctx.log.errorStream), + ConsoleOut.printStreamOut(ctx.log.streams.err), ctx.log.colored, ctx.log.colored, _ => None diff --git a/testrunner/src/mill/testrunner/TestRunnerUtils.scala b/testrunner/src/mill/testrunner/TestRunnerUtils.scala index 23c91fbea1a..0c02cb132f0 100644 --- a/testrunner/src/mill/testrunner/TestRunnerUtils.scala +++ b/testrunner/src/mill/testrunner/TestRunnerUtils.scala @@ -146,12 +146,12 @@ import scala.jdk.CollectionConverters.IteratorHasAsScala } }, Array(new Logger { - def debug(msg: String) = ctx.log.outputStream.println(msg) - def error(msg: String) = ctx.log.outputStream.println(msg) + def debug(msg: String) = ctx.log.streams.out.println(msg) + def error(msg: String) = ctx.log.streams.out.println(msg) def ansiCodesSupported() = true - def warn(msg: String) = ctx.log.outputStream.println(msg) - def trace(t: Throwable) = t.printStackTrace(ctx.log.outputStream) - def info(msg: String) = ctx.log.outputStream.println(msg) + def warn(msg: String) = ctx.log.streams.out.println(msg) + def trace(t: Throwable) = t.printStackTrace(ctx.log.streams.out) + def info(msg: String) = ctx.log.streams.out.println(msg) }) ) @@ -162,9 +162,9 @@ import scala.jdk.CollectionConverters.IteratorHasAsScala if (doneMessage != null && doneMessage.nonEmpty) { if (doneMessage.endsWith("\n")) - ctx.log.outputStream.print(doneMessage) + ctx.log.streams.out.print(doneMessage) else - ctx.log.outputStream.println(doneMessage) + ctx.log.streams.out.println(doneMessage) } val results = for (e <- events.iterator().asScala) yield {