springboot編寫日志環境搭建過程

AOP記錄日志

AOP記錄日志的主要優點包括:

1、低侵入性:AOP記錄日志不需要修改原有的業務邏輯代碼,只需要新增一個切面即可。

2、統一管理:通過AOP記錄日志可以將各個模塊中需要記錄日志的部分進行統一管理,降低了代碼重復度,提高了代碼可維護性和可擴展性。

3、提升效率:通過引入AOP記錄日志,可以避免手動編寫日志記錄代碼,減少了開發人員的工作量,提升了開發效率。

4、安全性:通過AOP記錄日志,可以收集系統的操作日志,幫助管理員及時發現問題并進行調整,從而提高系統的安全性。

AOP記錄日志的整體思想

1、基于自定義注解來確定切入點【優勢:可以通過自定義注解攜帶一些變化的參數,比如模塊名稱】

2、基于環繞通知來完成日志記錄

搭建之前,要明白AOP主要核心術語

下面:

切面類環境搭建

1.在common模塊下創建一個獨立的記錄日志的模塊【common-log】

在該模塊下加入如下的依賴

2.自定義Log注解,如下所示:

代碼如下:

import com.atguigu.spzx.common.log.enums.OperatorType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** // 自定義操作日志記錄注解*/
@SuppressWarnings("all")
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {// 模塊名稱public String title() ;// 操作人類別public OperatorType operatorType() default OperatorType.MANAGE;// 業務類型(0其它 1新增 2修改 3刪除)public int businessType() ;//是否保存請求的參數public boolean isSaveRequestData() default true;// 是否保存響應的參數public boolean isSaveResponseData() default true;
}

//操作人類別

OperatorType定義

public enum OperatorType {?? ??? ?// 操作人類別
? ? OTHER,?? ??? ?// 其他
? ? MANAGE,?? ??? ?// 后臺用戶
? ? MOBILE?? ??? ?// 手機端用戶
}

3.定義一個切面類,并且在該切面類中提供一個環繞通知方法

LogAspect

/*** 這個方法作為環繞通知,環繞通知方法中兩個參數,ProceedingJoinPoint和Log* 1.ProceedingJoinPoint 表示能調用我們業務方法并且可以得到相關的參數等信息* 2.Log 方法上加了public @interface Log這個注解,* 加上@Around(value = "@annotation(sysLog)"),方法執行時就會執行環繞通知,就可以完成機制* 實現當方法上加上注解之后,就會實行環繞通知*/

@Aspect
@Component
@Slf4j// 環繞通知切面類定義
public class LogAspect { ? ? ? ? ? ?

? ? @Around(value = "@annotation(sysLog)")
? ? public Object doAroundAdvice(ProceedingJoinPoint joinPoint , Log sysLog) {
? ??
? ? ? ? Object proceed = null;
? ? ? ? try {

???????????????? ?// 執行業務方法
? ? ? ? ? ? proceed = joinPoint.proceed(); ? ? ? ? ? ?
? ? ? ? } catch (Throwable e) { ? ? ? ? ?

????????????????// 代碼執行進入到catch中,業務方法執行產生異常 ? ? ? ? ? ? ?
? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? }

???????? ?// 返回執行結果
? ? ? ? return proceed ; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? }
}

4.想讓LogAspect這個切面類在其他的業務服務中進行使用,那么就需要該切面類納入到Spring容器中。Spring Boot默認會掃描和啟動類所在包相同包中的bean以及子包中的bean。而LogAspect切面類不滿足掃描條件,因此無法直接在業務服務中進行使用。那么此時可以通過自定義注解進行實現

代碼如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(value = LogAspect.class) ? ? ?

?// 通過Import注解導入日志切面類到Spring容器中 ? ?
public @interface EnableLogAspect {
? ??
}

5.在啟動類pom.xml 引入依賴

<dependency><groupId>org.liuliu</groupId><artifactId>common-log</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

6.在ManagerApplication服務的啟動類上添加@EnableLogAspect注解

