基于Java編寫統(tǒng)一日志工具類搞定雙日志體系的智能打印
作為一名后端開發(fā),不知道你有沒有遇到過這樣的頭疼事:測試環(huán)境日志瘋狂輸出,磁盤內(nèi)存分分鐘告急,生產(chǎn)環(huán)境又必須保留全量日志方便排查問題。
最近我就被這個問題狠狠折磨了一把。我們項目里有大量定時任務,用的是XxlJobHelper打印任務日志,業(yè)務代碼又依賴@Slf4j輸出業(yè)務日志,兩套日志體系各自為政。
測試環(huán)境一跑起來,不管是調(diào)試信息、中間參數(shù),還是無關緊要的流程日志,一股腦全打出來,沒幾天日志文件就占滿了磁盤,排查問題時翻半天也找不到關鍵信息;可生產(chǎn)環(huán)境又不敢精簡日志,萬一出問題,少一行日志都可能定位不到故障根因。
改吧?兩套日志寫法,到處都是log.info()、XxlJobHelper.log(),手動改成本太高;不改吧,測試環(huán)境內(nèi)存天天報警,運維天天找。
思來想去,我決定寫一個統(tǒng)一日志工具類UnifiedLogger,一把搞定兩套日志的智能過濾:生產(chǎn)環(huán)境全量打印,測試環(huán)境只打關鍵日志,配置一行搞定,不用改動原有業(yè)務邏輯!
一、痛點復盤:我們的日志到底亂在哪?
先跟大家同步下我們項目的日志現(xiàn)狀,相信很多團隊都有同款問題:
- 雙日志體系,無法統(tǒng)一管理:業(yè)務層用
@Slf4j打印業(yè)務日志,定時任務層用XxlJobHelper打印任務執(zhí)行日志,兩套API完全獨立,想做日志過濾得分別改,工作量翻倍。 - 測試環(huán)境日志泛濫,內(nèi)存占用爆表:測試環(huán)境不需要調(diào)試參數(shù)、循環(huán)日志、中間狀態(tài)這些冗余信息,但所有日志都原樣輸出,導致日志文件過大,磁盤IO高,排查問題效率極低。
- 生產(chǎn)環(huán)境不能精簡,必須全量保留:生產(chǎn)環(huán)境是底線,任何日志都不能丟,一旦故障,全量日志是定位問題的唯一依據(jù),所以不能直接關閉低級別的日志。
- 改造難度大,不能影響原有業(yè)務:項目已經(jīng)上線,不能大規(guī)模修改原有日志代碼,必須做到無侵入、兼容舊代碼、一鍵切換。
面對這些問題,我定下了改造目標:
- 一套工具兼容
@Slf4j+XxlJobHelper雙日志 - 配置化控制環(huán)境,無需修改代碼
- 測試環(huán)境只打印關鍵日志,生產(chǎn)環(huán)境全量打印
- 無侵入改造,原有代碼可平滑遷移
二、核心解決方案:UnifiedLogger 統(tǒng)一日志工具類
我直接把完整的工具類代碼貼出來,復制到項目里就能用,無需額外依賴,完美適配SpringBoot項目:
package com.at.mrp.bll.log;
import com.xxl.job.core.context.XxlJobHelper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 統(tǒng)一日志工具類
* 支持 slf4j 和 XxlJob 日志的智能過濾
*
* 使用方式:
* 1. UAT/測試環(huán)境:在消息中包含【重要】、【錯誤】、【失敗】等關鍵詞才會輸出
* 2. 生產(chǎn)環(huán)境:所有日志都輸出
*
* 示例:
* UnifiedLogger.info(log, "【重要】開始執(zhí)行批量任務");
* UnifiedLogger.info(log, "普通調(diào)試信息"); // UAT環(huán)境不輸出,生產(chǎn)環(huán)境輸出
* UnifiedLogger.error(log, "發(fā)生異常: {}", errorMsg); // 所有環(huán)境都輸出
*/
@Slf4j
@Component
public class UnifiedLogger {
private static String logEnv;
@Value("${log.env:prod}")
public void setLogEnv(String env) {
UnifiedLogger.logEnv = env;
}
/**
* 判斷是否應該記錄日志
* UAT/測試環(huán)境:只記錄包含特定關鍵詞的日志
* 生產(chǎn)環(huán)境:全部記錄
*/
private static boolean shouldLog(String message) {
if ("prod".equalsIgnoreCase(logEnv)) {
return true;
}
if (message == null || message.isEmpty()) {
return false;
}
String upperMsg = message.toUpperCase();
// UAT/測試環(huán)境:只記錄包含以下關鍵詞的日志
return upperMsg.contains("【重要】")
|| upperMsg.contains("【錯誤】")
|| upperMsg.contains("【失敗】")
|| upperMsg.contains("【警告】")
|| upperMsg.contains("【關鍵】")
|| upperMsg.contains("[ERROR]")
|| upperMsg.contains("[FAIL]")
|| upperMsg.contains("[WARN]")
|| upperMsg.contains("[IMPORTANT]");
}
// ==================== slf4j 日志方法 ====================
/**
* INFO 級別日志 - 智能過濾
*
* @param logger slf4j Logger 實例
* @param message 日志消息
* @param args 參數(shù)
*/
public static void info(Logger logger, String message, Object... args) {
if (shouldLog(message)) {
logger.info(message, args);
}
}
/**
* WARN 級別日志 - 所有環(huán)境都記錄(自動添加【警告】標記)
*
* @param logger slf4j Logger 實例
* @param message 日志消息
* @param args 參數(shù)
*/
public static void warn(Logger logger, String message, Object... args) {
String formattedMessage = "【警告】" + message;
logger.warn(formattedMessage, args);
}
/**
* ERROR 級別日志 - 所有環(huán)境都記錄(自動添加【錯誤】標記)
*
* @param logger slf4j Logger 實例
* @param message 日志消息
* @param args 參數(shù)
*/
public static void error(Logger logger, String message, Object... args) {
String formattedMessage = "【錯誤】" + message;
logger.error(formattedMessage, args);
}
/**
* DEBUG 級別日志 - 僅生產(chǎn)環(huán)境記錄
*
* @param logger slf4j Logger 實例
* @param message 日志消息
* @param args 參數(shù)
*/
public static void debug(Logger logger, String message, Object... args) {
if ("prod".equalsIgnoreCase(logEnv)) {
logger.debug(message, args);
}
}
// ==================== XxlJob 日志方法 ====================
/**
* XxlJob 智能日志記錄
* 根據(jù)環(huán)境和消息內(nèi)容決定是否輸出
*
* @param message 日志消息,可包含【重要】、【錯誤】等關鍵詞
* @param args 參數(shù)
*/
public static void xxlLog(String message, Object... args) {
if (shouldLog(message)) {
XxlJobHelper.log(message, args);
}
}
/**
* XxlJob 錯誤日志(自動添加【錯誤】標記)
*/
public static void xxlLogError(String message, Object... args) {
String formattedMessage = "【錯誤】" + message;
XxlJobHelper.log(formattedMessage, args);
}
/**
* XxlJob 警告日志(自動添加【警告】標記)
*/
public static void xxlLogWarn(String message, Object... args) {
String formattedMessage = "【警告】" + message;
XxlJobHelper.log(formattedMessage, args);
}
/**
* XxlJob 重要日志(自動添加【重要】標記)
*/
public static void xxlLogImportant(String message, Object... args) {
String formattedMessage = "【重要】" + message;
XxlJobHelper.log(formattedMessage, args);
}
/**
* XxlJob 詳細日志 - 僅生產(chǎn)環(huán)境記錄
*/
public static void xxlLogDetail(String message, Object... args) {
if ("prod".equalsIgnoreCase(logEnv)) {
XxlJobHelper.log(message, args);
}
}
}核心規(guī)則
- 生產(chǎn)環(huán)境(prod):所有日志無條件打印,保證問題可追溯
- 測試/UAT環(huán)境(uat/test):只打印包含關鍵標記的日志,自動過濾冗余信息
關鍵標記(測試環(huán)境生效)
只要日志里包含這些關鍵詞,測試環(huán)境就會打印,完美區(qū)分重要日志和調(diào)試日志:
- 業(yè)務關鍵:【重要】、【關鍵】
- 異常錯誤:【錯誤】、[ERROR]、【失敗】、[FAIL]
- 警告提示:【警告】、[WARN]
一行配置搞定環(huán)境切換
不用改代碼,只需要在對應環(huán)境的配置文件加一行配置:
測試/UAT環(huán)境
log: env: uat
生產(chǎn)環(huán)境
log: env: prod
工具類會自動讀取配置,切換日志打印策略。
三、實戰(zhàn)使用:兼容雙日志體系,用法超簡單
這個工具類最香的地方就是,同時支持@Slf4j和XxlJobHelper,原有代碼改個調(diào)用方法就行,零學習成本。
1. 業(yè)務日志(@Slf4j)用法
原來的log.info()直接替換成工具類方法,自動適配環(huán)境:
@Slf4j
@Service
public class OrderService {
public void syncOrder() {
// 【重要日志】測試/生產(chǎn)環(huán)境都會打印
UnifiedLogger.info(log, "【重要】開始同步訂單,總數(shù)量:{}", orderCount);
// 普通調(diào)試日志:測試環(huán)境不打印,生產(chǎn)環(huán)境打印
UnifiedLogger.info(log, "訂單詳情參數(shù):{}", JSON.toJSONString(order));
// 錯誤日志:自動加【錯誤】標記,所有環(huán)境必打印
UnifiedLogger.error(log, "訂單同步失?。簕}", e.getMessage());
// 警告日志:自動加【警告】標記,所有環(huán)境必打印
UnifiedLogger.warn(log, "訂單庫存不足,請及時處理");
}
}
2. 定時任務日志(XxlJobHelper)用法
定時任務里的XxlJobHelper.log()也能一鍵替換:
@XxlJob("batchTaskJob")
public void batchTask() {
// 重要任務日志:全環(huán)境打印
UnifiedLogger.xxlLog("【重要】定時任務開始執(zhí)行");
// 詳細任務日志:僅生產(chǎn)環(huán)境打印
UnifiedLogger.xxlLog("任務入?yún)ⅲ簕}", param);
// 任務錯誤日志:全環(huán)境打印
UnifiedLogger.xxlLogError("定時任務執(zhí)行失?。簕}", errorMsg);
}
工具類核心亮點
- 自動加標記:error/warn方法自動拼接【錯誤】【警告】,不用手動寫
- 智能過濾:測試環(huán)境自動過濾無關鍵詞的普通日志
- 無性能損耗:生產(chǎn)環(huán)境直接打印,無額外邏輯開銷
- 靜態(tài)方法:直接調(diào)用,無需注入,使用超方便
四、改造效果:測試環(huán)境日志直接瘦身90%
上線這個工具類后,效果立竿見影:
- 測試環(huán)境日志量直接減少90%,磁盤內(nèi)存不再報警
- 排查問題效率翻倍:打開日志全是關鍵信息,沒有冗余干擾
- 生產(chǎn)環(huán)境完全無影響:全量日志正常保留
- 改造零 風險:原有代碼逐步遷移,不影響業(yè)務運行
最關鍵的是,后續(xù)新增日志只需要按照規(guī)則加關鍵詞,不用再擔心日志泛濫問題,一次封裝,長期受益。
五、總結與最佳實踐
最后給大家總結下使用心得:
- 關鍵業(yè)務流程:務必加【重要】標記,保證測試環(huán)境可觀測
- 異常/錯誤:統(tǒng)一用
error方法,自動標記,全環(huán)境必打 - 調(diào)試/參數(shù)日志:不加標記,測試環(huán)境自動過濾,不占用內(nèi)存
- 環(huán)境切換:純配置化,一行代碼搞定,不用重新發(fā)布
如果你也在被測試環(huán)境日志泛濫、雙日志體系難管理的問題困擾,不妨試試這個統(tǒng)一日志工具類,低成本解決這個麻煩!
我一直覺得,好的工具不是花里胡哨的功能,而是能實實在在解決日常開發(fā)的痛點,讓我們少加班、多高效。這個小小的UnifiedLogger,就是我為團隊日志痛點交出的最優(yōu)解~
以上就是基于Java編寫統(tǒng)一日志工具類搞定雙日志體系的智能打印的詳細內(nèi)容,更多關于Java統(tǒng)一日志工具類的資料請關注腳本之家其它相關文章!
相關文章
Java以struts2為例介紹如何實現(xiàn)圖片上傳
這篇文章主要介紹了Java struts2中如何實現(xiàn)圖片上傳的相關資料,需要的朋友可以參考下2015-11-11
使用Java WebSocket獲取客戶端IP地址的示例代碼
在開發(fā)Web應用程序時,我們通常需要獲取客戶端的 IP 地址用于日志記錄、身份驗證、限制訪問等操作,本文將介紹如何使用Java WebSocket API獲取客戶端IP地址,以及如何在常見的WebSocket框架中獲得客戶端 IP地址,需要的朋友可以參考下2023-11-11
Java while(scanner.hasNext())無法跳出的解決方案
這篇文章主要介紹了Java while(scanner.hasNext())無法跳出的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
SpringBoot+WebSocket向前端推送消息的實現(xiàn)示例
WebSocket是一種在單個TCP連接上進行全雙工通信的協(xié)議,允許服務器主動向客戶端推送信息,同時也能從客戶端接收信息,本文主要介紹了SpringBoot+WebSocket向前端推送消息的實現(xiàn)示例,感興趣的可以了解一下2024-08-08
基于Spring MVC的文件上傳和下載實現(xiàn)方法
在Web應用程序中,文件上傳和下載是常見的功能,Spring MVC框架提供了方便的方式來實現(xiàn)這些功能,本文將介紹如何使用Spring MVC實現(xiàn)文件上傳和下載,需要的朋友可以參考下2023-05-05

