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