7.創建Log工具類,代碼如下

/*** 自定義注解搞完,切面環繞方法類方法創建完成,在manager引入common包依賴,在啟動類添加注解@EnableLogAspect* 然后封裝一個logutil工具類備用(切面環繞方法類執行時調用工具類)*/
import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.log.annotation.Log;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import com.atguigu.spzx.utils.AuthContextUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.Arrays;
public class LogUtil {//操作執行之后調用public static void afterHandlLog(Log sysLog, Object proceed,SysOperLog sysOperLog, int status ,String errorMsg) {if(sysLog.isSaveResponseData()) {sysOperLog.setJsonResult(JSON.toJSONString(proceed));}sysOperLog.setStatus(status);sysOperLog.setErrorMsg(errorMsg);}//操作執行之前調用public static void beforeHandleLog(Log sysLog,ProceedingJoinPoint joinPoint,SysOperLog sysOperLog) {// 設置操作模塊名稱sysOperLog.setTitle(sysLog.title());sysOperLog.setOperatorType(sysLog.operatorType().name());// 獲取目標方法信息MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature() ;Method method = methodSignature.getMethod();sysOperLog.setMethod(method.getDeclaringClass().getName());// 獲取請求相關參數ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();sysOperLog.setRequestMethod(request.getMethod());sysOperLog.setOperUrl(request.getRequestURI());sysOperLog.setOperIp(request.getRemoteAddr());// 設置請求參數if(sysLog.isSaveRequestData()) {String requestMethod = sysOperLog.getRequestMethod();if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {String params = Arrays.toString(joinPoint.getArgs());sysOperLog.setOperParam(params);}}sysOperLog.setOperName(AuthContextUtil.get().getUserName());}
}

8.在common-log模塊中定義保存日志數據的service接口,然后在具體的業務服務中給出實現。分析圖如下:

1)common-log 模塊下創建service包

上代碼:

import com.atguigu.spzx.model.entity.system.SysOperLog;/*** // 異步操作日志記錄服務接口*/
public interface AsyncOperLogService {}

2)啟動類項目中的serviceImpl包中實現日志記錄服務接口,創建Mapper和mapper.xml

(創建過程自動腦補,繼續實現項目),都創建完成,接下來去保存日志操作:

????????2.1)修改切面類,調用封裝工具

????????import com.atguigu.spzx.common.log.annotation.Log;
import com.atguigu.spzx.common.log.service.AsyncOperLogService;
import com.atguigu.spzx.common.log.utils.LogUtil;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** 定義一個切面類,并且在該切面類中提供一個環繞通知方法* // 環繞通知切面類定義*/@Aspect
@Component
@Slf4jpublic class LogAspect { /**    * 封裝工具類Logutil 后,日志切面類代碼修改 調用工具類實現環繞方法,標紅代碼,首先引入????????AsyncOperLogService*/@Autowiredprivate AsyncOperLogService asyncOperLogService ;
@Around(value = "@annotation(sysLog)")public Object doAroundAdvice(ProceedingJoinPoint joinPoint, Log sysLog){ // 構建前置參數SysOperLog sysOperLog = new SysOperLog() ;LogUtil.beforeHandleLog(sysLog , joinPoint , sysOperLog) ;Object proceed=null;try {// 執行業務方法proceed = joinPoint.proceed();?// 構建響應結果參數 LogUtil.afterHandlLog(sysLog , proceed, sysOperLog,0,null);} catch (Throwable e) { e.printStackTrace();LogUtil.afterHandlLog(sysLog , proceed, sysOperLog,1, e.getMessage());// 代碼執行進入到catch中,業務方法執行產生異常throw new RuntimeException();}
// 保存日志數據asyncOperLogService.saveSysOperLog(sysOperLog);// 返回執行結果return proceed;}
}

? ? ? ? 2.2)執行上述切面類保存日志方法:

// 保存日志數據 asyncOperLogService.saveSysOperLog(sysOperLog);

//service接口

