日志打印的這10個(gè)坑,你至少踩過一個(gè)...
前言
大家好,我是撿田螺的小男孩.
我們?nèi)粘i_發(fā)中,經(jīng)常需要打印日志.但是不當(dāng)?shù)娜罩臼褂每赡軙?huì)導(dǎo)致各種問題。整理了日志打印的10個(gè)坑,希望大家都能避坑~~
1.忽視日志級(jí)別,反手就是INFO
常見的日志級(jí)別有5種,分別是error、warn、info、debug、trace。日常開發(fā)中,我們需要選擇恰當(dāng)?shù)娜罩炯?jí)別,不要反手就是打印info哈~
- ERROR:錯(cuò)誤日志,指比較嚴(yán)重的錯(cuò)誤,對正常業(yè)務(wù)有影響,需要運(yùn)維配置監(jiān)控的;
- WARN:警告日志,一般的錯(cuò)誤,對業(yè)務(wù)影響不大,但是需要開發(fā)關(guān)注;
- INFO:信息日志,記錄排查問題的關(guān)鍵信息,如調(diào)用時(shí)間、出參入?yún)⒌鹊龋?/li>
- DEBUG:用于開發(fā)DEBUG的,關(guān)鍵邏輯里面的運(yùn)行時(shí)數(shù)據(jù);
- TRACE:最詳細(xì)的信息,一般這些信息只記錄到日志文件中
2.過度日志記錄
- 問題:記錄了過多的日志信息,導(dǎo)致日志文件過大,難以管理和分析。
比如這個(gè)例子(過度記錄DEBUG級(jí)別的日志):
// 過度記錄DEBUG級(jí)別的日志
public void processData() {
logger.debug("Entering processData method.");
// 業(yè)務(wù)邏輯
logger.debug("Exiting processData method.");
}
正例應(yīng)該這樣(僅記錄必要的日志信息)
// 僅在異常和重要步驟中記錄日志
public void processData() {
try {
logger.info("Start processing data.");
// 業(yè)務(wù)邏輯
logger.info("Finished processing data.");
} catch (Exception e) {
logger.error("Error occurred while processing data: ", e);
}
}
我們應(yīng)當(dāng)根據(jù)日志的重要性設(shè)置不同的日志級(jí)別(如ERROR、WARN、INFO、DEBUG),只記錄必要的日志信息,避免日志打印處理成流水賬.
3.將debug日志直接帶到生產(chǎn)環(huán)境
有些伙伴亂用日志級(jí)別,甚至將DEBUG級(jí)別的日志用于生產(chǎn)環(huán)境。
反例(將DEBUG級(jí)別的日志直接用于生產(chǎn)環(huán)境):
logger.debug("This is a debug message,should not be logged in production");
正例 (debug日志級(jí)別,最好判斷一下是否開啟):
if(log.isDebugEnable()){
logger.debug("This is a debug message,should not be logged in production");
}
4.日志缺少上下文信息
大家可以看下這行日志,覺得有啥問題
logger.info("User login request");
很明顯,日志缺少上下文信息,并不方便排查.比如說,你想知道是哪個(gè)用戶的登錄請求嘛? 至少把userId 打印出來吧,如下:
logger.info("User login request,userId:{}",userId);
5.同步IO導(dǎo)致性能問題
同步日志記錄會(huì)阻塞主線程,影響系統(tǒng)性能。因此使用異步日志框架(比如Log4j 2的異步日志記錄器)來減少對性能的影響。
// 使用Log4j 2的異步日志
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<Async name="AsyncConsole">
<AppenderRef ref="Console"/>
</Async>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="AsyncConsole"/>
</Root>
</Loggers>
</Configuration>
6.日志配置不合理:
有些日志配置文件復(fù)雜,難以維護(hù);配置文件中存在硬編碼路徑。如下:
// log4j.properties 示例
log4j.rootLogger=INFO, file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/var/log/tianluoboy.log
應(yīng)當(dāng)使用靈活的配置
// logback.xml 示例
<configuration>
<property name="LOG_HOME" value="${LOG_HOME:-/var/log/myapp}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/tianluoboy.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/tianluoboy.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE"/>
</root>
</configuration>
7.日志內(nèi)容泄露敏感信息
日志中記錄了用戶的敏感信息(如密碼、信用卡號(hào)),存在安全風(fēng)險(xiǎn)。
logger.info("User password: {}", password);
這都把用戶的密碼打印出來了...解決方法就是,不能打印密碼這些關(guān)鍵信息,如果是手機(jī)號(hào)、郵箱等敏感信息,則可以脫敏、或者掩碼處理。
8.日志文件輪轉(zhuǎn)和歸檔配置不當(dāng)。
如果日志文件過大時(shí)未能及時(shí)輪轉(zhuǎn),就很坑.
// 簡單的日志配置,沒有輪轉(zhuǎn)策略
log4j.appender.file.File=tianluoboy.log
日志要配置合理的輪轉(zhuǎn)和歸檔策略,避免文件過大.
// logback.xml 示例
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>tianluoboy.logg</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>tianluoboy.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE"/>
</root>
</configuration>
9.日志框架漏洞的問題
有些日志框架,在低版本,可能會(huì)存在安全漏洞問題.甚至有些可能會(huì)存在漏洞還沒被發(fā)現(xiàn).
我們?nèi)绾巫瞿? 如果是低版本存在安全漏洞的日志框架,我們要盡快升級(jí)到最新版本.
比如Log4Shell 是Log4j 2.x中一個(gè)嚴(yán)重的遠(yuǎn)程代碼執(zhí)行(RCE)漏洞。攻擊者可以通過特制的日志消息來觸發(fā)JNDI查找請求,從而在受影響的系統(tǒng)上執(zhí)行任意代碼。
可以升級(jí)到Log4j 2.17.0或更高版本,這些版本已經(jīng)修復(fù)了該漏洞。
尚未被發(fā)現(xiàn)的漏洞,也可能潛在地影響應(yīng)用程序的安全性。我們要使用成熟的日志框架,并且要定時(shí)更新和維護(hù).
10.錯(cuò)誤配置LevelFilter造成日志重復(fù)記錄
錯(cuò)誤配置LevelFilter可能導(dǎo)致日志重復(fù)記錄的問題.比如你這樣配置:
<Configuration status="WARN">
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
<Filters>
<LevelFilter level="INFO" notallow="ACCEPT" notallow="DENY"/>
</Filters>
</Console>
<File name="FileAppender" fileName="app.log">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
<Filters>
<LevelFilter level="INFO" notallow="ACCEPT" notallow="DENY"/>
</Filters>
</File>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="ConsoleAppender"/>
<AppenderRef ref="FileAppender"/>
</Root>
</Loggers>
</Configuration>
在上述配置中,由于兩個(gè)appender的LevelFilter條件相同,導(dǎo)致每條INFO級(jí)別的日志都會(huì)同時(shí)被兩個(gè)appender記錄,產(chǎn)生了重復(fù)日志。
可以使用不同的過濾器策略來確保日志只被一個(gè)appender記錄:
<Configuration status="WARN">
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
<Filters>
<LevelFilter level="INFO" notallow="ACCEPT" notallow="DENY"/>
</Filters>
</Console>
<File name="FileAppender" fileName="app.log">
<PatternLayout pattern="%d [%t] %-5level: %msg%n%throwable"/>
<Filters>
<LevelFilter level="DEBUG" notallow="ACCEPT" notallow="DENY"/>
<LevelFilter level="INFO" notallow="DENY" notallow="DENY"/>
</Filters>
</File>
</Appenders>
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="ConsoleAppender"/>
<AppenderRef ref="FileAppender"/>
</Root>
</Loggers>
</Configuration>
在這個(gè)配置中,ConsoleAppender只記錄INFO級(jí)別及以上的日志,而FileAppender記錄DEBUG級(jí)別但排除INFO級(jí)別的日志。這樣可以避免INFO級(jí)別的日志被兩個(gè)appender同時(shí)記錄。