Log4j、Log4j 二、Logback、SFL4J、JUL、JCL的比較

Log4j

     Log4j = Log for Java.
     author: Ceki Gülcü
     license: Apache License V2.0
     Log4j是Apache的一個開放源代碼項目,經過使用Log4j,咱們能夠控制日誌信息輸送的目的地是控制檯、文件、數據庫等;咱們也能夠控制每一條日誌的輸出格式;經過定義每一條日誌信息的級別,咱們可以更加細緻地控制日誌的生成過程。
     Log4j有7種不一樣的log級別,按照等級從低到高依次爲:TRACE<DEBUG<INFO<WARN<ERROR<FATAL<OFF。若是配置爲OFF級別,表示關閉log。    
     Log4j支持兩種格式的配置文件:properties和xml。包含三個主要的組件:Logger、appender、Layout。    
Example for log4j 1.2     html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration>    
  <!--an appender is an output destination, such as the console or a file; names of appenders are arbitrarily chosen-->    
  <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
    </layout>
  </appender>

  <!--loggers of category 'org.springframework' will only log messages of level "info" or higher; if you retrieve Loggers by using the class name (e.g. Logger.getLogger(AClass.class)) and if AClass is part of the org.springframework package, it will belong to this category-->    
  <logger name="org.springframework">        
    <level value="info"/>    
  </logger>    

  <!--everything of spring was set to "info" but for class PropertyEditorRegistrySupport we want "debug" logging-->    
  <logger name="org.springframework.beans.PropertyEditorRegistrySupport">        
    <level value="debug"/>    
  </logger>     
  
  <root>    
  <!--all log messages of level "debug" or higher will be logged, unless defined otherwise all log messages will be logged to the appender "stdout", unless defined otherwise-->        
    <level value="debug" />        
    <appender-ref ref="stdout" />    
  </root>

</log4j:configuration> 

SLF4J

     SLF4J = Simple Logging Facade for Java.
     author: Ceki Gülcü
     SLF4J,即簡單日誌門面(Simple Logging Facade for Java),不是具體的日誌解決方案,而是經過Facade Pattern提供一些Java logging API,它只服務於各類各樣的日誌系統。按照官方的說法,SLF4J是一個用於日誌系統的簡單Facade,容許最終用戶在部署其應用時使用其所但願的日誌系統。做者建立SLF4J的目的是爲了替代Jakarta Commons-Logging。
     實際上,SLF4J所提供的核心API是一些接口以及一個LoggerFactory的工廠類。在使用SLF4J的時候,不須要在代碼中或配置文件中指定你打算使用那個具體的日誌系統。SLF4J提供了統一的記錄日誌的接口,只要按照其提供的方法記錄便可,最終日誌的格式、記錄級別、輸出方式等經過具體日誌系統的配置來實現,所以能夠在應用中靈活切換日誌系統。
     那麼何時使用SLF4J比較合適呢?
     若是你開發的是類庫或者嵌入式組件,那麼就應該考慮採用SLF4J,由於不可能影響最終用戶選擇哪一種日誌系統。在另外一方面,若是是一個簡單或者獨立的應用,肯定只有一種日誌系統,那麼就沒有使用SLF4J的必要。假設你打算將你使用log4j的產品賣給要求使用JDK 1.4 Logging的用戶時,面對成千上萬的log4j調用的修改,相信這絕對不是一件輕鬆的事情。可是若是開始便使用SLF4J,那麼這種轉換將是很是輕鬆的事情。 java

Logback

      author: Ceki Gülcü
     licences:EPL v1.0 and LGPL 2.1
     Logback,一個「可靠、通用、快速而又靈活的Java日誌框架」。logback當前分紅三個模塊:logback-core,logback- classic和logback-access。logback-core是其它兩個模塊的基礎模塊。logback-classic是log4j的一個改良版本。此外logback-classic完整實現SLF4J API使你能夠很方便地更換成其它日誌系統如log4j或JDK14 Logging。logback-access訪問模塊與Servlet容器集成提供經過Http來訪問日誌的功能。
     
     1. logback-core: Joran, Status, context, pattern parsing
     2. logback-classic: developer logging
     3. logback-access: The log generated when a user accesses a web-page on a web server. Integrates seamlessly with Jetty and Tomcat.