import com.atguigu.spzx.model.entity.system.SysOperLog;/*** // 異步操作日志記錄服務接口*/
public interface AsyncOperLogService {保存日志數據public abstract void saveSysOperLog(SysOperLog sysOperLog) ;
}

//service實現類

import com.atguigu.spzx.common.log.service.AsyncOperLogService;
import com.atguigu.spzx.manager.mapper.SysOperLogMapper;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {@Autowiredprivate SysOperLogMapper sysOperLogMapper;@Overridepublic void saveSysOperLog(SysOperLog sysOperLog) { // 異步執行保存日志操作sysOperLogMapper.insert(sysOperLog);}
}

//mapper接口

import com.atguigu.spzx.model.entity.system.SysOperLog;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface SysOperLogMapper {//保存日志操作void insert(SysOperLog sysOperLog);
}

//mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.spzx.manager.mapper.SysOperLogMapper"><sql id="columns">id,title,method,request_method,operator_type,oper_name,oper_url,oper_ip,oper_param,json_result,status,error_msg,create_time,update_time,is_deleted</sql><insert id="insert">insert into sys_oper_log (id,title,method,request_method,operator_type,oper_name,oper_url,oper_ip,oper_param,json_result,status,error_msg) values (#{id},#{title},#{method},#{requestMethod},#{operatorType},#{operName},#{operUrl},#{operIp},#{operParam},#{jsonResult},#{status},#{errorMsg})</insert>
</mapper>

? ? ? ? 2.3)一切準備就緒,在需要添加操作日志的接口方法上添加@Log注解進行測試。(添加紅色表示框內容),啟動器啟動

//查詢列表
@Log(title = "品牌管理",businessType = 0,operatorType = OperatorType.OTHER)
@GetMapping("/{page}/{limit}")
public Result<PageInfo<Brand>> list(@PathVariable(value = "page") Integer page, @PathVariable(value = "limit") Integer limit){PageInfo<Brand> brandList=brandService.findByPage(page, limit);return Result.build(brandList, ResultCodeEnum.SUCCESS);
}

結果:

創作不易,謝謝大家

補充:附上日志數據表結構,方便理解

