log4j和slf4j的區別


之前在項目中用的日志記錄器都是log4j的日志記錄器,可是到了公司發現項目要求使用slf4j,於是想着研究一下slf4j的用法。

   注意:每次引入Logger的時候注意引入的jar包,因為有Logger的包太多了。。。。。。

     Logger必須作為類的靜態變量使用。原因如下:

1 使用static修飾的屬性是歸這個類使用的
2 也就是說不論這個類實例化多少個,大家用的都是同一個static屬性
3 log4j記錄的是當前類的日志,不是每個實例的日志
4 所以只要有一個記錄就可以了


創建日志記錄器方法:(最好聲明加final關鍵字)
    //private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志記錄器
    private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class.getName());// slf4j日志記錄器

 

簡要記錄一下日志級別:
復制代碼
每個Logger都被了一個日志級別(log level),用來控制日志信息的輸出。日志級別從高到低分為:
A:off         最高等級,用於關閉所有日志記錄。
B:fatal       指出每個嚴重的錯誤事件將會導致應用程序的退出。
C:error      指出雖然發生錯誤事件,但仍然不影響系統的繼續運行。
D:warm     表明會出現潛在的錯誤情形。
E:info         一般和在粗粒度級別上,強調應用程序的運行全程。
F:debug     一般用於細粒度級別上,對調試應用程序非常有幫助。
G:all           最低等級,用於打開所有日志記錄。
復制代碼


其實對於不同的版本有不同的級別,不過最常用的就是debug\info\warn\error.下面是摘自log4j-1.2.17.jar中的級別:
可以看出:all\TRACE\debug
同級別。off與fatal同級別。
復制代碼
package org.apache.log4j;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;


public class Level extends Priority implements Serializable {

   /**
    * TRACE level integer value.
    * @since 1.2.12
    */
  public static final int TRACE_INT = 5000;

final static public Level OFF = new Level(OFF_INT, "OFF", 0); final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0); final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3); final static public Level WARN = new Level(WARN_INT, "WARN", 4); final static public Level INFO = new Level(INFO_INT, "INFO", 6); final static public Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7); public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7); final static public Level ALL = new Level(ALL_INT, "ALL", 7); static final long serialVersionUID = 3491141966387921974L; protected Level(int level, String levelStr, int syslogEquivalent) { super(level, levelStr, syslogEquivalent); } public static Level toLevel(int val, Level defaultLevel) { switch(val) { case ALL_INT: return ALL; case DEBUG_INT: return Level.DEBUG; case INFO_INT: return Level.INFO; case WARN_INT: return Level.WARN; case ERROR_INT: return Level.ERROR; case FATAL_INT: return Level.FATAL; case OFF_INT: return OFF; case TRACE_INT: return Level.TRACE; default: return defaultLevel; } } 。。。。。
復制代碼


0.依賴的Jar包
復制代碼
        <!-- slf4j 依賴包 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.0-rc1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.0-rc1</version>
        </dependency>
復制代碼

 

或者手動導入下面的包:

 

介紹:slf4j-api提供接口,slf4j-log4j提供具體的實現。也就是 slf4j只是一個日志標准,並不是日志系統的具體實現,如果項目只有slf4j的包是沒有辦法實現日志功能的。

 

1 基本介紹

  SLF4J不同於其他日志類庫,與其它日志類庫有很大的不同。SLF4J(Simple logging Facade for Java)不是一個真正的日志實現,而是一個抽象層( abstraction layer),它允許你在后台使用任意一個日志類庫。如果是在編寫供內外部都可以使用的API或者通用類庫,那么你真不會希望使用你類庫的客戶端必須使用你選擇的日志類庫。

  如果一個項目已經使用了log4j,而你加載了一個類庫,比方說 Apache Active MQ——它依賴於於另外一個日志類庫logback,那么你就需要把它也加載進去。但如果Apache Active MQ使用了SLF4J,你可以繼續使用你的日志類庫而無需忍受加載和維護一個新的日志框架的痛苦。

  總的來說,SLF4J使你的代碼獨立於任意一個特定的日志API,這是對於API開發者的很好的思想。雖然抽象日志類庫的思想已經不是新鮮的事物,而且Apache commons logging也已經在使用這種思想了,但SLF4J正迅速成為Java世界的日志標准。讓我們再看幾個使用SLF4J而不是log4j、logback或者java.util.logging的理由。

