記錄是建置工具的主要「使用者介面」。如果它太過詳細,真正的警告和問題很容易因此被隱藏起來。另一方面,您需要相關資訊才能找出事情是否出錯。Gradle 定義了 6 個記錄層級,如 記錄層級 中所示。除了您通常可能會看到的層級之外,還有兩個 Gradle 特定的記錄層級。這些層級是 QUIET 和 LIFECYCLE。後者是預設值,用於報告建置進度。
記錄層級
ERROR |
錯誤訊息 |
QUIET |
重要資訊訊息 |
WARNING |
警告訊息 |
LIFECYCLE |
進度資訊訊息 |
INFO |
資訊訊息 |
DEBUG |
偵錯訊息 |
主控台的豐富元件(建置狀態和進行中區域)會顯示,而不管使用的記錄層級為何。在 Gradle 4.0 之前,這些豐富元件只會在記錄層級 LIFECYCLE 或以下顯示。
|
選擇記錄層級
您可以使用 記錄層級命令列選項 中顯示的命令列開關選擇不同的記錄層級。您也可以使用 gradle.properties
設定記錄層級,請參閱 Gradle 屬性。在 堆疊追蹤命令列選項 中,您可以找到影響堆疊追蹤記錄的命令列開關。
選項 | 輸出記錄層級 |
---|---|
|
QUIET 及以上 |
|
WARN 及以上 |
無記錄選項 |
LIFECYCLE 及以上 |
|
INFO 及以上 |
|
DEBUG 及以上(也就是所有記錄訊息) |
DEBUG 記錄層級可能會 將安全性敏感資訊公開到主控台。
|
堆疊追蹤命令列選項
-s
或--stacktrace
-
會印出截斷的堆疊追蹤。我們建議使用此選項,而非完整的堆疊追蹤。Groovy 完整的堆疊追蹤非常冗長(由於底層的動態呼叫機制。但它們通常不包含與 您的 程式碼中錯誤相關的資訊)。此選項會為不建議使用的警告呈現堆疊追蹤。
-S
或--full-stacktrace
-
會印出完整的堆疊追蹤。此選項會為不建議使用的警告呈現堆疊追蹤。
- <無堆疊追蹤選項>
-
在建置錯誤(例如編譯錯誤)的情況下,不會將堆疊追蹤印到主控台。只有在內部例外情況下,才會印出堆疊追蹤。如果選擇
DEBUG
記錄層級,則會永遠印出截斷的堆疊追蹤。
記錄敏感資訊
使用 DEBUG
記錄層級執行 Gradle 會將安全性敏感資訊公開到主控台和建置記錄。
此資訊可能包括但不限於
-
環境變數
-
私人儲存庫認證
-
建置快取和 Develocity 認證
-
外掛入口網站 發布認證
執行於公用的持續整合服務時,不應使用 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 也會提供一個 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
目錄的類別)掛接到 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/Ivy TRACE
記錄等級會對應至 Gradle DEBUG
記錄等級。這表示預設的 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) 方法取代記錄。這可以從組建指令碼、init 指令碼或透過嵌入式 API 存取。請注意,這會完全停用 Gradle 的預設輸出。以下是變更任務執行和組建完成記錄方式的範例 init 指令碼。
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
你的記錄器可以實作以下列出的任何監聽器介面。當你註冊記錄器時,只會取代它實作的介面的記錄。其他介面的記錄不會受到影響。你可以在 組建生命週期事件 中找到更多關於監聽器介面的資訊。