CREATE TABLE `sys_oper_log` (
? `id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主鍵',
? `title` varchar(50) DEFAULT '' COMMENT '模塊標題',
? `business_type` varchar(20) DEFAULT '0' COMMENT '業務類型(0其它 1新增 2修改 3刪除)',
? `method` varchar(100) DEFAULT '' COMMENT '方法名稱',
? `request_method` varchar(10) DEFAULT '' COMMENT '請求方式',
? `operator_type` varchar(20) DEFAULT '0' COMMENT '操作類別(0其它 1后臺用戶 2手機端用戶)',
? `oper_name` varchar(50) DEFAULT '' COMMENT '操作人員',
? `dept_name` varchar(50) DEFAULT '' COMMENT '部門名稱',
? `oper_url` varchar(255) DEFAULT '' COMMENT '請求URL',
? `oper_ip` varchar(128) DEFAULT '' COMMENT '主機地址',
? `oper_param` varchar(2000) DEFAULT '' COMMENT '請求參數',
? `json_result` varchar(2000) DEFAULT '' COMMENT '返回參數',
? `status` int DEFAULT '0' COMMENT '操作狀態(0正常 1異常)',
? `error_msg` varchar(2000) DEFAULT '' COMMENT '錯誤消息',
? `oper_time` datetime DEFAULT NULL COMMENT '操作時間',
? `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
? `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
? `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '刪除標記(0:不可用 1:可用)',
? PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=67 DEFAULT CHARSET=utf8mb3 COMMENT='操作日志記錄';

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20544.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20544.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20544.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

神經網絡的工程基礎(二)——隨機梯度下降法|文末送書

相關說明 這篇文章的大部分內容參考自我的新書《解構大語言模型&#xff1a;從線性回歸到通用人工智能》&#xff0c;歡迎有興趣的讀者多多支持。 本文涉及到的代碼鏈接如下&#xff1a;regression2chatgpt/ch06_optimizer/stochastic_gradient_descent.ipynb 本文將討論利用…

WinApp自動化測試之輔助工具介紹

前篇文章中&#xff0c;我們簡單介紹了部分WinApp自動化測試腳本常規操作&#xff0c;今天我們來講剩余的部分。 文件批量上傳 文件批量上傳和文件單個上傳原理是相同的&#xff0c;單個上傳直接傳入文件路徑即可&#xff0c;批量上傳需要進入批量上傳的文件所在目錄&#xf…

Redis到底是AP還是CP?

這個問題差評&#xff0c;沒問清楚。當然&#xff0c;網上一搜&#xff0c;各種各樣的狗屁答案都有&#xff0c;有時候是AP的&#xff0c;有時候是CP的&#xff0c;薛定諤的Redis。 好的&#xff0c;那應該怎么問呢&#xff1f;Q1.Redis Cluster集群是AP還是CP&#xff1f; A…

uniapp創建支付密碼實現(初始密碼,第二次密碼)

示例&#xff1a; 插件地址&#xff1a;自定義數字/身份證/密碼輸入框&#xff0c;鍵盤密碼框可分離使 - DCloud 插件市場 1.下載插件并導入HBuilderX&#xff0c;找到文件夾&#xff0c;copy number-keyboard.vue一份為number-keyboard2.vue&#xff08;number-keyboard.vue是…

C++ STL map容器erase操作避坑

map容器的erase方法有三種重載形式&#xff1a; //1.刪除迭代器所指向的元素 //返回值是指向下一個節點的迭代器 iterator erase(iterator it); //2.區間刪除 iterator erase(iterator first, iterator last); //3.根據鍵值刪除 //返回值為刪除的元素個數 size_type erase(con…

民國漫畫雜志《時代漫畫》第37期.PDF

時代漫畫37.PDF: https://url03.ctfile.com/f/1779803-1248636302-c017ee?p9586 (訪問密碼: 9586) 《時代漫畫》的雜志在1934年誕生了&#xff0c;截止1937年6月戰爭來臨被迫停刊共發行了39期。 ps: 資源來源網絡!

C++基礎編程100題-002 OpenJudge-1.1-04 輸出保留3位小數的浮點數

更多資源請關注紐扣編程微信公眾號 002 OpenJudge-1.1-04 輸出保留3位小數的浮點數 http://noi.openjudge.cn/ch0101/04/ 描述 讀入一個單精度浮點數&#xff0c;保留3位小數輸出這個浮點數。 輸入 只有一行&#xff0c;一個單精度浮點數。 輸出 也只有一行&#xff0c;…

塊設備層保序操作分析

Q:塊設備層保序功能的作用? A:通用塊層可以提交一個帶保序標簽(BIO_RW_BARRIER)的BIO到IO請求隊列,塊設備層可以保證在保序BIO之前提交的BIO都先于BIO執行且抵達存儲介質;保序BIO執行完畢后,它需要寫入的數據必定已經抵達存儲介質;在保序IO之后提交的BIO都晚于保序BIO執行,確保…

07.爬蟲---使用session發送請求

07.使用session發送請求 1.目標網站2.代碼實現 1.目標網站 我們以這個網站作為目標網站 http://www.360doc.com/ 注冊用戶 注冊后從登錄界面獲取到這些信息 2.代碼實現 import requestssession requests.Session() url http://www.360doc.com/ajax/login/login.ashx u…

深入剖析Java線程池的核心概念與源碼解析:從Executors、Executor、execute逐一揭秘

文章目錄 文章導圖前言Executors、Executor、execute對比剖析Executors生成的線程池&#xff1f;線程池中的 execute 方法execute 方法的作用execute的工作原理拒絕策略 源碼分析工作原理基本知識線程的狀態線程池的狀態線程池狀態和線程狀態總結線程池的狀態信息和線程數量信息…

RedisSearch與Elasticsearch:技術對比與選擇指南

碼到三十五 &#xff1a; 個人主頁 數據時代&#xff0c;全文搜索已經成為許多應用程序中不可或缺的一部分。RedisSearch和Elasticsearch是兩個流行的搜索解決方案&#xff0c;它們各自具有獨特的特點和優勢。本文簡單探討一些RedisSearch和Elasticsearch之間的技術差異。 目錄…

9款實用而不為人知的小眾軟件推薦!

AI視頻生成&#xff1a;小說文案智能分鏡智能識別角色和場景批量Ai繪圖自動配音添加音樂一鍵合成視頻https://aitools.jurilu.com/ 在電腦軟件的浩瀚海洋中&#xff0c;除了那些廣為人知的流行軟件外&#xff0c;還有許多簡單、干凈、功能強大且注重實用功能的小眾軟件等待我們…

[NISACTF 2022]sign_crypto(LATEX)

題目&#xff1a; 我們看出這是LATEX編碼&#xff0c;破解之后&#xff1a; 看出每個“\”之后的第一個字母連起來即使&#xff1a;nss....&#xff0c;在大寫即可得到flag。

linux各個日志的含義 以及使用方法

在Linux系統上&#xff0c;系統日志文件通常存儲在/var/log/目錄下。可以通過查看這些日志文件來了解系統的操作記錄、錯誤信息和其他相關信息。以下是一些常見的系統日志文件以及它們包含的信息&#xff1a; /var/log/messages&#xff1a;這是一個常見的系統日志文件&#xf…

Sui Nami Bags對NFT使用案例進行創新

在四月的Sui Basecamp活動中&#xff0c;與會者體驗了一系列Sui技術&#xff0c;這些技術以Nami Bags的形式呈現&#xff0c;這些數字禮包里滿是來自Sui生態的NFT和優惠券。通過Enoki&#xff08;Mysten Labs的新客戶參與平臺&#xff09;提供支持&#xff0c;即使沒有加密錢包…

OpenCV學習 基礎圖像操作(十七):泛洪與分水嶺算法

原理 泛洪填充算法和分水嶺算法是圖像處理中的兩種重要算法&#xff0c;主要用于區域分割&#xff0c;但它們的原理和應用場景有所不同&#xff0c;但是他們的基礎思想都是基于區域迭代實現的區域之間的劃分。 泛洪算法 泛洪填充算法&#xff08;Flood Fill&#xff09;是一…

修改element-ui el-radio顏色

修改element-ui el-radio顏色 需求效果圖代碼實現 小結 需求 撤銷扣分是綠色&#xff0c;駁回是紅色 效果圖 代碼實現 dom <el-table-columnlabel"操作"width"200px"><template v-slot"scope"><el-radio-group v-model"s…

Vue插槽與作用域插槽

title: Vue插槽與作用域插槽 date: 2024/6/1 下午9:07:52 updated: 2024/6/1 下午9:07:52 categories: 前端開發 tags:VueSlotScopeSlot組件通信Vue2/3插槽作用域API動態插槽插槽優化 第1章&#xff1a;插槽的概念與原理 插槽的定義 在Vue.js中&#xff0c;插槽&#xff08;…

如何用FPGA實現SINC濾波

目錄 簡介: 技術說明: 代碼如下: 簡介: sinc(音同“sink”)濾波器是由sinc函數構造的濾波器。sinc函數的定義可以參考抽樣信號Sa的定義,這里只需知道矩形脈沖和sinc函數是一個變換對。當矩形脈沖的頻譜沒有混疊時,它就是sin(x)/x,一個sinc函數,對于連續信號,矩形脈…

【記錄43】el-table @selection-change 數據回顯、條件約束、歷史回顯清除

場景 在其他地方設置好人員&#xff0c;到對應的頁面直接在表格中復選設置好的人員。解決方案用到selection-change方法 <el-button click"EchoClick()">回顯設置好的人</el-button> <el-table ref"choeck" :data"TableData" s…