選擇logback的理由:(http://logback.qos.ch/reasonsToSwitch.html#fasterImpl )
     1. logback比log4j要快大約10倍,並且消耗更少的內存。
     2. logback-classic模塊直接實現了SLF4J的接口,因此咱們遷移到logback幾乎是零開銷的。
     3. logback不只支持xml格式的配置文件,還支持groovy格式的配置文件。相比之下,Groovy風格的配置文件更加直觀,簡潔。
     4. logback-classic可以檢測到配置文件的更新,而且自動從新加載配置文件。
     5. logback可以優雅的從I/O異常中恢復,從而咱們不用從新啓動應用程序來恢復logger。
     6. logback可以根據配置文件中設置的上限值,自動刪除舊的日誌文件。
     7. logback可以自動壓縮日誌文件。
     8. logback可以在配置文件中加入條件判斷(if-then-else)。能夠避免不一樣的開發環境(dev、test、uat...)的配置文件的重複。
     9. logback帶來更多的filter。
     10. logback的stack trace中會包含詳細的包信息。
     11. logback-access和Jetty、Tomcat集成提供了功能強大的HTTP-access日誌。
     配置文件:須要在項目的src目錄下創建一個logback.xml。注:(1)logback首先會試着查找logback.groovy文件;(2)當沒有找到時,繼續試着查找logback-test.xml文件;(3)當沒有找到時,繼續試着查找logback.xml文件;(4)若是仍然沒有找到,則使用默認配置(打印到控制檯)。 詳細的配置在http://aub.iteye.com/blog/1101222這篇博客中解釋的很是清楚。在這裏感謝一下原做者(^_^)。git

JUL

     JUL = java.util.logging.

     Java提供了本身的日誌框架,相似於Log4J,可是API並不完善,對開發者不是很友好,並且對於日誌的級別分類也不是很清晰,好比:SEVERE, WARNING, INFO, CONFIG, FINE,FINER, FINEST。因此不推薦使用這種方式輸出日誌。github

JCL

     JCL = Jakarta Commons-Logging.
     Jakarta Commons Logging和SLF4J很是相似,也是提供的一套API來掩蓋了真正的Logger實現。便於不一樣的Logger的實現的替換,而不須要從新編譯代碼。缺點在於它的查找Logger的實現者的算法比較複雜,並且當出現了一些class loader之類的異常時,沒法去修復它。

web

Log4j2

     已經有不少其餘的日誌框架對Log4j進行了改良,好比說SLF4J、Logback等。並且Log4j 2在各個方面都與Logback很是類似,那麼爲何咱們還須要Log4j 2呢?
  1. 插件式結構。Log4j 2支持插件式結構。咱們能夠根據本身的須要自行擴展Log4j 2. 咱們能夠實現本身的appender、logger、filter。
  2. 配置文件優化。在配置文件中能夠引用屬性,還能夠直接替代或傳遞到組件。並且支持json格式的配置文件。不像其餘的日誌框架,它在從新配置的時候不會丟失以前的日誌文件。算法

Example for log4j 2    spring

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<!-- Log4j 2.x 配置文件。每30秒自動檢查和應用配置文件的更新; -->
<Configuration status="warn" monitorInterval="30" strict="true" schema="Log4J-V2.2.xsd">
    <Appenders>
           <!-- 輸出到控制檯 -->
           <Console name="Console" target="SYSTEM_OUT">
               <!-- 須要記錄的級別 -->
               <!-- <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" /> -->
               <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n" />
          </Console>

          <!-- 輸出到文件,按天或者超過80MB分割 -->
          <RollingFile name="RollingFile" fileName="../logs/xjj.log"    filePattern="../logs/$${date:yyyy-MM}/xjj-%d{yyyy-MM-dd}-%i.log.gz">
               <!-- 須要記錄的級別 -->
               <!-- <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" /> -->
               <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss,SSS}:%4p %t (%F:%L) - %m%n" />
               <Policies>
                    <OnStartupTriggeringPolicy />
                    <TimeBasedTriggeringPolicy />
                    <SizeBasedTriggeringPolicy size="80 MB" />
               </Policies>
          </RollingFile>
     </Appenders>
     <Loggers>
          <Root level="info"> <!-- 全局配置 -->
               <AppenderRef ref="Console" />
               <AppenderRef ref="RollingFile"/>
          </Root>
          <!-- 爲sql語句配置特殊的Log級別,方便調試 -->
          <Logger name="com.xjj.dao" level="${log.sql.level}" additivity="false">
               <AppenderRef ref="Console" />
          </Logger>
     </Loggers>
