日誌是建置工具的主要「UI」。如果它變得過於冗長,重要的警告和問題可能會被掩蓋。然而,必須要有相關資訊來判斷是否發生錯誤。
Gradle 定義了六個日誌層級,詳情請參閱日誌層級。除了標準日誌層級外,Gradle 還引入了兩個特定層級:QUIET 和 LIFECYCLE。LIFECYCLE 是用於報告建置進度的預設層級。
了解日誌層級
Gradle 中有 6 個日誌層級
ERROR |
錯誤訊息 |
QUIET |
重要資訊訊息 |
WARNING |
警告訊息 |
LIFECYCLE |
進度資訊訊息 |
INFO |
資訊訊息 |
DEBUG |
偵錯訊息 |
主控台的豐富元件(建置狀態和工作中區域)無論使用的日誌層級為何都會顯示。 |
選擇日誌層級
您可以從日誌層級命令列選項中顯示的命令列切換器中選擇不同的日誌層級。
您也可以使用gradle.properties
配置日誌層級。
在堆疊追蹤命令列選項中,您可以找到影響堆疊追蹤記錄的命令列切換器。
日誌層級命令列選項
選項 | 輸出日誌層級 |
---|---|
|
QUIET 及更高 |
|
WARN 及更高 |
無記錄選項 |
LIFECYCLE 及更高 |
|
INFO 及更高 |
|
DEBUG 及更高(即,所有日誌訊息) |
DEBUG 日誌層級可能會將敏感性安全資訊暴露於主控台。 |
堆疊追蹤命令列選項
-s
或--stacktrace
-
列印截斷的堆疊追蹤。我們建議使用此選項而非完整堆疊追蹤。由於底層的動態調用機制,Groovy 完整堆疊追蹤非常冗長。然而,它們通常不包含關於您的程式碼中發生錯誤的相關資訊。此選項會呈現棄用警告的堆疊追蹤。
-S
或--full-stacktrace
-
列印完整的堆疊追蹤。此選項會呈現棄用警告的堆疊追蹤。
- <無堆疊追蹤選項>
-
如果發生建置錯誤(例如,編譯錯誤),則不會將堆疊追蹤列印到主控台。僅在發生內部異常時才會列印堆疊追蹤。如果選擇了
DEBUG
日誌層級,則始終會列印截斷的堆疊追蹤。
記錄敏感資訊
以 DEBUG
日誌層級執行 Gradle 可能會將敏感資訊暴露於主控台和建置日誌。
此資訊可能包括
-
環境變數
-
私有儲存庫憑證
-
建置快取和 Develocity 憑證
-
外掛入口網站發布憑證
在公共持續整合 (CI) 服務上執行時,務必避免使用 DEBUG
日誌層級。這些服務上的建置日誌可公開存取,並且可能會暴露敏感資訊。即使在私有 CI 服務上,記錄敏感憑證也可能會帶來風險,具體取決於您組織的威脅模型。建議與您組織的安全團隊討論此問題。
某些 CI 提供者嘗試從日誌中編輯敏感憑證,但此過程並非萬無一失,通常只會編輯預先配置的密碼的精確匹配項。
如果您懷疑 Gradle 外掛可能會不慎暴露敏感資訊,請聯絡我們的安全團隊以尋求揭露方面的協助。
撰寫您自己的日誌訊息
在您的建置檔案中記錄的簡單選項是將訊息寫入標準輸出。Gradle 會將寫入標準輸出的任何內容重新導向到其記錄系統,日誌層級為 QUIET
println("A message which is logged at QUIET level")
println 'A message which is logged at QUIET level'
Gradle 還為建置腳本提供 logger
屬性,它是Logger的實例。此介面擴展了 SLF4J Logger
介面,並新增了一些 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.") // 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
目錄的類別)使用 SLF4J logger 連接到 Gradle 的記錄系統。您可以使用此 logger,就像在建置腳本中使用提供的 logger 一樣。
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/Ivy TRACE
日誌層級,它會對應到 Gradle DEBUG
日誌層級。這表示預設的 Gradle 日誌層級不會顯示任何 Ant/Ivy 輸出,除非它是錯誤或警告。
許多工具仍然使用標準輸出來進行記錄。預設情況下,Gradle 會將標準輸出重新導向到 QUIET
日誌層級,並將標準錯誤重新導向到 ERROR
層級。此行為是可配置的。
project
物件提供一個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
您的 logger 可以實作以下列出的任何接聽器介面。當您註冊 logger 時,只會替換它實作的介面的記錄。其他介面的記錄保持不變。您可以在建置生命週期事件中找到有關接聽器介面的更多資訊。