SpringBoot實(shí)現(xiàn)操作日志記錄的完整指南
引言
在項(xiàng)目中,無論是在開發(fā)環(huán)境還是生產(chǎn)環(huán)境,如果系統(tǒng)出現(xiàn)故障,迎面而來的第一個(gè)問題往往是: “這個(gè)操作是誰在什么時(shí)候執(zhí)行的?具體做了什么改動?” 如果系統(tǒng)對此一無所知,排查工作就如同大海撈針。
操作日志記錄正是為了解決這一問題而生。它是一個(gè)系統(tǒng)性的、用于追蹤用戶行為、厘清操作責(zé)任以及復(fù)現(xiàn)歷史流程的核心功能。它是系統(tǒng)的“黑匣子”,也是開發(fā)者的“記事本”。 在SpringBoot項(xiàng)目中,若將日志記錄代碼散亂地嵌入到每個(gè)業(yè)務(wù)方法的try-catch塊中,會導(dǎo)致代碼重復(fù)嚴(yán)重、核心業(yè)務(wù)邏輯被污染、不利于維護(hù)。
本文將探討如何利用SpringBoot面向切面編程(AOP) 來優(yōu)雅地解決這一難題。
一、環(huán)境準(zhǔn)備與項(xiàng)目搭建
1. 創(chuàng)建一個(gè)標(biāo)準(zhǔn)的SpringBoot項(xiàng)目
我使用的是SpringBoot3.5.5+JDK17
2. 引入核心依賴
- mysql-connector-j(數(shù)據(jù)庫驅(qū)動)
- mybatis-plus-spring-boot3-starter(數(shù)據(jù)持久化框架)
- spring-boot-starter-aop(AOP核心)
- aspectjweaver(面向切面工具)
- spring-boot-starter-web(Web項(xiàng)目)
- fastjson2(阿里json解析器)
二、實(shí)現(xiàn)步驟
1. 創(chuàng)建日志實(shí)體類(OperateLog)
@Data
public class OperateLog {
private Long id; //主鍵
private Long userId; //操作人id
private Date createTime; //創(chuàng)建時(shí)間
private String description; //請求描述
private String method; //請求方法 如:post、get
private String ip; //操作人ip
private String param; //請求參數(shù):json格式
private String result; //請求結(jié)果:json格式
private Integer success; //操作是否成功
private String errorMsg; //錯(cuò)誤信息
}
2. 創(chuàng)建操作日志記錄表(operate_log)
create table operate_log( id BIGINT PRIMARY key AUTO_INCREMENT COMMENT '主鍵', user_id BIGINT DEFAULT 100 COMMENT '操作人id', create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', description VARCHAR(200) COMMENT '請求描述', method VARCHAR(20) COMMENT '請求方法 如:post、get', ip VARCHAR(32) COMMENT '操作人ip', param VARCHAR(2000) COMMENT '請求參數(shù):json格式', result VARCHAR(2000) COMMENT '請求結(jié)果:json格式', success TINYINT DEFAULT 0 COMMENT '操作是否成功:1是、0否', error_msg VARCHAR(2000) COMMENT '錯(cuò)誤消息' ) COMMENT '操作日志記錄表';
3. 創(chuàng)建添加日志的相關(guān)代碼
//mapper接口
public interface OperateLogMapper extends BaseMapper<OperateLog> {}
//service接口
public interface OperateLogService extends IService<OperateLog> {}
//service實(shí)現(xiàn)類
@Service
public class OperateLogServiceImpl extends ServiceImpl<OperateLogMapper, OperateLog> implements OperateLogService {}
4. 創(chuàng)建自定義注解(@Log)
在注解類中,指定其可以對方法進(jìn)行修飾,并設(shè)置運(yùn)行時(shí)保留策略
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 請求描述
*/
String title() default "";
}
5. 創(chuàng)建切面類(LogAspect)- 核心
在切面類中,創(chuàng)建前置通知和后置通知,并指定被@Log注解修飾的方法為切點(diǎn),實(shí)現(xiàn)對目標(biāo)方法的“增強(qiáng)”。
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private OperateLogService logService;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
/**
* 請求前執(zhí)行
* @param aspLog
*/
@Before(value = "@annotation(aspLog)")
public void doBefore(Log aspLog){
log.info("請求日志記錄start");
}
/**
* 請求后執(zhí)行
* @param aspLog
* @param result
*/
@AfterReturning(pointcut = "@annotation(aspLog)",returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Log aspLog, Object result){
handleLog(joinPoint,aspLog,null,result);
}
/**
* 請求異常執(zhí)行
* @param aspLog
* @param e
*/
@AfterThrowing(value = "@annotation(aspLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint,Log aspLog,Exception e){
handleLog(joinPoint,aspLog,e,null);
}
/**
* 日志處理器-記錄日志
* @param aspLog
* @param e
* @param result
*/
private void handleLog(JoinPoint joinPoint,Log aspLog,Exception e,Object result){
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
OperateLog operateLog = new OperateLog();
operateLog.setIp(attributes.getRequest().getRemoteAddr());
operateLog.setMethod(attributes.getRequest().getMethod());
String param = JSON.toJSONString(joinPoint.getArgs());
operateLog.setParam(param);
if(e != null){
operateLog.setErrorMsg(e.getLocalizedMessage());
}else{
operateLog.setSuccess(1);
operateLog.setResult(JSON.toJSONString(result));
}
operateLog.setDescription(aspLog.title());
executor.submit(() -> logService.save(operateLog));
}
}
6. 編寫控制層代碼進(jìn)行測試
@RestController
public class TestController {
@Log(title = "測試操作")
@GetMapping("/test")
public String test(){
return "沒毛病";
}
@Log(title = "測試參數(shù)操作")
@PostMapping("/testParam")
public String testParam(@RequestBody String data){
return data;
}
@Log(title = "測試異常操作")
@GetMapping("/testExecption")
public String testExecption(){
return (1/0)+"";
}
}
7. 其他注意事項(xiàng)
如果你使用的Springboot也是3.0+,在引入mybatis-plus依賴的時(shí)候,一定要導(dǎo)入mybatis-plus-spring-boot3-starter,而不是mybatis-plus-boot-starter。如果導(dǎo)入的依賴不正確,會導(dǎo)致Spring無法注入mybatis相關(guān)的內(nèi)容并報(bào)錯(cuò)。
三、功能測試與驗(yàn)證(Postman)
測試三種情況,1、測試無參get請求,2、測試傳參post請求,3、測試異常請求,對于成功的請求要在日志記錄中標(biāo)記為成功,對于異常的請求要記錄異常信息。
1. 測試無參get請求

