第十八章. 日志

Chapter 18. Logging

日志是构建工具的主要“UI”。如果日志过于冗长,则真正的警告和问题很容易被隐藏。另一方面,如果出了错,你就需要相关的信息。 Gradle 定义了 6 个日志级别,如表18.1,“日志级别”所示。除了你可能通常都会看到的日志级别之外,还有两个 Gradle 特定的日志级别。这两个级别分别是QUIETLIFECYCLE。默认使用后面的这个日志级别,用于报告构建进度。
The log is the main 'UI' of a build tool. If it is too verbose, real warnings and problems are easily hidden by this. On the other hand you need the relevant information for figuring out if things have gone wrong. Gradle defines 6 log levels, as shown in Table 18.1, “Log levels”. There are two Gradle-specific log levels, in addition to the ones you might normally see. Those levels are QUIET and LIFECYCLE. The latter is the default, and is used to report build progress.

表 18.1. 日志级别 - Table 18.1. Log levels

级别
Level
用于
Used for
ERROR 错误消息
Error messages
QUIET 重要的信息消息
Important information messages
WARNING 警告消息
Warning messages
LIFECYCLE 进度信息消息
Progress information messages
INFO 信息消息
Information messages
DEBUG 调试消息
Debug messages

18.1. 选择一个日志级别

18.1. Choosing a log level

你可以使用表 18.2,“日志级别的命令行选项”中所示的命令行开关来选择不同的日志级别。在表 18.3,“栈跟踪的命令行选项”中,你可以看到影响栈跟踪日志的命令行开关。
You can use the command line switches shown in Table 18.2, “Log level command-line options” to choose different log levels. In Table 18.3, “Stacktrace command-line options” you find the command line switches which affect stacktrace logging.

表 18.2. 日志级别的命令行选项 - Table 18.2. Log level command-line options

选项
Option
输出日志级别
Outputs Log Levels
没有日志选项
no logging options
LIFECYCLE 及更高
LIFECYCLE and higher
-q or --quiet QUIET 及更高
QUIET and higher
-i or --info INFO 及更高
INFO and higher
-d or --debug DEBUG 及更高(即所有日志消息)
DEBUG and higher (that is, all log messages)

表 18.3. 栈跟踪的命令行选项 - Table 18.3. Stacktrace command-line options

选项
Option
意义
Meaning
没有栈跟踪选项
No stacktrace options
在构建错误(如编译错误)时,不会打印堆栈跟踪信息到控制台。只有在内部异常情况下才会打印堆栈跟踪。如果选择DEBUG日志级别,则总是打印截断的堆栈跟踪。
No stacktraces are printed to the console in case of a build error (e.g. a compile error). Only in case of internal exceptions will stacktraces be printed. If the DEBUG log level is chosen, truncated stacktraces are always printed.
-s or --stacktrace 打印截断的堆栈跟踪。我们推荐使用这一个选项,而不是打印完整的堆栈跟踪信息。Groovy 完整的堆栈跟踪是非常详细的(由于底层的动态调用机制,但它们通常不包含你的代码哪里出错了的相关信息)。
Truncated stacktraces are printed. We recommend this over full stacktraces. Groovy full stacktraces are extremely verbose (Due to the underlying dynamic invocation mechanisms. Yet they usually do not contain relevant information for what has gone wrong in your code.)
-S or --full-stacktrace 打印完整的栈跟踪信息。
The full stacktraces are printed out.

18.2. 编写自己的日志消息

18.2. Writing your own log messages

一种简单的在构建文件中打印日志的方法是把消息写到标准输出中。Gradle 会把写到标准输出的所有内容重定向到它的日志系统的 QUIET 级别中。
A simple option for logging in your build file is to write messages to standard output. Gradle redirects anything written to standard output to it's logging system at the QUIET log level.

示例18.1. 使用标准输出写日志 - Example 18.1. Using stdout to write log messages

build.gradle

println 'A message which is logged at QUIET level'

Gradle还提供了一个 logger 属性给构建脚本,它是一个 Logger 实例。该接口扩展了 SLF4J Logger 接口,并添加了一些 Gradle 的特有方法。下面是关于如何在构建脚本中使用它的示例:
Gradle also provides a logger property to a build script, which is an instance of Logger. This interface extends the SLF4J Logger interface and adds a few Gradle specific methods to it. Below is an example of how this is used in the build script:

示例 18.2. 编写自己的日志消息 - Example 18.2. Writing your own log messages

build.gradle

logger.quiet('An info log message which is always logged.')
logger.error('An error log message.')
logger.warn('A warning log message.')
logger.lifecycle('A lifecycle info log message.')
logger.info('An info log message.')
logger.debug('A debug log message.')
logger.trace('A trace log message.')

你也可以通过在构建中使用的其他类(例如 buildSrc 目录中的类) hook 到 Gradle 的日志系统。只需使用一个 SLF4J logger,你可以像使用构建脚本中提供的 logger 一样使用它。
You can also hook into Gradle's logging system from within other classes used in the build (classes from the buildSrc directory for example). Simply use an SLF4J logger. You can use this logger the same way as you use the provided logger in the build script.