2 SLF4J對比Log4J,logback和java.util.Logging的優勢

正如我之前說的,在你的代碼中使用SLF4J寫日志語句的主要出發點是使得你的程序獨立於任何特定的日志類庫,依賴於特定類庫可能需要使用不同於你已有的配置,並且導致更多維護的麻煩。除此之外,還有一個SLF4J API的特性是使得我堅持使用SLF4J而拋棄我長期間鍾愛的Log4j的理由,是被稱為占位符(place holder),在代碼中表示為“{}”的特性。占位符是一個非常類似於在Stringformat()方法中的%s,因為它會在運行時被某個提供的實際字符串所替換。這不僅降低了你代碼中字符串連接次數,而且還節省了新建的String對象。通過使用SLF4J,你可以在運行時延遲字符串的建立,這意味着只有需要的String對象才被建立。而如果你已經使用log4j,那么你已經對於在if條件中使用debug語句這種變通方案十分熟悉了,但SLF4J的占位符就比這個好用得多。

3.slf4j的簡單用法

1.簡單使用

1.配置文件(log4j.properties)

復制代碼
log4j.rootLogger=debug, C

log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

log4j.appender.B=org.apache.log4j.FileAppender
log4j.appender.B.File=E:\\log.log
log4j.appender.B.layout=org.apache.log4j.SimpleLayout

log4j.appender.C=org.apache.log4j.RollingFileAppender
log4j.appender.C.File=E:\\log.html
log4j.appender.C.MaxFileSize=1000KB
log4j.appender.C.MaxBackupIndex=10
log4j.appender.C.layout=org.apache.log4j.HTMLLayout
log4j.appender.C.encoding=gbk

log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=E:\\log.log
log4j.appender.D.layout=org.apache.log4j.TTCCLayout
復制代碼

 

注意:  

  1.如果需要輸出到多個位置的時候可以逗號隔開,比如:   log4j.rootLogger=info, A, B 

  2.    log4j.appender.C.encoding=gbk 的配置是為了解決中文亂碼,有時候也可以設置為UTF-8試試

 

關於pattern的設置如下:

復制代碼
%p: 輸出日志信息優先級,即DEBUG,INFO,WARN,ERROR,FATAL,%d: 輸出日志時間點的日期或時間,默認格式為ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},輸出類似:2011-10-18 22:10:28,921 
%r: 輸出自應用啟動到輸出該log信息耗費的毫秒數 
%c: 輸出日志信息所屬的類目,通常就是所在類的全名 
%t: 輸出產生該日志事件的線程名 
%l: 輸出日志事件的發生位置,相當於%C.%M(%F:%L)的組合,包括類目名、發生的線程,以及在代碼中的行數。 
%x: 輸出和當前線程相關聯的NDC(嵌套診斷環境),尤其用到像java servlets這樣的多客戶多線程的應用中。 
%%: 輸出一個"%"字符 
%F: 輸出日志消息產生時所在的文件名稱 
%L: 輸出代碼中的行號 
%m: 輸出代碼中指定的消息,產生的日志具體信息 
%n: 輸出一個回車換行符,Windows平台為"\r\n",Unix平台為"\n"輸出日志信息換行 
復制代碼

 

2.測試代碼:

復制代碼
package cn.xm.exam.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {
    private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志記錄器

    public static void main(String[] args) {

        // 普通的日志記錄
        logger.debug("普通的日志記錄");

        // {}占位符記錄日志
        for (int i = 0; i < 3; i++) {
            logger.debug("這是第{}條記錄", i);
        }

        // 用\轉義{}
        logger.debug("Set \\{} differs from {}", "3"); // output:Set {} differs
                                                        // from 3

        // 兩個參數
        logger.debug("兩個占位符,可以傳兩個參數{}----{}", 1, 2);

        // 多個參數(可變參數)
        logger.debug("debug:多個占位符,{},{},{},{}", 1, 2, 3, 4);

        // 多個參數(可變參數)
        logger.info("info:多個占位符,{},{},{},{}", 1, 2, 3, 4);

        // 多個參數(可變參數)
        logger.error("error:多個占位符,{},{},{},{}", 1, 2, 3, 4);

    }

}
復制代碼

