服务器日志文件中包含堆栈跟踪,日志框架 Logback 官方手册(第三章:Configuration)...
以下內(nèi)容翻譯整理自logback官方手冊,地址:logback官方手冊
logback 配置
將日志請求插入應(yīng)用程序代碼需要相當(dāng)多的計劃和工作。觀察表明,大約有4%的代碼用于日志記錄。因此,即使是一個中等大小的應(yīng)用程序,其代碼中也會包含數(shù)千條日志語句。考慮到它們的數(shù)量,我們需要工具來管理這些日志語句。
可以通過編程方式配置logback,也可以使用XML或Groovy格式表示的配置腳本配置logback。順便說一下,通過使用我們的 PropertiesTranslator web應(yīng)用程序,現(xiàn)有的log4j用戶可以將他們的log4j.properties文件轉(zhuǎn)換為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接口的實現(xiàn)。它的內(nèi)容應(yīng)該指定所需配置程序的完全限定類名。
如果以上方法都不成功,logback將使用BasicConfigurator自動配置自己,這將導(dǎo)致日志輸出定向到控制臺。
最后一步是在沒有配置文件的情況下提供默認(rèn)(但非常基本)日志功能。
如果您正在使用Maven,并且將logback-test.xml放在src/test/resources文件夾下,Maven將確保它不會包含在生成的包中。因此,您可以使用不同的配置文件,在測試期間使用logback-test. xml,在生產(chǎn)中使用另一個文件logback.xml。
自動配置logback
配置logback的最簡單方法是讓logback使用其默認(rèn)配置。
使用 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.");
}
}
該類定義一個靜態(tài)日志記錄器變量。然后實例化一個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!");
}
}
假設(shè)不存在 logback-test.xml或logback.xml配置文件,logback將默認(rèn)調(diào)用BasicConfigurator設(shè)置一個最小配置。這個最小配置由一個附加到根logger的ConsoleAppender組成,使用PatternLayoutEncoder格式化輸出。
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
此外,在默認(rèn)情況下,根logger被指定為DEBUG級別。
因此,上面程序運行結(jié)果顯示如下:
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 應(yīng)用程序通過調(diào)用org.slf4j.LoggerFactory和org.slf4j.Logger鏈接到logback。獲取到它希望使用的日志記錄器,然后繼續(xù)。注意,Foo類對logback的唯一依賴關(guān)系是通過org.slf4j.LoggerFactory和org.slf4j.Logger的導(dǎo)入。除了配置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 應(yīng)用程序應(yīng)該會得到與其前一次運行相同的結(jié)果。
如果發(fā)生警告或錯誤自動打印狀態(tài)信息
如果在解析配置文件期間出現(xiàn)警告或錯誤,logback將自動在控制臺上打印其內(nèi)部狀態(tài)消息。注意,為了避免重復(fù),如果用戶顯式注冊了狀態(tài)偵聽器(定義如下),則禁用自動狀態(tài)打印。在沒有警告或錯誤的情況下,如果仍然希望檢查logback的內(nèi)部狀態(tài),則可以通過調(diào)用StatusPrinter類的print()方法命令logback打印狀態(tài)數(shù)據(jù)。只需要添加兩行代碼即可:
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) {
//打印內(nèi)部狀態(tài)
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
logger.info("Entering application.");
Foo foo = new Foo();
foo.doIt();
logger.info("Exiting application.");
}
}
執(zhí)行結(jié)果:
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.
狀態(tài)數(shù)據(jù)
您可以指示配置文件轉(zhuǎn)儲狀態(tài)數(shù)據(jù),而不是從代碼中以編程方式調(diào)用StatusPrinter,即使在沒有錯誤的情況下也是如此。要實現(xiàn)這一點,您需要設(shè)置configuration元素的debug屬性,即配置文件中最上面的元素,如下所示。請注意,這個debug屬性只與狀態(tài)數(shù)據(jù)相關(guān)。它不會影響logback關(guān)于日志程序級別的配置。
使用調(diào)試模式的基本配置文件:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
在元素中設(shè)置debug="true"將輸出狀態(tài)信息。我們假定:
找到配置文件。
配置文件是格式良好的 XML。
如果這兩個條件都沒有滿足,Joran 無法解釋debug屬性,因為配置文件無法讀取。如果找到配置文件但格式不正確,然后logback將檢測錯誤條件并在控制臺上自動打印其內(nèi)部狀態(tài)。但是,如果找不到配置文件,logback不會自動打印其狀態(tài)數(shù)據(jù),因為這并不一定是一個錯誤條件。這種情況下以編程方式調(diào)用StatusPrinter.print()可以確保在每種情況下都打印狀態(tài)信息。
強制狀態(tài)輸出
在沒有狀態(tài)消息的情況下,跟蹤一個異常的logback.xml配置文件可能很困難,特別是在生產(chǎn)環(huán)境中,應(yīng)用程序源代碼不容易修改。為了幫助識別惡意配置文件的位置,你可以通過logback.statusListenerClass系統(tǒng)屬性設(shè)置一個StatusListener來強制輸出狀態(tài)信息。logback.statusListenerClass系統(tǒng)屬性還可用于在出現(xiàn)錯誤時自動生成的輸出靜默。
順便說一下,設(shè)置debug="true"嚴(yán)格地等同于安裝OnConsoleStatusListener。下面將進(jìn)一步討論狀態(tài)偵聽器。接下來展示OnConsoleStatusListener的安裝。
注冊狀態(tài)偵聽器:
... 配置文件的其余部分
通過設(shè)置debug屬性或安裝OnConsoleStatusListener,將在很大程度上幫助您診斷l(xiāng)ogback問題。因此,強烈建議啟用logback狀態(tài)數(shù)據(jù),并應(yīng)將其視為首選資源。
將默認(rèn)配置文件的位置指定為系統(tǒng)屬性
您可以使用名為“l(fā)ogback.configurationFile”的系統(tǒng)屬性指定默認(rèn)配置文件的位置。此屬性的值可以是一個URL、類路徑上的一個資源或應(yīng)用程序外部的一個文件的路徑。
java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
注意,文件擴展名必須是.xml或者.groovy。忽略其他擴展。顯式注冊狀態(tài)偵聽器可能有助于調(diào)試定位配置文件的問題。
鑒于“l(fā)ogback.configurationFile”是一個Java系統(tǒng)屬性,它也可以在您的應(yīng)用程序中設(shè)置。但是,必須在創(chuàng)建任何記錄器實例之前設(shè)置系統(tǒng)屬性。
import ch.qos.logback.classic.util.ContextInitializer;
public class ServerMain {
public static void main(String args[]) throws IOException, InterruptedException {
// 必須在第一次調(diào)用 LoggerFactory.getLogger(); 之前設(shè)置
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
...
}
}
public static final String CONFIG_FILE_PROPERTY = "logback.configurationFile";
修改后自動重新加載配置文件
如果指示這樣做,logback-classic將掃描配置文件中的更改,并在配置文件更改時自動重新配置自己。為了指示logback-classic掃描配置文件中的更改并自動重新配置自己,將元素的scan屬性設(shè)置為true,如下所示。
掃描配置文件中的更改和自動重新配置:
...
默認(rèn)情況下,配置文件將每分鐘掃描一次更改。您可以通過設(shè)置元素的scanPeriod屬性來指定不同的掃描周期。
指定不同的掃描周期:
...
注意:如果沒有指定時間單位,則假定時間單位為毫秒,這通常是不合適的。如果更改了默認(rèn)掃描周期,請不要忘記指定時間單位。
在幕后,當(dāng)您將scan屬性設(shè)置為true時,將安裝一個ReconfigureOnChangeTask,此任務(wù)在單獨的線程中運行,并將檢查配置文件是否已更改。ReconfigureOnChangeTask將自動監(jiān)視文件的任何改動。
由于在編輯配置文件時很容易出錯,如果配置文件的最新版本有XML語法錯誤,它將返回到?jīng)]有XML語法錯誤的前一個配置文件。
啟用堆棧跟蹤中的打包數(shù)據(jù)
注意:從1.1.4版本開始,默認(rèn)情況下將禁用打包數(shù)據(jù)。
如果指示這樣做,logback可以包含它輸出的堆棧跟蹤行的每一行的打包數(shù)據(jù)。打包數(shù)據(jù)由jar文件的名稱和版本組成,該jar文件是堆棧跟蹤行類的起源。打包數(shù)據(jù)對于識別軟件版本問題非常有用。然而,計算非常耗費資源,特別是在經(jīng)常拋出異常的應(yīng)用程序中。下面是一個輸出示例:
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]
打包數(shù)據(jù)在默認(rèn)情況下是禁用的,但可以通過配置啟用:
...
或者,可以通過調(diào)用LoggerContext中的setPackagingDataEnabled(boolean)方法以編程方式啟用/禁用打包數(shù)據(jù),如下所示:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);
直接調(diào)用 JoranConfigurator
logback依賴于一個名為Joran的配置庫,logback-core的一部分。logback的默認(rèn)配置機制在類路徑上找到的默認(rèn)配置文件上調(diào)用JoranConfigurator。無論出于什么原因,如果您希望覆蓋logback的默認(rèn)配置機制,您可以通過直接調(diào)用JoranConfigurator來實現(xiàn)這一點。
直接調(diào)用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) {
//假設(shè)SLF4J綁定到當(dāng)前環(huán)境中的logback
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
//調(diào)用context.reset()清除任何以前的配置,比如默認(rèn)配置。
//對于多步驟配置,省略調(diào)用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.");
}
}
這個應(yīng)用程序獲取當(dāng)前有效的LoggerContext,創(chuàng)建一個新的JoranConfigurator,設(shè)置將在其上操作的上下文,重置記錄器上下文,然后最后要求配置器使用作為參數(shù)傳遞給應(yīng)用程序的配置文件來配置上下文。如果出現(xiàn)警告或錯誤,將打印內(nèi)部狀態(tài)數(shù)據(jù)。注意,對于多步驟配置,應(yīng)該省略context.reset()調(diào)用。
查看狀態(tài)信息
Logback將其內(nèi)部狀態(tài)數(shù)據(jù)收集到StatusManager對象中,通過LoggerContext進(jìn)行訪問。
給定StatusManager,您可以訪問與logback上下文關(guān)聯(lián)的所有狀態(tài)數(shù)據(jù)。為了使內(nèi)存使用保持在合理的水平,默認(rèn)的StatusManager實現(xiàn)將狀態(tài)消息存儲在兩個單獨的部分:頭部和尾部。頭部分存儲第一個H狀態(tài)消息,而尾部分存儲最后一個T消息。目前H=T=150,盡管這些值在將來的版本中可能會改變。
Logback-classic附帶一個名為ViewStatusMessagesServlet的 servlet。這個 servlet 將StatusManager中與當(dāng)前LoggerContext關(guān)聯(lián)的內(nèi)容打印為HTML表。這是樣本輸出。
lbClassicStatus.jpg
要將此 servlet 添加到 web 應(yīng)用程序中,請將以下行添加到其 WEB-INF/web.xml 文件中。
ViewStatusMessages
ch.qos.logback.classic.ViewStatusMessagesServlet
ViewStatusMessages
/lbClassicStatus
ViewStatusMessages servlet 可以通過 URL http://host/yourWebapp/lbClassicStatus訪問。
監(jiān)聽狀態(tài)消息
您還可以將StatusListener附加到StatusManager,以便您可以立即響應(yīng)狀態(tài)消息,特別是在回退配置之后發(fā)生的消息。注冊狀態(tài)偵聽器是一種方便的方法,可以在不需要人工干預(yù)的情況下監(jiān)視登錄的內(nèi)部狀態(tài)。
Logback附帶一個名為OnConsoleStatusListener的StatusListener實現(xiàn),顧名思義,它將在控制臺打印所有新的傳入狀態(tài)消息。
下面是向StatusManager注冊O(shè)nConsoleStatusListener實例的示例代碼。
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusManager statusManager = lc.getStatusManager();
OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
statusManager.add(onConsoleListener);
注意,已注冊的狀態(tài)偵聽器只會在注冊之后接收狀態(tài)事件。它將不會接收先前的消息。因此,通常最好將狀態(tài)偵聽器注冊指令放在配置文件的頂部,然后才是其他指令。
還可以在配置文件中注冊一個或多個狀態(tài)偵聽器。這里有一個例子。
注冊狀態(tài)偵聽器:
... 配置文件的其余部分
logback.statusListenerClass 系統(tǒng)特性
還可以通過設(shè)置“l(fā)ogback”來注冊狀態(tài)偵聽器。將Java系統(tǒng)屬性設(shè)置為要注冊的偵聽器類的名稱。例如:
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
Logback附帶幾個狀態(tài)偵聽器實現(xiàn)。OnConsoleStatusListener在控制臺打印輸入狀態(tài)消息,即在System.out上打印。OnErrorConsoleStatusListener在System.err上打印傳入的狀態(tài)消息。NopStatusListener刪除傳入的狀態(tài)消息。
注意,如果在配置期間注冊了任何狀態(tài)偵聽器,特別是如果用戶通過logback.statusListenerClass指定了一個狀態(tài)偵聽器,則禁用自動狀態(tài)打印(以防出現(xiàn)錯誤)。因此,通過將NopStatusListener設(shè)置為狀態(tài)偵聽器,可以完全禁用內(nèi)部狀態(tài)打印。
java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...
停止 logback-classic
為了釋放logback-classic使用的資源,停止logback上下文總是一個好主意。停止上下文將關(guān)閉附加到上下文定義的日志記錄器的所有附加程序,并以有序的方式停止任何活動線程。請閱讀下面關(guān)于“關(guān)機掛鉤”的部分。
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
// 假設(shè)SLF4J在當(dāng)前環(huán)境中綁定到logback-classic
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();
在web應(yīng)用程序中,可以從ServletContextListener的contextDestroyed方法中調(diào)用上面的代碼,以停止logback-classic并釋放資源。從1.1.10版本開始,將自動為您安裝適當(dāng)?shù)腟ervletContextListener(見下面)。
通過關(guān)閉掛鉤停止 logback-classic
安裝JVM關(guān)機掛鉤是關(guān)閉登錄和釋放相關(guān)資源的一種方便方法。
....
請注意,您可以通過將 class 屬性設(shè)置為與關(guān)機鉤子的類名相對應(yīng)來安裝自己制作的關(guān)機鉤子。
默認(rèn)的關(guān)閉鉤子,即 DefaultShutdownHook,將在指定的延遲(默認(rèn)為0)之后停止logback上下文。停止上下文將允許在后臺運行的任何日志文件壓縮任務(wù)在30秒內(nèi)完成。在獨立的 Java 應(yīng)用程序中,向配置文件中添加指令是確保允許在 JVM 退出之前完成任何正在進(jìn)行的壓縮任務(wù)的簡單方法。在Web服務(wù)器中的應(yīng)用程序中,webShutdownHook將自動安裝,使指令相當(dāng)冗余和不必要。
web應(yīng)用程序中的WebShutdownHook或stop logback-classic
自1.1.10以后, Logback-classic將自動要求web服務(wù)器安裝一個LogbackServletContainerInitializer來實現(xiàn)ServletContainerInitializer接口(可在servlet-api 3.x和更高的版本中獲得)。
通過在 web 應(yīng)用程序的web.xml文件中設(shè)置一個名為logbackDisableServletContainerInitializer的。下面是相關(guān)的代碼片段。
logbackDisableServletContainerInitializer
true
....
注意,logbackDisableServletContainerInitializer變量也可以設(shè)置為 OS 環(huán)境變量的 Java 系統(tǒng)屬性。最本地的設(shè)置具有優(yōu)先級,即 web-app 第一,系統(tǒng)屬性第二,操作系統(tǒng)環(huán)境最后。
配置文件的語法
正如您在手冊中所看到的,還有很多例子需要跟隨,logback允許您重新定義日志記錄行為,而不需要重新編譯代碼。事實上,您可以很容易地配置logback以禁用應(yīng)用程序的某些部分日志,或直接輸出到UNIX 系統(tǒng)日志守護(hù)進(jìn)程,到一個數(shù)據(jù)庫,到一個日志可視化工具,或?qū)⑷罩臼录D(zhuǎn)發(fā)給遠(yuǎn)程logback服務(wù)器,根據(jù)本地服務(wù)器策略進(jìn)行日志記錄,例如,通過將日志事件轉(zhuǎn)發(fā)到第二個logback服務(wù)器。
本節(jié)的其余部分介紹配置文件的語法。
我們將一遍又一遍地演示,logback配置文件的語法非常靈活。因此,不可能使用DTD文件或XML模式指定允許的語法。然而,配置文件的基本結(jié)構(gòu)可以描述為,元素,包含零個或多個元素,然后是零個或多個元素,隨后是元素,下圖說明了這個基本結(jié)構(gòu)。
標(biāo)簽名稱的大小寫敏感性
自從logback 0.9.17版本之后,顯式規(guī)則下標(biāo)簽名稱不區(qū)分大小寫。例如,、和都是有效的配置元素,將以相同的方式解釋。注意,XML格式規(guī)則仍然適用,如果你打開一個標(biāo)簽為你必須關(guān)閉它為,而不能使用作為關(guān)閉標(biāo)簽。對于隱式規(guī)則,除了第一個字母外,標(biāo)記名是區(qū)分大小寫的。因此,和是等價的,但不等價。隱式規(guī)則通常遵循駝峰命名規(guī)則,這在 Java 世界中很常見。由于很難區(qū)分標(biāo)記何時與顯式操作關(guān)聯(lián),何時與隱式操作關(guān)聯(lián),XML 標(biāo)記對于第一個字母是區(qū)分大小寫的還是不區(qū)分大小寫的,這一點很重要。如果您不確定對給定的標(biāo)記名使用哪種情況,請遵循駝峰命名規(guī)則,它幾乎總是正確的約定。
配置日志記錄器,或元素
此時,您至少應(yīng)該對級別繼承和基本選擇規(guī)則有一些了解。日志程序是使用元素配置的。一個元素只接受一個強制的name屬性、一個可選的level屬性和一個可選的additivity屬性。level屬性的值允許不區(qū)分大小寫的字符串值:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。不區(qū)分大小寫的特殊值 INHERITED,或其同義詞NULL,將強制從層次結(jié)構(gòu)的較高層次繼承日志程序的級別。如果您設(shè)置了日志程序的級別,然后決定它應(yīng)該繼承其級別,那么這將非常方便。
元素可以包含零個或多個元素;這樣引用的每個追加器都被添加到指定的日志程序中。注意,與log4j不同,logback-classic在配置給定的日志程序時不會關(guān)閉或刪除任何以前引用的附加程序。
配置根記錄器,或元素
元素配置根記錄器。它支持單個屬性,即level屬性。它不允許任何其他屬性,因為additivity標(biāo)志不適用于根記錄器。此外,由于根日志記錄器已經(jīng)被命名為“ROOT”,所以它也不允許使用name屬性。level屬性的值可以是不區(qū)分大小寫的字符串之一:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。注意,根日志程序的級別不能設(shè)置為INHERITED或NULL。
與元素類似,元素可以包含零個或多個元素;這樣引用的每個追加器都被添加到根日志記錄器中。注意,與log4j不同,logback-classic在配置根日志程序時不會關(guān)閉或刪除任何以前引用的附加程序。
示例
設(shè)置日志程序或根日志程序的級別與聲明和設(shè)置其級別一樣簡單,正如下一個例子所示。假設(shè)我們不再有興趣去看屬于com.wangbo.cto.logback包的任何組件的任何調(diào)試信息。下面的配置文件展示了如何實現(xiàn)這一點。
設(shè)置日志程序的級別:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
當(dāng)將上述配置文件作為 MyApp1 應(yīng)用程序的參數(shù)時,它將產(chǎn)生以下輸出:
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級別信息執(zhí)行了。“Foo”類中的debug級別日志未打印。
您可以根據(jù)需要配置任意多的日志記錄器的級別。在下一個配置文件中,我們設(shè)置了包的日志級別info,也設(shè)置了Foo類的日志級別debug,
設(shè)置多個日志記錄器的級別:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
執(zhí)行 MyApp1,打印結(jié)果:
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消息都是啟用的。注意,根日志程序的級別總是設(shè)置為非空值,默認(rèn)情況下為DEBUG。
讓我們注意,基本選擇規(guī)則取決于被調(diào)用的日志程序的有效級別,不是附加程序所在的記錄器級別。Logback將首先確定是否啟用了日志語句,如果啟用,它將調(diào)用logger層次結(jié)構(gòu)中找到的appenders,而不管它們的級別如何。下面的配置文件就是一個例子:
class="ch.qos.logback.core.ConsoleAppender">
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
sui
下表列出了應(yīng)用 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,是配置文件中唯一配置的追加器,附加到根記錄器,其級別設(shè)置為OFF,但是,運行 MyApp1 將產(chǎn)生以下結(jié)果:
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屬性相對應(yīng)的任意數(shù)量的元素。無縫地支持給定logback組件的任何屬性是Joran的主要優(yōu)勢之一,這將在后面的章節(jié)中討論。下圖說明了常見的結(jié)構(gòu)。注意,對屬性的支持是不可見的。
元素接受一個強制性的class屬性,該屬性指定要實例化的布局類的完全限定名。與元素一樣,可能包含與layout實例屬性相對應(yīng)的其他元素。因為這是很常見的情況,如果布局類是PatternLayout,可以省略class屬性。按照默認(rèn)的類映射規(guī)則指定。
登錄到多個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元素中按名稱引用附加程序,附加程序被附加到根日志程序。注意,每個附加器都有自己的編碼器。編碼器通常不設(shè)計為多個附加程序共享,布局也是如此,因此,logback配置文件不提供共享編碼器或布局的任何語法方法。
Appenders 累積
默認(rèn)情況下,附加程序是累積的:日志記錄器將記錄到附加到其自身(如果有的話)的附加程序,以及附加到其祖先的所有附加程序。因此,將相同的追加器附加到多個日志記錄器將導(dǎo)致日志重復(fù)輸出。
重復(fù)的appender:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
修改配置文件為上面的配置,運行 MyApp1 程序,結(jié)果如下:
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.
注意重復(fù)的輸出。名為STDOUT的附加程序被附加到兩個日志記錄器,root和com.wangbo.cto.logback。因為根記錄器是所有日志記錄器的祖先,com.wangbo.cto.logback記錄器又是com.wangbo.cto.logback.MyApp1和com.wangbo.cto.logback.Foo的父級日志記錄器,使用這兩個日志記錄器發(fā)出的每個日志請求都將輸出兩次,一次是com.wangbo.cto.logback輸出的,另一次是root輸出的。
附加程序的可累加性并不是為新用戶設(shè)計的陷阱。這是一個非常方便的logback特性。例如,您可以配置日志記錄,使日志消息出現(xiàn)在控制臺上(對于系統(tǒng)中的所有日志記錄程序),而只有來自特定日志記錄程序集的消息流進(jìn)特定的附加程序。
多個appender:
myApp.log
%date %level [%thread] %logger{10} [%file:%line] %msg%n
%msg%n
在本例中,console appender將記錄所有消息(對于系統(tǒng)中的所有日志記錄器),而file appender只記錄來自com.wangbo.cto.logback請求進(jìn)入myApp.log文件。
覆蓋默認(rèn)的累積行為
如果默認(rèn)的累積行為不適合您的需要,您可以通過將累加標(biāo)志設(shè)置為false來覆蓋它。因此,日志記錄器樹中的分支可以將輸出直接輸出到一組與樹的其他部分不同的追加器。
累加性標(biāo)志:
foo.log
%date %level [%thread] %logger{10} [%file : %line] %msg%n
%msg%n
在本例中,名為FILE的附加器被附加到com.wangbo.cto.logback.Foo記錄器。此外,com.wangbo.cto.logback.Foo記錄器的可累加標(biāo)記設(shè)置為false,這樣它的日志輸出將只會被發(fā)送到foo.log文件中,而不會發(fā)送到層次結(jié)構(gòu)中更高的附錄中(對于本例也就是說不會被打印在控制臺上)。
運行 MyApp1,控制臺打印結(jié)果如下:
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!
設(shè)置上下文名稱
如前一章所述,每個日志程序都附加到一個日志程序上下文。默認(rèn)情況下,日志記錄器上下文稱為“default”。但是,您可以使用配置指令設(shè)置一個不同的名稱。注意,一旦設(shè)置好,日志程序上下文名稱將無法更改。設(shè)置上下文名稱是一種簡單而直接的方法,用于區(qū)分多個應(yīng)用程序?qū)ν荒繕?biāo)的日志記錄。
設(shè)置上下文名稱并顯示它:
myAppName
%d %contextName [%t] %level %logger{36} - %msg%n
最后一個示例演示了日志記錄器上下文的命名。在layout的模式中添加contextName轉(zhuǎn)換詞將輸出所述名稱。
運行 MyApp1,結(jié)果如下:
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被輸出了。
變量替換
該文檔的早期版本使用了術(shù)語“屬性替換”而不是術(shù)語“變量”。請考慮這兩個術(shù)語是可以互換的,盡管后者表達(dá)了更清晰的含義。
與許多腳本語言一樣,logback配置文件支持定義和替換變量。變量有一個作用域(見下面)。此外,變量可以在配置文件本身、外部文件、外部資源中定義,甚至可以動態(tài)計算和定義。
變量替換可以發(fā)生在配置文件中指定值的任何位置。變量替換的語法類似于 Unix shell 的語法。開始${和結(jié)束}之間的字符串被解釋為對屬性值的引用。對于屬性aName,字符串“${aName}”將替換為aName屬性所持有的值。
由于它們經(jīng)常會派上用場,主機名HOSTNAME和CONTEXT_NAME變量是自動定義的,并且具有上下文作用域。考慮到在某些環(huán)境中計算主機名可能需要一些時間,它的值是延遲計算的(僅在需要時)。此外,也可以直接在配置中設(shè)置主機名。
定義變量
可以在配置文件本身中一次定義一個變量,也可以從外部屬性文件或外部資源批量加載變量。由于歷史原因,用于定義變量的XML元素是,盡管在logback 1.0.7和以后的版本中,元素可以互換使用。
下一個示例顯示配置文件開頭聲明的變量。然后在文件的后面使用它來指定輸出文件的位置。
簡單的變量替換:
${USER_HOME}/myApp.log
%msg%n
下一個示例展示了如何使用系統(tǒng)屬性來實現(xiàn)相同的結(jié)果。該屬性沒有在配置文件中聲明,因此logback將在系統(tǒng)屬性中查找它。Java系統(tǒng)屬性可以在命令行上設(shè)置,如下所示:
java -DUSER_HOME="/home/sebastien" MyApp2
系統(tǒng)變量替換:
${USER_HOME}/myApp.log
%msg%n
當(dāng)需要多個變量時,創(chuàng)建一個包含所有變量的單獨文件可能更方便。下面是如何進(jìn)行這樣的設(shè)置。
使用單獨的文件替換變量:
${USER_HOME}/myApp.log
%msg%n
這個配置文件包含對一個名為variables1.properties文件的引用。該文件中包含的變量將被讀取,然后在本地范圍內(nèi)定義。這是變量。屬性文件可能看起來像下面這樣。
USER_HOME=/home/sebastien
您還可以在類路徑上引用資源,而不是引用文件。
${USER_HOME}/myApp.log
%msg%n
作用域
屬性可以定義為插入到本地范圍、上下文范圍或系統(tǒng)范圍中。本地范圍是缺省值。雖然可以從 OS 環(huán)境中讀取變量,但不可能寫入 OS 環(huán)境。
局部作用域:具有局部作用域的屬性從其定義開始存在于配置文件中,直到所述配置文件的解釋/執(zhí)行結(jié)束。因此,每次解析和執(zhí)行配置文件時,局部范圍內(nèi)的變量都會重新定義。
上下文作用域:具有上下文作用域的屬性被插入到上下文中,并持續(xù)到上下文被清除為止。一旦定義了上下文范圍中的屬性,它就是上下文的一部分。因此,它在所有日志事件中都可用,包括那些通過序列化發(fā)送到遠(yuǎn)程主機的日志事件。
系統(tǒng)作用域:具有系統(tǒng)范圍的屬性被插入到JVM的系統(tǒng)屬性中,并在JVM運行期間或清除之前一直有效。
在替換期間,首先在本地范圍內(nèi)查找屬性,然后在上下文范圍內(nèi)查找屬性,最后在系統(tǒng)屬性范圍內(nèi)查找屬性,最后在 OS 環(huán)境中查找屬性。
可以使用元素的scope屬性,元素或元素的scope屬性設(shè)置屬性的范圍。scope屬性允許“l(fā)ocal”、“context” 和 “system”字符串作為可能的值。如果沒有指定,則默認(rèn)為“l(fā)ocal”。
在“上下文”范圍中定義的變量:
/opt/${nodeId}/myApp.log
%msg%n
在上面的例子中,假定nodeId屬性是在上下文范圍中定義的,它將在每個日志事件中可用,即使是那些通過序列化發(fā)送到遠(yuǎn)程主機的日志事件。
變量的默認(rèn)值
在某些情況下,如果變量沒有聲明或其值為null,則可能希望它具有默認(rèn)值。與在Bash shell中一樣,可以使用“:-”操作符指定默認(rèn)值。例如,假設(shè)沒有定義名為aName的變量,“${aName:-golden}”將被解釋為“golden”。
嵌套的變量
完全支持變量嵌套。變量的名稱、默認(rèn)值和值定義都可以引用其他變量。
值嵌套
變量的值定義可以包含對其他變量的引用。假設(shè)您希望使用變量不僅指定目標(biāo)目錄,還指定文件名,并將這兩個變量組合到名為“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”的變量。
默認(rèn)值嵌套
變量的默認(rèn)值可以引用另一個變量。例如,假設(shè)變量'id'未賦值,變量'userid'被賦值為"alice",那么表達(dá)式"${id:-${userid}}"將返回"alice"。
HOSTNAME 屬性
由于HOSTNAME屬性通常很方便,所以在配置過程中會使用上下文范圍自動定義它。
CONTEXT_NAME 屬性
正如其名稱所示,CONTEXT_NAME屬性對應(yīng)于當(dāng)前日志上下文的名稱。
設(shè)置一個時間戳
timestamp元素可以根據(jù)當(dāng)前日期和時間定義屬性。時間戳元素將在下一章中解釋。
動態(tài)定義屬性
可以使用元素動態(tài)定義屬性。define元素接受兩個強制屬性:name和class。name屬性指定要設(shè)置的屬性的名稱,而class屬性指定實現(xiàn)PropertyDefiner接口的任何類。PropertyDefiner實例的getPropertyValue()方法返回的值將是命名屬性的值。您還可以通過指定scope屬性為指定的屬性設(shè)定范圍。
round
brown
24
在上面的例子中,形狀、顏色和大小是“a.class.implementing.PropertyDefiner”的屬性。只要在PropertyDefiner實例的實現(xiàn)中有一個給定屬性的setter方法,logback會將配置文件中指定的屬性值注入到實例的屬性中。
目前,logback附帶了PropertyDefiner的兩個相當(dāng)簡單的實現(xiàn)。
實現(xiàn)類
描述
CanonicalHostNamePropertyDefiner
將命名變量設(shè)置為本地主機的規(guī)范主機名。注意,獲得規(guī)范主機名可能需要幾秒鐘。
FileExistsPropertyDefiner
如果path屬性指定的文件存在,則將命名變量設(shè)置為“true”,否則設(shè)置為“false”。
ResourceExistsPropertyDefiner
如果用戶指定的資源在類路徑上可用,則將命名變量設(shè)置為“true”,否則設(shè)置為“false”。
配置文件的條件處理
開發(fā)人員經(jīng)常需要在幾個針對不同環(huán)境(如開發(fā)、測試和生產(chǎn))的logback配置文件之間來回切換。這些配置文件有很多共同之處,只是在一些地方有所不同。為了避免重復(fù),logback在、和元素的幫助下支持配置文件的條件處理,這樣一個配置文件就可以用于對多個環(huán)境。注意,條件處理需要Janino庫。
條件語句的一般格式如下所示。
...
...
...
該條件是一個 Java 表達(dá)式,其中只能訪問上下文屬性或系統(tǒng)屬性。對于作為參數(shù)傳遞的鍵,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語法非常麻煩,不適合作為通用編程語言的基礎(chǔ)。因此,太多的條件將很快使后續(xù)讀者(包括您自己)無法理解您的配置文件。
從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屬性插入。注意,指令根據(jù)前面的指令插入的appName屬性的值設(shè)置上下文名稱。
文件包含
Joran 支持從另一個文件中包含配置文件的部分。這是通過聲明一個元素來實現(xiàn)的,如下所示:
目標(biāo)文件必須將其元素嵌套在元素中。例如,一個ConsoleAppender可以聲明為:
"%d - %m%n"
再次強調(diào),必須使用元素。
要包含的內(nèi)容可以作為文件、資源或URL引用。
作為一個文件:
要包含文件,請使用file屬性。您可以使用相對路徑,但是請注意,當(dāng)前目錄是由應(yīng)用程序定義的,并不一定與配置文件的路徑相關(guān)。
作為一個資源:
為了包含資源,比如在類路徑上找到一個文件,使用resource屬性。
作為一個 URL:
要包含 URL 的內(nèi)容,請使用 URL 屬性。
如果找不到要包含的文件,logback將通過打印狀態(tài)消息來提示。如果包含的文件是可選的,您可以通過將元素中的可選屬性設(shè)置為true來抑制警告消息。
添加上下文偵聽器
LoggerContextListener接口的實例偵聽與日志記錄器上下文的生命周期相關(guān)的事件。
JMXConfigurator是LoggerContextListener接口的一個實現(xiàn)。它將在下一章進(jìn)行描述。
#######LevelChangePropagator
從0.9.25版開始,logback-classic附帶LevelChangePropagator,這是LoggerContextListener的實現(xiàn),它將任何logback-classic日志程序級別的更改傳播到j(luò)ava.util.logging框架。這種傳播消除了禁用日志語句的性能影響。LogRecord實例將僅通過啟用的日志語句發(fā)送到logback(通過SLF4J)。這使得實際應(yīng)用程序可以合理地使用jul-to-slf4j橋接。
contextListener元素可用于安裝LevelChangePropagator,如下所示。
....
設(shè)置LevelChangePropagator的resetJUL屬性將重置所有j.u.l.日志記錄器的所有以前的級別配置。但是,以前安裝的處理程序?qū)⒈3植蛔儭?/p>
true
....
總結(jié)
以上是生活随笔為你收集整理的服务器日志文件中包含堆栈跟踪,日志框架 Logback 官方手册(第三章:Configuration)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信能转账到银行卡吗
- 下一篇: html盒子有哪些属性,盒子模型有哪些属