示例 18.3. 使用 SLF4J 写入日志消息 - Example 18.3. Using SLF4J to write log messages

build.gradle

import org.slf4j.Logger
import org.slf4j.LoggerFactory

Logger slf4jLogger = LoggerFactory.getLogger('some-logger')
slf4jLogger.info('An info log message logged using SLF4j')

18.3. 使用外部的工具和类库来写日志

18.3. Logging from external tools and libraries

Gradle 内部使用 Ant 和 Ivy,它们都有自己的日志系统。Gradle 将它们的日志输出重定向到 Gradle 的日志记录系统上。从 Ant 或 Ivy 的日志级别到 Gradle 的日志级别,除了Ant 或 Ivy 的 TRACE 是被映射到 Gradle 的 DEBUG日志级别之外,其他的都是一对一的映射。这意味着默认的 Gradle 日志级别不会显示任何 Ant 或 Ivy 输出,除非是错误或警告信息。
Internally, Gradle uses Ant and Ivy. Both have their own logging system. Gradle redirects their logging output into the Gradle logging system. There is a 1:1 mapping from the Ant/Ivy log levels to the Gradle log levels, except the Ant/Ivy TRACE log level, which is mapped to Gradle DEBUG log level. This means the default Gradle log level will not show any Ant/Ivy output unless it is an error or a warning.

有许多工具仍然是使用标准输出来记录日志。默认情况下,Gradle 把标准输出重定向到 QUIET 日志级别,把标准错误则重定向到 ERROR 级别上。这种行为是可配置的。Project 对象提供了一个 LoggingManager,它能在评估构建脚本时,让你修改标准输出或错误所重定向的日志级别。
There are many tools out there which still use standard output for logging. By default, Gradle redirects standard output to the QUIET log level and standard error to the ERROR level. This behavior is configurable. The project object provides a LoggingManager, which allows you to change the log levels that standard out or error are redirected to when your build script is evaluated.

示例 18.4. 配置标准输出捕获 - Example 18.4. Configuring standard output capture

build.gradle

logging.captureStandardOutput LogLevel.INFO
println 'A message which is logged at INFO level'

为能在任务执行过程中更改标准输出或错误的日志级别,任务也提供了一个 LoggingManager
To change the log level for standard out or error during task execution, tasks also provide a LoggingManager.

示例 18.5. 为任务配置标准输出捕获 - Example 18.5. Configuring standard output capture for a task

build.gradle

task logInfo {
    logging.captureStandardOutput LogLevel.INFO
    doFirst {
        println 'A task message which is logged at INFO level'
    }
}

Gradle 还提供了对 Java Util Logging,Jakarta Commons Logging 和 Log4j 的日志工具的集成。构建中的类使用这些日志记录工具所输出的所有日志消息,都将被重定向到 Gradle 的日志系统上。
Gradle also provides integration with the Java Util Logging, Jakarta Commons Logging and Log4j logging toolkits. Any log messages which your build classes write using these logging toolkits will be redirected to Gradle's logging system.

18.4. 更改 Gradle 日志

18.4. Changing what Gradle logs

你可以用自己的 logging UI 替换大部分的 Gradle logging UI。你可能会这样,比如,如果你想以某种方式自定义 UI —— 来记录更多或更少的信息,或修改日志格式。你可以通过 Gradle.useLogger() 方法来替换这个 logging。它可以从构建脚本、init 脚本,或者是通过嵌入 API 访问。请注意,它完全禁用 Gradle的默认输出。下面是在 init 脚本中修改如何打印任务执行和构建完成的日志的示例。
You can replace much of Gradle's logging UI with your own. You might do this, for example, if you want to customize the UI in some way - to log more or less information, or to change the formatting. You replace the logging using the Gradle.useLogger() method. This is accessible from a build script, or an init script, or via the embedding API. Note that this completely disables Gradle's default output. Below is an example init script which changes how task execution and build completion is logged.

示例 18.6 的例子. 自定义 Gradle 日志 - Example 18.6. Customizing what Gradle logs

init.gradle

useLogger(new CustomEventLogger())

class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {

    public void beforeExecute(Task task) {
        println "[$task.name]"
    }

    public void afterExecute(Task task, TaskState state) {
        println()
    }
    
    public void buildFinished(BuildResult result) {
        println 'build completed'
        if (result.failure != null) {
            result.failure.printStackTrace()
        }
    }
}

gradle -I init.gradle build 的输出结果
Output of gradle -I init.gradle build

> gradle -I init.gradle build
[compile]
compiling source

[testCompile]
compiling test source

[test]
running unit tests

[build]

build completed

你的 logger 可以实现下面列出的任何监听器接口。当你注册一个 logger 时,只会替换其实现的接口的日志记录,其他接口的日志记录则保持不变。于侦听器的接口的详细信息,可以参阅《第 55.6 节,“在构建脚本中响应生命周期”》。
Your logger can implement any of the listener interfaces listed below. When you register a logger, only the logging for the interfaces that it implements is replaced. Logging for the other interfaces is left untouched. You can find out more about the listener interfaces in Section 55.6, “Responding to the lifecycle in the build script”.