springBoot 项目中需要记录服务端访问日志,所谓的服务端访问日志其实就是Controller层的访问日志,首先想到通过AOP切面实现。
<!--用于日志存储,不引用打包时会找不到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>LATEST</version>
</dependency>
<!-- aop注解 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>`
package com.ss.jwt.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @version V1.0
* @Package com.ssb.ssb2.pojo
* @author: Liu
* @Date: 10:49
*/
@Data
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; //异常描述
}
package com.ss.jwt.annotation;
import java.lang.annotation.*;
/**
1. @version V1.0
2. @Package com.ssb.ssb2.annotation
3. @author: Liu
4. @Date: 11:31
*/
@Target({ ElementType.PARAMETER,ElementType.METHOD }) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented//生成文档
public @interface SystemControllerLog {
/** 操作事件 */
String operation() default "";
/** 日志类型 */
String type();
}
package com.ss.jwt.config;
import com.ss.jwt.annotation.SystemControllerLog;
import com.ss.jwt.pojo.AdminLog;
import com.ss.jwt.pojo.AdminUser;
import com.ss.jwt.service.AddLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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.ssb.ssb2.aspect
* @author: Liu
* @Date: 11:37
*/
@Aspect
@Component
public class SystemLogAspect {
private static Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);
/**
* 操作数据库
*/
@Autowired
private AddLogService addLogService;
/*定义切点
* Controller层切点 注解拦截
*/
@Pointcut("@annotation(com.ss.jwt.annotation.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.addLog(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中就可以保存当前用户,后面因为自己觉得不需要就注释掉了。
/*测试返回体*/
@SystemControllerLog(operation = "测试",type = "info")
@GetMapping("/api/test")
@ResponseBody
public R test(){
String str="相信未来";
if (str!=null){
//RUtils是我自己的统一返回体,根据自己情况
return RUtils.success(str);
}else {
//这是自己的全局异常处理
throw new StudentException(Renum.TSEST_IS_ERROR);
}
}
/*模拟需要登录后的业务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地址工具类可以提出去单独放入工具类。
希望给到你一些参考,喜欢请点赞。