log4j平稳升级到log4j2
一、前言
公司中的項(xiàng)目雖然已經(jīng)用了很多的新技術(shù)了,但是日志的底層框架還是log4j,個(gè)人還是不喜歡用這個(gè)的。最近項(xiàng)目再生產(chǎn)環(huán)境上由于log4j引起了一場(chǎng)血案,于是決定升級(jí)到log4j2。
二、現(xiàn)象
雖然生產(chǎn)環(huán)境有多個(gè)結(jié)點(diǎn)分散高并發(fā)帶來的壓力,但是消息中心上一周好多接入方接入,導(dǎo)致并發(fā)量一下就增多了,導(dǎo)致服務(wù)卡死。在堆棧信息中看到大量的BLOCK異常,如下。
"http-nio-172.17.20.113-28080-exec-6452" #381905 daemon prio=5 os_prio=0 tid=0x00007f49e857e000 nid=0x8427f waiting for monitor entry [0x00007f49c1c75000]java.lang.Thread.State: BLOCKED (on object monitor)at org.apache.log4j.Category.callAppenders(Category.java:204)- waiting to lock <0x00000000e5915bd8> (a org.apache.log4j.spi.RootLogger)at org.apache.log4j.Category.forcedLog(Category.java:391)at org.apache.log4j.Category.log(Category.java:856)at org.slf4j.impl.Log4jLoggerAdapter.log(Log4jLoggerAdapter.java:581)at com.cmos.core.logger.DefaultLogger.log(DefaultLogger.java:385)at com.cmos.core.logger.DefaultLogger.log(DefaultLogger.java:398)at com.cmos.core.logger.DefaultLogger.doLog(DefaultLogger.java:350)at com.cmos.core.logger.DefaultLogger.info(DefaultLogger.java:200)at com.cmos.core.logger.DefaultLogger.info(DefaultLogger.java:195) "http-nio-172.17.20.113-28080-exec-6452" #381905 daemon prio=5 os_prio=0 tid=0x00007f49e857e000 nid=0x8427f waiting for monitor entry [0x00007f49c1c75000]java.lang.Thread.State: BLOCKED (on object monitor)at org.apache.log4j.Category.callAppenders(Category.java:204)- waiting to lock <0x00000000e5915bd8> (a org.apache.log4j.spi.RootLogger)at org.apache.log4j.Category.forcedLog(Category.java:391)at org.apache.log4j.Category.log(Category.java:856)at org.slf4j.impl.Log4jLoggerAdapter.log(Log4jLoggerAdapter.java:581)at com.cmos.core.logger.DefaultLogger.log(DefaultLogger.java:385)at com.cmos.core.logger.DefaultLogger.log(DefaultLogger.java:398)at com.cmos.core.logger.DefaultLogger.doLog(DefaultLogger.java:350)at com.cmos.core.logger.DefaultLogger.info(DefaultLogger.java:200)at com.cmos.core.logger.DefaultLogger.info(DefaultLogger.java:195)三、log4j高并發(fā)線程block原因
log4j-1.2.16?Category?forcedLog邏輯如下
log4j版本1.x中,使用的是synchronized(this)進(jìn)行同步操作,所有線程共用一個(gè)Category,而它通過log4j.properties指定。 同一個(gè)Category下的線程打log時(shí),需要進(jìn)行全局同步,因此它的效率會(huì)很低,log4j 1.x版不適合高并發(fā)的場(chǎng)景。
為了杜絕這種現(xiàn)象的發(fā)生,最好升級(jí)到log4j2,或者更換為logback。
四、log4j2和logback選擇
到底是升級(jí)到log4j2呢,還是將底層日志框架更換為logback呢?
檢查了一下項(xiàng)目直接使用log4j Logger的情況,發(fā)現(xiàn)部分工具類中使用了(這倒沒有問題,可以統(tǒng)一改一下),沒有想到是系統(tǒng)部封裝的框架中居然也直接使用了log4j 的Logger,心里頓時(shí)說了一聲“草尼瑪啊...”。
? ? ? ?既然是這樣的話,肯定不能使用logback了,也不能直接升級(jí)成log4j2了。
五、log4j1 如何平滑升級(jí)到log4j2
The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use Log4j 2 instead.
1.依賴如下
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-1.2-api</artifactId><version>2.6.2</version> </dependency>我們看一下 log4j-1.2-api-2.6.2 Category?forcedLog邏輯如下,并沒有調(diào)用callAppenders方法。
Log4j2 包含了基于 LMAX Disruptor(高性能線程間消息通信庫(kù))的下一代 Asynchronous Loggers。在多線程環(huán)境下,Asynchronous Loggers 的吞吐量是 Log4j1 和 Logback 的 18 倍,而延遲時(shí)間也要低一個(gè)數(shù)量級(jí)。
相信大家已經(jīng)明白了,log4j-1.2-api-2.6.2橋接的原理就是復(fù)寫了log4j-1.2.16相關(guān)的類,再輸出日志的時(shí)候調(diào)用的是log4j2中的方法。
2.刪除掉?log4j的依賴
3.將log4j.properties?替換成 log4j2.xml
log4j.properties內(nèi)容如下
log4j.rootLogger=INFO,ConsoleAppender,FileAppenderlog4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender log4j.appender.ConsoleAppender.layout=org.apache.log4j.PatternLayoutlog4j.appender.ConsoleAppender.layout.ConversionPattern=%d %p [%t] %C.%M(%L) | %m%nlog4j.appender.FileAppender=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.FileAppender.File=${user.dir}/logs/@logging.file-web@log4j.appender.FileAppender.DatePattern = '.'yyyy-MM-ddlog4j.appender.FileAppender.layout=org.apache.log4j.PatternLayout#log4j.appender.FileAppender.layout.ConversionPattern=%-5p %d [%t] %l - %m%n log4j.appender.FileAppender.layout.ConversionPattern=%d %p [%t] %C.%M(%L) | %m%nlog4j.logger.com.ibatis=debug log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug log4j.logger.java.sql.Connection=debug log4j.logger.java.sql.Statement=debug log4j.logger.java.sql.PreparedStatement=debug,ConsoleAppender對(duì)應(yīng)的log4j2.xml內(nèi)容如下
<?xml version="1.0" encoding="UTF-8"?> <configuration><Properties><Property name="LOG_HOME">${sys:user.dir}/logs</Property><Property name="LOG_FILE">@logging.file-web@</Property></Properties><Appenders><Console name="console_appender" target="SYSTEM_OUT"><PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %p [%t] %C.%M(%L) | %m%n"/></Console><RollingFile name="file_appender" immediateFlush="true" fileName="${LOG_HOME}/${LOG_FILE}"filePattern="${LOG_HOME}/${LOG_FILE}.%d{yyyy-MM-dd}"><PatternLayout><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %p [%t] %C.%M(%L) | %m%n</pattern></PatternLayout><Policies><TimeBasedTriggeringPolicy modulate="true" interval="1"/></Policies><DefaultRolloverStrategy max="30"/></RollingFile ></Appenders><Loggers><logger name="com.ibatis.common.jdbc.SimpleDataSource" level="debug"/><logger name="java.sql.Connection" level="debug"/><logger name="com.ibatis" level="debug"/><logger name="com.ibatis.common.jdbc.ScriptRunner" level="debug"/><logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="debug"/><logger name="java.sql.Statement" level="debug"/><logger name="java.sql.PreparedStatement" level="debug"><appender-ref ref="console_appender"/></logger><Root level="INFO"><appender-ref ref="console_appender"/><appender-ref ref="file_appender"/></Root></Loggers></configuration>如果在springboot項(xiàng)目中配置了?logging.config?屬性,請(qǐng)修改?logging.config=classpath:log4j.properties 為 logging.config=classpath:log4j2.xml
六、springboot 對(duì)log4j2的支持
springboot 日志系統(tǒng)結(jié)構(gòu)如下。
LoggingSystem是個(gè)抽象類,功能如下。
可支持的日志系統(tǒng)配置如下。
AbstractLoggingSystem繼承了LoggingSystem,復(fù)寫了initialize方法,如下。 @Override public void initialize(LoggingInitializationContext initializationContext,String configLocation, LogFile logFile) {if (StringUtils.hasLength(configLocation)) {initializeWithSpecificConfig(initializationContext, configLocation, logFile);return;}initializeWithConventions(initializationContext, logFile); }
initializeWithSpecificConfig方法時(shí)在你指定日志配置文件時(shí)(也就是指定了 ?logging.config 屬性)調(diào)用。initializeWithConventions方法則是使用默認(rèn)的配置。
我們簡(jiǎn)單的看一下Log4J2LoggingSystem初始化過程。
1.默認(rèn)查找的配置文件名稱
? 2.log4j2具體的初始化配置過程
可以發(fā)現(xiàn),log4j2通過LogManager管理著多個(gè)LoggerContext,每個(gè)LoggerContext管理著不同的logger。
3.動(dòng)態(tài)設(shè)置logger的level
4.沒找到日志配置文件的話使用loadDefaults方法加載
5.springboot具體是采用哪一個(gè)LoggingSystem是在LoggingApplicationListener中決定的,LoggingApplicationListener是一個(gè)ApplicationListener,springboot工程在啟動(dòng)的時(shí)候會(huì)被加載。
以下摘自網(wǎng)絡(luò)LoggingApplicationListener所做的事情...
1. 讀取配置文件中"logging."開頭的配置,比如logging.pattern.level, logging.pattern.console等設(shè)置到系統(tǒng)屬性中 2. 構(gòu)造一個(gè)LogFile(LogFile是對(duì)日志對(duì)外輸出文件的封裝),使用LogFile的靜態(tài)方法get構(gòu)造,會(huì)使用配置文件中l(wèi)ogging.file和logging.path配置構(gòu)造 3. 判斷配置中是否配置了debug并為true,如果是,設(shè)置level的DEBUG,然后繼續(xù)查看是否配置了trace并為true,如果是,設(shè)置level的TRACE 4. 構(gòu)造LoggingInitializationContext,查看是否配置了logging.config,如有配置,調(diào)用LoggingSystem的initialize方法并帶上該參數(shù),否則調(diào)用initialize方法并且configLocation為null 5. 設(shè)置一些比如org.springframework.boot、org.springframework、org.apache.tomcat、org.apache.catalina、org.eclipse.jetty、org.hibernate.tool.hbm2ddl、org.hibernate.SQL這些包的log el,跟第3步的level一樣
6. 查看是否配置了logging.register-shutdown-hook,如配置并設(shè)置為true,使用addShutdownHook的addShutdownHook方法加入LoggingSystem的getShutdownHandler
七、spring默認(rèn)日志系統(tǒng)
順便說一下,Spring的日志默認(rèn)采用commons-logging。以下摘自網(wǎng)上!
log4j如何切換到logback?
1.將logback-classic和logback-core的jar包引入到工程,將有關(guān)log4j的jar包從工程的classpath中移除。2.確認(rèn)工程引入了slf4j的jar包,作為日志的適配。3.在工程中新建logback.xml文件,將原來log4j配置文件(log4j.properties),轉(zhuǎn)換為logback的對(duì)應(yīng)配置,然后將log4j.properties刪除。4.將工程中,由于缺失了log4j.jar引起的錯(cuò)誤進(jìn)行修正,改為利用logback實(shí)現(xiàn)。可能遇到的問題及解決方案:1.Log4j轉(zhuǎn)換到logback后,運(yùn)行后spring的日志都以紅字輸出到控制臺(tái),而不受logback控制。因?yàn)镾pring的日志默認(rèn)采用commons-logging,解決方法是在工程中引入jcl-over-slf4j-1.6.1.jar,這樣就將commons-logging與slf4j對(duì)接,再通過logback進(jìn)行了日志的統(tǒng)一輸出。2.切換完成后,啟動(dòng)工程時(shí)會(huì)出現(xiàn)java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory這個(gè)錯(cuò)誤。原因是slf4j-api的jar包版本太低,改為slf4j-api-1.6.4.jar即可解決。?
轉(zhuǎn)載于:https://www.cnblogs.com/hujunzheng/p/9937097.html
總結(jié)
以上是生活随笔為你收集整理的log4j平稳升级到log4j2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win7系统转到Win10系统的装机方法
- 下一篇: 《软件项目管理(第二版)》第 7 章——