Inside a custom plugin or task class (not a build script), how do you obtain a logger correctly?
answer
- Task -> inherited logger
- Plugin.apply -> project.logger
- elsewhere -> Logging.getLogger(Class)
- SLF4J works, loses lifecycle/quiet
- don't stash Project for config-cache
basics
~10 sIn a Task subclass, just use the inherited logger. In a plain plugin class with no project handy, call org.gradle.api.logging.Logging.getLogger(MyClass::class.java) to get a Gradle Logger.
solid answer
~40 sHow you get a logger depends on context. A custom `Task` (subclass of `DefaultTask`) inherits a per-task `logger` property — use it directly. A `Plugin<Project>`'s `apply` method receives the `Project`, so `project.logger` works there. But helper classes, services, or anything without a project reference should call `org.gradle.api.logging.Logging.getLogger(MyHelper::class.java)` (or by name), which returns a Gradle `Logger` (with `lifecycle`/`quiet`). You can also declare a regular SLF4J `LoggerFactory.getLogger(...)`; since Gradle routes SLF4J through its system, the messages still appear at the right levels — you just lose the `lifecycle`/`quiet` convenience methods. The anti-pattern is capturing `project` into a field to log later: that defies configuration-cache rules. Prefer `Logging.getLogger(...)` for stateless logging in plugin internals.
code
kotlin · 8 linesimport org.gradle.api.logging.Logging
class ManifestWriter {
private val logger = Logging.getLogger(ManifestWriter::class.java)
fun write() {
logger.lifecycle("Writing manifest") // Gradle-specific level still available
}
}go deeper
Likely beyond scope; at most know tasks have an inherited logger.
Know to use the inherited Task logger and project.logger in apply().
Explain Logging.getLogger for context-free classes and that SLF4J also routes through Gradle.
Set conventions for stateless logging in plugin internals that stay configuration-cache compatible.
## The contexts ### Custom Task ```kotlin abstract class GenerateTask : DefaultTask() { @TaskAction fun run() { logger.lifecycle("Generating...") // inherited per-task logger } } ``` Every `Task` exposes a `logger` of type `org.gradle.api.logging.Logger`. No setup needed. ### Plugin apply method ```kotlin class MyPlugin : Plugin<Project> { override fun apply(project: Project) { project.logger.info("Applying MyPlugin") } } ``` The `Project` is the parameter, so `project.logger` is right there. ### A class with no Project/Task For helpers, build services, or static utilities, use the factory: ```kotlin import org.gradle.api.logging.Logging class DependencyScanner { private val logger = Logging.getLogger(DependencyScanner::class.java) fun scan() { logger.info("Scanning") } } ``` `Logging.getLogger(Class)` and `Logging.getLogger(String)` return a Gradle `Logger`, so you keep `lifecycle`/`quiet`. ### Plain SLF4J also works ```kotlin private val log = org.slf4j.LoggerFactory.getLogger(MyClass::class.java) ``` Because Gradle binds SLF4J to its own logging backend, these messages flow into Gradle's console at the mapped levels. The only loss is the Gradle-specific `lifecycle`/`quiet` methods. ## Why not stash the Project? Capturing `project` (or `project.logger`'s enclosing state) into a field used during execution can break the **configuration cache**, which forbids holding live `Project` references at execution time. A logger obtained via `Logging.getLogger(...)` carries no such state, so it is the safe choice inside reusable plugin internals. ## Summary table - In a Task: `logger`. - In `Plugin.apply`: `project.logger`. - Anywhere else: `Logging.getLogger(Class)` (Gradle logger) or `LoggerFactory.getLogger(Class)` (SLF4J).
- If you use org.slf4j.LoggerFactory inside a plugin, do messages still reach Gradle's console?Yes. Gradle binds SLF4J to its own backend, so SLF4J messages appear at the mapped levels. You only lose the lifecycle/quiet convenience methods.
- Why is capturing project into a field to log later a problem?Holding a live Project reference at execution time violates configuration-cache rules. Logging.getLogger(...) gives a stateless logger that sidesteps this.
saying these in an interview costs you the question
- Using println in plugin code because 'there is no logger here' — Logging.getLogger exists.
- Storing a Project reference in a field purely to access its logger during execution.
- Assuming SLF4J loggers in a plugin are silenced by Gradle — they are routed through it.