Log4j2 簡明教程



一、概述

log4j2官方文檔內容非常多,要一次性了解全部是不可能的。正確的步驟應當是先了解最常見的配置,當發現原有知識無法解決問題,再重新查看文檔看有沒有合適的配置。
下面將從文件結構入手,再到簡單的實例,從實例入手分析常見的配置的用途,其中涉及其中包括Appenders, Filters, Layout, Lookups的知識,最后根據學習。

可以搜索到的關於log4j2的教程非常少,這篇文章更多的是讓大家對log4j2有個大體的了解,免得大家看到官方文檔那么多就暈了!

歡迎關注我的github: https://github.com/benson-lin

如果覺得排版不好,可以訪問:http://blog.bensonlin.me/post/log4j2-tutorial

log4j2.xml文件結構

<?xml version="1.0" encoding="UTF-8"?>;
<Configuration>
  <Properties>
    <Property name="name1">value</property>
    <Property name="name2" value="value2"/>
  </Properties>
  <Filter type="type" ... />
  <Appenders>
    <Appender type="type" name="name">
      <Filter type="type" ... />
    </Appender>
    ...
  </Appenders>
  <Loggers>
    <Logger name="name1">
      <Filter type="type" ... />
    </Logger>
    ...
    <Root level="level">
      <AppenderRef ref="name"/>
    </Root>
  </Loggers>
</Configuration>

  

下面是一個比較完整的例子:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 設置log4j2的自身log級別為warn -->
<!-- OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<configuration status="WARN" monitorInterval="30">
    <appenders>
        <console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
        </console>

        <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
            <!--控制台只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->         
            <Filters>
                <ThresholdFilter level="INFO"/>
                <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>

        <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
            <Filters>
                <ThresholdFilter level="WARN"/>
                <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>

        <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="ERROR"/>
            <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="100 MB"/>
            </Policies>
        </RollingFile>

    </appenders>

    <loggers>
        <!--過濾掉spring和mybatis的一些無用的DEBUG信息-->
        <logger name="org.springframework" level="INFO"></logger>
        <logger name="org.mybatis" level="INFO"></logger>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>

  

log4j2有默認的配置,如果要替換配置,只需要在classpath根目錄下放置log4j2.xml。
log4j 2.0與以往的1.x有一個明顯的不同,其配置文件只能采用.xml, .json或者 .jsn。在默認情況下,系統選擇configuration文件的優先級如下:(classpath為src文件夾)

  • classpath下名為 log4j-test.json 或者log4j-test.jsn文件
  • classpath下名為 log4j2-test.xml
  • classpath下名為 log4j.json 或者log4j.jsn文件
  • classpath下名為 log4j2.xml

如果本地要測試,可以把log4j2-test.xml放到classpath,而正式環境使用log4j2.xml,則在打包部署的時候不要打包log4j2-test.xml即可。

下面是其缺省配置:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

  

下面將對上面的配置文件進行一一講解。

二、示例Java代碼

package com.foo;
// Import log4j classes.
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class MyApp {

    // Define a static logger variable so that it references the
    // Logger instance named "MyApp".
    private static final Logger logger = LogManager.getLogger(MyApp.class);

    public static void main(final String... args) {

        // Set up a simple configuration that logs on the console.

        logger.trace("Entering application.");
        Bar bar = new Bar();
        if (!bar.doIt()) {
            logger.error("Didn't do it.");
        }
        logger.trace("Exiting application.");
    }
}

package com.foo;


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Bar {

  static final Logger logger = LogManager.getLogger(Bar.class.getName());

  public boolean doIt() {
    logger.entry();
    logger.error("Did it again!");
    return logger.exit(false);
  }
}

  

如果使用如下配置,也就是缺省配置:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

  

輸出如下:只輸出error以上的日志信息

17:13:01.540 [main] ERROR com.foo.Bar - Did it again!
17:13:01.540 [main] ERROR MyApp - Didn't do it.

如果我們希望除了com.foo.Bar類下輸出TRACE以上到控制台外,其他停止TRACE的輸出到控制台,只輸出ERROR以上的日志。可以如下配置:

  <Loggers>
    <Logger name="com.foo.Bar" level="TRACE"/>
    <Root level="ERROR">
      <AppenderRef ref="STDOUT">
    </Root>
  </Loggers>

結果如下:

14:14:17.176 [main] TRACE com.foo.Bar - Enter
14:14:17.182 [main] ERROR com.foo.Bar - Did it again!
14:14:17.182 [main] TRACE com.foo.Bar - Exit with(false)
14:14:17.182 [main] ERROR com.foo.MyApp - Didn't do it.

因為com.foo.Bar沒有自己的Appender,所以會使用ROOT的Appender,如果自己也配置了在控制台打印,就要注意可加性:如下配置,會ERROR以上的會打印兩次

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Logger name="com.foo.Bar" level="trace">
      <AppenderRef ref="Console"/>
    </Logger>
    <Root level="error">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

