當執行命令時,預設情況下,會將比螢幕上更詳細的日誌輸出傳送到檔案。 可以透過執行 last
來回顧剛執行的命令的輸出。
例如,當來源是最新的時,run
的輸出為
> run
[info] Running A
Hi!
[success] Total time: 0 s, completed Feb 25, 2012 1:00:00 PM
可以透過執行 last
來回顧此執行的詳細資訊
> last
[debug] Running task... Cancelable: false, max worker threads: 4, check cycles: false
[debug]
[debug] Initial source changes:
[debug] removed:Set()
[debug] added: Set()
[debug] modified: Set()
[debug] Removed products: Set()
[debug] Modified external sources: Set()
[debug] Modified binary dependencies: Set()
[debug] Initial directly invalidated sources: Set()
[debug]
[debug] Sources indirectly invalidated by:
[debug] product: Set()
[debug] binary dep: Set()
[debug] external source: Set()
[debug] Initially invalidated: Set()
[debug] Copy resource mappings:
[debug]
[info] Running A
[debug] Starting sandboxed run...
[debug] Waiting for threads to exit or System.exit to be called.
[debug] Classpath:
[debug] /tmp/e/target/scala-2.9.2/classes
[debug] /tmp/e/.sbt/0.12.0/boot/scala-2.9.2/lib/scala-library.jar
[debug] Waiting for thread runMain to exit
[debug] Thread runMain exited.
[debug] Interrupting remaining threads (should be all daemons).
[debug] Sandboxed run complete..
[debug] Exited with code 0
[success] Total time: 0 s, completed Jan 1, 2012 1:00:00 PM
控制台和備份檔案的日誌記錄層級的組態將在以下章節中說明。
當執行任務時,預設情況下,會將比螢幕上更詳細的日誌輸出傳送到檔案。 可以透過執行 last <task>
來回顧特定任務的輸出。 例如,第一次執行 compile
時,輸出可能如下所示
> compile
[info] Updating {file:/.../demo/}example...
[info] Resolving org.scala-lang#scala-library;2.9.2 ...
[info] Done updating.
[info] Compiling 1 Scala source to .../demo/target/scala-2.9.2/classes...
[success] Total time: 0 s, completed Jun 1, 2012 1:11:11 PM
輸出表示已執行相依性解析和編譯。 可以分別回顧這些的詳細輸出。 例如,
> last compile
[debug]
[debug] Initial source changes:
[debug] removed:Set()
[debug] added: Set(/home/mark/tmp/a/b/A.scala)
[debug] modified: Set()
...
和
> last update
[info] Updating {file:/.../demo/}example...
[debug] post 1.3 ivy file: using exact as default matcher
[debug] :: resolving dependencies :: example#example_2.9.2;0.1-SNAPSHOT
[debug] confs: [compile, runtime, test, provided, optional, compile-internal, runtime-internal, test-internal, plugin, sources, docs, pom]
[debug] validate = true
[debug] refresh = false
[debug] resolving dependencies for configuration 'compile'
...
預設情況下,Scala 編譯器不會列印警告的完整詳細資訊。 編譯使用 Predef 中已棄用的 error
方法的程式碼可能會產生以下輸出
> compile
[info] Compiling 1 Scala source to <...>/classes...
[warn] there were 1 deprecation warnings; re-run with -deprecation for details
[warn] one warning found
未提供詳細資訊,因此有必要將 -deprecation
新增至傳遞至編譯器 (scalacOptions
) 的選項並重新編譯。 使用 Scala 2.10 及更高版本時的另一種方法是執行 printWarnings
。 此任務將顯示先前編譯的所有警告。 例如,
> printWarnings
[warn] A.scala:2: method error in object Predef is deprecated: Use sys.error(message) instead
[warn] def x = error("Failed.")
[warn] ^
變更日誌記錄層級的最快方法是使用 error
、warn
、info
或 debug
命令。 這些會設定命令和任務的預設日誌記錄層級。 例如,
> warn
預設情況下,只會顯示警告和錯誤。 若要在啟動時執行任何命令之前設定日誌記錄層級,請在日誌記錄層級之前使用 --
。 例如,
$ sbt --warn
> compile
[warn] there were 2 feature warning(s); re-run with -feature for details
[warn] one warning found
[success] Total time: 4 s, completed ...
>
可以更精細地覆寫日誌記錄層級,這將在下文說明。
日誌記錄量由 logLevel
設定控制,該設定的值取自 Level
列舉。 有效值為 Error
、Warn
、Info
和 Debug
,依詳細程度遞增排序。 日誌記錄層級可以全域設定(如上一節所述),也可以套用至特定專案、組態或任務。 例如,若要將編譯的日誌記錄層級變更為僅顯示警告和錯誤
> set Compile / compile / logLevel := Level.Warn
若要為目前專案中的所有任務啟用偵錯日誌記錄,
> set logLevel := Level.Warn
常見的情況是,在執行任務後,您注意到您需要比預設顯示的更多資訊。 基於 logLevel
的解決方案通常需要變更日誌記錄層級並再次執行任務。 不過,在兩種情況下,這是沒有必要的。 首先,可以使用 printWarnings
顯示主要來源的先前編譯中的警告,或者使用 Test/printWarnings
顯示測試來源的先前編譯中的警告。 其次,先前執行的輸出可供單個任務或整體使用。 請參閱printWarnings 和 先前輸出的章節。
預設情況下,sbt 會隱藏執行期間擲回的大多數例外狀況的堆疊追蹤。 它會列印一則訊息,指出如何顯示例外狀況。 不過,您可能預設想要顯示更多堆疊追蹤。
要設定的設定是 traceLevel
,它是具有 Int 值的設定。 當 traceLevel
設定為負值時,不會顯示堆疊追蹤。 當它為零時,會顯示堆疊追蹤,直到第一個 sbt 堆疊框架。 當為正數時,堆疊追蹤會顯示到多個堆疊框架。
例如,以下設定 sbt 為顯示堆疊追蹤,直到第一個 sbt 框架
> set every traceLevel := 0
every
部分表示要覆寫所有作用域中的設定。 若要變更單個專案、組態或任務的追蹤列印行為,請適當地限定 traceLevel
的範圍
> set Test / traceLevel := 5
> set update / traceLevel := 0
> set ThisProject / traceLevel := -1
預設情況下,sbt 會緩衝測試的日誌輸出,直到整個類別完成。 這樣可以避免在平行執行時混合輸出。 若要停用緩衝,請將 logBuffered
設定設為 false
logBuffered := false
可以使用設定 extraLoggers
新增自訂記錄器。 在內部,sbt 使用 log4j2 函式庫,因此自訂記錄器應該實作 org.apache.logging.log4j.core.Appender
,通常是透過擴充 AbstractAppender
。
extraLoggers
是一個函式 ScopedKey[_] => Seq[Appender]
。 這表示它可以根據要求記錄器的任務來提供不同的日誌記錄。
extraLoggers := {
val currentFunction = extraLoggers.value
(key: ScopedKey[_]) => {
myCustomLogger(key) +: currentFunction(key)
}
}
在此,我們採用設定的目前函式 currentFunction
並提供一個新的函式。 新函式會將我們的自訂記錄器添加到舊函式提供的記錄器前面。
log4j2 中的 Appender
會附加一個 LogEvent
,其核心在內部是一個 Message
。 可以有多種類型的 Message,但 sbt 會產生包含 ObjectMessage
執行個體的事件,其中包含可以透過呼叫 getParameter()
擷取的酬載。
sbt 日誌記錄發出的酬載是 StringEvent
的執行個體,其中包含 String
欄位,包括 message
和 level
。
將所有這些放在一起,以下是一個(完全無用的!)額外記錄器的範例,該記錄器會以相反的順序將任務中的訊息記錄到控制台
extraLoggers := {
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.message.{Message,ObjectMessage}
import sbt.internal.util.StringEvent
def loggerNameForKey( key : sbt.Def.ScopedKey[_] ) = s"""reverse.${key.scope.task.toOption.getOrElse("<unknown>")}"""
class ReverseConsoleAppender( key : ScopedKey[_] ) extends AbstractAppender (
loggerNameForKey( key ), // name : String
null, // filter : org.apache.logging.log4j.core.Filter
null, // layout : org.apache.logging.log4j.core.Layout[ _ <: Serializable]
false // ignoreExceptions : Boolean
) {
this.start() // the log4j2 Appender must be started, or it will fail with an Exception
override def append( event : LogEvent ) : Unit = {
val output = {
def forUnexpected( message : Message ) = s"[${this.getName()}] Unexpected: ${message.getFormattedMessage()}"
event.getMessage() match {
case om : ObjectMessage => { // what we expect
om.getParameter() match {
case se : StringEvent => s"[${this.getName()} - ${se.level}] ${se.message.reverse}"
case other => forUnexpected( om )
}
}
case unexpected : Message => forUnexpected( unexpected )
}
}
System.out.synchronized { // sbt adopts a convention of acquiring System.out's monitor printing to the console
println( output )
}
}
}
val currentFunction = extraLoggers.value
(key: ScopedKey[_]) => {
new ReverseConsoleAppender(key) +: currentFunction(key)
}
}
現在,如果我們執行一個記錄訊息的任務,我們應該會看到我們的記錄器被叫用
sbt:sbt-logging-example> update
[info] Updating ...
[reverse.update - info] ... gnitadpU
[info] Done updating.
[reverse.update - info] .gnitadpu enoD
[success] Total time: 0 s, completed Oct 16, 2019 5:22:22 AM
特殊任務 streams
透過 Streams 執行個體提供每個任務的日誌記錄和 I/O。 若要記錄,任務會使用 streams
任務中的 log
成員。 呼叫 log
會提供 Logger
import sbt.Keys.streams
myTask := {
val log = streams.value.log
log.warn("A warning.")
}
由於設定無法參考任務,因此無法使用特殊任務 streams
在設定初始化期間提供日誌記錄。 建議的方式是使用 sLog
。 呼叫 sLog.value
會提供 Logger。
mySetting := {
val log = sLog.value
log.warn("A warning.")
}