建日志表
CREATE TABLE `system_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`method_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '方法名称',
`url` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '路径',
`request_params` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求参数',
`response_result` text COLLATE utf8mb4_general_ci NOT NULL COMMENT '响应结果',
`cost_time` int NOT NULL COMMENT '执行时间(毫秒)',
`message` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '说明',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
实现代码
定义注解
package com.study.systemlog;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemLogAOP {
}
与表对应的实体类
package com.study.systemlog;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("system_log")
public class SystemLog {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String methodName;
private String url;
private String requestParams;
private String responseResult;
private Long costTime;
private String message;
private Date createTime;
}
这里我用的是mybatisplus,如果这里的类名不是表名的驼峰命名,就需要加上 @TableName(“system_log”) 这个注解。
比如类名是 SystemLogDB,那么mybatisplus就会把这个类映射到表 system_log_d_b,而不是system_log
@TableId(value = “id”, type = IdType.AUTO) 这个注解指定了ID字段是自增的。若没有这个注解,虽然传入的对象id值为null,但mybatisplus会自动给id分配一个随机值,然后写入数据库。这样就不能依靠数据库实现ID自增。
Mapper 和 Service
package com.study.systemlog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SystemLogMapper extends BaseMapper<SystemLog> {}
package com.study.systemlog;
public interface SystemLogService {
int insertSystemLog(SystemLog systemLog);
}
package com.study.systemlog;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
@Service
public class SystemLogServiceImpl implements SystemLogService {
@Resource
private SystemLogMapper systemLogMapper;
@Override
public int insertSystemLog(SystemLog systemLog) {
systemLog.setCreateTime(new Date());
return systemLogMapper.insert(systemLog);
}
}
实现自定义注解
package com.study.systemlog;
import com.alibaba.fastjson2.JSON;
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.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Aspect
@Service("systemLogAOPService")
public class SystemLogAOPService {
@Resource
private SystemLogService systemLogService;
@Pointcut("@annotation(com.study.systemlog.SystemLogAOP)")
public void LogPointCut() {}
@Around("LogPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable{
SystemLog systemLog = new SystemLog();
// 请求路径
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request = attributes.getRequest();
systemLog.setUrl(request.getRequestURI());
long start = System.currentTimeMillis();
// 拦截的方法名称。当前正在执行的方法
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
systemLog.setMethodName(className + "." + methodName);
// 拦截的放参数类型
MethodSignature msig = (MethodSignature)point.getSignature();
// 拦截的参数
String[] parameterNames = msig.getParameterNames();
Object[] args = point.getArgs();
Map<String, Object> params = generateParams(parameterNames, args);
systemLog.setRequestParams(JSON.toJSONString(params));
try {
// 获得被拦截的方法
point.getTarget().getClass().getMethod(methodName, msig.getMethod().getParameterTypes());
} catch (NoSuchMethodException | SecurityException e1) {
log.error("获取方法异常:" + e1.getMessage());
}
// 执行方法返回对象
Object object = point.proceed();
try {
systemLog.setResponseResult(JSON.toJSONString(object));
systemLog.setCostTime(System.currentTimeMillis() - start);
systemLog.setMessage("执行成功!");
} catch (Throwable e) {
systemLog.setCostTime(System.currentTimeMillis() - start);
systemLog.setMessage("执行失败!" + e.getMessage());
log.error("执行方法[" + methodName + "]异常!" + e.getMessage());
}
systemLogService.insertSystemLog(systemLog);
return object;
}
private Map<String,Object> generateParams(String[] paramNames, Object[] values){
Map<String,Object> rs = new HashMap<>();
for (int i = 0; i < paramNames.length; i++) {
rs.put(paramNames[i],values[i]);
}
return rs;
}
}
需要导入的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${spring-boot.version}</version>
</dependency>
为了方便,所有类都在同一层目录,也就是同一个包。
使用注解
将注解标在需要记录日志的方法上,就能实现日志功能。
@SystemLogAOP
@GetMapping("/{id}")
public AjaxResult get(@PathVariable Long id) {
return studentService.selectStudentById(id);
}
实现效果