</Configuration>

  3. Java 5的併發性。Log4j 2利用Java 5中的併發特性支持,儘量地執行最低層次的加鎖。解決了在log4j 1.x中存留的死鎖的問題。若是你的程序仍然在飽受內存泄露的折磨,請堅決果斷地試一下log4j 2吧。sql

  4. 異步logger。Log4j 2是基於LMAX Disruptor庫的。在多線程的場景下,和已有的日誌框架相比,異步的logger擁有10左右的效率提高。
     還有更多的新特性,在這裏就不一一贅述了。瞭解更多請移步到:http://logging.apache.org/log4j/2.x/manual/index.html。 數據庫

注意:由於Log4j 2使用的是Jackson Data Processor來解析json文件的,因此想要使用json格式的配置文件,必須在項目中加入Jackson的依賴:apache

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.2.2</version>
</dependency>
 
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.2.2</version>
</dependency> 

slf4j與log4j聯合使用

  slf4j是什麼?slf4j只是定義了一組日誌接口,但並未提供任何實現,既然這樣,爲何要用slf4j呢?log4j不是已經知足要求了嗎?

  是的,log4j知足了要求,可是,日誌框架並不僅有log4j一個,你喜歡用log4j,有的人可能更喜歡logback,有的人甚至用jdk自帶的日誌框架,這種狀況下,若是你要依賴別人的jar,整個系統就用了兩個日誌框架,若是你依賴10個jar,每一個jar用的日誌框架都不一樣,豈不是一個工程用了10個日誌框架,那就亂了!

  若是你的代碼使用slf4j的接口,具體日誌實現框架你喜歡用log4j,其餘人的代碼也用slf4j的接口,具體實現未知,那你依賴其餘人jar包時,整個工程就只會用到log4j日誌框架,這是一種典型的門面模式應用,與jvm思想相同,咱們面向slf4j寫日誌代碼,slf4j處理具體日誌實現框架之間的差別,正如咱們面向jvm寫java代碼,jvm處理操做系統之間的差別,結果就是,一處編寫,處處運行。何況,如今愈來愈多的開源工具都在用slf4j了

  那麼,怎麼用slf4j呢?

  首先,得弄到slf4j的jar包,maven依賴以下,log4j配置過程徹底不變

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>

而後,弄到slf4j與log4j的關聯jar包,經過這個東西,將對slf4j接口的調用轉換爲對log4j的調用,不一樣的日誌實現框架,這個轉換工具不一樣

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>

固然了,slf4j-log4j12這個包確定依賴了slf4j和log4j,因此使用slf4j+log4j的組合只要配置上面這一個依賴就夠了

最後,代碼裏聲明logger要改一下,原來使用log4j是這樣的

import org.apache.log4j.Logger;
class Test {
    final Logger log = Logger.getLogger(Test.class);
    public void test() {
        log.info("hello this is log4j info log");
    }
}

如今要改爲這樣

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Test {
    Logger log = LoggerFactory.getLogger(Test.class);
    public void test() {
        log.info("hello, my name is {}", "chengyi");
    }
}

依賴的Logger變了,並且,slf4j的api還能使用佔位符,很方便。 

slf4j與log4j2聯合使用

一、引用依賴包及相關注釋:

<!-- log配置:Log4j2 + Slf4j -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.2</version>
</dependency>
<dependency> <!-- 橋接:告訴Slf4j使用Log4j2 -->
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.2</version>
</dependency>
<dependency> <!-- 橋接:告訴commons logging使用Log4j2 -->
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jcl</artifactId>
    <version>2.2</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.10</version>
</dependency> 

**注:**log4j-api-2.x 和 log4j-core-2.x是必須的,其餘包根據須要引入,以下圖所示: 
這裏寫圖片描述

二、代碼中使用:

