1、
導入pom文件
<!--用於日誌存儲,不引用打包時會找不到JDBCAppender -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--spring切面aop依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 一個工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.3.7</version>
</dependency>
<!-- aop註解 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
2、設計日誌實體,即設計表結構
package com.gwh.axb.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
//import lombok.Data;
/**
* @version V1.0
* @Package com.gwh.axb.entity
* @author: gaowenhui
* @Date: 10:49
*/
//@Data
@Component
public class AdminLog implements Serializable {
private static final long serialVersionUID = 1L;
private Integer logId; //日誌主鍵
private String type; //日誌類型
private String operation; //日誌操作事件描述
private String remoteAddr; //請求地址ip
private String requestUri; //URI
private String method; //請求方式
private String params; //提交參數
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date operateDate; //開始時間
private Integer userId; //用戶ID
private String userName; //用戶名稱
private String resultParams; //返回參數
private String exceptionLog; //異常描述
public AdminLog() {
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public Integer getLogId() {
return logId;
}
public void setLogId(Integer logId) {
this.logId = logId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOperation() {
return operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
public String getRemoteAddr() {
return remoteAddr;
}
public void setRemoteAddr(String remoteAddr) {
this.remoteAddr = remoteAddr;
}
public String getRequestUri() {
return requestUri;
}
public void setRequestUri(String requestUri) {
this.requestUri = requestUri;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public Date getOperateDate() {
return operateDate;
}
public void setOperateDate(Date operateDate) {
this.operateDate = operateDate;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getResultParams() {
return resultParams;
}
public void setResultParams(String resultParams) {
this.resultParams = resultParams;
}
public String getExceptionLog() {
return exceptionLog;
}
public void setExceptionLog(String exceptionLog) {
this.exceptionLog = exceptionLog;
}
public AdminLog(Integer logId, String type, String operation, String remoteAddr, String requestUri, String method, String params, Date operateDate, Integer userId, String userName, String resultParams, String exceptionLog) {
this.logId = logId;
this.type = type;
this.operation = operation;
this.remoteAddr = remoteAddr;
this.requestUri = requestUri;
this.method = method;
this.params = params;
this.operateDate = operateDate;
this.userId = userId;
this.userName = userName;
this.resultParams = resultParams;
this.exceptionLog = exceptionLog;
}
}
3、自定義註解(此註解需要放在監控的Controller的方法上。)
package com.gwh.axb.config;
import java.lang.annotation.*;
/**
import java.lang.annotation.*;
*/
@Target({ ElementType.PARAMETER,ElementType.METHOD }) //註解放置的目標位置,METHOD是可註解在方法級別上
@Retention(RetentionPolicy.RUNTIME) //註解在哪個階段執行
@Documented//生成文檔
public @interface SystemControllerLog {
/** 操作事件 */
String operation() default "";
/** 日誌類型 */
String type();
}
4、切面(攔截)
package com.gwh.axb.config;
import com.gwh.axb.entity.AdminLog;
import com.gwh.axb.service.AddLogService;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @version V1.0
* @Package com.gwh.axb.config
* @author: gaowenhui
* @Date: 11:37
*/
@Aspect
@Component
public class SystemLogAspect {
private static Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
/**
* 操作數據庫
*/
@Autowired
private AddLogService addLogService;
/*定義切點
* Controller層切點 註解攔截
*/
@Pointcut("@annotation(com.icbc.axb.config.SystemControllerLog)")
public void logPointCut() {
}
/*切面*/
@Around(value = "logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
logger.info("調用日誌監控");
AdminLog adminLog = new AdminLog();
/*從切面值入點獲取植入點方法*/
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
/*獲取切入點方法*/
Method method = signature.getMethod();
/*獲取方法上的值*/
SystemControllerLog systemControllerLog = method.getAnnotation(SystemControllerLog.class);
/*保存操作事件*/
if (systemControllerLog != null) {
String operation = systemControllerLog.operation();
adminLog.setOperation(operation);
/*保存日誌類型*/
adminLog.setOperation(operation);
String type = systemControllerLog.type();
adminLog.setType(type);
/*打印*/
logger.info("操作事件 :" + operation);
logger.info("事件類型爲:" + type);
}
/*獲取請求體內容*/
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String requestUri = request.getRequestURI();/*獲取請求地址*/
String requestMethod = request.getMethod();/*獲取請求方式*/
String remoteAddr1 = request.getRemoteAddr();/*獲取請求IP*/
String remoteAddr = this.getIpAddress(request);
// logger.info(remoteAddr1);
// System.out.println(remoteAddr1 + "處理前的ip-----------" + remoteAddr + "處理後的ip");
/*存請求地址,請求方式,請求IP*/
adminLog.setRemoteAddr(remoteAddr);
logger.info("客戶端IP爲:" + remoteAddr);
adminLog.setRequestUri(requestUri);
logger.info("請求路徑爲:" + requestUri);
adminLog.setMethod(requestMethod);
logger.info("請求方式爲:" + requestMethod);
/*獲取參數*/
Object[] args = joinPoint.getArgs();
if (args != null) {
for (Object obj : args) {
/* System.out.println("傳遞的參數" + obj);*/
String params = obj.toString();
/* System.out.println("傳遞的參數" + params);*/
logger.info("請求參數爲:" + params);
/*保存請求參數*/
adminLog.setParams(params);
}
}
/* // 操作人賬號、姓名(需要提前將用戶信息存到session)
AdminUser adminUser = (AdminUser) request.getSession().getAttribute("adminUser");
if (adminUser != null) {
Integer userId = adminUser.getUserId();
// System.out.println(userId);
String userName = adminUser.getUserName();
adminLog.setUserId(userId); *//*存入操作人Id*//*
adminLog.setUserName(userName); *//*存入操作人名字*//*
logger.info("操作員是" + userName);
logger.info("操作員Id爲"+userId);
}*/
Object proceed = null;
try {
//執行增強後的方法
proceed = joinPoint.proceed();
/* System.out.println(proceed+"這是什麼!!!");
System.out.println("當前方法執行完成");*/
if (method.isAnnotationPresent(SystemControllerLog.class)) {
adminLog.setExceptionLog("無異常");
adminLog.setType("info");
adminLog.setResultParams(proceed.toString());
}
} catch (Throwable throwable) {
throwable.printStackTrace();
adminLog.setExceptionLog(throwable.getMessage());
adminLog.setType("Err");
adminLog.setResultParams(proceed.toString());
/* System.out.println(throwable.getMessage() + "123456異常信息");
System.out.println(adminLog.getExceptionLog() + "654321異常信息");
System.out.println(adminLog);*/
} finally {
addLogService.insert(adminLog);
}
logger.info("返回參數爲"+proceed);
return proceed;
}
//ip處理工具類
public String getIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//根據網卡取本機配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
//對於通過多個代理的情況,第一個IP爲客戶端真實IP,多個IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
}
當前用戶信息在session中就可以保存當前用戶,後面因爲自己覺得不需要就註釋掉了。
5、controller層(只需要加前面自定義的註釋)
/*測試返回體*/
@SystemControllerLog(operation = "測試",type = "info")
@GetMapping("/api/test")
@ResponseBody
public String test(){
String str="相信未來";
if (str!=null){
//RUtils是我自己的統一返回體,根據自己情況
return "str: "+str;
}else {
//這是自己的全局異常處理
//throw new Exception();
return "str-null: "+str;
}
}
運行項目測試
6.1
postman測試
控制檯信息
數據庫
6.2
以上爲無異常。下面爲發生異常時。此處演示異常爲1/0異常。
controller層
/模擬需要登錄後的業務2/
@SystemControllerLog(operation = “測試”,type = “info”)
@GetMapping("/api/findById")
public R findById(){
Student student = studentService.findById();
if(student!=null){
int a=1;
int b=0;
int c=a/b;
return RUtils.success(student);
}else {
throw new StudentException(Renum.TSEST_IS_ERROR);
}
}
postman顯示,此處返回體是自己的異常處理後統一返回體
控制檯顯示(紅色爲異常信息)
數據庫
總結:需要根據自己需求設計表結構。上面省略了日誌信息的添加的地方,就是一套簡單的增加流程。難點應該在於關於AOP切面攔截上面異常。對於IP地址工具類可以提出去單獨放入工具類。
希望給到你一些參考,喜歡請點贊。
示例代碼已傳到github:https://github.com/gwh2008/spring-boot-aop