Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward Dokka Generator messages to Gradle logger #3833

Merged
merged 12 commits into from
Oct 17, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,30 @@ package org.jetbrains.dokka.gradle.internal

import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.utilities.LoggingLevel
import org.jetbrains.dokka.utilities.LoggingLevel.*
import org.slf4j.Logger
import java.io.File
import java.io.Writer
import java.util.concurrent.atomic.AtomicInteger

/**
* Logs all Dokka messages to a file.
* A logger for [org.jetbrains.dokka.DokkaGenerator].
*
* All messages will be written to [logWriter] and forwarded to a Gradle [logger] (at an appropriate log level).
*
* [org.jetbrains.dokka.DokkaGenerator] makes heavy use of coroutines and parallelization,
* so use thread-safe practices when handling logging messages.
*
* @param logTag Prepend all [logger] messages with this tag.
* @see org.jetbrains.dokka.DokkaGenerator
*/
// Gradle causes OOM errors when there is a lot of console output. Logging to file is a workaround.
// https://github.com/gradle/gradle/issues/23965
// https://github.com/gradle/gradle/issues/15621
internal class LoggerAdapter(
outputFile: File
outputFile: File,
private val logger: Logger,
private val logTag: String,
) : DokkaLogger, AutoCloseable {

private val logWriter: Writer
Expand All @@ -43,22 +53,32 @@ internal class LoggerAdapter(
get() = errorsCounter.get()
set(value) = errorsCounter.set(value)

override fun debug(message: String) = log(LoggingLevel.DEBUG, message)
override fun progress(message: String) = log(LoggingLevel.PROGRESS, message)
override fun info(message: String) = log(LoggingLevel.INFO, message)
override fun debug(message: String) = log(DEBUG, message)
override fun progress(message: String) = log(PROGRESS, message)
override fun info(message: String) = log(INFO, message)

override fun warn(message: String) {
warningsCount++
log(LoggingLevel.WARN, message)
log(WARN, message)
}

override fun error(message: String) {
errorsCount++
log(LoggingLevel.ERROR, message)
log(ERROR, message)
}

@Synchronized
private fun log(level: LoggingLevel, message: String) {
when (level) {
PROGRESS,
INFO -> logger.info("[$logTag] " + message.prependIndent().trimStart())

DEBUG -> logger.debug("[$logTag] " + message.prependIndent().trimStart())

WARN -> logger.warn("w: [$logTag] " + message.prependIndent().trimStart())

ERROR -> logger.error("e: [$logTag] " + message.prependIndent().trimStart())
}
logWriter.appendLine("[${level.name}] $message")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ constructor(
workQueue.submit(DokkaGeneratorWorker::class) {
this.dokkaParameters.set(dokkaConfiguration)
this.logFile.set(workerLogFile)
this.taskPath.set(this@DokkaGenerateTask.path)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package org.jetbrains.dokka.gradle.workers

import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
Expand All @@ -27,6 +29,12 @@ abstract class DokkaGeneratorWorker : WorkAction<DokkaGeneratorWorker.Parameters
interface Parameters : WorkParameters {
val dokkaParameters: Property<DokkaConfiguration>
val logFile: RegularFileProperty

/**
* The [org.gradle.api.Task.getPath] of the task that invokes this worker.
* Only used in log messages.
*/
val taskPath: Property<String>
}

override fun execute() {
Expand Down Expand Up @@ -56,7 +64,11 @@ abstract class DokkaGeneratorWorker : WorkAction<DokkaGeneratorWorker.Parameters
logFile: File,
dokkaParameters: DokkaConfiguration
) {
LoggerAdapter(logFile).use { logger ->
LoggerAdapter(
logFile,
logger,
logTag = parameters.taskPath.get(),
).use { logger ->
logger.progress("Executing DokkaGeneratorWorker with dokkaParameters: $dokkaParameters")

val generator = DokkaGenerator(dokkaParameters, logger)
Expand All @@ -69,6 +81,8 @@ abstract class DokkaGeneratorWorker : WorkAction<DokkaGeneratorWorker.Parameters

@DokkaInternalApi
companion object {
private val logger: Logger = Logging.getLogger(DokkaGeneratorWorker::class.java)

// can't use kotlin.Duration or kotlin.time.measureTime {} because
// the implementation isn't stable across Kotlin versions
private fun measureTime(block: () -> Unit): Duration =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package internal

import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import org.jetbrains.dokka.gradle.internal.LoggerAdapter
import org.slf4j.event.SubstituteLoggingEvent
import org.slf4j.helpers.NOPLogger
import org.slf4j.helpers.SubstituteLoggerFactory
import kotlin.io.path.createTempFile
import kotlin.io.path.readText

class LoggerAdapterTest : FunSpec({

test("slf4j logger output") {
val logFactory = SubstituteLoggerFactory()

val loggerAdapter = LoggerAdapter(
createTempFile().toFile(),
logFactory.getLogger("test"),
"LOG-TAG"
)

loggerAdapter.error("an error msg")
loggerAdapter.warn("a warn msg")
loggerAdapter.debug("a debug msg")
loggerAdapter.info("an info msg")
loggerAdapter.progress("a progress msg")

loggerAdapter.errorsCount shouldBe 1
loggerAdapter.warningsCount shouldBe 1

logFactory.eventQueue.map { it.render() }.shouldContainExactly(
"ERROR e: [LOG-TAG] an error msg",
"WARN w: [LOG-TAG] a warn msg",
"DEBUG [LOG-TAG] a debug msg",
"INFO [LOG-TAG] an info msg",
"INFO [LOG-TAG] a progress msg",
)
}

test("logfile output") {
val logFile = createTempFile()

LoggerAdapter(
logFile.toFile(),
NOPLogger.NOP_LOGGER,
"LOG-TAG"
).use { loggerAdapter ->
loggerAdapter.error("an error msg")
loggerAdapter.warn("a warn msg")
loggerAdapter.debug("a debug msg")
loggerAdapter.info("an info msg")
loggerAdapter.progress("a progress msg")

loggerAdapter.errorsCount shouldBe 1
loggerAdapter.warningsCount shouldBe 1
}

logFile.readText() shouldBe """
|[ERROR] an error msg
|[WARN] a warn msg
|[DEBUG] a debug msg
|[INFO] an info msg
|[PROGRESS] a progress msg
|
""".trimMargin()
}

}) {
companion object {
private fun SubstituteLoggingEvent.render(): String = buildString {
append("$level")
append(" ")
append(message)
}
}
}
Loading
Loading