log4j平穩升級到log4j2

一、前言

  公司中的項目雖然已經用了很多的新技術了,但是日志的底層框架還是log4j,個人還是不喜歡用這個的。最近項目再生產環境上由于log4j引起了一場血案,于是決定升級到log4j2。

二、現象

  雖然生產環境有多個結點分散高并發帶來的壓力,但是消息中心上一周好多接入方接入,導致并發量一下就增多了,導致服務卡死。在堆棧信息中看到大量的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高并發線程block原因

  log4j-1.2.16?Category?forcedLog邏輯如下

  

  

  log4j版本1.x中,使用的是synchronized(this)進行同步操作,所有線程共用一個Category,而它通過log4j.properties指定。 同一個Category下的線程打log時,需要進行全局同步,因此它的效率會很低,log4j 1.x版不適合高并發的場景。

  為了杜絕這種現象的發生,最好升級到log4j2,或者更換為logback。

四、log4j2和logback選擇

  到底是升級到log4j2呢,還是將底層日志框架更換為logback呢?

  檢查了一下項目直接使用log4j Logger的情況,發現部分工具類中使用了(這倒沒有問題,可以統一改一下),沒有想到是系統部封裝的框架中居然也直接使用了log4j 的Logger,心里頓時說了一聲“草尼瑪啊...”。

? ? ? ?既然是這樣的話,肯定不能使用logback了,也不能直接升級成log4j2了。

五、log4j1 如何平滑升級到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邏輯如下,并沒有調用callAppenders方法。

  

  Log4j2 包含了基于 LMAX Disruptor(高性能線程間消息通信庫)的下一代 Asynchronous Loggers。在多線程環境下,Asynchronous Loggers 的吞吐量是 Log4j1 和 Logback 的 18 倍,而延遲時間也要低一個數量級。

  相信大家已經明白了,log4j-1.2-api-2.6.2橋接的原理就是復寫了log4j-1.2.16相關的類,再輸出日志的時候調用的是log4j2中的方法。

  2.刪除掉?log4j的依賴

  3.將log4j.properties?替換成 log4j2.xml

  log4j.properties內容如下

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

  對應的log4j2.xml內容如下

<?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項目中配置了?logging.config?屬性,請修改?logging.config=classpath:log4j.properties 為 logging.config=classpath:log4j2.xml

六、springboot 對log4j2的支持

  springboot 日志系統結構如下。

  

  LoggingSystem是個抽象類,功能如下。

  1. beforeInitialize方法:日志系統初始化之前需要處理的事情。抽象方法,不同的日志架構進行不同的處理
  2. initialize方法:初始化日志系統。默認不進行任何處理,需子類進行初始化工作
  3. cleanUp方法:日志系統的清除工作。默認不進行任何處理,需子類進行清除工作
  4. getShutdownHandler方法:返回一個Runnable用于當jvm退出的時候處理日志系統關閉后需要進行的操作,默認返回null,也就是什么都不做
  5. setLogLevel方法:抽象方法,用于設置對應logger的級別

  可支持的日志系統配置如下。

  

AbstractLoggingSystem繼承了LoggingSystem,復寫了initialize方法,如下。
@Override
public void initialize(LoggingInitializationContext initializationContext,String configLocation, LogFile logFile) {if (StringUtils.hasLength(configLocation)) {initializeWithSpecificConfig(initializationContext, configLocation, logFile);return;}initializeWithConventions(initializationContext, logFile);
}

  initializeWithSpecificConfig方法時在你指定日志配置文件時(也就是指定了 ?logging.config 屬性)調用。initializeWithConventions方法則是使用默認的配置。

  我們簡單的看一下Log4J2LoggingSystem初始化過程。

  1.默認查找的配置文件名稱

  

?  2.log4j2具體的初始化配置過程

  

  可以發現,log4j2通過LogManager管理著多個LoggerContext,每個LoggerContext管理著不同的logger。

  3.動態設置logger的level

  

  4.沒找到日志配置文件的話使用loadDefaults方法加載

  

  5.springboot具體是采用哪一個LoggingSystem是在LoggingApplicationListener中決定的,LoggingApplicationListener是一個ApplicationListener,springboot工程在啟動的時候會被加載。

以下摘自網絡
LoggingApplicationListener所做的事情...

