Tomcat 的內(nèi)部日志使用 JULI 組件,這是一個(gè) Apache Commons 日志的重命名的打包分支,默認(rèn)被硬編碼,使用 java.util.logging 架構(gòu)。這能保證 Tomcat 內(nèi)部日志與 Web 應(yīng)用的日志保持獨(dú)立,即使 Web 應(yīng)用使用的是 Apache Commons Logging。
假如想用另外的日志框架來(lái)替換 Tomcat 的內(nèi)部日志系統(tǒng),那么就必須采用一種能夠保持完整的 Commons 日志機(jī)制的 JULI 實(shí)現(xiàn),用它來(lái)替換通過(guò)硬編碼使用 java.util.logging 的 JULI 實(shí)現(xiàn)。通常這種替代實(shí)現(xiàn)都是以額外組件的形式出現(xiàn)的。利用 Log4j 框架用于 Tomcat 內(nèi)部日志的配置如下文所示。
在 Apache Tomcat 上運(yùn)行的 Web 應(yīng)用可以使用:
各個(gè)應(yīng)用可以使用不同的日志框架,詳情參見(jiàn)類(lèi)加載器。java.util.logging 則是例外。如果日志庫(kù)直接或間接地用到了這一 API,那么 Web 應(yīng)用就能共享使用它的元素,因?yàn)樵?API 是由系統(tǒng)類(lèi)加載器所加載的。
Apache Tomcat 本身已經(jīng)實(shí)現(xiàn)了 java.util.logging API 的幾個(gè)關(guān)鍵元素。這種實(shí)現(xiàn)就是 JULI。其中的關(guān)鍵組件是一個(gè)自定義的 LogManager 實(shí)現(xiàn),它能分辨運(yùn)行在 Tomcat 上的不同 Web 應(yīng)用(以及它們所用的不同的類(lèi)加載器),還能針對(duì)每一應(yīng)用進(jìn)行私有的日志配置。另外,當(dāng) Web 應(yīng)用沒(méi)能從內(nèi)存中加載時(shí),Tomcat 會(huì)給予它相應(yīng)通知,從而清除相應(yīng)的引用類(lèi),防止內(nèi)存泄露。
在啟動(dòng) Java 時(shí),通過(guò)提供特定的系統(tǒng)屬性,可以啟用 java.util.logging 實(shí)現(xiàn)。Apache Tomcat 啟動(dòng)腳本可以實(shí)現(xiàn)這個(gè)操作,但如果使用不同工具來(lái)運(yùn)行 Tomcat(比如 jsvc,或者從某個(gè) IDE 中運(yùn)行 Tomcat),就必須自己來(lái)啟用實(shí)現(xiàn)。
關(guān)于 java.util.logging 實(shí)現(xiàn)的詳細(xì)情況可以查閱 JDK 文檔,具體位于 java.util.logging 包的相關(guān) javadoc 頁(yè)面中。
關(guān)于 Tomcat JULI 的詳細(xì)介紹見(jiàn)下文。
Tomcat 內(nèi)部日志能夠處理對(duì) javax.servlet.ServletContext.log(...) 的調(diào)用,從而寫(xiě)入日志消息。這種消息都被記錄到一種特定類(lèi)別中,命名方式如下:
org.apache.catalina.core.ContainerBase.[${engine}].[${host}].[${context}]
這種日志是依照 Tomcat 日志配置而執(zhí)行的,無(wú)法在 Web 應(yīng)用中重寫(xiě)。
Servlets logging API 的問(wèn)世要先于 Java 所提供的 java.util.logging API,所以,它無(wú)法提供太多的選項(xiàng),比如無(wú)法用它來(lái)控制日志級(jí)別。然而需要注意的是,在 Tomcat 實(shí)現(xiàn)中, 對(duì) ServletContext.log(String) 和 GenericServlet.log(String) 的調(diào)用都被記錄在 INFO 級(jí)別。對(duì) ServletContext.log(String, Throwable) 或 GenericServlet.log(String, Throwable) 的調(diào)用都被記錄在 ERROR 級(jí)別。
在 UNIX 系統(tǒng)下運(yùn)行 Tomcat 時(shí),控制臺(tái)輸出經(jīng)常會(huì)重定向到 catalina.out 的文件中。通過(guò)一個(gè)環(huán)境變量,可以配置該文件(參見(jiàn)啟動(dòng)腳本)。
寫(xiě)入 System.err/out 的任何內(nèi)容都會(huì)被 catalina.out 文件所捕獲。這些內(nèi)容可能包括:
在 Windows 上以服務(wù)形式運(yùn)行時(shí),控制臺(tái)輸出也會(huì)被捕獲及重定向,但文件名有所不同。
Tomcat 默認(rèn)的日志配置會(huì)將同樣的消息寫(xiě)入控制臺(tái)和一個(gè)日志文件中。這一特點(diǎn)非常有利于使用 Tomcat 進(jìn)行開(kāi)發(fā),但往往并不適用于生產(chǎn)環(huán)境。
老的應(yīng)用可能還在使用 System.out 或 System.err,可以通過(guò)在 Context 元素上設(shè)置 swallowOutput 屬性來(lái)調(diào)整。如該屬性設(shè)為 true,那么在請(qǐng)求階段對(duì) System.out/err 的調(diào)用就會(huì)被攔截,它們的輸出也會(huì)通過(guò) javax.servlet.ServletContext.log(...) 調(diào)用反饋給日志系統(tǒng)。
注意:swallowOutput 雖然是一個(gè)小技巧,但還是有局限性的:它需要直接調(diào)用 System.out/err,并且要在請(qǐng)求處理周期內(nèi)完成。而且,它可能還并不適用于應(yīng)用所創(chuàng)建的其他線程。不能將其用于攔截本身寫(xiě)入系統(tǒng)流的日志框架(它們可能早先已經(jīng)啟動(dòng),并且在重定向發(fā)生前就已經(jīng)獲取了對(duì)流的直接引用)。
Access 日志功能相近,但還是有所不同。它是一個(gè) Valve,使用自包含的邏輯來(lái)編寫(xiě)日志文件。訪問(wèn)日志的基本需求是以較低開(kāi)銷(xiāo)處理大型連續(xù)數(shù)據(jù)流,所以只能使用 Commomns Logging 來(lái)處理自身的調(diào)試消息。這種實(shí)現(xiàn)方法避免了額外的開(kāi)銷(xiāo),并且可能具有較復(fù)雜的配置。請(qǐng)參考 Valves 文檔了解更多配置詳情,其中包含了各種報(bào)告格式。