1.引入AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建日志记录表
DROP TABLE IF EXISTS `rule_operate_log`;
CREATE TABLE `rule_operate_log` (
id INT(11) NOT NULL AUTO_INCREMENT COMMENT '日志id',
path VARCHAR(4000) NULL DEFAULT NULL COMMENT '接口地址',
http_method VARCHAR(32) NULL DEFAULT NULL COMMENT '请求方法',
status_code VARCHAR(32) NULL DEFAULT NULL COMMENT '请求返回状态码',
create_time_char VARCHAR(32) NULL DEFAULT NULL COMMENT '日志时间',
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '日志时间戳',
ip varchar(200) NULL DEFAULT NULL COMMENT '请求ip',
params mediumtext NULL COMMENT '请求参数',
result mediumtext NULL COMMENT '返回值',
exception mediumtext NULL COMMENT '接口异常',
user_id VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户',
user_account VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户账号',
user_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作用户名称',
user_org_id VARCHAR(32) NULL DEFAULT NULL COMMENT '操作用户机构id',
user_org_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作用户机构名称',
operate_name VARCHAR(200) NULL DEFAULT NULL COMMENT '操作名称',
operate_position VARCHAR(200) NULL DEFAULT NULL COMMENT '操作位置',
log_type VARCHAR(32) NULL DEFAULT NULL COMMENT '日志类型 error:错误日志 operate:操作日志',
category_id VARCHAR(32) NULL DEFAULT NULL COMMENT '分类机构id',
cost INT(11) NULL DEFAULT NULL COMMENT '接口耗时',
PRIMARY KEY (id)
) COMMENT = '操作日志表';
3.日志实体类
import java.util.Date;
public class RuleOperateLog {
/**
* id
*/
private Integer id;
/**
* 接口地址
*/
private String path;
/**
* 请求方法
*/
private String httpMethod;
/**
* 请求返回状态码
*/
private String statusCode;
/**
* 日志时间
*/
private String createTimeChar;
/**
* 日志时间戳
*/
private Date createTime;
/**
* 请求ip
*/
private String ip;
/**
* 请求参数
*/
private String params;
/**
* 返回值
*/
private String result;
/**
* 接口异常
*/
private String exception;
/**
* 操作用户
*/
private String userId;
/**
* 操作用户账号
*/
private String userAccount;
/**
* 操作用户名称
*/
private String userName;
/**
* 操作用户机构
*/
private String userOrgId;
/**
* 操作用户机构名称
*/
private String userOrgName;
/**
* 操作名称
*/
private String operateName;
/**
* 操作位置
*/
private String operatePosition;
/**
* 日志类型
*/
private String logType;
/**
* 分类机构id
*/
private String categoryId;
/**
* 请求耗时
*/
private Integer cost;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public String getCreateTimeChar() {
return createTimeChar;
}
public void setCreateTimeChar(String createTimeChar) {
this.createTimeChar = createTimeChar;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserAccount() {
return userAccount;
}
public void setUserAccount(String userAccount) {
this.userAccount = userAccount;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserOrgId() {
return userOrgId;
}
public void setUserOrgId(String userOrgId) {
this.userOrgId = userOrgId;
}
public String getUserOrgName() {
return userOrgName;
}
public void setUserOrgName(String userOrgName) {
this.userOrgName = userOrgName;
}
public String getOperateName() {
return operateName;
}
public void setOperateName(String operateName) {
this.operateName = operateName;
}
public String getOperatePosition() {
return operatePosition;
}
public void setOperatePosition(String operatePosition) {
this.operatePosition = operatePosition;
}
public String getLogType() {
return logType;
}
public void setLogType(String logType) {
this.logType = logType;
}
public String getCategoryId() {
return categoryId;
}
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
public Integer getCost() {
return cost;
}
public void setCost(Integer cost) {
this.cost = cost;
}
}
4.Dao+Mapper+service
import com.xxx.xxx.xxx.entity.RuleOperateLog;
/**
* 操作日志(RuleOperateLog)表数据库访问层
*
* @author hx
* @since 2022-08-23
*/
public interface RuleOperateLogDao {
/**
* 新增数据
*
* @param operateLog
* @return
*/
int insert(RuleOperateLog operateLog);
}
<?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.xxx.xxx.xxx.dao.RuleOperateLogDao">
<resultMap type="com.xxx.xxx.xxx.entity.RuleOperateLog" id="RuleOperateLogMap">
<result property="id" column="id" jdbcType="INTEGER"/>
<result property="path" column="path" jdbcType="VARCHAR"/>
<result property="httpMethod" column="http_method" jdbcType="VARCHAR"/>
<result property="statusCode" column="status_code" jdbcType="VARCHAR"/>
<result property="createTimeChar" column="create_time_char" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="ip" column="ip" jdbcType="VARCHAR"/>
<result property="params" column="params" jdbcType="VARCHAR"/>
<result property="result" column="result" jdbcType="VARCHAR"/>
<result property="exception" column="exception" jdbcType="VARCHAR"/>
<result property="userId" column="user_id" jdbcType="VARCHAR"/>
<result property="userAccount" column="user_account" jdbcType="VARCHAR"/>
<result property="userName" column="user_name" jdbcType="VARCHAR"/>
<result property="userOrgId" column="user_org_id" jdbcType="VARCHAR"/>
<result property="userOrgName" column="user_org_name" jdbcType="VARCHAR"/>
<result property="operateName" column="operate_name" jdbcType="VARCHAR"/>
<result property="operatePosition" column="operate_position" jdbcType="VARCHAR"/>
<result property="logType" column="log_type" jdbcType="VARCHAR"/>
<result property="categoryId" column="category_id" jdbcType="VARCHAR"/>
<result property="cost" column="cost" jdbcType="INTEGER"/>
</resultMap>
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into rule_operate_log (id, path, http_method, status_code, create_time_char, create_time,
ip, params, result, exception, user_id, user_account, user_name, user_org_id,
user_org_name, operate_name, operate_position, log_type, category_id, cost)
values (#{id}, #{path}, #{httpMethod}, #{statusCode}, #{createTimeChar}, #{createTime}, #{ip}, #{params}, #{result},
#{exception},#{userId}, #{userAccount}, #{userName}, #{userOrgId}, #{userOrgName}, #{operateName}, #{operatePosition},
#{logType}, #{categoryId}, #{cost})
</insert>
</mapper>
import com.xxx.xxx.xxx.entity.RuleOperateLog;
/**
* 操作日志(RuleOperateLog)表服务接口
*
* @author hx
* @since 2022-08-23
*/
public interface RuleOperateLogService {
/**
* 保存日志
*
* @param ruleOperateLog
* @return
*/
void saveLog(RuleOperateLog ruleOperateLog);
}
import com.xxx.xxx.xxx.dao.RuleOperateLogDao;
import com.xxx.xxx.xxx.entity.RuleOperateLog;
import com.xxx.xxx.xxx.service.RuleOperateLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 操作日志(RuleOperateLog)表服务实现类
*
* @author hx
* @since 2022-08-23
*/
@Service("RuleOperateLogService")
public class RuleOperateLogServiceImpl implements RuleOperateLogService {
@Autowired
private RuleOperateLogDao operateLogDao;
@Override
public void saveLog(RuleOperateLog ruleOperateLog) {
operateLogDao.insert(ruleOperateLog);
}
}
5.自定义注解
import java.lang.annotation.*;
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface LogResource {
/**
* 服务名称
* @return
*/
String name();
/**
* 操作位置描述
* @return
*/
String position() default "";
/**
* 日志类型
* @return
*/
String logType() default "";
}
6.操作日志切面类
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.com.xxx.xxx.xxx.annotation.LogResource;
import com.com.xxx.xxx.xxx.constants.LogTypeConstants;
import com.com.xxx.xxx.xxx.entity.RuleOperateLog;
import com.com.xxx.xxx.xxx.service.RuleOperateLogService;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Async;
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 javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 操作日志切面类
*
* @author hx
* @since 2022-08-23
*/
@Aspect
@Component
public class OperateLogAspect {
@Autowired
private RuleOperateLogService operateLogService;
//扫描使用@LogResource注解的方法
@Pointcut("@annotation(com.com.xxx.xxx.xxx.annotation.LogResource)")
public void logPointCut() { };
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Date startTime = new Date();
String exception = null;
String result = null;
try {
Object obj = point.proceed();
if (obj != null) {
result = JSONObject.toJSONString(obj);
}
return obj;
} catch (Exception e) {
//请求时报错
exception = e.toString();
throw e;
} finally {
//操作和报错日志都记录
HttpServletResponse response
= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
int statusCode = response.getStatus();
if (exception != null) {
/** CHECKSTYLE:OFF:MagicNumber */
statusCode = 500;
/** CHECKSTYLE:ON:MagicNumber */
}
syncSaveLog(point, startTime, new Date(), exception, result, statusCode);
}
}
@Async
void syncSaveLog(ProceedingJoinPoint joinPoint, Date startTime, Date endTime,
String exception, String result, int statusCode) {
RuleOperateLog log = new RuleOperateLog();
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogResource annotation = method.getAnnotation(LogResource.class);
if (annotation != null) {
//注解上的描述
log.setOperateName(annotation.name());
}
Date nowDate = new Date();
log.setCreateTimeChar(new SimpleDateFormat("yyyyMMddhhmmss").format(nowDate));
log.setCreateTime(nowDate);
//入参
if (joinPoint.getArgs() != null) {
try {
log.setParams(JSONObject.toJSONString(joinPoint.getArgs(),
SerializerFeature.IgnoreNonFieldGetter));
} catch (Exception e) {
e.printStackTrace();
}
}
Long cost = endTime.getTime() - startTime.getTime();
log.setCost(cost.intValue());
HttpServletRequest request
= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null) {
log.setUserName(request.getHeader(HttpHeaders.USER_AGENT));
log.setPath(request.getRequestURI());
log.setHttpMethod(request.getMethod());
log.setIp(request.getRemoteAddr());
}
log.setStatusCode(String.valueOf(statusCode));
log.setResult(result);
/** CHECKSTYLE:OFF:MagicNumber */
if (statusCode > 400 && exception != null) {
log.setException(exception);
log.setLogType(LogTypeConstants.ERROR);
} else {
log.setLogType(LogTypeConstants.OPERATE);
}
/** CHECKSTYLE:ON:MagicNumber */
operateLogService.saveLog(log);
} catch (Exception e) {
e.printStackTrace();
}
/* //启动一个线程,执行报错日志防止影响主请求
new Thread() {
@Override
public void run() {
try {
//保存到数据库
operLogMapper.insertOper(operLog);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();*/
}
}