项目例子: https://github.com/nlxs0511/springmybatisplus.git
一、所有方法
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.eebbk.demo.dao.RequestLogMapper;
import com.eebbk.demo.pojo.RequestLogEntity;
import com.eebbk.demo.utils.PropertiesUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
/**
* <p>ControllerLogAspect</p>
*/
@Aspect
@Component
public class ControllerLogAspect {
@Resource
private RequestLogMapper logMapper;
private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);
@Pointcut("execution (* com.eebbk.demo.controller..*.*(..))")
public void controllerAspect(){
}
@Around("controllerAspect()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
String logtoDb = PropertiesUtil.findAppConfigByName("request.logtodb");
if (!org.springframework.util.StringUtils.hasText(logtoDb)) {
return joinPoint.proceed();
}
if(RequestContextHolder.getRequestAttributes() == null){
return joinPoint.proceed();
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Object[] o = joinPoint.getArgs();
List<Object> args = o == null ? Collections.emptyList() : Lists.newArrayList(o);
Iterator<Object> it = args.iterator();
while (it.hasNext()) {
Object arg = it.next();
if (arg instanceof BindingResult) {
it.remove();
}
if (arg instanceof ServletRequest) {
it.remove();
}
}
long timestamp = System.currentTimeMillis();
String url = request.getRequestURI();
String ip = request.getRemoteAddr();
// 由于文件上传日志打印太大,关闭此类接口日志输出
if(StringUtils.isNoneBlank(url) && url.contains("/searchQuestionByImage")){
return joinPoint.proceed();
}
String methodName = joinPoint.getSignature().toShortString();
Object resp = joinPoint.proceed();
long cast = System.currentTimeMillis() - timestamp;
try {
// String param = JSON.toJSONString(args,true);
String param = JSON.toJSONString(args, SerializerFeature.IgnoreErrorGetter);
RequestLogEntity log = new RequestLogEntity();
log.setCreateTime(new Date());
log.setRequestUrl(url);
log.setParam(param);
if("true".equalsIgnoreCase(logtoDb)){
saveLog(log);
}
String responseLog = StrUtil.format("请求路径:{},方法:{},参数:{},耗时:{} ms",url,methodName, param,cast);
LOGGER.info(responseLog);
} catch (Exception e) {
//ignore
}
return resp;
}
private void saveLog(RequestLogEntity log){
if(log.getRequestUrl().contains("/healthy")){
return;
}
logMapper.insert(log);
}
}
1.pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.自定义log注解类
package com.test.mybatisplus.aop;
import java.lang.annotation.*;
/**
* @Description 日志注解
* @project
* @author:hf
* @date:
* @company:人生有限公司
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 要执行的操作类型比如:add操作
**/
public String operationType() default "";
/**
* 要执行的具体操作比如:添加用户
**/
public String operationName() default "";
}
3.SystemLogAspect
package com.test.mybatisplus.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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.HttpSession;
import java.lang.reflect.Method;
/**
* @Description〈
* @project
* @author:hf
* @date:
* @company:人生电子有限公司
*/
@Component
@Aspect
@Slf4j
public class SystemLogAspect {
//正常 device : -->around-->before-->method-->after-->afterReturn
//报错 device : -->around-->before-->method-->after-->afterthrowing
@Pointcut("execution(* com.test.mybatisplus.controller.AopController.*(..))")
public void controllerAspect() {
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@Before("controllerAspect()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("==========执行controller前置通知===============");
if (log.isInfoEnabled()) {
log.info("before " + joinPoint);
log.info("before start");
joinPoint.getArgs();//输入的参数列表
joinPoint.getTarget().getClass().getName();///类全路径
joinPoint.getSignature().getDeclaringTypeName();//接口全路径
joinPoint.getSignature().getName();//调用的方法
log.info("before end");
}
}
/**
* @Description:配置controller环绕通知,使用在方法aspect()上注册的切入点
* @Return:
* @Author: hf
* @Time: 2021/3/5 15:32
* @Company 人生有限公司
*/
@Around("controllerAspect()")
public Object around (ProceedingJoinPoint pjp){
log.info("==========开始执行controller环绕通知===============");
long start = System.currentTimeMillis();
Object object=null;
try {
object= pjp.proceed();
// ((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
//日志级别过滤
if (log.isInfoEnabled()) {
log.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
}
log.info("==========结束执行controller环绕通知===============");
} catch (Throwable e) {
long end = System.currentTimeMillis();
if (log.isInfoEnabled()) {
log.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
return object;
}
/**
* @Description:
* @Return:
* @Author: hf
* @Time: 2021/3/5 15:32
* @Company 人生有限公司
*/
@After("controllerAspect()")
public void after (JoinPoint joinPoint){
log.info("=====after=====");
}
/**
* @Description:配置后置返回通知,使用在方法aspect()上注册的切入点
* @Return:
* @Author: hf
* @Time: 2021/3/5 15:32
* @Company 人生有限公司
*/
@AfterReturning("controllerAspect()")
public void afterReturn(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request =attributes.getRequest();
//读取session中的用户
HttpSession session = request.getSession();
//请求的IP
String ip = request.getRemoteAddr();
try {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String operationType = "";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(Log.class).operationType();
operationName = method.getAnnotation(Log.class).operationName();
break;
}
}
}
//*========控制台输出=========*//
log.info("=====controller后置通知开始=====");
log.info("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
log.info("方法描述:" + operationName);
log.info("请求人:" + "xxx");
log.info("请求IP:" + ip);
//*========数据库日志=========*//
log.info("=====controller后置通知结束=====");
} catch (Exception e) {
//记录本地异常日志
log.error("==后置通知异常==");
log.error("异常信息:{}", e.getMessage());
}
}
/**
* @Description:异常通知 用于拦截记录异常日志
* @Return:
* @Author: hf
* @Time: 2021/3/5 15:32
* @Company 人生有限公司
*/
@AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
public void doAfterThrowing (JoinPoint joinPoint, Throwable e){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//读取session中的用户
HttpSession session = request.getSession();
String ip = request.getRemoteAddr();
String params = "";
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for (int i = 0; i < joinPoint.getArgs().length; i++) {
//params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";";
}
}
try {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String operationType = "";
String operationName = "";
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
operationType = method.getAnnotation(Log.class).operationType();
operationName = method.getAnnotation(Log.class).operationName();
break;
}
}
}
/*========控制台输出=========*/
log.info("=====异常通知开始=====");
log.info("异常代码:" + e.getClass().getName());
log.info("异常信息:" + e.getMessage());
log.info("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()") + "." + operationType);
log.info("方法描述:" + operationName);
log.info("请求人:" + "xxx");
log.info("请求IP:" + ip);
log.info("请求参数:" + params);
/*==========数据库日志=========*/
//保存数据库
log.info("=====异常通知结束=====");
} catch (Exception ex) {
//记录本地异常日志
log.error("==异常通知异常==");
log.error("异常信息:{}", ex.getMessage());
}
log.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName());
}
}
4.AopController
package com.test.mybatisplus.controller;
import com.test.mybatisplus.aop.Log;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description aop测试类
* @project
* @author:hf
* @date:
* @company:人生有限公司
*/
@Api
@RestController
@RequestMapping("aopController")
@Slf4j
public class AopController {
/**
* @Description: 测试方法
* @Author: hf
* @Time: 2021/3/5 16:26
* @Company 人生有限公司
*/
@GetMapping("/testAOP")
@ApiOperation(value = "testAOP",notes = "testAOP")
@Log(operationType="testAOP",operationName="测试接口")
public String testAOP() {
/* String a=null;
a.equals("");*/
return "测试testAOP接口...";
}
}
5.执行
6.日志输出
2021-03-05 16:38:40.023 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - ==========开始执行controller环绕通知===============
==========执行controller前置通知===============
2021-03-05 16:38:40.026 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - before execution(String com.test.mybatisplus.controller.AopController.testAOP())
2021-03-05 16:38:40.026 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - before start
2021-03-05 16:38:40.027 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - before end
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - =====controller后置通知开始=====
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - 请求方法:com.test.mybatisplus.controller.AopController.testAOP().testAOP
2021-03-05 16:38:40.040 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - 方法描述:测试接口
2021-03-05 16:38:40.041 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - 请求人:xxx
2021-03-05 16:38:40.041 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - 请求IP:0:0:0:0:0:0:0:1
2021-03-05 16:38:40.042 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - =====controller后置通知结束=====
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - =====after=====
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - around execution(String com.test.mybatisplus.controller.AopController.testAOP()) Use time : 21 ms!
2021-03-05 16:38:40.044 [http-nio-8084-exec-10] INFO com.test.mybatisplus.aop.SystemLogAspect - ==========结束执行controller环绕通知===============
7.注意:使用AOP的@Around后无返回值
在@Around中执行并返回 object= pjp.proceed();
@Around("controllerAspect()")
public Object around (ProceedingJoinPoint pjp){
log.info("==========开始执行controller环绕通知===============");
long start = System.currentTimeMillis();
Object object=null;
try {
object= pjp.proceed();
// ((ProceedingJoinPoint) joinPoint).proceed();
long end = System.currentTimeMillis();
//日志级别过滤
if (log.isInfoEnabled()) {
log.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
}
log.info("==========结束执行controller环绕通知===============");
} catch (Throwable e) {
long end = System.currentTimeMillis();
if (log.isInfoEnabled()) {
log.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
}
}
return object;
}