結果:

2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 普通的日志記錄
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 這是第0條記錄
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 這是第1條記錄
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 這是第2條記錄
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] Set {} differs from 3
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 兩個占位符,可以傳兩個參數1----2
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] debug:多個占位符,1,2,3,4
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[INFO] info:多個占位符,1,2,3,4
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:多個占位符,1,2,3,4

 

注意:debug,info,error,等各個級別的方法都可以傳入多個可變參數:

 

至於詳細的日志級別的介紹參考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/7192947.html

 

2.關於正式開發中的日記記錄的方法:(重要)

  項目中日志記錄還是有很多說法的,比如保存日志級別,日志應該打印的信息,日志參數的設置等:

 

1.一般是將捕捉到的Exception對象作為日志記錄的最后一個參數(會顯示具體的出錯信息以及出錯位置),而且要放在{}可以格式化的參數之外,防止被{}轉為e.toString()

例如一個標准的日記記錄的方法:

復制代碼
package cn.xm.exam.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {

    private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);

    public static void main(String[] args) {
        openFile("xxxxxx");
    }

    public static void openFile(String filePath) {
        File file = new File(filePath);
        try {
            InputStream in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            log.error("can found file [{}]", filePath, e);
        }
    }

}
復制代碼

結果:

復制代碼
2018-08-18 10:50:03 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx]
java.io.FileNotFoundException: xxxxxx (系統找不到指定的文件。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at cn.xm.exam.test.Slf4jTest.openFile(Slf4jTest.java:22)
    at cn.xm.exam.test.Slf4jTest.main(Slf4jTest.java:16)
復制代碼

 

注意: {}的作用是調用對應參數的toString()方法格式化日志,如果exception放在對應參數的位置上也會被格式化。所以,e要放在{}參數之外,而且只能放在最后一個。如果放在中間也不會被打印錯誤信息:

例如:

  • 只要放到{}之外的最后一個參數可以打印錯誤信息(包括信息和位置)
復制代碼
    public static void openFile(String filePath) {
        File file = new File(filePath);
        try {
            InputStream in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            log.error("error:can found file [{}]", filePath, 3, e);
        }
    }
復制代碼

結果:

復制代碼
2018-08-18 11:11:18 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:can found file [xxxxxx]
java.io.FileNotFoundException: xxxxxx (系統找不到指定的文件。)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at cn.xm.exam.test.Slf4jTest.openFile(Slf4jTest.java:22)
    at cn.xm.exam.test.Slf4jTest.main(Slf4jTest.java:16)
復制代碼

 

  • 放到{}之外的非最后一個參數不會打印錯誤信息
復制代碼
    public static void openFile(String filePath) {
        File file = new File(filePath);
        try {
            InputStream in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            log.error("error:can found file [{}]", filePath,e,3);
        }
    }
復制代碼

結果:

2018-08-18 11:10:38 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:can found file [xxxxxx]

 

 

 

2.盡量不使用e.getMessage(),因為有的異常不一定有message,可以使用e.toString只會顯示信息,不會顯示出錯的位置信息(不建議這種)

例如:

復制代碼
package cn.xm.exam.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {

    private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);

    public static void main(String[] args) {
        openFile("xxxxxx");
    }

    public static void openFile(String filePath) {
        File file = new File(filePath);
        try {
            InputStream in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            //下面兩句的效果一樣
            log.error("can found file [{}],cause:{}", filePath, e.toString());
            log.error("can found file [{}],cause:{}", filePath, e);
        }
    }

}
復制代碼

結果:

2018-08-18 10:53:31 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx],cause:java.io.FileNotFoundException: xxxxxx (系統找不到指定的文件。)
2018-08-18 10:53:31 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx],cause:java.io.FileNotFoundException: xxxxxx (系統找不到指定的文件。)

 

 

補充:一份上線系統使用的log4j.properties配置:

復制代碼
log4j.rootLogger=info,B

log4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

log4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.File=E:\\test.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
復制代碼

%d{yyyy-MM-dd HH:mm:ss} 也可以改為%d{yyyy-MM-dd HH:mm:ss,SSS} SSS代表至少輸出3位數字的毫秒。如果4個S的話不足位補0。

 

