一、创建日志记录表、异常日志表,表结构如下:
操作表:
异常表:
DDL:
CREATE TABLE `operlog` (
`oper_id` varchar(64) NOT NULL COMMENT '主键',
`oper_model` varchar(64) DEFAULT NULL COMMENT '功能模块',
`oper_type` varchar(64) DEFAULT NULL COMMENT '操作类型',
`oper_desc` varchar(500) DEFAULT NULL COMMENT '操作描述',
`oper_requ_param` text COMMENT '请求参数',
`oper_resp_param` text COMMENT '返回参数',
`oper_user_id` varchar(64) DEFAULT NULL COMMENT '操作员id',
`oper_user_name` varchar(64) DEFAULT NULL COMMENT '操作员名称',
`oper_method` varchar(255) DEFAULT NULL COMMENT '操作方法',
`oper_uri` varchar(255) DEFAULT NULL COMMENT '请求URI',
`oper_ip` varchar(64) DEFAULT NULL COMMENT '请求id',
`oper_create_time` datetime DEFAULT NULL COMMENT '操作时间',
`oper_ver` varchar(64) DEFAULT NULL COMMENT '操作版本号',
PRIMARY KEY (`oper_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `errorlog` (
`error_id` varchar(64) NOT NULL COMMENT '主键ID',
`oper_user_id` varchar(64) DEFAULT NULL COMMENT '操作员ID',
`oper_user_name` varchar(64) DEFAULT NULL COMMENT '操作员名称',
`oper_method` varchar(255) DEFAULT NULL COMMENT '操作方法',
`oper_uri` varchar(255) DEFAULT NULL COMMENT '请求URI',
`oper_ip` varchar(64) DEFAULT NULL COMMENT '请求IP',
`oper_ver` varchar(64) DEFAULT NULL COMMENT '操作版本',
`oper_create_time` datetime DEFAULT NULL COMMENT '创建时间',
`error_requ_param` text COMMENT '请求参数',
`error_name` varchar(255) DEFAULT NULL COMMENT '异常名称',
`error_message` text COMMENT '异常信息',
PRIMARY KEY (`error_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
二、添加Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建操作日志注解类OperLog.java和操作类型类
//注解放置的目标位置,METHOD是可注解在方法级别上
@Target(ElementType.METHOD)
//注解在哪个阶段执行
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
// 操作模块
String operModul() default "";
// 操作类型
LogActionType operType() default LogActionType.SELECT;
// 操作说明
String operDesc() default "";
}
public enum LogActionType {
/**
* 增删改查
*/
ADD("新增"),
SELECT("查询"),
UPDATE("更新"),
DELETE("删除");
private String value;
LogActionType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
创建切面类记录操作日志
/**
* 切片的实现
* 监控controller的请求信息和响应信息
*
* @author sxyuser
*/
@Aspect
@Slf4j
@Component
public class WebLogAspect {
/**
* 操作版本号
* <p>
* 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
* </p>
*/
// @Value("${version}")
private final String operVer = "1";
@Resource
private ErrorlogService errorlogService;
@Resource
private OperlogService operlogService;
/**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
*/
@Pointcut("@annotation(com.demo.shiyan2.utils.annotation.OperLog)")
public void operLogPoinCut() {
}
/**
* 设置操作异常切入点记录异常日志 扫描所有controller包下操作
*/
@Pointcut("execution(* com.demo.shiyan2.controller..*.*(..))")
public void operExceptionLogPoinCut() {
}
/**
* 配置环绕通知,使用在方法logPointcut()上注册的切入点
*
* @param joinPoint join point for advice
*/
@Before(value = "operLogPoinCut()")
public void doBefore(JoinPoint joinPoint) {
HttpServletRequest request = RequestHolder.getHttpServletRequest();
if (request != null) {
// 记录下请求内容
log.info("URL : {}", request.getRequestURL().toString());
log.info("HTTP_METHOD :{} ", request.getMethod());
log.info("IP : {}", request.getRemoteAddr());
log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
log.info("ARGS : " + JsonUtils.obiectToJson(joinPoint.getArgs()));
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = enu.nextElement();
log.info("name:{},value:{}", name, request.getParameter(name));
}
} else {
log.error("ServletRequestAttributes为空!");
}
}
/**
* 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
*
* @param joinPoint 切入点
* @param keys 返回结果
*/
@AfterReturning(value = "operExceptionLogPoinCut()", returning = "keys")
public void saveOperLog(JoinPoint joinPoint, Object keys) {
HttpServletRequest request = RequestHolder.getHttpServletRequest();
if (request != null) {
Operlog operlog = new Operlog();
// 主键ID
operlog.setOperId(UUID.randomUUID().toString());
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
// 获取操作
OperLog opLog = method.getAnnotation(OperLog.class);
if (opLog != null) {
String operModul = opLog.operModul();
String operType = JsonUtils.obiectToJson(opLog.operType());
String operDesc = opLog.operDesc();
// 操作模块
operlog.setOperModel(operModul);
// 操作类型
operlog.setOperType(operType);
// 操作描述
operlog.setOperDesc(operDesc);
}
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
// 请求方法
operlog.setOperMethod(methodName);
// 将参数所在的数组转换成json
String params = JsonUtils.obiectToJson(request.getParameterMap());
// 请求参数
operlog.setOperRequParam(params);
// 返回结果
operlog.setOperRespParam(JsonUtils.obiectToJson(keys));
// operlog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // 请求用户ID
// operlog.setOperUserName(UserShiroUtil.getCurrentUserName()); // 请求用户名称
// 请求IP
operlog.setOperIp(request.getRemoteAddr());
// 请求URI
operlog.setOperUri(request.getRequestURI());
// 创建时间
operlog.setOperCreateTime(Instant.now());
// 操作版本
operlog.setOperVer(operVer);
operlogService.save(operlog);
} else {
log.error("ServletRequestAttributes为空!");
}
}
/**
* 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
*
* @param joinPoint 切入点
* @param e 异常信息
*/
@AfterThrowing(pointcut = "operLogPoinCut()", throwing = "e")
public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
try {
HttpServletRequest request = RequestHolder.getHttpServletRequest();
if (request != null) {
Errorlog excepLog = new Errorlog();
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
excepLog.setErrorId(UUID.randomUUID().toString());
// 获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
// 获取请求的方法名
String methodName = method.getName();
methodName = className + "." + methodName;
// 将参数所在的数组转换成json
String params = JsonUtils.obiectToJson(request.getParameterMap());
excepLog.setErrorRequParam(params); // 请求参数
excepLog.setOperMethod(methodName); // 请求方法名
excepLog.setErrorName(e.getClass().getName()); // 异常名称
excepLog.setErrorMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
// 异常信息
// excepLog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // 操作员ID
// excepLog.setOperUserName(UserShiroUtil.getCurrentUserName()); // 操作员名称
// 操作URI
excepLog.setOperUri(request.getRequestURI());
// 操作员IP
excepLog.setOperIp(request.getRemoteAddr());
// 操作版本号
excepLog.setOperVer(operVer);
// 发生异常时间
excepLog.setOperCreateTime(Instant.now());
errorlogService.save(excepLog);
} else {
log.error("ServletRequestAttributes为空!");
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
/**
* 转换异常信息为字符串
*
* @param exceptionName 异常名称
* @param exceptionMessage 异常信息
* @param elements 堆栈信息
*/
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
StringBuilder strbuff = new StringBuilder();
for (StackTraceElement stet : elements) {
strbuff.append(stet).append("\n");
}
return exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
}
}
RequestHolder类
/**
* 获取 HttpServletRequest
*/
public class RequestHolder {
public static HttpServletRequest getHttpServletRequest() {
HttpServletRequest request=null;
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
request = attributes.getRequest();
}
return request;
}
}
位于JsonUtils类中的obiectToJson方法
public static String obiectToJson(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (Exception e) {
return null;
}
}
在Controller层方法添加@OperLog注解
@OperLog(operModul = "测试类",operType = LogActionType.ADD,operDesc = "测试方法")
@GetMapping("/")
public String test(HttpServletRequest request) {
userService.test();
return request.getRequestURI();
}