springboot通过切面编程实现系统请求操作日志记录
2020-12-17 13:34
标签:报错 origin blog att 判断 poi tcl sage dep 切面编程注解操作日志 springboot通过切面编程实现系统请求操作日志记录 标签:报错 origin blog att 判断 poi tcl sage dep 原文地址:https://www.cnblogs.com/buzheng/p/14101189.html1、引入依赖包
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-starter-aopartifactId>
dependency>
dependency>
groupId>org.aspectjgroupId>
artifactId>aspectjrtartifactId>
version>1.8.6version>
dependency>
dependency>
groupId>org.slf4jgroupId>
artifactId>slf4j-apiartifactId>
version>1.7.25version>
dependency>
dependency>
groupId>cn.hutoolgroupId>
artifactId>hutool-allartifactId>
version>5.3.8version>
dependency>
2、相应数据库
系统操作日志表
CREATE TABLE `sys_log_operation` (
`id` varchar(32) NOT NULL COMMENT ‘id‘,
`request_uri` varchar(200) DEFAULT NULL COMMENT ‘请求URI‘,
`request_method` varchar(20) DEFAULT NULL COMMENT ‘请求方式‘,
`class_method` varchar(200) DEFAULT NULL COMMENT ‘请求函数‘,
`request_params` text COMMENT ‘请求参数‘,
`request_time` bigint DEFAULT NULL COMMENT ‘请求时长(毫秒)‘,
`user_agent` varchar(500) DEFAULT NULL COMMENT ‘用户代理‘,
`request_ip` varchar(32) DEFAULT NULL COMMENT ‘操作ip‘,
`state` int DEFAULT NULL COMMENT ‘状态(0、失败,1、成功)‘,
`create_time` datetime NOT NULL COMMENT ‘创建时间‘,
`create_user` varchar(32) DEFAULT NULL COMMENT ‘创建人‘,
`creator_name` varchar(32) DEFAULT NULL COMMENT ‘创建人名‘,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=‘系统操作日志‘;
系统错误日志表
CREATE TABLE `sys_log_error` (
`id` varchar(32) NOT NULL COMMENT ‘id‘,
`request_uri` varchar(200) DEFAULT NULL COMMENT ‘请求URI‘,
`request_method` varchar(20) DEFAULT NULL COMMENT ‘请求方式‘,
`class_method` varchar(200) DEFAULT NULL COMMENT ‘请求函数‘,
`request_params` text COMMENT ‘请求参数‘,
`request_time` int DEFAULT NULL COMMENT ‘请求时长(毫秒)‘,
`user_agent` varchar(500) DEFAULT NULL COMMENT ‘用户代理‘,
`request_ip` varchar(32) DEFAULT NULL COMMENT ‘操作ip‘,
`error_info` text COMMENT ‘异常信息‘,
`create_time` datetime NOT NULL COMMENT ‘创建时间‘,
`create_user` varchar(32) DEFAULT NULL COMMENT ‘创建人‘,
`creator_name` varchar(32) DEFAULT NULL COMMENT ‘创建人名‘,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=‘系统错误日志‘;
3、日志实体类
package com.bz.bean;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
* 异常日志
*/
@Data
@TableName(value = "sys_log_error")
public class SysLogError implements Serializable {
/**
* id
*/
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value = "id")
private String id;
/**
* 请求URI
*/
@TableField(value = "request_uri")
@ApiModelProperty(value = "请求URI")
private String requestUri;
/**
* 请求方式
*/
@TableField(value = "request_method")
@ApiModelProperty(value = "请求方式")
private String requestMethod;
/**
* 请求参数
*/
@TableField(value = "request_params")
@ApiModelProperty(value = "请求参数")
private String requestParams;
/**
* 用户代理
*/
@TableField(value = "user_agent")
@ApiModelProperty(value = "用户代理")
private String userAgent;
/**
* 操作IP
*/
@TableField(value = "ip")
@ApiModelProperty(value = "操作IP")
private String ip;
/**
* 异常信息
*/
@TableField(value = "error_info")
@ApiModelProperty(value = "异常信息")
private String errorInfo;
/**
* 创建人
*/
@TableField(value = "create_user")
@ApiModelProperty(value = "创建人")
private String createUser;
/**
* 创建人名
*/
@TableField(value = "creator_name")
@ApiModelProperty(value = "创建人名")
private String creatorName;
/**
* 创建时间
*/
@TableField(value = "create_date")
@ApiModelProperty(value = "创建时间")
private Date createDate;
/**
* 类方法
*/
@TableField(value = "class_method")
@ApiModelProperty(value = "类方法")
private String classMethod;
private static final long serialVersionUID = 1L;
}
package com.enzenith.homemaking.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 系统操作日志
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "sys_log_operation")
public class SysLogOperation implements Serializable {
/**
* id
*/
@TableId(value = "id", type = IdType.INPUT)
@ApiModelProperty(value = "id")
private String id;
/**
* 请求URI
*/
@TableField(value = "request_uri")
@ApiModelProperty(value = "请求URI")
private String requestUri;
/**
* 请求方式
*/
@TableField(value = "request_method")
@ApiModelProperty(value = "请求方式")
private String requestMethod;
/**
* 请求函数
*/
@TableField(value = "class_method")
@ApiModelProperty(value = "请求函数")
private String classMethod;
/**
* 请求参数
*/
@TableField(value = "request_params")
@ApiModelProperty(value = "请求参数")
private String requestParams;
/**
* 请求时长(毫秒)
*/
@TableField(value = "request_time")
@ApiModelProperty(value = "请求时长(毫秒)")
private Long requestTime;
/**
* 用户代理
*/
@TableField(value = "user_agent")
@ApiModelProperty(value = "用户代理")
private String userAgent;
/**
* 操作ip
*/
@TableField(value = "request_ip")
@ApiModelProperty(value = "操作ip")
private String requestIp;
/**
* 状态(0、失败,1、成功)
*/
@TableField(value = "state")
@ApiModelProperty(value = "状态(0、失败,1、成功)")
private Integer state;
/**
* 创建时间
*/
@TableField(value = "create_time")
@ApiModelProperty(value = "创建时间")
private Date createTime;
/**
* 创建人
*/
@TableField(value = "create_user")
@ApiModelProperty(value = "创建人")
private String createUser;
/**
* 创建人名
*/
@TableField(value = "creator_name")
@ApiModelProperty(value = "创建人名")
private String creatorName;
/**
* 备注(用于数据迁移)
*/
@TableField(value = "memo")
@ApiModelProperty(value = "备注(用于数据迁移)")
private String memo;
private static final long serialVersionUID = 1L;
}
4、切面实现类
package com.bz.aspect;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.bz.bean.SysLogError;
import com.bz.bean.SysLogOperation;
import com.bz.common.enums.OperationStatusEnum;
import com.bz.service.SysLogErrorService;
import com.bz.service.SysLogOperationService;
import org.apache.commons.lang.StringEscapeUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
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.json.JSONObject;
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 org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 切面日志
* @author Buzheng
*/
@Aspect
@Component
public class RequestLogAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);
@Autowired(required = false)
private SysLogOperationService sysLogOperationService;
@Autowired(required = false)
private SysLogErrorService sysLogErrorService;
/**
* 定义切入点
*/
@Pointcut("execution(* com.bz.api..*.*(..))")
public void requestServer() {
}
/**
* 环绕通知
* 既可以在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作;
* 可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标目标方法的执行;
* 可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值; 当需要改变目标方法的返回值时,只能使用Around方法;
*/
@Around("requestServer()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取系统时间
long start = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//获取请求信息
HttpServletRequest request = attributes.getRequest();
Object result = proceedingJoinPoint.proceed();
//保存录入数据库
SysLogOperation requestInfo = new SysLogOperation();
requestInfo.setId(IdUtil.createSnowflake(1,1).nextId());
//获取客户端的IP地址
requestInfo.setRequestIp(request.getRemoteAddr());
//获取请求的接口地址
requestInfo.setRequestUri(request.getRequestURL().toString());
requestInfo.setRequestMethod(request.getMethod());
//获取请求哪个类以及哪个方法
requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
//获取请求参数
requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
requestInfo.setState(OperationStatusEnum.SUCCESS.value());
requestInfo.setRequestTime(System.currentTimeMillis() - start);
requestInfo.setCreateTime(new Date());
/**
* 判断是否有token,因为登录的时候是没有的。所以需要进行判断
* ps:前端每次请求接口时都会带token进行请求。
*/
String token = request.getHeader("token");
if(StrUtil.isNotBlank(token)){
requestInfo.setCreatorName("获取请求者名称");
requestInfo.setCreateUser("获取请求者用户id");
}
sysLogOperationService.save(requestInfo);
return result;
}
/**
* 异常通知:目标方法抛出异常时执行
*/
@AfterThrowing(pointcut = "requestServer()", throwing = "e")
public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
SysLogError requestErrorInfo = new SysLogError();
requestErrorInfo.setId(String.valueOf(IdUtil.createSnowflake(1,1).nextId()));
requestErrorInfo.setIp(request.getRemoteAddr());
requestErrorInfo.setRequestUri(request.getRequestURL().toString());
requestErrorInfo.setRequestMethod(request.getMethod());
requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName()));
requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
//获取报错信息
requestErrorInfo.setErrorInfo(e.getMessage());
requestErrorInfo.setCreateDate(new Date());
/**
* 判断是否有token,因为登录的时候是没有的。所以需要进行判断
* ps:前端每次请求接口时都会带token进行请求。
*/
String token = request.getHeader("token");
if(StrUtil.isNotBlank(token)){
requestErrorInfo.setCreatorName("获取请求者名称");
requestErrorInfo.setCreateUser("获取请求者用户id");
}
boolean save = sysLogErrorService.save(requestErrorInfo);
LOGGER.info("Error Request Info : {}", JSON.toJSONString(requestErrorInfo));
}
/**
* 获取入参
* @param proceedingJoinPoint
* */
private String getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
//参数名
String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
//去除反斜杠
return StringEscapeUtils.unescapeJavaScript(buildRequestParam(paramNames, paramValues).toString());
}
/**
* 获取入参
* @param proceedingJoinPoint
* */
private String getRequestParamsByJoinPoint(JoinPoint joinPoint) {
//参数名
String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = joinPoint.getArgs();
return StringEscapeUtils.unescapeJavaScript(buildRequestParam(paramNames, paramValues).toString());
}
/**
* 将参数名称以及参数值转成json字符串
* @param paramNames 参数名称
* @param paramValues 参数值
* @return cn.hutool.json.JSONObject
* @author Buzheng
**/
private JSONObject buildRequestParam(String[] paramNames, Object[] paramValues) {
JSONObject jsonObject = new JSONObject();
for (int i = 0; i ) {
Object value = paramValues[i];
//如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
value = file.getOriginalFilename(); //获取文件名
}
jsonObject.putOpt(paramNames[i], value != null ? JSONUtil.toJsonStr(value) : null);
}
return jsonObject;
}
}
相关内容链接跳转:
文章标题:springboot通过切面编程实现系统请求操作日志记录
文章链接:http://soscw.com/essay/36859.html