008_logback配置语法
1. 配置文件的基本結構
1.1. Logback配置文件的語法非常靈活。正因為靈活, 所以無法用DTD或XML schema進行定義。盡管如此, 可以這樣描述配置文件的基本結構: 以<configuration>開頭, 后面有零個或多個<appender>元素, 有零個或多個<logger>元素, 有最多一個<root>元素。
2. 配置logger或<logger>元素
2.1. Logger是用<logger>元素配置的。<logger>元素有且僅有一個name屬性、一個可選的level屬性和一個可選的additivity屬性。Level屬性的值大小寫無關, 其值為: TRACE、DEBUG、INFO、 WARN、ERROR、ALL和OFF中的一個值。還可以是一個特殊的字符串"INHERITED"或其同義詞"NULL", 表示強制繼承上級的級別。
2.2. <logger>元素可以包含零個或多個<appender-ref>元素, 表示這個appender會被添加到該logger。強調一下, 每個用<logger>元素聲明的logger, 首先會移除所有的appender, 然后添加引用的appender, 所以如果logger沒有引用任何appender, 就會失去所有appender。
2.3. 例子
2.3.1. 新建一個名為LogbackCfgGrammer的Java項目, 同時添加相關jar包
2.3.2. 編輯LoggerCfg.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class LoggerCfg {private static final Logger logger = LoggerFactory.getLogger(LoggerCfg.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/logger.xml");} catch (JoranException e) {e.printStackTrace();}logger.info("配置logger或<logger>元素");logger.debug("配置logger或<logger>元素");} }2.3.3. 新建cfg文件夾, 在該文件夾下編輯logger.xml
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.zr.cfg.LoggerCfg" level="info"><appender-ref ref="stdout" /></logger></configuration>2.3.4. 只會打印info和高于info級別的日志
3. 配置根logger或<root>元素?
3.1. <root>元素配置根logger。該元素有一個level屬性。沒有name屬性, 因為已經被命名為"ROOT"。Level屬性的值大小寫無關, 其值為: TRACE、DEBUG、INFO、 WARN、ERROR、ALL和OFF中的一個值。注意不能設置為"INHERITED"或"NULL"。
3.2. <logger>元素可以包含零個或多個<appender-ref>元素。與<logger>元素類似, 聲明<root>元素后, 會先關閉然后移除全部當前appender, 只引用聲明的appender。如果root元素沒有引用任何appender, 就會失去所有appender。
3.3. 基本選擇規則依賴于被調用的logger的有效級別, 而不是appender所關聯的logger的級別。Logback首先判斷記錄語句是否被啟用, 如果啟用, 則調用logger等級里的appender, 同時如果有繼承的話, 還會調用祖先的appender, 無視logger的級別。
3.4. 例子
3.4.1. 編輯RootLoggerCfg.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class RootLoggerCfg {private static final Logger logger = LoggerFactory.getLogger(RootLoggerCfg.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/rootlogger.xml");} catch (JoranException e) {e.printStackTrace();}logger.info("配置根logger或<root>元素");logger.debug("配置根logger或<root>元素");} }3.4.2. 在cfg文件夾下編輯rootlogger.xml
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.zr.cfg.RootLoggerCfg" level="info"></logger><root level="off"><appender-ref ref="stdout" /></root> </configuration>3.4.3. 各個logger的級別
3.4.4. 配置里唯一的appender"stdout", 被關聯到級別為OFF的根logger, 根logger的級別不起任何作用。com.zr.cfg.RootLoggerCfg類的INFO級別是啟用的, 因此會輸出com.zr.cfg.RootLoggerCfg類INFO級別及其更高級別的日志:
4. 配置Appender
4.1. Appender用<appender>元素配置, 該元素必要屬性name和class。name屬性指定appender的名稱, class屬性指定appender類的全限定名。
4.2. <appender>元素可以包含零個或一個<layout>元素或者零個或一個<encoder>元素和零個或多個<filter>元素。除了這三個常用元素之外, 還可以包含任意數量的javabean屬性。
4.3. 下圖演示了常用結構, 注意對javabean屬性的支持在圖中不可見。
4.4. <layout>元素的class屬性是必要的, 表示將被實例化的layout類的全限定名。因為太常用了, 所以當layout是PatternLayout 時, 可以省略class屬性。
4.5. <encoder>元素class屬性是必要的, 表示將被實例化的encoder類的全限定名。因為太常用了, 所以當encoder是PatternLayoutEncoder時, 可以省略class屬性。
4.6. 記錄輸出到多個appender很簡單, 先定義各種appender, 然后在logger里進行引用就行了。
4.7. 注意每個appender都有自己的encoder。Encoder通常不能被多個appender共享, layout也是。
4.8. 例子
4.8.1. 編輯AppenderCfg.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class AppenderCfg {private static final Logger logger = LoggerFactory.getLogger(AppenderCfg.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/appender.xml");} catch (JoranException e) {e.printStackTrace();}logger.info("Appender用<appender>元素配置, 該元素必要屬性name和class。");logger.debug("name屬性指定appender的名稱, class屬性指定appender類的全限定名。");} }4.8.2. 在cfg文件夾下編輯appender.xml
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></layout></appender><appender name="file" class="ch.qos.logback.core.FileAppender"><file>log/my.log</file><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="file" /><appender-ref ref="stdout" /></root></configuration>4.8.3. 運行結果
4.9. Appender累積
4.9.1. 默認情況下, appender是可累積的: logger會把記錄輸出到它自身的appender和它所有祖先的appender。因此, 把同一appender關聯到多個logger會導致重復輸出。
4.9.2. Appender的疊加性對新手來說并不是陷阱, 反而是非常方便的。舉例來說, 你可以讓某些系統里所有logger的記錄信息出現在控制臺, 卻讓某些特定logger的記錄信息發到一個特定的appender。
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></layout></appender><appender name="file" class="ch.qos.logback.core.FileAppender"><file>log/error.log</file><append>false</append><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><logger name="com.zr.cfg.AccumulateCfg" level="error"><appender-ref ref="file" /></logger><root><appender-ref ref="stdout" /></root></configuration>4.9.3. 如果你覺得默認的累積行為不合適, 可以設置疊加性標識為false以關閉它。這樣的話, logger樹里的某個分支可以輸出到與其他logger不同的appender。
<configuration><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><layout class="ch.qos.logback.classic.PatternLayout"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></layout></appender><appender name="file" class="ch.qos.logback.core.FileAppender"><file>log/error.log</file><append>false</append><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><logger name="com.zr.cfg.AccumulateCfg" level="error" additivity="false"><appender-ref ref="file" /></logger><root><appender-ref ref="stdout" /></root></configuration>4.9.4. 創建AccumulateCfg.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class AccumulateCfg {private static final Logger logger = LoggerFactory.getLogger(AccumulateCfg.class);private static final Logger root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/accumulate.xml");} catch (JoranException e) {e.printStackTrace();}logger.error("默認情況下, appender是可累積的: logger會把記錄輸出到它自身的appender和它所有祖先的appender。");logger.error("因此, 把同一appender關聯到多個logger會導致重復輸出。");logger.error("Appender的疊加性對新手來說并不是陷阱, 反而是非常方便的。");root.warn("舉例來說, 你可以讓某些系統里所有logger的記錄信息出現在控制臺, 卻讓某些特定logger的記錄信息發到一個特定的appender。");root.info("如果你覺得默認的累積行為不合適, 可以設置疊加性標識為false以關閉它。");root.debug("這樣的話, logger樹里的某個分支可以輸出到與其他logger不同的appender。");} }4.9.5. 創建accumulate.xml, 使用步驟4.9.2.的配置文件
4.9.6. 運行AccumulateCfg.java
4.9.7. 修改accumulate.xml配置, 使用步驟4.9.3.的配置文件, 再次運行AccumulateCfg.java
5. 設置上下文名稱?
5.1. 每個logger都關聯到logger上下文。默認情況下, logger上下文名為"default"。但是你可以借助配置指令<contextName>設置成其他名字。注意一旦設置logger上下文名稱后, 不能再改。設置上下文名稱后, 可以方便地區分來自不同應用程序的記錄。
5.2. 例子
5.2.1. 編輯ContextName.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class ContextName {private static final Logger logger = LoggerFactory.getLogger(ContextName.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/contextName.xml");} catch (JoranException e) {e.printStackTrace();}logger.info("每個logger都關聯到logger上下文。");logger.debug("你可以借助配置指令<contextName>設置上下文名字。");} }5.2.2. 在cfg文件夾下編輯contextName.xml
<configuration debug="true"><contextName>日志服務器</contextName><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root><appender-ref ref="stdout" /></root> </configuration>5.2.3. 運行結果
6. 變量替換?
6.1. 原則上, 指定變量的地方就能夠發生變量替換。變量替換的語法與Unix shell中的變量替換相似。位于"${"與"}"之間的字符串是鍵(key), 取代鍵的值可以在同一配置文件里指定, 也可以在外部文件或通過系統屬性進行指定。
6.2. 屬性被插入logger上下文
6.2.1. 注意通過<property>元素定義的值實際上會被插入logger上下文。換句話說, 這些值變成了logger上下文的屬性。所以, 它們對所有記錄事件都可用, 包括通過序列化方式被發送到遠程主機的記錄事件。
6.2.2. 下面的例子在配置文件的開頭聲明了一個變量又名替換屬性, 它代表輸出文件的位置, 然后在后面的配置文件里使用它。
<property name="fileName" value="my.log" />6.3. 屬性文件
6.3.1. 當需要很多變量時, 更方便的做法是在一個單獨的文件里聲明所有變量。
<property file="variables.properties" />6.3.2. 還可以不引用文件, 而是引用class path上的資源。
<property resource="variables.properties" />6.3.3. variables.properties文件內容類似于:
pathPre=/log fileName=my.log fullPath=/log/my.log6.4. 嵌套變量替換
6.4.1. Logback支持嵌套變量替換。這里的嵌套是指變量的值里包含對其他變量的引用。
pathPre=/log fileName=my.log fullPath=${pathPre}/${fileName}6.5. 變量的默認替換值?
6.5.1. 在某些特定情況下, 最好給變量一個默認值, 以免變量未被聲明或值為null。Bash shell用":-"指定默認值。例如, 假設"appName"未被聲明, 那么"${appName:-zrApp}"將被解釋為"zrApp"。
6.6. Logback自動定義了一個常用變量"${HOSTNAME}"。
7. 配置文件里的條件化處理?
7.1. 開發者經常需要針對不同的環境在不同的配置文件里換來換去, 比如開發、測試和生產環境。這些配置文件大同小異。為避免重復勞動, logback支持在配置文件里進行條件化處理, 用<if>、<then>和<else>這些元素可以讓一個配置文件適用于多個環境。
7.2. 條件語句一般格式如下
<configuration> <!-- if-then form --> <if condition="some conditional expression"> <then> ... </then> </if> <!-- if-then-else form --> <if condition="some conditional expression"> <then> ... </then> <else> ... </else> </if> </configuration7.3. 其中"condition"是java表達式, 只允許訪問上下文屬性和系統屬性。對于作為參數傳入的鍵, property()方法或其等價的p()方法將返回屬性的字符串值。例如, 想訪問屬性鍵為"k"的值, 你可以用property("k")或等價的 p("k")。如果鍵為"k"的屬性未被定義, property方法將返回空字符串而不是null, 這樣避免了檢查null值。
7.4. 例子
7.4.1. 條件語句需要兩個額外的包commons-compiler-3.1.3.jar和janino-3.1.3.jar
7.4.2. 編輯Condition.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class Condition {private static final Logger logger = LoggerFactory.getLogger(Condition.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/condition.xml");} catch (JoranException e) {e.printStackTrace();}logger.error("錯誤信息");logger.warn("警告信息");logger.info("信息");logger.debug("測試信息");} }7.4.3. 在cfg文件夾下編輯condition.xml
<configuration><property name="development" value="dev" /><contextName>${pathPre:-zrApp}</contextName><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <if condition='p("development").contains("test")'> <then> <root level="debug"><appender-ref ref="stdout" /></root></then><else> <root level="warn"><appender-ref ref="stdout" /></root></else></if> </configuration>7.4.4. 運行結果
8. 從JNDI獲取變量
8.1. 在某些特定情況下, 你也許利用JNDI里存儲的env項, <insertFromJNDI>指令會從JNDI里取得env項, 然后用as屬性把它們作為變量。
8.2. 新建一個名為JNDIGetVariable的動態Web工程, 同時添加相關jar包
8.3. 創建JNDIAction.java
package com.fj;import java.io.IOException; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class JNDIAction extends HttpServlet {private static final long serialVersionUID = 1L;private static final Logger logger = LoggerFactory.getLogger(JNDIAction.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {InitialContext ctx = new InitialContext();String contextName = (String) ctx.lookup("java:comp/env/logback/context-name");logger.error("從JNDI獲取變量: {}", contextName);} catch (NamingException e) {e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);} }8.4. 在src目錄下添加logback.xml
<configuration><insertFromJNDI env-entry-name="java:comp/env/logback/context-name" as="context-name" /><contextName>${context-name}</contextName><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>%contextName %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="stdout" /></root></configuration>8.5. 配置web.xml
8.6. 運行項目
9. 文件包含?
9.1. Joran支持在配置文件里包含其他文件。方法是聲明<include>元素, 被包含的內容可以是文件、資源或URL。
9.2. 作為文件, 用"file"屬性包含一個文件。可以用相對路徑, 但是需要注意, 當前目錄是由應用程序決定的, 與配置文件的路徑必要的聯系。
<include file="includedConfig.xml" />9.3. 作為資源, 用"resource"屬性包含一個資源, 也就是在class path上的文件。
<include resource="includedConfig.xml" />9.4. 作為URL, 用"url"屬性包括一個URL。
<include url="http://some.host.com/includedConfig.xml" />9.5. 被包含的文件必須把它的元素嵌套在<included>元素里。請注意<included>元素是必需的。
9.6. 例子
9.6.1. 編輯IncludeCfg.java
package com.zr.cfg;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException;public class IncludeCfg {private static final Logger logger = LoggerFactory.getLogger(IncludeCfg.class);public static void main(String[] args) {// 上下文LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();// Joran配置文件處理類JoranConfigurator cfg = new JoranConfigurator();cfg.setContext(lc);// 上下文已經讀取配置文件或使用默認配置文件, 這里進行重置操作。lc.reset();try {cfg.doConfigure("cfg/containingcfg.xml");} catch (JoranException e) {e.printStackTrace();}logger.error("錯誤信息");logger.warn("警告信息");logger.info("信息");logger.debug("測試信息");} }9.6.2. 在cfg文件夾下編輯containingcfg.xml
<included><appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <encoder charset="UTF-8"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> </included>9.6.3. 在cfg文件夾下編輯includecfg.xml
<configuration><include file="cfg/includecfg.xml"></include><root><appender-ref ref="stdout" /></root> </configuration>9.6.4. 運行結果
總結
以上是生活随笔為你收集整理的008_logback配置语法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 001-SDK框架之Unity游戏调用S
- 下一篇: 017_layout排版