Log4j2與Slf4j的最佳實踐

前言

日誌對於項目的重要性不言而喻,現在市面上的日誌框架多種多樣:Log4j、Log4j2、Slf4j、JDKLog、Logback等等,如果沒有真正深入瞭解過,可能會被搞得眼花繚亂。本文將介紹目前Java項目中最常見的Log4j2 + Slf4j的使用組合,這也是我自己項目中目前使用的。

另外,由於現在項目基本都是Servlet 3.0及以上版本,因此本文針對Servlet 3.0及更高的版本,如果使用的是Servlet 2.5可以參考官方文檔進行適當的調整。

官方文檔地址:http://logging.apache.org/log4j/2.x/manual/webapp.html


如何查看Servlet版本?

查看web.xml文件中<web-app>標籤中的version字段即可。


關於Log4j2

在上面提到的日誌框架中,以Log4j + Slf4j的使用組合最爲常見,但是我們知道Log4j目前已經停止更新了。Apache推出了新的Log4j2來代替Log4j,Log4j2是對Log4j的升級,與其前身Log4j相比有了顯着的改進,並提供了許多Logback可用的改進,同時解決了Logback體系結構中的一些固有問題。因此,Log4j2 + Slf4j應該是未來的大勢所趨。


Log4j2的性能

Log4j2最牛逼的地方在於異步輸出日誌時的性能表現,Log4j2在多線程的環境下吞吐量與Log4j和Logback的比較如下圖。下圖比較中Log4j2有三種模式:1)全局使用異步模式;2)部分Logger採用異步模式;3)異步Appender。可以看出在前兩種模式下,Log4j2的性能較之Log4j和Logback有很大的優勢。

Log4j2完整的官方性能文檔:http://logging.apache.org/log4j/2.x/performance.html


Log4j2使用的幾個點

  1. 在Web項目中需要添加 log4j-web jar包。
  2. Log4j允許使用log4jConfiguration參數在web.xml中指定配置文件位置。Log4j將通過以下方式搜索配置文件:
    1. 如果配置了路徑(log4jConfiguration參數配置),Log4j將去搜索這個位置。
    2. 如果未配置路徑,Log4j將搜索WEB-INF目錄中“log4j2”開頭的文件。如果找到多個文件,並且存在以“log4j2-name”開頭的文件,其中name是Web應用程序的名稱,則會使用它。否則,將使用第一個文件。
    3. 在resources目錄下搜索配置文件,規則同b。
  3. Log4j2不支持Servlet 2.4及更老的的Web應用程序。

第2點講的簡單點就是:Log4j2的配置文件名以“log4j2”開頭時(例如常見的log4j2.xml),放在WEB-INF和resources的根路徑時不需要在web.xml中配置路徑,放在其他位置時需要配置路徑。


基本的使用(同步模式)

1.maven依賴

<!--log4j2核心包-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.9.1</version>
</dependency>
<!-- Web項目需添加 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.9.1</version>
</dependency>
<!--用於與slf4j保持橋接-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.9.1</version>
</dependency>
<!-- slf4j核心包-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>


2.在resources根路徑下添加log4j2.xml配置文件(無需配置路徑)

logj2.xml文件內容:將info及以上級別的日誌輸出到Console和指定路徑的文件中。


3.web.xml配置log4j2配置文件的路徑

如果log4j2.xml放在WEB-INF和resources根路徑則不需要。

<!-- log4j2.xml路徑 -->
<context-param>
    <param-name>log4jConfiguration</param-name>
    <param-value>/config/log4j2.xml</param-value>
</context-param>


4.如果是從Log4j等其他日誌轉的Log4j2 + Slf4j的,記得把Mavne中原來日誌框架的依賴去掉或exclusion掉

常見的有:log4j和slf4j-log4j12,如下。

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>


5.代碼中使用

import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @Author joonwhee
 * @Date 2018/3/31
 */
@Controller
@RequestMapping("/user")
public class UserController {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
    @RequestMapping("/userList")
    public String userList(HttpServletRequest request, Model model) {
        LOGGER.info("Test log4j2 info");
        LOGGER.warn("Test log4j2 warn");
        LOGGER.error("Test log4j2 error");
        return "userList";
    }
}


6.使用結果

調用該方法後,Console輸出:


在log4j2.xml文件中配置的路徑下生成日誌文件:


打開日誌查看輸出內容:

這裏Console輸出和日誌文件輸出“前綴”不完全一致是由於上面log4j2.xml中Console和RollingFile的PatternLayout配置不一致導致的。


至此,Log4j2集成Slf4j就完成了。


擴展測試

關於上面提到的Log4j2配置文件存放路徑的問題,我們來做幾個簡單的測試:

場景1:log4j2.xml放在resources/config路徑下,在web.xml中配置log4jConfiguration指定路徑。

效果:日誌正常輸出。

結論:Log4j能找到配置文件。


場景2:log4j2.xml放在resources/config路徑下,註釋掉web.xml中的log4jConfiguration配置。

效果:明顯看出我們的配置已經失效了,但是輸出了ERROR級別的日誌,可以推測這是Log4j在找不到配置文件時的兜底策略。

結論:Log4j不能找到配置文件。


場景3:log4j2.xml放在resources根路徑下,註釋掉web.xml中的log4jConfiguration配置。

效果:日誌正常輸出。

結論:Log4j能找到配置文件。


場景4:log4j2.xml放在WEB-INF根路徑下,註釋掉web.xml中的log4jConfiguration配置。

效果:日誌正常輸出。

結論:Log4j能找到配置文件。


看完測試結果,大家就可以根據自己的習慣將log4j2.xml放到自己喜歡的地方了。


進階使用(異步模式)

異步模式下,默認情況不會輸出位置信息,因爲輸出位置信息會慢30-100倍。如果需要位置信息,需要在所有相關記錄器(包括根記錄器)的配置中設置“includeLocation = true”。

例如以下代碼:

<asyncRoot level="info" includeLocation="true">
    <AppenderRef ref="RandomAccessFile"/>
</asyncRoot>


什麼是位置信息?

直接看下面兩張圖就很明顯了。

有位置信息:

無位置信息:


1.全局使用異步(在同步模式的基礎上修改)

1.maven增加disruptor依賴,Log4j2版本2.9及以上時需要disruptor-3.3.4.jar或更高版本;Log4j2版本2.9以下時需要disruptor-3.0.0.jar或更高版本。

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

2.將系統屬性log4j2.contextSelector設置爲org.apache.logging.log4j.core.async.AsyncLoggerContextSelector。

方式:添加一個名字爲log4j2.component.properties的文件,放到classpath下面,log4j2會在啓動的時候自動加載。如下:

3.log4j2.xml的配置修改成如下,跟同步模式的區別爲RollingFile變成了RandomAccessFile。


2.部分Logger採用異步方式(在同步模式的基礎上修改)

1.maven增加disruptor依賴,Log4j2版本2.9及以上時需要disruptor-3.3.4.jar或更高版本;Log4j2版本2.9以下時需要disruptor-3.0.0.jar或更高版本。

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

2.使用<asyncRoot>或<asyncLogger>配置來指定需要異步的記錄器。

<asyncRoot level="info">
    <AppenderRef ref="RandomAccessFile"/>
</asyncRoot>

3.log4j2.xml的配置修改成如下,跟同步模式的區別爲:1)RollingFile變成了RandomAccessFile;2)loggers中使用了<asyncRoot>或<asyncLogger>。

注意:配置只能包含一個根記錄器(<root>或<asyncRoot>元素),但可以組合異步和非異步記錄器。例如,包含<asyncLogger>元素的配置文件也可以包含同步記錄器的<Root>和<Logger>元素。


異步模式完整的官方文檔地址:http://logging.apache.org/log4j/2.x/manual/async.html


簡單性能測試

簡單的測試下以下三種模式的耗時:全局使用異步的Log4j2、部分Logger使用異步的Log4j2、使用同步模式的Log4j2。

測試代碼

1.測試例子很簡單,就是將之前的3行輸出外面加個64萬次的循環。

2.將輸出Console去掉,只輸出到文件,同步模式下的Appenders爲RollingFile;異步模式下的Appenders爲RandomAccessFile。


場景1:RollingFile + 同步模式不帶位置信息

場景1的配置文件主要內容如下

測試結果如下圖:總共耗時17678ms


場景2:RollingFile + 同步模式帶位置信息

場景2的配置文件主要內容如下,跟場景1只有includeLocation屬性值不同

測試結果如下圖:總共耗時257268ms


場景3:RandomAccessFile + 全局使用異步

場景3的配置文件主要內容如下

測試結果如下圖:總共耗時2303ms


場景4:RandomAccessFile + 部分使用異步

場景4的配置文件主要內容如下

測試結果如下圖:總共耗時5864ms


輸出384萬行簡單日誌的測試結果統計:

以上測試只是簡單的測試,詳細的性能比較可以查看官方文檔:http://logging.apache.org/log4j/2.x/performance.html


結論:

  1. 通過測試結果可以看出,Log4j2的異步模式性能提升還是比較明顯的,但是需根據實際情況來確認是否需要。正常項目使用同步模式一般都夠用了。
  2. 輸出位置信息帶來的性能損耗太高了。因此,在線上項目的日誌記錄一定不要輸出位置信息。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章