SpringBoot中日志切面實(shí)現(xiàn)小結(jié)
在應(yīng)用開發(fā)中,日志記錄對于監(jiān)控、調(diào)試和追蹤用戶行為至關(guān)重要。Spring Boot 雖然內(nèi)置了強(qiáng)大的日志框架,但在某些情況下,我們可能需要更細(xì)粒度的日志管理。Spring AOP 提供了一種靈活的方式來實(shí)現(xiàn)方法級別的日志記錄,而無需侵入業(yè)務(wù)代碼。本文將介紹如何通過 Spring AOP 切面來實(shí)現(xiàn)這一功能。
什么是Spring AOP?
Spring AOP 是一個(gè)面向切面的編程(AOP)框架,它允許開發(fā)者將橫切關(guān)注點(diǎn)(如日志記錄、事務(wù)管理等)與業(yè)務(wù)邏輯分離。通過使用 Spring AOP,我們可以在不修改業(yè)務(wù)代碼的情況下,為應(yīng)用程序添加日志記錄功能。
Spring AOP 詳細(xì)可以看這篇文章 Spring AOP入門
日志切面的設(shè)計(jì)
在我們的示例中,我們定義了一個(gè) SysLog
注解,用于標(biāo)記需要記錄日志的方法。接著,我們創(chuàng)建了一個(gè)日志切面 LogAspect
,它會(huì)攔截所有帶有 SysLog
注解的方法,并記錄日志信息。
SysLog 注解
首先,我們定義了一個(gè) SysLog
注解,它可以用來標(biāo)記需要記錄日志的方法:
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; }
這個(gè)注解非常簡單,它只有一個(gè) value
屬性,用于存儲(chǔ)日志的描述信息。
日志切面 LogAspect
接下來,我們創(chuàng)建了 LogAspect
切面類,它會(huì)攔截所有帶有 SysLog
注解的方法:
@Aspect @Component public class LogAspect { // 省略其他成員變量和方法 @Around("@annotation(sysLog)") public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable { // 日志記錄邏輯 } }
在這個(gè)切面中,我們使用了 @Around
注解來定義一個(gè)環(huán)繞通知,它會(huì)在目標(biāo)方法執(zhí)行前后記錄日志信息。
日志記錄的實(shí)現(xiàn)
在 LogAspect
切面類中,我們實(shí)現(xiàn)了 logAround
方法,它會(huì)在目標(biāo)方法執(zhí)行前后記錄日志信息:
@Around("@annotation(sysLog)") public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable { Log log = new Log(); log.setTimestamp(new Date()); log.setDescription(sysLog.value()); log.setMethodName(joinPoint.getSignature().getName()); log.setParameters(Arrays.toString(joinPoint.getArgs())); try { Object result = joinPoint.proceed(); log.setLevel("INFO"); log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行成功。"); return result; } catch (Exception e) { log.setLevel("ERROR"); log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行過程中發(fā)生異常。"); log.setException(e.toString()); throw e; } finally { logService.save(log); } }
在這個(gè)環(huán)繞通知中,我們首先創(chuàng)建了一個(gè) Log
對象,并設(shè)置了日志的基本信息,如時(shí)間戳、描述、方法名和參數(shù)。然后,我們執(zhí)行目標(biāo)方法,并根據(jù)執(zhí)行結(jié)果記錄日志信息。如果目標(biāo)方法執(zhí)行過程中發(fā)生異常,我們會(huì)記錄異常信息。
測試
完整代碼
Maven依賴
在 pom.xml
文件中添加依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
SQL腳本
創(chuàng)建日志表的 SQL 腳本:
DROP TABLE IF EXISTS `logs`; CREATE TABLE `logs` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '描述', `level` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志級別', `timestamp` datetime NOT NULL COMMENT '時(shí)間戳', `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '日志消息', `class_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '類名', `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '方法名', `parameters` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '參數(shù)', `user_identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用戶標(biāo)識', `exception` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '異常信息', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '日志表' ROW_FORMAT = DYNAMIC;
日志實(shí)體類 Log
我們定義了一個(gè) Log
實(shí)體類,用于存儲(chǔ)日志信息:
/** * 日志實(shí)體類 * * @author 王聞薪 */ @Data @TableName("logs") public class Log implements Serializable { private static final long serialVersionUID = 1L; /** * 主鍵ID */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 描述 */ @TableField("description") private String description; /** * 日志級別 */ @TableField("level") private String level; /** * 時(shí)間戳 */ @TableField("timestamp") private Date timestamp; /** * 日志消息 */ @TableField("message") private String message; /** * 類名 */ @TableField("class_name") private String className; /** * 方法名 */ @TableField("method_name") private String methodName; /** * 參數(shù) */ @TableField("parameters") private String parameters; /** * 用戶標(biāo)識 */ @TableField("user_identifier") private String userIdentifier; /** * 異常信息 */ @TableField("exception") private String exception; }
這個(gè)實(shí)體類映射到數(shù)據(jù)庫中的 logs
表,用于存儲(chǔ)日志的詳細(xì)信息。
切面代碼
日志切面類 LogAspect
:
package org.example.demo.aspectj; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.example.demo.annotation.SysLog; import org.example.demo.entity.Log; import org.example.demo.service.ILogService; import org.example.demo.utils.TokenService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.Date; /** * 日志切面類 * 用于攔截標(biāo)注有@SysLog注解的方法,并記錄日志信息。 * * @author 王聞薪 */ @Slf4j @Aspect // 標(biāo)記為切面類 @Component // 將此切面類交給Spring管理 public class LogAspect { @Autowired private ILogService logService; @Autowired private TokenService tokenService; /** * 環(huán)繞通知 * 攔截帶有@SysLog注解的方法,并在方法執(zhí)行前后進(jìn)行日志記錄。 * * @param joinPoint 連接點(diǎn)對象,包含方法的調(diào)用信息。 * @param sysLog 注解對象,包含日志描述信息。 * @return 方法執(zhí)行結(jié)果。 * @throws Throwable 可能拋出的異常。 */ @Around("@annotation(sysLog)") public Object logAround(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable { // 創(chuàng)建日志對象 Log log = new Log(); log.setLevel("INFO"); log.setTimestamp(new Date()); log.setUserIdentifier(getUserId().toString()); log.setDescription(sysLog.value()); // 記錄方法調(diào)用前的日志 logBefore(joinPoint, log); try { // 繼續(xù)執(zhí)行目標(biāo)方法 Object result = joinPoint.proceed(); // 記錄方法調(diào)用后的日志 logAfter(joinPoint, result, log); log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行成功。"); return result; } catch (Exception e) { // 記錄異常日志 logException(joinPoint, e, log); log.setLevel("ERROR"); log.setMessage("方法 " + joinPoint.getSignature().getName() + " 執(zhí)行過程中發(fā)生異常。"); throw e; // 重新拋出異常 } finally { // 保存日志到數(shù)據(jù)庫 logService.save(log); } } /** * 記錄方法調(diào)用前的日志信息。 * * @param joinPoint 連接點(diǎn)對象,包含方法的調(diào)用信息。 * @param log 日志對象,用于存儲(chǔ)日志信息。 */ private void logBefore(ProceedingJoinPoint joinPoint, Log log) { log.setClassName(joinPoint.getSignature().getDeclaringTypeName()); log.setMethodName(joinPoint.getSignature().getName()); log.setParameters(Arrays.toString(joinPoint.getArgs())); log.setTimestamp(new Date()); // 設(shè)置日志時(shí)間 } /** * 記錄方法調(diào)用后的日志信息。 * * @param joinPoint 連接點(diǎn)對象,包含方法的調(diào)用信息。 * @param result 方法執(zhí)行結(jié)果。 * @param log 日志對象,用于存儲(chǔ)日志信息。 */ private void logAfter(ProceedingJoinPoint joinPoint, Object result, Log log) { log.setMessage(log.getMessage() + result); } /** * 記錄方法執(zhí)行過程中的異常信息。 * * @param joinPoint 連接點(diǎn)對象,包含方法的調(diào)用信息。 * @param e 捕獲的異常對象。 * @param log 日志對象,用于存儲(chǔ)日志信息。 */ private void logException(ProceedingJoinPoint joinPoint, Exception e, Log log) { log.setException(e.toString()); log.setMessage(log.getMessage() + " 異常信息:" + e.getMessage()); } /** * 獲取當(dāng)前用戶ID。 * * @return 用戶ID。 */ private Long getUserId() { return tokenService.getUserId(); } }
結(jié)論
通過使用 Spring AOP 和注解,我們可以靈活地為 Spring Boot 應(yīng)用程序添加日志記錄功能,而無需修改業(yè)務(wù)邏輯代碼。這不僅提高了代碼的可維護(hù)性,還使得日志記錄變得更加方便和強(qiáng)大。
到此這篇關(guān)于SpringBoot中日志切面實(shí)現(xiàn)小結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot 日志切面內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity JWT基于令牌的無狀態(tài)認(rèn)證實(shí)現(xiàn)
Spring Security中實(shí)現(xiàn)基于JWT的無狀態(tài)認(rèn)證是一種常見的做法,本文就來介紹一下SpringSecurity JWT基于令牌的無狀態(tài)認(rèn)證實(shí)現(xiàn),感興趣的可以了解一下2025-04-04Java的Spring框架中bean的繼承與內(nèi)部bean的注入
這篇文章主要介紹了Java的Spring框架中bean的繼承與內(nèi)部bean的注入,Spring框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-12-12IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文)
這篇文章主要介紹了IDEA 創(chuàng)建一個(gè)Mybatis Maven項(xiàng)目的方法步驟(圖文),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03MyBatis自定義映射resultMap的實(shí)現(xiàn)
本文主要介紹了MyBatis自定義映射resultMap的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03SpringMVC中Controller層獲取前端請求參數(shù)的方式匯總
這篇文章主要介紹了SpringMVC中Controller層獲取前端請求參數(shù)的幾種方式,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密
這篇文章主要介紹了使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密的示例,需要的朋友可以參考下2014-03-03java中hasNextInt判斷后無限循環(huán)輸出else項(xiàng)的解決方法
這篇文章主要介紹了java中hasNextInt判斷后無限循環(huán)輸出else項(xiàng)的解決方法的相關(guān)資料,需要的朋友可以參考下2016-10-10Spring框架+jdbcTemplate實(shí)現(xiàn)增刪改查功能
這篇文章主要介紹了Spring框架+jdbcTemplate實(shí)現(xiàn)增刪改查功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09