結果如下

14:11:27.103 [main] TRACE com.foo.Bar - Enter
14:11:27.103 [main] TRACE com.foo.Bar - Enter
14:11:27.106 [main] ERROR com.foo.Bar - Did it again!
14:11:27.106 [main] ERROR com.foo.Bar - Did it again!
14:11:27.107 [main] TRACE com.foo.Bar - Exit with(false)
14:11:27.107 [main] TRACE com.foo.Bar - Exit with(false)
14:11:27.107 [main] ERROR com.foo.MyApp - Didn't do it.

如果我們確實有這種需求(不想遵循父類的Appender),可以加上additivity="false"參數。如下配置,com.foo.Bar的trace以上日志將保存到文件中,並且不會打印到控制台。

<Configuration status="WARN">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
    </Console>
    <RollingFile name="RollingFile" fileName="${sys:user.home}/logs/trace.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
           ...
    </RollingFile>
  </Appenders>
  <Loggers>
    <Logger name="com.foo.Bar" level="trace" additivity="false">
      <AppenderRef ref="RollingFile"/>
    </Logger>
    <Root level="error">
      <AppenderRef ref="Console"/>
    </Root>
  </Loggers>
</Configuration>

log4j2支持自動重新配置,如果配置了monitorInterval,那么log4j2每隔一段時間就會檢查一遍這個文件是否修改。最小是5s

<?xml version="1.0" encoding="UTF-8"?>
<Configuration monitorInterval="30">
...
</Configuration>

 

三、Appenders

ConsoleAppender

將使用 System.out 或 System.err輸出到控制台。

可以有如下參數

  • name:Appender的名字
  • target:SYSTEM_OUT 或 SYSTEM_ERR,默認是SYSTEM_OUT
  • layout:如何格式化,如果沒有默認是%m%n

典型的ConsoleAppender如下

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <Console name="STDOUT" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="STDOUT"/>
    </Root>
  </Loggers>
</Configuration>

  

RollingFileAppender

顧名思義,日志文件回滾,也就是刪除最舊的日志文件,默認是3個文件。可以通過DefaultRolloverStrategy設置max參數為多個

例子如下:

<Appenders>
    <RollingFile name="RollingFile" fileName="logs/app.log"
                 filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <TimeBasedTriggeringPolicy />
        <SizeBasedTriggeringPolicy size="250 MB"/>
      </Policies>
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>
  </Appenders>

  

現在說說TimeBasedTriggeringPolicy和SizeBasedTriggeringPolicy的作用。
第一個是基於時間的rollover,第二個是基於大小的rollover。第二個很容易理解,如果大小大於某個閾值,上面是50MB的時候就會滾動。

TimeBasedTriggeringPolicy中有其中一個參數是interval,表示多久滾動一次。默認是1 hour。modulate=true用來調整時間:比如現在是早上3am,interval是4,那么第一次滾動是在4am,接着是8am,12am...而不是7am

四、Layouts

這里只描述最常見的PatternLayout!更多看官方文檔Layouts

<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
                     filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
            <ThresholdFilter level="ERROR"/>
    <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
    <Policies>
        <TimeBasedTriggeringPolicy/>
        <SizeBasedTriggeringPolicy size="50 MB"/>
    </Policies>
     <DefaultRolloverStrategy max="20"/>
</RollingFile>

  

上面的%是什么含義,還有哪些呢?其實最主要的參數還是%d, %p, %l, %m, %n, %X。下面的圖是摘取網上的。

%X用來獲取MDC記錄,這些記錄從哪來的?我們可以使用org.apache.logging.log4j.ThreadContext將需要記錄的值put進去。(我發現slf的MDC.java的put方法對log4j2不可用,因為底層依賴的是log4j1)

package com.bensonlin.service.web.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.ThreadContext;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MDCInterceptor implements HandlerInterceptor {

    public final static String USER_KEY            = "user_id";
    public final static String REQUEST_REQUEST_URI = "request_uri";

    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2)
                                                                                                                         throws Exception {
        ThreadContext.put(REQUEST_REQUEST_URI, httpServletRequest.getRequestURI());
        return true;
    }

    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object arg2,
                           ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                Object arg2, Exception exception) throws Exception {
        ThreadContext.remove(USER_KEY);
        ThreadContext.remove(REQUEST_REQUEST_URI);
    }

    public static void setUserKeyForMDC(String userId) {
        ThreadContext.put(USER_KEY, userId);
    }
}

  

xml中使用%X{aaa}取出來:

<console name="Console" target="SYSTEM_OUT">
    <PatternLayout pattern="%X{user_id} %X{request_uri} [%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>

  

對應ThreadContext的文檔在這里

五、Filters

