log4j 1 升級方案 目標 解決重要安全漏洞 多項目日誌監測改造(可選) 性能提升(可選) 功能擴展(可選) 升級要求 少修改代碼 或 不修改代碼 功能儘可能平替,而不發生基本功能變化 低學習成本 具體方案 完全平替的改造 方案特征 完全平替 沒有額外的學習成本,沒有其他的操作,只需要將jar包 ...
log4j 1 升級方案
目標
- 解決重要安全漏洞
- 多項目日誌監測改造(可選)
- 性能提升(可選)
- 功能擴展(可選)
升級要求
- 少修改代碼 或 不修改代碼
- 功能儘可能平替,而不發生基本功能變化
- 低學習成本
具體方案
完全平替的改造
方案特征
- 完全平替
沒有額外的學習成本,沒有其他的操作,只需要將jar包完全替換掉 log4j 即可。
方案具體內容
組件:reload4j
<!-- pom.xml -->
<dependency>
<groupId>ch.qos.reload4j</groupId>
<artifactId>reload4j</artifactId>
<version>1.2.22</version>
</dependency>
reload4j 是 log4j 原作者 Ceki Gülcü 發起 是基於 log4j 版本 1.2.17 的分支,其主要目的是為瞭解決 log4j 1.2.17 中的漏洞。
reload4j 可以做到完全平替 log4j 。
其中 log4j 1中 對安全性必要大影響的 CVE-2021-4104 CVE-2022-23302 已經在 reload4j 的 1.2.22 版本中修複。
有一定學習成本但不多(log4j1 升級到 log4j2)
方案特征
- 部分平替
- 有一定學習成本(log4j1 和 log4j2 的配置還是有一些不一樣的)
- 擴展的額外功能對於項目維護與正常運行有更好的幫助(不是主要因素)
- 不支持 jdk1.5 及以下 (reload4j 支持 1.5)
方案具體介紹
英文方案
英文方案主要涉及到代碼的改造,將原本的 log4j1 徹底改造成 log4j2 。
或者我們使用下麵的方式通過路由鏈接 log4j1 的api 完成準無代碼遷移。
組件: log4j-api 日誌介面; log4j-core 具體的實現; log4j-1.2-api log4j1 到 log4j2 的路由器,在使用 log4j1 的 api 時會在內部路由到 log4j2 的 api。
<!-- pom.xml -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.12.4</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.4</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.4</version>
</dependency>
但無論如何幾乎都無法替代以下幾個類的內容缺失導致的代碼變更。
org.apache.log4j.spi.ThrowableInformation
org.apache.log4j.spi.ErrorHandler
等等
改造的功能點更多的傾向於 log4j 中對異常消息處理,異常數據處理等功能,這部分功能在項目中儘可能不要使用,從某些角度上來說,數據處理、消息處理這部分有專門的獨立處理組件,日誌組件就應該處理日誌,尤其儘可能非同步列印到日誌文件或日誌流中。
翻天覆地的調整(通過 SLF4J / commons-logging 替換 現有的 日誌配置)
方案特征
- 日誌擴展性極佳。
- 日誌性能極佳。
- 日誌模塊化,對其他模塊幾乎沒有影響
- 日誌管理更加多樣化,選擇更多。
方案具體介紹
找到所有用到 org.apache.log4j 的包,調整代碼使用過程即可。
此處只介紹 SLF4J 和 logback
<!-- pom.xml -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.4</version>
</dependency>
<!--logback.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="target/log/logback.log" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="com.example.logback.filter.MyFilter" /> -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<logger name="org.apache" level="info" />
<logger name="com.gargoylesoftware" level="warn" />
<logger name="com.example.logback" level="warn" />
<!-- com.gargoylesoftware-->
<!-- <logger name="com.gargoylesoftware" level="info" />-->
</configuration>