1. 讀取配置文件中"logging."開頭的配置,比如logging.pattern.level, logging.pattern.console等設置到系統屬性中 2. 構造一個LogFile(LogFile是對日志對外輸出文件的封裝),使用LogFile的靜態方法get構造,會使用配置文件中logging.file和logging.path配置構造 3. 判斷配置中是否配置了debug并為true,如果是,設置level的DEBUG,然后繼續查看是否配置了trace并為true,如果是,設置level的TRACE 4. 構造LoggingInitializationContext,查看是否配置了logging.config,如有配置,調用LoggingSystem的initialize方法并帶上該參數,否則調用initialize方法并且configLocation為null 5. 設置一些比如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,如配置并設置為true,使用addShutdownHook的addShutdownHook方法加入LoggingSystem的getShutdownHandler

七、spring默認日志系統

  順便說一下,Spring的日志默認采用commons-logging。以下摘自網上!

  log4j如何切換到logback?

  
1.將logback-classic和logback-core的jar包引入到工程,將有關log4j的jar包從工程的classpath中移除。2.確認工程引入了slf4j的jar包,作為日志的適配。3.在工程中新建logback.xml文件,將原來log4j配置文件(log4j.properties),轉換為logback的對應配置,然后將log4j.properties刪除。4.將工程中,由于缺失了log4j.jar引起的錯誤進行修正,改為利用logback實現。可能遇到的問題及解決方案:1.Log4j轉換到logback后,運行后spring的日志都以紅字輸出到控制臺,而不受logback控制。因為Spring的日志默認采用commons-logging,解決方法是在工程中引入jcl-over-slf4j-1.6.1.jar,這樣就將commons-logging與slf4j對接,再通過logback進行了日志的統一輸出。2.切換完成后,啟動工程時會出現java.lang.IllegalAccessError: tried to access field org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory這個錯誤。原因是slf4j-api的jar包版本太低,改為slf4j-api-1.6.4.jar即可解決。

?

轉載于:https://www.cnblogs.com/hujunzheng/p/9937097.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/531214.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/531214.shtml
英文地址,請注明出處:http://en.pswp.cn/news/531214.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Springboot集成ES啟動報錯