@RunWith(SpringJUnit4ClassRunner.class)  //使用Spring Junit4進行測試  
@ContextConfiguration ({"classpath:spring/applicationContext.xml"}) //加載配置文件
public abstract class BaseJunit4Test {
}

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
mport com.xjj.test.BaseJunit4Test; 
public class Log4j2Test extends BaseJunit4Test {
 static public Logger logger = LoggerFactory.getLogger(Log4j2Test.class); 

   @Test
   public void logTC1(){
      logger.error("error");
      logger.debug("debug");
      logger.info("info");
      logger.trace("trace");
      logger.warn("warn");
      logger.error("error {}", "param");
    logger.info("請求處理結束,耗時:{}毫秒", (System.currentTimeMillis() - beginTime)); //第一種用法
logger.info("請求處理結束,耗時:" + (System.currentTimeMillis() - beginTime) + "毫秒"); //第二種用法
} }
 

輸出結果:

16:19:28.779 [main] ERROR com.xjj.test.mytest.Log4j2Test - error
16:19:28.781 [main] ERROR com.xjj.test.mytest.Log4j2Test - error param

注:若是沒有任何配置,Log4j2會使用缺省配置(級別:ERROR):

root logger:ConsoleAppender
PatternLayout:"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"

Tips:根據官方測試的數據,第一種用法比第二種快47倍! 

日誌級別動態調整

如下代碼演示動態調整log4j的日誌級別:

public class Log4jTest {
    private static final ConcurrentHashMap<String, Object> loggerContainer = new ConcurrentHashMap<>();
    private static final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
    private static final Logger LOGGER = LoggerFactory.getLogger(Log4jTest.class);

    @Test
    public void put() throws InterruptedException {
        LOGGER.debug("log4j debug msg:{}","start");
        LOGGER.info("log4j info msg:{}","start");
        LOGGER.error("log4j error msg:{}","start");

        setLoggerLevel4Log4j("com.junzi.log", Level.ERROR.toString());

        LOGGER.debug("log4j debug msg:{}","end");
        LOGGER.info("log4j info msg:{}","end");
        LOGGER.error("log4j error msg:{}","end");
    }

    @Before
    public void init() {
        String type = StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
        System.out.println(type);
        Enumeration enumeration = org.apache.log4j.LogManager.getCurrentLoggers();
        while (enumeration.hasMoreElements()) {
            org.apache.log4j.Logger logger = (org.apache.log4j.Logger) enumeration.nextElement();
            if (logger.getLevel() != null) {
                loggerContainer.put(logger.getName(), logger);
            }
        }
        org.apache.log4j.Logger rootLogger = org.apache.log4j.LogManager.getRootLogger();
        loggerContainer.put(rootLogger.getName(), rootLogger);
    }

    /**
     * 適用於log4j2
     * @param loggerName
     * @param level
     */
    public static void setLoggerLevel4Log4j2(String loggerName, String level) {
        LoggerConfig loggerConfig = (LoggerConfig) loggerContainer.get(loggerName);
        Level targetLevel = StringUtils.isBlank(level) ? Level.INFO : Level.toLevel(level);
        if (loggerConfig == null) {
            System.out.println(loggerName + " logger not exist.");
            return;
        }
        loggerConfig.setLevel(targetLevel);
        loggerContext.updateLoggers();
        System.out.println("set logger: " + loggerName + ", level: " + targetLevel.name());
        return;
    }

    /**
     * 適用於log4j
     * @param loggerName
     * @param level
     */
    public static void setLoggerLevel4Log4j(String loggerName, String level) {
        org.apache.log4j.Logger targetLogger = (org.apache.log4j.Logger) loggerContainer.get(loggerName);
        org.apache.log4j.Level targetLevel = org.apache.log4j.Level.toLevel(level);
        targetLogger.setLevel(targetLevel);
    } 

參考資料

http://tech.lede.com/2017/02/06/rd/server/log4jSearch/

http://www.cnblogs.com/penghongwei/p/3417179.html

http://www.cnblogs.com/ywlaker/p/6124067.html

https://tech.meituan.com/change_log_level.html

http://hongbing.github.io/posts/blog/17/10/15/dynamic-change-logger-level.html

相關文章
相關標籤/搜索