解釋:org.apache.log4j.RollingFileAppender是log4j的一個類,下面的配置都是它的屬性的配置:(其中還有好多屬性來自其父類,需要的時候我們可以去查閱)

 

log4j.appender.B.File    指定輸出的日志的文件名稱以及路徑

log4j.appender.B.MaxFileSize   指定每個文件的最大大小

log4j.appender.B.MaxBackupIndex  指定文件達到文件大小之后最多重命名的文件數量

log4j.appender.B.layout  指定采用的樣式

log4j.appender.B.layout.ConversionPattern  指定樣式的格式(值一般固定)

 

測試代碼:

復制代碼
package cn.qlq.slf4jTest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {
    private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志記錄器

    public static void main(String[] args) {

        while (true) {

            // 普通的日志記錄
            logger.error("普通的日志記錄");

            // {}占位符記錄日志
            for (int i = 0; i < 3; i++) {
                logger.info("這是第{}條記錄", i);
            }

            // 用\轉義{}
            logger.info("Set \\{} differs from {}", "3"); // output:Set {} differs from 3

            // 兩個參數
            logger.info("兩個占位符,可以傳兩個參數{}----{}", 1, 2);

        }

    }

}
復制代碼

結果:(會產生對應的文件,test.log永遠保存最新的日志,達到10M后會重命名文件為test.log.1,並將原來test.log.1命名為test.log.2....)

log內容如下:

 

 

補充:Threshold還可以指定輸出的日志級別: (如果設置不起作用查看項目是不是用的log4j的包,有可能有多個log4j包,造成沖突)

  有時候我們需要把一些報錯ERROR日志單獨存到指定文件 ,這時候,Threshold屬性就派上用場了,比如:

復制代碼
log4j.rootLogger=info,B,C

log4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.Threshold=info
log4j.appender.B.File=E:\\info.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

log4j.appender.C=org.apache.log4j.RollingFileAppender
log4j.appender.C.Threshold=error
log4j.appender.C.File=E:\\error.log
log4j.appender.C.MaxFileSize=10MB
log4j.appender.C.MaxBackupIndex=5
log4j.appender.C.layout=org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
復制代碼

 

上面B格式的就是info級別以上的日志,包括error信息 

  C格式的就是error級別以上的信息。

 

  當然這里有個提前  rootLogger里配置的level必須小於Threshold等級,否則無效 還是按照總的rootLogger里的level來輸出,一般我們實際實用的話 rootLogger里配置DEBUG,然后某個文件專門存儲ERRO日志,就配置下Threshold為ERROR

 

代碼還是上面的測試代碼:

結果:

 

 

 error.log內容:全是error級別以上的日志

 

info.log內容(info級別以上,包括error)

 

補充:log4j也可以對不同的包進行不同的配置,也就是針對不同的包采用不同的日志級別與日志控制器

  有時候我們也希望對不同的包采用不通的日志記錄級別以及不同的日志記錄方式。log4j完全可以做到這點,例如下面

默認包采用rootLogger的配置,info級別、在控制台與文件中進行顯示;同時又修改了cn下面的a、b、c包的日志級別:

  cn.a只降低了級別為debug,輸出方式還是console與file兩種。(一般我們采用這種方式修改級別即可,如果再設置輸出方式會在原來的基礎上增加方式)

  cn.b級別設為info,方式設為console,實際是加了第三種方式

  cn.c級別設置為error,方式設為file,實際也是在rootLogger基礎上增加第三種方式

   級別會以log4j.logger.XX級別為准,不管rootLogger級別高於對具體包的設置還是低於具體包的設置;輸出方式會在rootLogger的基礎上增加新的方式,如果沒有額外的方式采用rootLogger的方式。

例如:(注意標紅地方)

復制代碼
log4j.rootLogger=info,console,file

log4j.logger.cn.a=debug
log4j.logger.cn.b=info,console
log4j.logger.cn.c=error,file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=E:\\test.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
復制代碼

 

測試:

包結構:

 

Root

復制代碼
package cn;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RootClass {
    private static final Logger LOGGER = LoggerFactory.getLogger(RootClass.class);

    public static void main(String[] args) {
        LOGGER.debug("RootClass");
        LOGGER.info("RootClass");
        LOGGER.error("RootClass");
    }
}
復制代碼