2. 測試post帶參數(shù)請求

3. 測試異常請求

四、 總結(jié)
到這里,就完成了操作日志的記錄功能,需要注意的是,在日常開發(fā)中,一定要將記錄日志的代碼交給另一個(gè)線程執(zhí)行,避免持久化操作對主線程產(chǎn)生性能上的影響。面向切面的本質(zhì)上是對切點(diǎn)方法做了一層代理,在不影響業(yè)務(wù)代碼的前提下對其進(jìn)行功能擴(kuò)展,降低了業(yè)務(wù)代碼和非業(yè)務(wù)代碼的耦合度,這種設(shè)計(jì)適用于很多其他的業(yè)務(wù)場景,比如異常處理、接口性能監(jiān)控、參數(shù)校驗(yàn)與預(yù)處理等。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)操作日志記錄的完整指南的文章就介紹到這了,更多相關(guān)SpringBoot操作日志記錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談SpringMVC+Spring3+Hibernate4開發(fā)環(huán)境搭建
MVC已經(jīng)是現(xiàn)代Web開發(fā)中的一個(gè)很重要的部分,本文介紹一下SpringMVC+Spring3+Hibernate4的開發(fā)環(huán)境搭建,有興趣的可以了解一下。2017-01-01
springboot 自定義配置Boolean屬性不生效的解決
這篇文章主要介紹了springboot 自定義配置Boolean屬性不生效的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景
ReentrantReadWriteLock是Java中一種高效的讀寫鎖,適用于讀多寫少的并發(fā)場景,它通過允許多個(gè)線程同時(shí)讀取,但在寫入時(shí)限制為單線程訪問,從而提高了程序的并發(fā)性和性能,本文給大家介紹Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景,感興趣的朋友跟隨小編一起看看吧2024-10-10
spring中EnvironmentPostProcessor接口的實(shí)現(xiàn)
EnvironmentPostProcessor是SpringBoot用于動態(tài)修改環(huán)境配置的接口,本文主要介紹了spring中EnvironmentPostProcessor接口的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2025-05-05
MyBatis-Plus 中 的動態(tài)SQL 片段(sqlSegment)詳解
MyBatis-Plus的sqlSegment通過Wrapper動態(tài)生成SQL片段,支持XML中${ew.customSqlSegment}引用,結(jié)合Lambda表達(dá)式避免硬編碼,適用于動態(tài)查詢、邏輯刪除等場景,提升代碼可維護(hù)性與靈活性,本文給大家介紹MyBatis-Plus中的動態(tài)SQL片段(sqlSegment)講解,感興趣的朋友一起看看吧2025-06-06
SpringMVC攔截器創(chuàng)建配置及執(zhí)行順序
這篇文章主要為大家介紹了SpringMVC攔截器創(chuàng)建配置及執(zhí)行順序,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
工廠模式_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了工廠模式_動力節(jié)點(diǎn)Java學(xué)院整理的相關(guān)資料,需要的朋友可以參考下2017-08-08
springboot?正確的在異步線程中使用request的示例代碼
這篇文章主要介紹了springboot中如何正確的在異步線程中使用request,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07

