以下內容翻譯整理自logback官方手冊,地址:logback官方手冊
logback 配置
將日志請求插入應用程序代碼需要相當多的計劃和工作。觀察表明,大約有4%的代碼用于日志記錄。因此,即使是一個中等大小的應用程序,其代碼中也會包含數千條日志語句。考慮到它們的數量,我們需要工具來管理這些日志語句。
可以通過編程方式配置logback,也可以使用XML或Groovy格式表示的配置腳本配置logback。順便說一下,通過使用我們的 PropertiesTranslator web應用程序,現有的log4j用戶可以將他們的log4j.properties文件轉換為logback.xml。
logback的初始化配置步驟:
Logback嘗試在類路徑中找到一個名為logback -test.xml的文件。
如果沒有找到這樣的文件,logback將嘗試在類路徑中找到一個名為logback.groovy的文件。
如果沒有找到這樣的文件,它將檢查類路徑中的logback.xml文件。
如果沒有找到這樣的文件, service-provider loading facility
(在 JDK 1.6 引入)通過在類路徑中查找META-INF\services\ch. qs .logback.classic.spi.Configurator文件,用于解析com. qs .logback.classic.spi.Configurator接口的實現。它的內容應該指定所需配置程序的完全限定類名。
如果以上方法都不成功,logback將使用BasicConfigurator自動配置自己,這將導致日志輸出定向到控制臺。
最后一步是在沒有配置文件的情況下提供默認(但非常基本)日志功能。
如果您正在使用Maven,并且將logback-test.xml放在src/test/resources文件夾下,Maven將確保它不會包含在生成的包中。因此,您可以使用不同的配置文件,在測試期間使用logback-test. xml,在生產中使用另一個文件logback.xml。
自動配置logback
配置logback的最簡單方法是讓logback使用其默認配置。
使用 BasicConfigurator 的簡單示例
package com.wangbo.cto.logback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:54
* @auther wangbo
*/
public class MyApp1 {
final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
public static void main(String[] args) {
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
該類定義一個靜態日志記錄器變量。然后實例化一個Foo對象。Foo類如下所示:
package com.wangbo.cto.logback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:53
* @auther wangbo
*/
public class Foo {
static final Logger logger = LoggerFactory.getLogger(Foo.class);
public void doIt(){
logger.debug("Did it again!");
}
}
假設不存在 logback-test.xml或logback.xml配置文件,logback將默認調用BasicConfigurator設置一個最小配置。這個最小配置由一個附加到根logger的ConsoleAppender組成,使用PatternLayoutEncoder格式化輸出。
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
此外,在默認情況下,根logger被指定為DEBUG級別。
因此,上面程序運行結果顯示如下:
13:56:07.357 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
13:56:07.359 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
13:56:07.359 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
MyApp1 應用程序通過調用org.slf4j.LoggerFactory和org.slf4j.Logger鏈接到logback。獲取到它希望使用的日志記錄器,然后繼續。注意,Foo類對logback的唯一依賴關系是通過org.slf4j.LoggerFactory和org.slf4j.Logger的導入。除了配置logback的代碼(如果存在這樣的代碼),客戶端代碼不需要依賴于logback。由于SLF4J允許在其抽象層下使用任何日志框架,所以將大量代碼從一個日志框架遷移到另一個日志框架是很容易的。
使用 logback-test.xml 或 logback.xml 自動配置
如前所述,logback將嘗試使用在類路徑上找到的文件logback-test.xml或logback.xml來配置自己。這是一個配置文件,與我們剛才看到的BasicConfigurator所建立的配置文件等效。
基本配置文件:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
將上面的配置文件命名為logback.xml(或logback-test.xml)后,將其放入可以從類路徑訪問的目錄中。運行 MyApp1 應用程序應該會得到與其前一次運行相同的結果。
如果發生警告或錯誤自動打印狀態信息
如果在解析配置文件期間出現警告或錯誤,logback將自動在控制臺上打印其內部狀態消息。注意,為了避免重復,如果用戶顯式注冊了狀態偵聽器(定義如下),則禁用自動狀態打印。在沒有警告或錯誤的情況下,如果仍然希望檢查logback的內部狀態,則可以通過調用StatusPrinter類的print()方法命令logback打印狀態數據。只需要添加兩行代碼即可:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
下面我們可以新建一個類 MyApp2,運行觀察日志信息。
package com.wangbo.cto.logback;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 13:54
* @auther wangbo
*/
public class MyApp2 {
final static Logger logger = LoggerFactory.getLogger(MyApp2.class);
public static void main(String[] args) {
//打印內部狀態
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
執行結果:
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Programmer/projects/mytest/target/classes/logback.xml]
14:18:25,918 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
14:18:25,919 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
14:18:25,923 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
14:18:25,929 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
14:18:25,965 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
14:18:25,966 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@4f933fd1 - Registering current configuration as safe fallback point
14:18:25.968 [main] INFO com.wangbo.cto.logback.MyApp2 - Entering application.
14:18:25.969 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
14:18:25.969 [main] INFO com.wangbo.cto.logback.MyApp2 - Exiting application.
狀態數據
您可以指示配置文件轉儲狀態數據,而不是從代碼中以編程方式調用StatusPrinter,即使在沒有錯誤的情況下也是如此。要實現這一點,您需要設置configuration元素的debug屬性,即配置文件中最上面的元素,如下所示。請注意,這個debug屬性只與狀態數據相關。它不會影響logback關于日志程序級別的配置。
使用調試模式的基本配置文件:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
在元素中設置debug="true"將輸出狀態信息。我們假定:
找到配置文件。
配置文件是格式良好的 XML。
如果這兩個條件都沒有滿足,Joran 無法解釋debug屬性,因為配置文件無法讀取。如果找到配置文件但格式不正確,然后logback將檢測錯誤條件并在控制臺上自動打印其內部狀態。但是,如果找不到配置文件,logback不會自動打印其狀態數據,因為這并不一定是一個錯誤條件。這種情況下以編程方式調用StatusPrinter.print()可以確保在每種情況下都打印狀態信息。
強制狀態輸出
在沒有狀態消息的情況下,跟蹤一個異常的logback.xml配置文件可能很困難,特別是在生產環境中,應用程序源代碼不容易修改。為了幫助識別惡意配置文件的位置,你可以通過logback.statusListenerClass系統屬性設置一個StatusListener來強制輸出狀態信息。logback.statusListenerClass系統屬性還可用于在出現錯誤時自動生成的輸出靜默。
順便說一下,設置debug="true"嚴格地等同于安裝OnConsoleStatusListener。下面將進一步討論狀態偵聽器。接下來展示OnConsoleStatusListener的安裝。
注冊狀態偵聽器:
... 配置文件的其余部分
通過設置debug屬性或安裝OnConsoleStatusListener,將在很大程度上幫助您診斷logback問題。因此,強烈建議啟用logback狀態數據,并應將其視為首選資源。
將默認配置文件的位置指定為系統屬性
您可以使用名為“logback.configurationFile”的系統屬性指定默認配置文件的位置。此屬性的值可以是一個URL、類路徑上的一個資源或應用程序外部的一個文件的路徑。
java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
注意,文件擴展名必須是.xml或者.groovy。忽略其他擴展。顯式注冊狀態偵聽器可能有助于調試定位配置文件的問題。
鑒于“logback.configurationFile”是一個Java系統屬性,它也可以在您的應用程序中設置。但是,必須在創建任何記錄器實例之前設置系統屬性。
import ch.qos.logback.classic.util.ContextInitializer;
public class ServerMain {
public static void main(String args[]) throws IOException, InterruptedException {
// 必須在第一次調用 LoggerFactory.getLogger(); 之前設置
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
...
}
}
public static final String CONFIG_FILE_PROPERTY = "logback.configurationFile";
修改后自動重新加載配置文件
如果指示這樣做,logback-classic將掃描配置文件中的更改,并在配置文件更改時自動重新配置自己。為了指示logback-classic掃描配置文件中的更改并自動重新配置自己,將元素的scan屬性設置為true,如下所示。
掃描配置文件中的更改和自動重新配置:
...
默認情況下,配置文件將每分鐘掃描一次更改。您可以通過設置元素的scanPeriod屬性來指定不同的掃描周期。
指定不同的掃描周期:
...
注意:如果沒有指定時間單位,則假定時間單位為毫秒,這通常是不合適的。如果更改了默認掃描周期,請不要忘記指定時間單位。
在幕后,當您將scan屬性設置為true時,將安裝一個ReconfigureOnChangeTask,此任務在單獨的線程中運行,并將檢查配置文件是否已更改。ReconfigureOnChangeTask將自動監視文件的任何改動。
由于在編輯配置文件時很容易出錯,如果配置文件的最新版本有XML語法錯誤,它將返回到沒有XML語法錯誤的前一個配置文件。
啟用堆棧跟蹤中的打包數據
注意:從1.1.4版本開始,默認情況下將禁用打包數據。
如果指示這樣做,logback可以包含它輸出的堆棧跟蹤行的每一行的打包數據。打包數據由jar文件的名稱和版本組成,該jar文件是堆棧跟蹤行類的起源。打包數據對于識別軟件版本問題非常有用。然而,計算非常耗費資源,特別是在經常拋出異常的應用程序中。下面是一個輸出示例:
14:28:48.835 [btpool0-7] INFO c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]
打包數據在默認情況下是禁用的,但可以通過配置啟用:
...
或者,可以通過調用LoggerContext中的setPackagingDataEnabled(boolean)方法以編程方式啟用/禁用打包數據,如下所示:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);
直接調用 JoranConfigurator
logback依賴于一個名為Joran的配置庫,logback-core的一部分。logback的默認配置機制在類路徑上找到的默認配置文件上調用JoranConfigurator。無論出于什么原因,如果您希望覆蓋logback的默認配置機制,您可以通過直接調用JoranConfigurator來實現這一點。
直接調用JoranConfigurator:
package com.wangbo.cto.logback;
import ch.qos.logback.access.joran.JoranConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @date 2019/9/14 22:01
* @auther wangbo
*/
public class MyApp3 {
final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
public static void main(String[] args) {
//假設SLF4J綁定到當前環境中的logback
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
//調用context.reset()清除任何以前的配置,比如默認配置。
//對于多步驟配置,省略調用context.reset()。
context.reset();
configurator.doConfigure(args[0]);
} catch (JoranException e) {
//StatusPrinter將處理這個問題
}
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
這個應用程序獲取當前有效的LoggerContext,創建一個新的JoranConfigurator,設置將在其上操作的上下文,重置記錄器上下文,然后最后要求配置器使用作為參數傳遞給應用程序的配置文件來配置上下文。如果出現警告或錯誤,將打印內部狀態數據。注意,對于多步驟配置,應該省略context.reset()調用。
查看狀態信息
Logback將其內部狀態數據收集到StatusManager對象中,通過LoggerContext進行訪問。
給定StatusManager,您可以訪問與logback上下文關聯的所有狀態數據。為了使內存使用保持在合理的水平,默認的StatusManager實現將狀態消息存儲在兩個單獨的部分:頭部和尾部。頭部分存儲第一個H狀態消息,而尾部分存儲最后一個T消息。目前H=T=150,盡管這些值在將來的版本中可能會改變。
Logback-classic附帶一個名為ViewStatusMessagesServlet的 servlet。這個 servlet 將StatusManager中與當前LoggerContext關聯的內容打印為HTML表。這是樣本輸出。
lbClassicStatus.jpg
要將此 servlet 添加到 web 應用程序中,請將以下行添加到其 WEB-INF/web.xml 文件中。
ViewStatusMessages
ch.qos.logback.classic.ViewStatusMessagesServlet
ViewStatusMessages
/lbClassicStatus
ViewStatusMessages servlet 可以通過 URL http://host/yourWebapp/lbClassicStatus訪問。
監聽狀態消息
您還可以將StatusListener附加到StatusManager,以便您可以立即響應狀態消息,特別是在回退配置之后發生的消息。注冊狀態偵聽器是一種方便的方法,可以在不需要人工干預的情況下監視登錄的內部狀態。
Logback附帶一個名為OnConsoleStatusListener的StatusListener實現,顧名思義,它將在控制臺打印所有新的傳入狀態消息。
下面是向StatusManager注冊OnConsoleStatusListener實例的示例代碼。
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusManager statusManager = lc.getStatusManager();
OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
statusManager.add(onConsoleListener);
注意,已注冊的狀態偵聽器只會在注冊之后接收狀態事件。它將不會接收先前的消息。因此,通常最好將狀態偵聽器注冊指令放在配置文件的頂部,然后才是其他指令。
還可以在配置文件中注冊一個或多個狀態偵聽器。這里有一個例子。
注冊狀態偵聽器:
... 配置文件的其余部分
logback.statusListenerClass 系統特性
還可以通過設置“logback”來注冊狀態偵聽器。將Java系統屬性設置為要注冊的偵聽器類的名稱。例如:
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
Logback附帶幾個狀態偵聽器實現。OnConsoleStatusListener在控制臺打印輸入狀態消息,即在System.out上打印。OnErrorConsoleStatusListener在System.err上打印傳入的狀態消息。NopStatusListener刪除傳入的狀態消息。
注意,如果在配置期間注冊了任何狀態偵聽器,特別是如果用戶通過logback.statusListenerClass指定了一個狀態偵聽器,則禁用自動狀態打印(以防出現錯誤)。因此,通過將NopStatusListener設置為狀態偵聽器,可以完全禁用內部狀態打印。
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...
停止 logback-classic
為了釋放logback-classic使用的資源,停止logback上下文總是一個好主意。停止上下文將關閉附加到上下文定義的日志記錄器的所有附加程序,并以有序的方式停止任何活動線程。請閱讀下面關于“關機掛鉤”的部分。
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
// 假設SLF4J在當前環境中綁定到logback-classic
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
在web應用程序中,可以從ServletContextListener的contextDestroyed方法中調用上面的代碼,以停止logback-classic并釋放資源。從1.1.10版本開始,將自動為您安裝適當的ServletContextListener(見下面)。
通過關閉掛鉤停止 logback-classic
安裝JVM關機掛鉤是關閉登錄和釋放相關資源的一種方便方法。
....
請注意,您可以通過將 class 屬性設置為與關機鉤子的類名相對應來安裝自己制作的關機鉤子。
默認的關閉鉤子,即 DefaultShutdownHook,將在指定的延遲(默認為0)之后停止logback上下文。停止上下文將允許在后臺運行的任何日志文件壓縮任務在30秒內完成。在獨立的 Java 應用程序中,向配置文件中添加指令是確保允許在 JVM 退出之前完成任何正在進行的壓縮任務的簡單方法。在Web服務器中的應用程序中,webShutdownHook將自動安裝,使指令相當冗余和不必要。
web應用程序中的WebShutdownHook或stop logback-classic
自1.1.10以后, Logback-classic將自動要求web服務器安裝一個LogbackServletContainerInitializer來實現ServletContainerInitializer接口(可在servlet-api 3.x和更高的版本中獲得)。
通過在 web 應用程序的web.xml文件中設置一個名為logbackDisableServletContainerInitializer的。下面是相關的代碼片段。
logbackDisableServletContainerInitializer
true
....
注意,logbackDisableServletContainerInitializer變量也可以設置為 OS 環境變量的 Java 系統屬性。最本地的設置具有優先級,即 web-app 第一,系統屬性第二,操作系統環境最后。
配置文件的語法
正如您在手冊中所看到的,還有很多例子需要跟隨,logback允許您重新定義日志記錄行為,而不需要重新編譯代碼。事實上,您可以很容易地配置logback以禁用應用程序的某些部分日志,或直接輸出到UNIX 系統日志守護進程,到一個數據庫,到一個日志可視化工具,或將日志事件轉發給遠程logback服務器,根據本地服務器策略進行日志記錄,例如,通過將日志事件轉發到第二個logback服務器。
本節的其余部分介紹配置文件的語法。
我們將一遍又一遍地演示,logback配置文件的語法非常靈活。因此,不可能使用DTD文件或XML模式指定允許的語法。然而,配置文件的基本結構可以描述為,元素,包含零個或多個元素,然后是零個或多個元素,隨后是元素,下圖說明了這個基本結構。
標簽名稱的大小寫敏感性
自從logback 0.9.17版本之后,顯式規則下標簽名稱不區分大小寫。例如,、和都是有效的配置元素,將以相同的方式解釋。注意,XML格式規則仍然適用,如果你打開一個標簽為你必須關閉它為,而不能使用作為關閉標簽。對于隱式規則,除了第一個字母外,標記名是區分大小寫的。因此,和是等價的,但不等價。隱式規則通常遵循駝峰命名規則,這在 Java 世界中很常見。由于很難區分標記何時與顯式操作關聯,何時與隱式操作關聯,XML 標記對于第一個字母是區分大小寫的還是不區分大小寫的,這一點很重要。如果您不確定對給定的標記名使用哪種情況,請遵循駝峰命名規則,它幾乎總是正確的約定。
配置日志記錄器,或元素
此時,您至少應該對級別繼承和基本選擇規則有一些了解。日志程序是使用元素配置的。一個元素只接受一個強制的name屬性、一個可選的level屬性和一個可選的additivity屬性。level屬性的值允許不區分大小寫的字符串值:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。不區分大小寫的特殊值 INHERITED,或其同義詞NULL,將強制從層次結構的較高層次繼承日志程序的級別。如果您設置了日志程序的級別,然后決定它應該繼承其級別,那么這將非常方便。
元素可以包含零個或多個元素;這樣引用的每個追加器都被添加到指定的日志程序中。注意,與log4j不同,logback-classic在配置給定的日志程序時不會關閉或刪除任何以前引用的附加程序。
配置根記錄器,或元素
元素配置根記錄器。它支持單個屬性,即level屬性。它不允許任何其他屬性,因為additivity標志不適用于根記錄器。此外,由于根日志記錄器已經被命名為“ROOT”,所以它也不允許使用name屬性。level屬性的值可以是不區分大小寫的字符串之一:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。注意,根日志程序的級別不能設置為INHERITED或NULL。
與元素類似,元素可以包含零個或多個元素;這樣引用的每個追加器都被添加到根日志記錄器中。注意,與log4j不同,logback-classic在配置根日志程序時不會關閉或刪除任何以前引用的附加程序。
示例
設置日志程序或根日志程序的級別與聲明和設置其級別一樣簡單,正如下一個例子所示。假設我們不再有興趣去看屬于com.wangbo.cto.logback包的任何組件的任何調試信息。下面的配置文件展示了如何實現這一點。
設置日志程序的級別:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
當將上述配置文件作為 MyApp1 應用程序的參數時,它將產生以下輸出:
23:44:53.876 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
23:44:53.877 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
注意,只有info級別信息執行了。“Foo”類中的debug級別日志未打印。
您可以根據需要配置任意多的日志記錄器的級別。在下一個配置文件中,我們設置了包的日志級別info,也設置了Foo類的日志級別debug,
設置多個日志記錄器的級別:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
執行 MyApp1,打印結果:
23:51:49.116 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
23:51:49.118 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
23:51:49.118 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
下表列出了JoranConfigurator使用sample3.xml配置文件配置logback之后的日志記錄器及其級別。
Logger name
指定級別
有效級別
root
DEBUG
DEBUG
com.wangbo.cto.logback
INFO
INFO
com.wangbo.cto.logback.MyApp1
null
INFO
com.wangbo.cto.logback.Foo
DEBUG
DEBUG
因此,MyApp1類中的兩個INFO日志語句以及Foo.doIt()中的DEBUG消息都是啟用的。注意,根日志程序的級別總是設置為非空值,默認情況下為DEBUG。
讓我們注意,基本選擇規則取決于被調用的日志程序的有效級別,不是附加程序所在的記錄器級別。Logback將首先確定是否啟用了日志語句,如果啟用,它將調用logger層次結構中找到的appenders,而不管它們的級別如何。下面的配置文件就是一個例子:
class="ch.qos.logback.core.ConsoleAppender">
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
sui
下表列出了應用 sample4.xml配置文件后的日志記錄器及其級別。
Logger name
指定級別
有效級別
root
OFF
OFF
com.wangbo.cto.logback
INFO
INFO
com.wangbo.cto.logback.MyApp1
null
INFO
com.wangbo.cto.logback.Foo
null
INFO
名為STDOUT的ConsoleAppender,是配置文件中唯一配置的追加器,附加到根記錄器,其級別設置為OFF,但是,運行 MyApp1 將產生以下結果:
00:01:55.395 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
00:01:55.397 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
因此,根日志記錄器的級別沒有明顯的影響,因為在com.wangbo.cto.logback中有日志記錄器。MyApp3 和 Foo 類的日志級別都是INFO。
配置 Appenders
追加器使用元素配置,它接受兩個強制屬性name和class。name屬性指定追加器的名稱,class屬性指定要實例化的appender類的完全限定名。元素可以包含零個或一個元素,零個或多個元素,零個或多個元素。除了這三個通用的元素,元素可以包含與appender類的JavaBean屬性相對應的任意數量的元素。無縫地支持給定logback組件的任何屬性是Joran的主要優勢之一,這將在后面的章節中討論。下圖說明了常見的結構。注意,對屬性的支持是不可見的。
元素接受一個強制性的class屬性,該屬性指定要實例化的布局類的完全限定名。與元素一樣,可能包含與layout實例屬性相對應的其他元素。因為這是很常見的情況,如果布局類是PatternLayout,可以省略class屬性。按照默認的類映射規則指定。
登錄到多個appenders就像定義不同的appenders并在日志程序中引用它們一樣簡單,如下面的配置文件所示:
多個日志實例:
myApp.log
%date %level [%thread] %logger{10} [%file:%line] %msg%n
%msg%n
上面的配置文件定義了兩個appenders,分別稱為 FILE和STDOUT。FILE 追加日志到一個myApp.log的文件。此附加程序的編碼器是PatternLayoutEncoder,它輸出日期、級別、線程名稱、日志程序名稱、文件名稱和日志請求所在的行號、消息和行分隔符。第二個追加器稱為STDOUT輸出到控制臺。此附加程序的編碼器僅輸出消息字符串和行分隔符。
通過在appender-ref元素中按名稱引用附加程序,附加程序被附加到根日志程序。注意,每個附加器都有自己的編碼器。編碼器通常不設計為多個附加程序共享,布局也是如此,因此,logback配置文件不提供共享編碼器或布局的任何語法方法。
Appenders 累積
默認情況下,附加程序是累積的:日志記錄器將記錄到附加到其自身(如果有的話)的附加程序,以及附加到其祖先的所有附加程序。因此,將相同的追加器附加到多個日志記錄器將導致日志重復輸出。
重復的appender:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
修改配置文件為上面的配置,運行 MyApp1 程序,結果如下:
10:35:46.197 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.197 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
10:35:46.198 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
注意重復的輸出。名為STDOUT的附加程序被附加到兩個日志記錄器,root和com.wangbo.cto.logback。因為根記錄器是所有日志記錄器的祖先,com.wangbo.cto.logback記錄器又是com.wangbo.cto.logback.MyApp1和com.wangbo.cto.logback.Foo的父級日志記錄器,使用這兩個日志記錄器發出的每個日志請求都將輸出兩次,一次是com.wangbo.cto.logback輸出的,另一次是root輸出的。
附加程序的可累加性并不是為新用戶設計的陷阱。這是一個非常方便的logback特性。例如,您可以配置日志記錄,使日志消息出現在控制臺上(對于系統中的所有日志記錄程序),而只有來自特定日志記錄程序集的消息流進特定的附加程序。
多個appender:
myApp.log
%date %level [%thread] %logger{10} [%file:%line] %msg%n
%msg%n
在本例中,console appender將記錄所有消息(對于系統中的所有日志記錄器),而file appender只記錄來自com.wangbo.cto.logback請求進入myApp.log文件。
覆蓋默認的累積行為
如果默認的累積行為不適合您的需要,您可以通過將累加標志設置為false來覆蓋它。因此,日志記錄器樹中的分支可以將輸出直接輸出到一組與樹的其他部分不同的追加器。
累加性標志:
foo.log
%date %level [%thread] %logger{10} [%file : %line] %msg%n
%msg%n
在本例中,名為FILE的附加器被附加到com.wangbo.cto.logback.Foo記錄器。此外,com.wangbo.cto.logback.Foo記錄器的可累加標記設置為false,這樣它的日志輸出將只會被發送到foo.log文件中,而不會發送到層次結構中更高的附錄中(對于本例也就是說不會被打印在控制臺上)。
運行 MyApp1,控制臺打印結果如下:
Entering application.
Exiting application.
foo.log文件記錄如下:
2019-09-15 10:54:13,667 DEBUG [main] c.w.c.l.Foo [Foo.java : 14] Did it again!
設置上下文名稱
如前一章所述,每個日志程序都附加到一個日志程序上下文。默認情況下,日志記錄器上下文稱為“default”。但是,您可以使用配置指令設置一個不同的名稱。注意,一旦設置好,日志程序上下文名稱將無法更改。設置上下文名稱是一種簡單而直接的方法,用于區分多個應用程序對同一目標的日志記錄。
設置上下文名稱并顯示它:
myAppName
%d %contextName [%t] %level %logger{36} - %msg%n
最后一個示例演示了日志記錄器上下文的命名。在layout的模式中添加contextName轉換詞將輸出所述名稱。
運行 MyApp1,結果如下:
2019-09-15 11:05:09,344 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
2019-09-15 11:05:09,345 myAppName [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
2019-09-15 11:05:09,345 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.
可以看出,上下文名稱myAppName被輸出了。
變量替換
該文檔的早期版本使用了術語“屬性替換”而不是術語“變量”。請考慮這兩個術語是可以互換的,盡管后者表達了更清晰的含義。
與許多腳本語言一樣,logback配置文件支持定義和替換變量。變量有一個作用域(見下面)。此外,變量可以在配置文件本身、外部文件、外部資源中定義,甚至可以動態計算和定義。
變量替換可以發生在配置文件中指定值的任何位置。變量替換的語法類似于 Unix shell 的語法。開始${和結束}之間的字符串被解釋為對屬性值的引用。對于屬性aName,字符串“${aName}”將替換為aName屬性所持有的值。
由于它們經常會派上用場,主機名HOSTNAME和CONTEXT_NAME變量是自動定義的,并且具有上下文作用域。考慮到在某些環境中計算主機名可能需要一些時間,它的值是延遲計算的(僅在需要時)。此外,也可以直接在配置中設置主機名。
定義變量
可以在配置文件本身中一次定義一個變量,也可以從外部屬性文件或外部資源批量加載變量。由于歷史原因,用于定義變量的XML元素是,盡管在logback 1.0.7和以后的版本中,元素可以互換使用。
下一個示例顯示配置文件開頭聲明的變量。然后在文件的后面使用它來指定輸出文件的位置。
簡單的變量替換:
${USER_HOME}/myApp.log
%msg%n
下一個示例展示了如何使用系統屬性來實現相同的結果。該屬性沒有在配置文件中聲明,因此logback將在系統屬性中查找它。Java系統屬性可以在命令行上設置,如下所示:
java -DUSER_HOME="/home/sebastien" MyApp2
系統變量替換:
${USER_HOME}/myApp.log
%msg%n
當需要多個變量時,創建一個包含所有變量的單獨文件可能更方便。下面是如何進行這樣的設置。
使用單獨的文件替換變量:
${USER_HOME}/myApp.log
%msg%n
這個配置文件包含對一個名為variables1.properties文件的引用。該文件中包含的變量將被讀取,然后在本地范圍內定義。這是變量。屬性文件可能看起來像下面這樣。
USER_HOME=/home/sebastien
您還可以在類路徑上引用資源,而不是引用文件。
${USER_HOME}/myApp.log
%msg%n
作用域
屬性可以定義為插入到本地范圍、上下文范圍或系統范圍中。本地范圍是缺省值。雖然可以從 OS 環境中讀取變量,但不可能寫入 OS 環境。
局部作用域:具有局部作用域的屬性從其定義開始存在于配置文件中,直到所述配置文件的解釋/執行結束。因此,每次解析和執行配置文件時,局部范圍內的變量都會重新定義。
上下文作用域:具有上下文作用域的屬性被插入到上下文中,并持續到上下文被清除為止。一旦定義了上下文范圍中的屬性,它就是上下文的一部分。因此,它在所有日志事件中都可用,包括那些通過序列化發送到遠程主機的日志事件。
系統作用域:具有系統范圍的屬性被插入到JVM的系統屬性中,并在JVM運行期間或清除之前一直有效。
在替換期間,首先在本地范圍內查找屬性,然后在上下文范圍內查找屬性,最后在系統屬性范圍內查找屬性,最后在 OS 環境中查找屬性。
可以使用元素的scope屬性,元素或元素的scope屬性設置屬性的范圍。scope屬性允許“local”、“context” 和 “system”字符串作為可能的值。如果沒有指定,則默認為“local”。
在“上下文”范圍中定義的變量:
/opt/${nodeId}/myApp.log
%msg%n
在上面的例子中,假定nodeId屬性是在上下文范圍中定義的,它將在每個日志事件中可用,即使是那些通過序列化發送到遠程主機的日志事件。
變量的默認值
在某些情況下,如果變量沒有聲明或其值為null,則可能希望它具有默認值。與在Bash shell中一樣,可以使用“:-”操作符指定默認值。例如,假設沒有定義名為aName的變量,“${aName:-golden}”將被解釋為“golden”。
嵌套的變量
完全支持變量嵌套。變量的名稱、默認值和值定義都可以引用其他變量。
值嵌套
變量的值定義可以包含對其他變量的引用。假設您希望使用變量不僅指定目標目錄,還指定文件名,并將這兩個變量組合到名為“destination”的第三個變量中。下面顯示的屬性文件給出了一個示例。
嵌套變量引用:
USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}
注意,在上面的屬性文件中,“destination”由另外兩個變量組成,即“USER_HOME”和“fileName”。
配置文件如下:
${destination}
%msg%n
名字嵌套
在引用變量時,變量名可能包含對另一個變量的引用。例如,如果名為“userid”的變量被賦值為“alice”,那么"${${userid}.password}"就是引用名為“alice.password”的變量。
默認值嵌套
變量的默認值可以引用另一個變量。例如,假設變量'id'未賦值,變量'userid'被賦值為"alice",那么表達式"${id:-${userid}}"將返回"alice"。
HOSTNAME 屬性
由于HOSTNAME屬性通常很方便,所以在配置過程中會使用上下文范圍自動定義它。
CONTEXT_NAME 屬性
正如其名稱所示,CONTEXT_NAME屬性對應于當前日志上下文的名稱。
設置一個時間戳
timestamp元素可以根據當前日期和時間定義屬性。時間戳元素將在下一章中解釋。
動態定義屬性
可以使用元素動態定義屬性。define元素接受兩個強制屬性:name和class。name屬性指定要設置的屬性的名稱,而class屬性指定實現PropertyDefiner接口的任何類。PropertyDefiner實例的getPropertyValue()方法返回的值將是命名屬性的值。您還可以通過指定scope屬性為指定的屬性設定范圍。
round
brown
24
在上面的例子中,形狀、顏色和大小是“a.class.implementing.PropertyDefiner”的屬性。只要在PropertyDefiner實例的實現中有一個給定屬性的setter方法,logback會將配置文件中指定的屬性值注入到實例的屬性中。
目前,logback附帶了PropertyDefiner的兩個相當簡單的實現。
實現類
描述
CanonicalHostNamePropertyDefiner
將命名變量設置為本地主機的規范主機名。注意,獲得規范主機名可能需要幾秒鐘。
FileExistsPropertyDefiner
如果path屬性指定的文件存在,則將命名變量設置為“true”,否則設置為“false”。
ResourceExistsPropertyDefiner
如果用戶指定的資源在類路徑上可用,則將命名變量設置為“true”,否則設置為“false”。
配置文件的條件處理
開發人員經常需要在幾個針對不同環境(如開發、測試和生產)的logback配置文件之間來回切換。這些配置文件有很多共同之處,只是在一些地方有所不同。為了避免重復,logback在、和元素的幫助下支持配置文件的條件處理,這樣一個配置文件就可以用于對多個環境。注意,條件處理需要Janino庫。
條件語句的一般格式如下所示。
...
...
...
該條件是一個 Java 表達式,其中只能訪問上下文屬性或系統屬性。對于作為參數傳遞的鍵,property()或其簡寫p()方法返回該屬性的字符串值。例如,要訪問鍵為“k”的屬性的值,您可以編寫propert(“k”)或等價的p(“k”)。如果名稱為"k"的屬性未定義,屬性方法將返回空字符串,而不是null。這避免了檢查空值的需要。
isDefined()方法可用于檢查是否定義了屬性。例如,要檢查屬性“k”是否已定義,可以使用isDefined(“k”),如果需要檢查屬性是否為null,提供了isNull()方法。例如:isNull (“k”)。
%d %-5level %logger{35} - %msg %n
${randomOutputDir}/conditional.log
%d %-5level %logger{35} - %msg %n
元素中的任何位置都支持條件處理。還支持嵌套if-then-else語句。然而,XML語法非常麻煩,不適合作為通用編程語言的基礎。因此,太多的條件將很快使后續讀者(包括您自己)無法理解您的配置文件。
從JNDI獲取變量
在某些情況下,您可能希望使用 JNDI 中存儲的env-entries。配置指令提取 JNDI 中存儲的一個env-entry,并用as屬性指定的鍵插入本地范圍中的屬性。所有屬性,在scope屬性的幫助下,可以將新屬性插入不同的范圍。
作為通過JNDI獲得的屬性env-entry插入:
${appName}
%d ${CONTEXT_NAME} %level %msg %logger{50}%n
在最后一個示例中,“java:comp/env/appName”作為appName屬性插入。注意,指令根據前面的指令插入的appName屬性的值設置上下文名稱。
文件包含
Joran 支持從另一個文件中包含配置文件的部分。這是通過聲明一個元素來實現的,如下所示:
目標文件必須將其元素嵌套在元素中。例如,一個ConsoleAppender可以聲明為:
"%d - %m%n"
再次強調,必須使用元素。
要包含的內容可以作為文件、資源或URL引用。
作為一個文件:
要包含文件,請使用file屬性。您可以使用相對路徑,但是請注意,當前目錄是由應用程序定義的,并不一定與配置文件的路徑相關。
作為一個資源:
為了包含資源,比如在類路徑上找到一個文件,使用resource屬性。
作為一個 URL:
要包含 URL 的內容,請使用 URL 屬性。
如果找不到要包含的文件,logback將通過打印狀態消息來提示。如果包含的文件是可選的,您可以通過將元素中的可選屬性設置為true來抑制警告消息。
添加上下文偵聽器
LoggerContextListener接口的實例偵聽與日志記錄器上下文的生命周期相關的事件。
JMXConfigurator是LoggerContextListener接口的一個實現。它將在下一章進行描述。
#######LevelChangePropagator
從0.9.25版開始,logback-classic附帶LevelChangePropagator,這是LoggerContextListener的實現,它將任何logback-classic日志程序級別的更改傳播到java.util.logging框架。這種傳播消除了禁用日志語句的性能影響。LogRecord實例將僅通過啟用的日志語句發送到logback(通過SLF4J)。這使得實際應用程序可以合理地使用jul-to-slf4j橋接。
contextListener元素可用于安裝LevelChangePropagator,如下所示。
....
設置LevelChangePropagator的resetJUL屬性將重置所有j.u.l.日志記錄器的所有以前的級別配置。但是,以前安裝的處理程序將保持不變。
true
....