報錯內容 None of the configured nodes are available elasticsearch.yml配置 cluster.name: ftest node.name: node-72 node.master: true node.data: true network.host: 112.122.245.212 http.port: 39200 transport.tcp.port: 39300 discovery.zen.ping.unicast.hosts: [&…

高效使用hibernate-validator校驗框架

一、前言 高效、合理的使用hibernate-validator校驗框架可以提高程序的可讀性&#xff0c;以及減少不必要的代碼邏輯。接下來會介紹一下常用一些使用方式。 二、常用注解說明 限制說明Null限制只能為nullNotNull限制必須不為nullAssertFalse限制必須為falseAssertTrue限制必須為…

kafka-manager配置和使用

kafka-manager配置 最主要配置就是用于kafka管理器狀態的zookeeper主機。這可以在conf目錄中的application.conf文件中找到。 kafka-manager.zkhosts"my.zookeeper.host.com:2181" 當然也可以聲明為zookeeper集群。 kafka-manager.zkhosts"my.zookeeper.host.co…

kafka告警簡單方案

一、前言 為什么要設計kafka告警方案&#xff1f;現成的監控項目百度一下一大堆&#xff0c;KafkaOffsetMonitor、KafkaManager、 Burrow等&#xff0c;具體參考&#xff1a;kafka的消息擠壓監控。由于本小組的項目使用的kafka集群并沒有被公司的kafka-manager管理&#xff0c;…

RedisCacheManager設置Value序列化器技巧

CacheManager基本配置 請參考博文&#xff1a;springboot2.0 redis EnableCaching的配置和使用 RedisCacheManager構造函數 /*** Construct a {link RedisCacheManager}.* * param redisOperations*/ SuppressWarnings("rawtypes") public RedisCacheManager(RedisOp…

Nginx配置以及域名轉發

工程中的nginx配置 #user nobody; worker_processes 24; error_log /home/xxx/opt/nginx/logs/error.log; pid /home/xxx/opt/nginx/run/nginx.pid;events {use epoll;worker_connections 102400; }http {include /home/xxx/opt/nginx/conf.d/mime.types;default_…

java接口簽名(Signature)實現方案續

一、前言 由于之前寫過的一片文章 &#xff08;java接口簽名(Signature)實現方案 &#xff09;收獲了很多好評&#xff0c;此次來說一下另一種簡單粗暴的簽名方案。相對于之前的簽名方案&#xff0c;對body、paramenter、path variable的獲取都做了簡化的處理。也就是說這種方式…

支付寶敏感信息解密

支付寶官方解密文檔&#xff1a;https://docs.alipay.com/mini/introduce/aes String response "小程序前端提交的";//1. 獲取驗簽和解密所需要的參數 Map<String, String> openapiResult JSON.parseObject(response,new TypeReference<Map<String, St…

HashMap 源碼閱讀

前言 之前讀過一些類的源碼&#xff0c;近來發現都忘了&#xff0c;再讀一遍整理記錄一下。這次讀的是 JDK 11 的代碼&#xff0c;貼上來的源碼會去掉大部分的注釋, 也會加上一些自己的理解。 Map 接口 這里提一下 Map 接口與1.8相比 Map接口又新增了幾個方法&#xff1a;   …

SpringMvc接口中轉設計(策略+模板方法)

一、前言 最近帶著兩個兄弟做支付寶小程序后端相關的開發&#xff0c;小程序首頁涉及到很多查詢的服務。小程序后端服務在我司屬于互聯網域&#xff0c;相關的查詢服務已經在核心域存在了&#xff0c;查詢這塊所要做的工作就是做接口中轉。參考了微信小程序的代碼&#xff0c;發…

SpringSecurity整合JWT

一、前言 最近負責支付寶小程序后端項目設計&#xff0c;這里主要分享一下用戶會話、接口鑒權的設計。參考過微信小程序后端的設計&#xff0c;會話需要依靠redis。相關的開發人員和我說依靠Redis并不是很靠譜&#xff0c;redis在業務高峰期不穩定&#xff0c;容易出現問題&…

Springboot定時任務原理及如何動態創建定時任務

一、前言 上周工作遇到了一個需求&#xff0c;同步多個省份銷號數據&#xff0c;解綁微信粉絲。分省定時將銷號數據放到SFTP服務器上&#xff0c;我需要開發定時任務去解析文件。因為是多省份&#xff0c;服務器、文件名規則、數據規則都不一定&#xff0c;所以要做成可配置是有…

轉載:ThreadPoolExecutor 源碼閱讀

前言 之前研究了一下如何使用ScheduledThreadPoolExecutor動態創建定時任務(Springboot定時任務原理及如何動態創建定時任務)&#xff0c;簡單了解了ScheduledThreadPoolExecutor相關源碼。今天看了同學寫的ThreadPoolExecutor 的源碼解讀&#xff0c;甚是NB&#xff0c;必須轉…

Spring BPP中優雅的創建動態代理Bean

一、前言 本文章所講并沒有基于Aspectj&#xff0c;而是直接通過Cglib以及ProxyFactoryBean去創建代理Bean。通過下面的例子&#xff0c;可以看出Cglib方式創建的代理Bean和ProxyFactoryBean創建的代理Bean的區別。 二、基本測試代碼 測試實體類&#xff0c;在BPP中創建BppTest…

使用pdfBox實現pdf轉圖片,解決中文方塊亂碼等問題

一、引入依賴 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>fontbox</artifactId><version>2.0.13</version> </dependency> <dependency><groupId>org.apache.pdfbox</groupId><artif…

Spring異步調用原理及SpringAop攔截器鏈原理

一、Spring異步調用底層原理 開啟異步調用只需一個注解EnableAsync Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Import(AsyncConfigurationSelector.class) public interface EnableAsync {/*** Indicate the async annotation type to be detec…

線程池優化之充分利用線程池資源

一、前言 最近做了電子發票的需求&#xff0c;分省開票接口和發票下載接口都有一定的延遲。為了完成開票后自動將發票插入用戶微信卡包&#xff0c;目前的解決方案是利用線程池&#xff0c;將開票后插入卡包的任務&#xff08;輪詢分省發票接口&#xff0c;直到獲取到發票相關信…

Spring MVC源碼——Root WebApplicationContext

Spring MVC源碼——Root WebApplicationContext 打算開始讀一些框架的源碼,先拿 Spring MVC 練練手,歡迎點擊這里訪問我的源碼注釋, SpringMVC官方文檔一開始就給出了這樣的兩段示例: WebApplicationInitializer示例: public class MyWebApplicationInitializer implements Web…

Spring MVC源碼——Servlet WebApplicationContext

上一篇筆記(Spring MVC源碼——Root WebApplicationContext)中記錄了下 Root WebApplicationContext 的初始化代碼.這一篇來看 Servlet WebApplicationContext 的初始化代碼 DispatcherServlet 是另一個需要在 web.xml 中配置的類, Servlet WebApplicationContext 就由它來創建…

Springboot源碼——應用程序上下文分析

前兩篇(Spring MVC源碼——Root WebApplicationContext 和 Spring MVC源碼——Servlet WebApplicationContext)講述了springmvc項目創建上下文的過程&#xff0c;這一篇帶大家了解一下springboot項目創建上下文的過程。 SpringApplication引導類 SpringApplication類用于啟動或…