Filters決定日志事件能否被輸出。過濾條件有三個值:ACCEPT(接受), DENY(拒絕) or NEUTRAL(中立).

ACCEP和DENY比較好理解就是接受和拒絕的意思,在使用單個過濾器的時候,一般就是使用這兩個值。但是在組合過濾器中,如果用接受ACCEPT的話,日志信息就會直接寫入日志文件,后續的過濾器不再進行過濾。所以,在組合過濾器中,接受使用NEUTRAL(中立),被第一個過濾器接受的日志信息,會繼續用后面的過濾器進行過濾,只有符合所有過濾器條件的日志信息,才會被最終寫入日志文件。

ThresholdFilter

有幾個參數:

  • level:將被過濾的級別。
  • onMatch:默認值是NEUTRAL
  • onMismatch:默認是DENY

如果LogEvent 中的 Log Level 大於 ThresholdFilter 中配置的 Log Level,那么返回 onMatch 的值, 否則返回 onMismatch 的值,例如 : 如果ThresholdFilter 配置的 Log Level 是 ERROR , LogEvent 的Log Level 是 DEBUG。 那么 onMismatch 的值將被返回, 因為 ERROR 小於DEBUG。如果是Accept,將自己被接受,而不經過下一個過濾器

下面的例子可以這樣理解:如果是INFO級別及其以上,將經過通過第一個過濾,進入第二個,否則是onMismatch:拒絕進入。對於第二個,如果是大於等於WARN(WARN/ERROR/ERROR),那么將返回onMatch,也就是拒絕,如果是其他情況(也就是INFO),將是中立情況,因為后面沒有其他過濾器,則被接受。最后的結果就只剩下INFO級別的日志。也就符合了RollingFileInfo只記錄Info級別的規則。

<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
             filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
    <!--控制台只輸出level及以上級別的信息(onMatch),其他的直接拒絕(onMismatch)-->         
    <Filters>
        <ThresholdFilter level="INFO"/>
        <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
    </Filters>
    <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
    <Policies>
        <TimeBasedTriggeringPolicy interval="24" modulate="true"/>
        <SizeBasedTriggeringPolicy size="50 MB"/>
    </Policies>
    <DefaultRolloverStrategy max="20"/>
</RollingFile>

  

六、Lookups

提供另外一種方式添加某些特殊的值到日志中。

Date Lookup

與其他lookup不同,它不是通過key去查找值,而是通過SimpleDateFormat驗證格式是否有效,然后記錄當前時間

<RollingFile name="Rolling-${map:type}" fileName="${filename}" filePattern="target/rolling1/test1-$${date:MM-dd-yyyy}.%i.log.gz">
  <PatternLayout>
    <pattern>%d %p %c{1.} [%t] %m%n</pattern>
  </PatternLayout>
  <SizeBasedTriggeringPolicy size="500" />
</RollingFile>

Context Map Lookup: 如記錄loginId

<File name="Application" fileName="application.log">
  <PatternLayout>
    <pattern>%d %p %c{1.} [%t] $${ctx:loginId} %m%n</pattern>
  </PatternLayout>
</File>

這個的結果和前面的MDC是一樣的,即 %X{loginId}

Environment Lookup:記錄系統環境變量

比如可以獲取如/etc/profile中的變量值

<File name="Application" fileName="application.log">
  <PatternLayout>
    <pattern>%d %p %c{1.} [%t] $${env:USER} %m%n</pattern>
  </PatternLayout>
</File>

  

System Properties Lookup

可以獲取Java中的系統屬性值。

<Appenders>
  <File name="ApplicationLog" fileName="${sys:logPath}/app.log"/>
</Appenders>

和系統屬性值有什么區別呢?其實就是System.getProperties();和System.getenv();的區別。下面是一個小例子。

package com.bensonlin.service.common;

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

public class Main {

    public static void main(String[] args) {

        Properties properties = System.getProperties();
        Iterator i = properties.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry entry = (Map.Entry) i.next();
            Object key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key + "=" + value);
        }

        System.out.println("===================");
        Map map = System.getenv();
        Iterator it = map.entrySet().iterator();
        while (it.hasNext()) {
            Entry entry = (Entry) it.next();
            System.out.print(entry.getKey() + "=");
            System.out.println(entry.getValue());
        }
    }
}

輸出(摘取部分):

java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=C:\Program Files\Java\jdk1.8.0_25\jre\bin
java.vm.version=25.25-b02
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
path.separator=;
...
===================
JAVA_HOME=C:\Program Files\Java\jdk1.8.0_25
TEMP=D:\Temp
ProgramFiles=C:\Program Files
...

可以看到其實Environment是獲取環境變量,而System Properties獲取的更多是與Java相關的值

轉載注明地址:http://blog.bensonlin.me/post/log4j2-tutorial


免責聲明!

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



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