日志是构建工具的主要“UI”。如果太冗长,真正的警告和问题很容易被隐藏。另一方面,您需要相关信息来确定是否出了问题。 Gradle定义了6个日志级别,如日志级别所示。除了您通常看到的日志级别之外,还有两个特定于 Gradle 的日志级别。这些级别是QUIET和LIFECYCLE。后者是默认值,用于报告构建进度。
日志级别
错误 |
错误信息 |
安静的 |
重要信息消息 |
警告 |
警告信息 |
生命周期 |
进度信息消息 |
信息 |
信息消息 |
调试 |
调试消息 |
无论使用何种日志级别,都会显示控制台的丰富组件(构建状态和正在进行的工作区域)。在 Gradle 4.0 之前,这些丰富的组件仅在日志级别LIFECYCLE 或以下级别
显示。 |
选择日志级别
您可以使用日志级别命令行选项中显示的命令行开关来选择不同的日志级别。您还可以使用配置日志级别gradle.properties
,请参阅Gradle 属性。在Stacktrace 命令行选项中,您可以找到影响堆栈跟踪日志记录的命令行开关。
选项 | 输出日志级别 |
---|---|
|
安静且更高 |
|
警告及更高级别 |
没有日志记录选项 |
生命周期及更高 |
|
信息及更高级别 |
|
DEBUG 及更高版本(即所有日志消息) |
日志DEBUG 级别可以向控制台公开安全敏感信息。
|
堆栈跟踪命令行选项
-s
或者--stacktrace
-
打印截断的堆栈跟踪。我们建议在完整的堆栈跟踪中使用此方法。 Groovy 完整堆栈跟踪非常冗长(由于底层的动态调用机制。但它们通常不包含代码中出现问题的相关信息。)此选项呈现堆栈跟踪以显示弃用警告。
-S
或者--full-stacktrace
-
打印出完整的堆栈跟踪。此选项呈现堆栈跟踪以获取弃用警告。
- <无堆栈跟踪选项>
-
如果出现构建错误(例如编译错误),则不会将堆栈跟踪打印到控制台。仅在出现内部异常的情况下才会打印堆栈跟踪。如果
DEBUG
选择了日志级别,则始终打印截断的堆栈跟踪。
记录敏感信息
使用DEBUG
日志级别运行 Gradle 可能会将安全敏感信息暴露给控制台和构建日志。
这些信息可以包括但不限于:
-
环境变量
-
私有存储库凭据
-
构建缓存和开发凭证
-
插件 Portal发布凭证
在公共持续集成服务上运行时不应使用DEBUG 日志级别。公共持续集成服务的构建日志是全世界可见的,并且可能会暴露这些敏感信息。根据您组织的威胁模型,在私有 CI 中记录敏感凭据也可能是一个漏洞。请与您组织的安全团队讨论此问题。
一些 CI 提供商尝试从日志中清除敏感凭据;然而,这并不完美,通常只会删除预先配置的秘密的精确匹配。
如果您认为 Gradle 插件可能会泄露敏感信息,请联系security@gradle.com寻求披露帮助。
编写您自己的日志消息
登录构建文件的一个简单选项是将消息写入标准输出。 Gradle 将任何写入标准输出的内容重定向到其QUIET
日志级别的日志系统。
println("A message which is logged at QUIET level")
println 'A message which is logged at QUIET level'
Gradle 还为构建脚本提供了一个属性,它是Loggerlogger
的实例。该接口扩展了 SLF4J接口,并向其中添加了一些 Gradle 特定方法。下面是如何在构建脚本中使用它的示例:Logger
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.") // Gradle never logs TRACE level logs
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.') // Gradle never logs TRACE level logs
使用典型的 SLF4J 模式将占位符替换为实际值作为日志消息的一部分。
logger.info("A {} log message", "info")
logger.info('A {} log message', 'info')
您还可以从构建中使用的其他类(buildSrc
例如目录中的类)连接到 Gradle 的日志系统。只需使用 SLF4J 记录器即可。您可以像在构建脚本中使用提供的记录器一样使用此记录器。
import org.slf4j.LoggerFactory
val slf4jLogger = LoggerFactory.getLogger("some-logger")
slf4jLogger.info("An info log message logged using SLF4j")
import org.slf4j.LoggerFactory
def slf4jLogger = LoggerFactory.getLogger('some-logger')
slf4jLogger.info('An info log message logged using SLF4j')
从外部工具和库记录
在内部,Gradle 使用 Ant 和 Ivy。两者都有自己的日志系统。 Gradle 将其日志输出重定向到 Gradle 日志系统。 Ant/Ivy 日志级别与 Gradle 日志级别之间存在 1:1 映射,但 Ant/IvyTRACE
日志级别除外,它映射到 GradleDEBUG
日志级别。这意味着默认的 Gradle 日志级别不会显示任何 Ant/Ivy 输出,除非它是错误或警告。
有许多工具仍然使用标准输出进行日志记录。默认情况下,Gradle 将标准输出重定向到QUIET
日志级别,将标准错误重定向到该ERROR
级别。此行为是可配置的。项目对象提供了一个LoggingManager,它允许您更改在评估构建脚本时标准输出或错误重定向到的日志级别。
logging.captureStandardOutput(LogLevel.INFO)
println("A message which is logged at INFO level")
logging.captureStandardOutput LogLevel.INFO
println 'A message which is logged at INFO level'
要更改任务执行期间标准输出或错误的日志级别,任务还提供LoggingManager。
tasks.register("logInfo") {
logging.captureStandardOutput(LogLevel.INFO)
doFirst {
println("A task message which is logged at INFO level")
}
}
tasks.register('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 记录的内容
您可以用自己的 UI 替换 Gradle 的大部分日志记录 UI。例如,如果您想以某种方式自定义 UI - 记录更多或更少的信息,或者更改格式,您可以这样做。您可以使用Gradle.useLogger(java.lang.Object)方法替换日志记录。这可以从构建脚本、初始化脚本或通过嵌入 API 访问。请注意,这会完全禁用 Gradle 的默认输出。下面是一个示例初始化脚本,它更改了任务执行和构建完成的记录方式。
useLogger(CustomEventLogger())
@Suppress("deprecation")
class CustomEventLogger() : BuildAdapter(), TaskExecutionListener {
override fun beforeExecute(task: Task) {
println("[${task.name}]")
}
override fun afterExecute(task: Task, state: TaskState) {
println()
}
override fun buildFinished(result: BuildResult) {
println("build completed")
if (result.failure != null) {
(result.failure as Throwable).printStackTrace()
}
}
}
useLogger(new CustomEventLogger())
@SuppressWarnings("deprecation")
class CustomEventLogger extends BuildAdapter implements TaskExecutionListener {
void beforeExecute(Task task) {
println "[$task.name]"
}
void afterExecute(Task task, TaskState state) {
println()
}
void buildFinished(BuildResult result) {
println 'build completed'
if (result.failure != null) {
result.failure.printStackTrace()
}
}
}
$ gradle -I customLogger.init.gradle.kts build > Task :compile [compile] compiling source > Task :testCompile [testCompile] compiling test source > Task :test [test] running unit tests > Task :build [build] build completed 3 actionable tasks: 3 executed
$ gradle -I customLogger.init.gradle build > Task :compile [compile] compiling source > Task :testCompile [testCompile] compiling test source > Task :test [test] running unit tests > Task :build [build] build completed 3 actionable tasks: 3 executed
您的记录器可以实现下面列出的任何侦听器接口。当您注册记录器时,仅替换其实现的接口的日志记录。其他接口的日志记录保持不变。您可以在构建生命周期事件中找到有关侦听器接口的更多信息。