Skip to content

Commit

Permalink
bugfix: Forward standard output to logger
Browse files Browse the repository at this point in the history
  • Loading branch information
tgodzik committed Jul 16, 2024
1 parent 8092d59 commit 3d5d6c9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
3 changes: 3 additions & 0 deletions backend/src/main/scala/bloop/logging/BloopLogger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ final class BloopLogger(
val debugFilter: DebugFilter,
originId: Option[String]
) extends Logger {

redirectOutputToLogs(out)

override def ansiCodesSupported() = true
override def debug(msg: String)(implicit ctx: DebugFilter): Unit =
if (isVerbose && debugFilter.isEnabledFor(ctx)) print(msg, printDebug)
Expand Down
1 change: 1 addition & 0 deletions backend/src/test/scala/bloop/logging/RecordingLogger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class RecordingLogger(
) extends Logger {
private[this] val messages = new ConcurrentLinkedQueue[(String, String)]

redirectOutputToLogs(System.out)
def clear(): Unit = messages.clear()

def debugs: List[String] = getMessagesAt(Some("debug"))
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/test/scala/bloop/BaseCompileSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,38 @@ abstract class BaseCompileSpec extends bloop.testing.BaseSuite {
}
}
}
test("compile-with-Vprint:typer") {
TestUtil.withinWorkspace { workspace =>
val sources = List(
"""/main/scala/Foo.scala
|class Foo
""".stripMargin
)

val logger = new RecordingLogger(ansiCodesSupported = false)
val `A` = TestProject(workspace, "a", sources, scalacOptions = List("-Vprint:typer"))
val projects = List(`A`)
val state = loadState(workspace, projects, logger)
val compiledState = state.compile(`A`)
assertExitStatus(compiledState, ExitStatus.Ok)
assertValidCompilationState(compiledState, projects)

assertNoDiff(
logger.infos.filterNot(_.contains("Compiled")).mkString("\n").trim(),
"""|Compiling a (1 Scala source)
|[[syntax trees at end of typer]] // Foo.scala
|package <empty> {
| class Foo extends scala.AnyRef {
| def <init>(): Foo = {
| Foo.super.<init>();
| ()
| }
| }
|}
|""".stripMargin
)
}
}

test("compile a project, delete an analysis and then write it back during a no-op compilation") {
TestUtil.withinWorkspace { workspace =>
Expand Down
70 changes: 70 additions & 0 deletions shared/src/main/scala/bloop/logging/DuplicatingOutputStream.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package bloop.logging

import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.OutputStream

import scala.util.control.NonFatal

final class DuplicatingOutputStream(
val stdout: OutputStream,
val logged: ByteArrayOutputStream,
logger: Logger
) extends OutputStream {

private val cache = new StringBuilder
if (stdout == null || logged == null) {
throw new NullPointerException()
}

@throws[IOException]
override def write(b: Int): Unit = {
stdout.write(b)
logged.write(b)
logAndReset()
}

@throws[IOException]
override def write(b: Array[Byte]): Unit = {
stdout.write(b)
logged.write(b)
logAndReset()
}

@throws[IOException]
override def write(b: Array[Byte], off: Int, len: Int): Unit = {
stdout.write(b, off, len)
logged.write(b, off, len)
logAndReset()
}

private def logAndReset() = synchronized {
try {
cache.append(logged.toString())
logged.reset()
val logMessage = cache.toString.reverse.dropWhile(_ != '\n').reverse.trim()

if (logMessage != "") {
cache.delete(0, logMessage.size + 1)
logger.info(logMessage)
}
} catch {
case NonFatal(_) =>
}
}

@throws[IOException]
override def flush(): Unit = {
stdout.flush()
logged.flush()
}

@throws[IOException]
override def close(): Unit = {
try {
stdout.close()
} finally {
logged.close()
}
}
}
9 changes: 9 additions & 0 deletions shared/src/main/scala/bloop/logging/Logger.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package bloop.logging

import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.util.function.Supplier

import bloop.io.Environment

abstract class Logger extends xsbti.Logger with BaseSbtLogger {

// Duplicate the standard output so that we get printlns from the compiler
protected def redirectOutputToLogs(out: PrintStream) = {
val baos = new ByteArrayOutputStream()
val duplicating = new DuplicatingOutputStream(out, baos, this)
System.setOut(new PrintStream(duplicating));
}

/** The name of the logger */
def name: String

Expand Down

0 comments on commit 3d5d6c9

Please sign in to comment.