運行結果:

2018-10-23 19:38:04 [cn.RootClass]-[INFO] RootClass
2018-10-23 19:38:04 [cn.RootClass]-[ERROR] RootClass

 

 AC1

復制代碼
package cn.a;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AC1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(AC1.class);

    public static void main(String[] args) {
        LOGGER.debug("AC1");
        LOGGER.info("AC1");
        LOGGER.error("AC1");
    }
}
復制代碼

運行結果:

2018-10-23 19:38:31 [cn.a.AC1]-[DEBUG] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[INFO] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[ERROR] AC1

 

BC1:

復制代碼
package cn.b;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BC1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(BC1.class);

    public static void main(String[] args) {
        LOGGER.debug("BC1");
        LOGGER.info("BC1");
        LOGGER.error("BC1");
    }
}
復制代碼

運行結果:

2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1

 

CC1:

復制代碼
package cn.c;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CC1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(CC1.class);
    public static void main(String[] args) {
        LOGGER.debug("CC1");
        LOGGER.info("CC1");
        LOGGER.error("CC1");
    }
}
復制代碼

運行結果:

2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1

 

最后查看E:/test.log

復制代碼
2018-10-23 19:38:04 [cn.RootClass]-[INFO] RootClass
2018-10-23 19:38:04 [cn.RootClass]-[ERROR] RootClass
2018-10-23 19:38:31 [cn.a.AC1]-[DEBUG] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[INFO] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[ERROR] AC1
2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1
2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1
2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1
復制代碼

 

補充:對父包設置日志級別,如果子包沒有設置默認采用父包的設置,如果子包設置了會采用單獨的設置

配置修改cn包設置以及對b包單獨進行設置:

log4j.logger.cn=error
log4j.logger.cn.b=info,console

 

測試cn.d包默認采用cn包的error級別:

復制代碼
package cn.d;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DC1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(DC1.class);

    public static void main(String[] args) {
        LOGGER.debug("D");
        LOGGER.info("D");
        LOGGER.error("D");
    }
}
復制代碼

結果:

2018-12-29 13:30:09 [cn.d.DC1]-[ERROR] D

 

測試cn.b采用單獨對cn.b包的配置

復制代碼
package cn.b;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BC1 {
    private static final Logger LOGGER = LoggerFactory.getLogger(BC1.class);

    public static void main(String[] args) {
        LOGGER.debug("BC1");
        LOGGER.info("BC1");
        LOGGER.error("BC1");
    }
}
復制代碼

結果:

2018-12-29 13:31:27 [cn.b.BC1]-[INFO] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[INFO] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[ERROR] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[ERROR] BC1

 

補充:上面的對包的具體設置日志級別雖然不受總的log4j.rootLogger的日志級別的限制,但是卻受特殊的日志的Threshold屬性的限制,也就是對具體包的設置必須高於其記錄器的Threshold屬性,否則以其記錄器的Threshold屬性為准

例如:修改上面的日志配置(每個日志記錄器增加Threshold屬性)

復制代碼
log4j.rootLogger=info,console,file

log4j.logger.cn.a=debug
log4j.logger.cn.b=info,console
log4j.logger.cn.c=error,file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=info
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.Threshold=error
log4j.appender.file.File=E:\\test.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
復制代碼

運行RootClass:

2018-11-12 23:02:44 [cn.RootClass]-[INFO] RootClass
2018-11-12 23:02:44 [cn.RootClass]-[ERROR] RootClass

 

運行AC1:

2018-11-12 23:03:08 [cn.a.AC1]-[INFO] AC1
2018-11-12 23:03:08 [cn.a.AC1]-[ERROR] AC1

 

運行BC1:

2018-11-12 23:03:33 [cn.b.BC1]-[INFO] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[INFO] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1

 

運行CC1:

2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1

 

查看test.log:

2018-11-12 23:02:44 [cn.RootClass]-[ERROR] RootClass
2018-11-12 23:03:08 [cn.a.AC1]-[ERROR] AC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1
2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1
2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1

所以總結上面的日記記錄級別的設置優先級可以總結為:Threshold  > 具體包的設置  >  rootLogger的全局配置


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM