【Java】SpringBoot使用AOP进行日志解析打印+系统异常全局处理配置

╰半夏微凉° 2023-09-30 23:31 123阅读 0赞

文章目录

  • 前言
  • 一、导入Lombok
  • 二、创建日志打印Model
  • 三、创建日志切面工具类
  • 四、需要用到的一些常量类
  • 五、创建接口请求切面
  • 六、系统异常全局配置
  • 总结

前言

为了方便项目部署在服务器之后,当出现BUG以及某些特殊需求时,会因为无法看到日志或者出现异常无法快速及时处理的情况的时候,那么AOP日志拦截打印的作用就体现出来了。同时加上作者日志生成到服务器的工具,将会更加方便处理问题;


提示:以下是本篇文章正文内容,下面案例可供参考

一、导入Lombok

代码如下

  1. <!--Lombok-->
  2. <dependency>
  3. <groupId>org.projectlombok</groupId>
  4. <artifactId>lombok</artifactId>
  5. <version>1.18.24</version>
  6. </dependency>

二、创建日志打印Model

代码如下:

  1. import lombok.Data;
  2. /**
  3. * @author 孤巷.
  4. * @description 日志打印model
  5. * @date 2021/3/13 11:44
  6. */
  7. @Data
  8. public class SystemRequestLogModel {
  9. private String ip;
  10. private String url;
  11. private String httpMethod;
  12. private String classMethod;
  13. private Object requestParams;
  14. private Object result;
  15. private Long timeCost;
  16. }

三、创建日志切面工具类

代码如下:

  1. import com.alibaba.fastjson.JSONObject;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.aspectj.lang.JoinPoint;
  5. import org.aspectj.lang.reflect.MethodSignature;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.web.context.request.RequestAttributes;
  9. import org.springframework.web.context.request.RequestContextHolder;
  10. import org.springframework.web.multipart.MultipartFile;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13. import java.util.HashMap;
  14. import java.util.Iterator;
  15. import java.util.Map;
  16. /**
  17. * @author 孤巷.
  18. * @description 方法级日志切面
  19. * @date 2021/3/22 11:36
  20. */
  21. @Slf4j
  22. @Component
  23. public class RequestUtil {
  24. @Autowired(required = false)
  25. protected HttpServletRequest request; // 自动注入request
  26. /**
  27. * reqContext对象中的key: 转换好的json对象
  28. */
  29. private static final String REQ_CONTEXT_KEY_PARAMJSON = "REQ_CONTEXT_KEY_PARAMJSON";
  30. /**
  31. * 获取客户端ip地址 *
  32. */
  33. public String getClientIp() {
  34. String ipAddress = null;
  35. ipAddress = request.getHeader("x-forwarded-for");
  36. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  37. ipAddress = request.getHeader("Proxy-Client-IP");
  38. }
  39. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  40. ipAddress = request.getHeader("WL-Proxy-Client-IP");
  41. }
  42. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
  43. ipAddress = request.getRemoteAddr();
  44. }
  45. // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  46. if (ipAddress != null && ipAddress.length() > 15) {
  47. if (ipAddress.indexOf(",") > 0) {
  48. ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
  49. }
  50. }
  51. if ("0:0:0:0:0:0:0:1".equals(ipAddress)) {
  52. ipAddress = "127.0.0.1";
  53. }
  54. return ipAddress;
  55. }
  56. public String getRequestParams(JoinPoint proceedingJoinPoint) {
  57. // 参数名
  58. String[] paramNames =
  59. ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
  60. // 参数值
  61. Object[] paramValues = proceedingJoinPoint.getArgs();
  62. return buildRequestParam(paramNames, paramValues);
  63. }
  64. private String buildRequestParam(String[] paramNames, Object[] paramValues) {
  65. Map<String, Object> requestParams = new HashMap<>();
  66. for (int i = 0; i < paramNames.length; i++) {
  67. Object value = paramValues[i];
  68. if (value instanceof HttpServletRequest || value instanceof HttpServletResponse) {
  69. continue;
  70. }
  71. // 如果是文件对象
  72. if (value instanceof MultipartFile) {
  73. MultipartFile file = (MultipartFile) value;
  74. value = file.getOriginalFilename(); // 获取文件名
  75. }
  76. requestParams.put(paramNames[i], value);
  77. }
  78. JSONObject json = new JSONObject(requestParams);
  79. return json.toJSONString();
  80. }
  81. }

四、需要用到的一些常量类

代码如下:

  1. /**
  2. * @author 孤巷.
  3. * @description 常量
  4. * @date 2021/3/13 11:44
  5. */
  6. public class Constant {
  7. public static final String NULL_POINTER_EXCEPTION = "空指针异常";
  8. public static final String PARSE_EXCEPTION = "格式解析异常";
  9. public static final String ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = "超出索引异常";
  10. public static final String SQL_EXCEPTION = "SQL异常";
  11. public static final String INTERNAL_SERVER_ERROR = "内部服务器异常";
  12. public static final String NETWORK_BUSY = "网络繁忙,请稍后重试";
  13. public static final String SQL_EXCEPTION_MESSAGE = "数据库执行异常:";
  14. public static final String DATE_FORMAT_ERROR = "时间格式异常";
  15. public static final String DATABASE_CONNECT_OUT_TIME = "数据库连接超时";
  16. }

五、创建接口请求切面

重点来了哈!代码如下:

  1. import cn.hutool.core.io.IORuntimeException;
  2. import cn.hutool.crypto.CryptoException;
  3. import cn.hutool.http.HttpException;
  4. import com.alibaba.fastjson.JSONException;
  5. import com.alibaba.fastjson.JSONObject;
  6. import com.whxx.common.constant.Constant;
  7. import com.whxx.common.exception.IllegalTokenException;
  8. import com.whxx.common.exception.NetworkBusyException;
  9. import com.whxx.common.exception.PayException;
  10. import com.whxx.common.exception.SysConfigException;
  11. import com.whxx.common.model.SystemRequestLogModel;
  12. import com.whxx.common.util.RequestUtil;
  13. import com.whxx.common.util.ResultUtil;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.aspectj.lang.ProceedingJoinPoint;
  16. import org.aspectj.lang.annotation.Around;
  17. import org.aspectj.lang.annotation.Aspect;
  18. import org.aspectj.lang.annotation.Pointcut;
  19. import org.springframework.dao.DuplicateKeyException;
  20. import org.springframework.jdbc.BadSqlGrammarException;
  21. import org.springframework.jdbc.CannotGetJdbcConnectionException;
  22. import org.springframework.stereotype.Component;
  23. import org.springframework.web.context.request.RequestContextHolder;
  24. import org.springframework.web.context.request.ServletRequestAttributes;
  25. import javax.annotation.Resource;
  26. import javax.servlet.http.HttpServletRequest;
  27. import java.io.UnsupportedEncodingException;
  28. import java.net.URLDecoder;
  29. import java.sql.SQLException;
  30. import java.text.ParseException;
  31. import java.time.format.DateTimeParseException;
  32. /**
  33. * @author 孤巷.
  34. * @description 接口请求切面
  35. * @date 2020/11/17 9:44
  36. */
  37. @Aspect
  38. @Component
  39. @Slf4j
  40. public class ControllerRequestAspect {
  41. @Resource
  42. private RequestUtil requestUtil;
  43. /**
  44. * 扫描所有Controller
  45. */
  46. @Pointcut("execution(public * com.whxx.exchange.controller.*.*(..))")
  47. public void request() {
  48. }
  49. @Around("request()")
  50. public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  51. long start = System.currentTimeMillis();
  52. ServletRequestAttributes attributes =
  53. (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  54. if (attributes == null) {
  55. throw new Exception("ServletRequestAttributes is null");
  56. }
  57. HttpServletRequest request = attributes.getRequest();
  58. SystemRequestLogModel systemRequestLogModel = new SystemRequestLogModel();
  59. systemRequestLogModel.setIp(request.getRemoteAddr());
  60. systemRequestLogModel.setUrl(request.getRequestURL().toString());
  61. systemRequestLogModel.setHttpMethod(request.getMethod());
  62. if ("get".equalsIgnoreCase(request.getMethod())) {
  63. // 处理GET请求中文参数乱码
  64. String decodeParams = "";
  65. if (request.getQueryString() != null) {
  66. try {
  67. decodeParams = URLDecoder.decode(request.getQueryString(), "utf-8"); // 将中文转码
  68. } catch (UnsupportedEncodingException e) {
  69. e.printStackTrace();
  70. }
  71. }
  72. systemRequestLogModel.setRequestParams(decodeParams);
  73. } else {
  74. systemRequestLogModel.setRequestParams(
  75. requestUtil.getRequestParams(proceedingJoinPoint));
  76. }
  77. systemRequestLogModel.setClassMethod(
  78. String.format(
  79. "%s.%s",
  80. proceedingJoinPoint.getSignature().getDeclaringTypeName(),
  81. proceedingJoinPoint.getSignature().getName()));
  82. Object result;
  83. try {
  84. result = proceedingJoinPoint.proceed();
  85. systemRequestLogModel.setResult(result);
  86. systemRequestLogModel.setTimeCost(System.currentTimeMillis() - start);
  87. log.info("请求信息 == {}", systemRequestLogModel);
  88. } catch (Exception e) {
  89. result = exceptionGet(e, systemRequestLogModel);
  90. }
  91. return result;
  92. }
  93. private JSONObject exceptionGet(Exception e, SystemRequestLogModel systemRequestLogModel) {
  94. systemRequestLogModel.setResult(e);
  95. log.error(e.getMessage(), e);
  96. log.error("异常信息 == {}", systemRequestLogModel);
  97. if (e instanceof NetworkBusyException) {
  98. // 自定义异常:对外接口调用异常,失败/或对外接口自身报错
  99. return ResultUtil.error(Constant.NETWORK_BUSY);
  100. } else if (e instanceof PayException) {
  101. //自定义异常:支付异常
  102. return ResultUtil.fail(e.getMessage());
  103. } else if (e instanceof SysConfigException) {
  104. //自定义异常:系统配置异常
  105. return ResultUtil.fail(e.getMessage());
  106. } else if (e instanceof IllegalTokenException) {
  107. //自定义异常:非法token
  108. return ResultUtil.fail(e.getMessage());
  109. } else if (e instanceof NullPointerException) {
  110. // 空指针异常
  111. return ResultUtil.error(Constant.NULL_POINTER_EXCEPTION);
  112. } else if (e instanceof DuplicateKeyException) {
  113. // 唯一键冲突
  114. return ResultUtil.error(Constant.DUPLICATE_KEY);
  115. } else if (e instanceof ParseException) {
  116. // 时间格式解析异常
  117. return ResultUtil.error(Constant.PARSE_EXCEPTION);
  118. } else if (e instanceof ArrayIndexOutOfBoundsException) {
  119. // 超出索引异常
  120. return ResultUtil.error(Constant.ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION);
  121. } else if (e instanceof SQLException) {
  122. // sql异常
  123. return ResultUtil.error(Constant.SQL_EXCEPTION);
  124. } else if (e instanceof DateTimeParseException) {
  125. // 时间格式解析异常
  126. return ResultUtil.error(Constant.DATE_FORMAT_ERROR);
  127. } else if (e instanceof CannotGetJdbcConnectionException) {
  128. // 数据库连接超时
  129. return ResultUtil.error(Constant.DATABASE_CONNECT_OUT_TIME);
  130. } else if (e instanceof CryptoException) {
  131. // 二维码无效
  132. return ResultUtil.fail(Constant.CRYPTO_ERROR);
  133. } else if (e instanceof IORuntimeException) {
  134. //请求被拒绝,服务器未开启
  135. return ResultUtil.fail(e.getMessage());
  136. } else if (e instanceof HttpException) {
  137. //hutool http请求超时
  138. return ResultUtil.fail(e.getMessage());
  139. } else if (e instanceof BadSqlGrammarException) {
  140. //数据库执行异常
  141. return ResultUtil.error(Constant.SQL_EXCEPTION_MESSAGE + ((BadSqlGrammarException) e).getSQLException().getMessage());
  142. } else if (e instanceof JSONException) {
  143. //JSON相关异常
  144. return ResultUtil.fail(e.getMessage());
  145. } else if (e instanceof RuntimeException) {
  146. return ResultUtil.error(e.getMessage());
  147. } else {
  148. // 其他未知异常
  149. return ResultUtil.error(Constant.INTERNAL_SERVER_ERROR);
  150. }
  151. }
  152. }

最后只需要把下方的controller路径修改为实际的项目路径即可

@Pointcut(“execution(public * com.whxx.exchange.controller..(…))”)

六、系统异常全局配置

代码如下:

  1. import com.alibaba.fastjson.JSONObject;
  2. import com.fasterxml.jackson.databind.JsonMappingException;
  3. import com.fasterxml.jackson.databind.exc.InvalidFormatException;
  4. import com.whxx.common.util.ResultUtil;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.http.converter.HttpMessageConversionException;
  7. import org.springframework.validation.BindException;
  8. import org.springframework.validation.BindingResult;
  9. import org.springframework.validation.ObjectError;
  10. import org.springframework.web.bind.MethodArgumentNotValidException;
  11. import org.springframework.web.bind.MissingServletRequestParameterException;
  12. import org.springframework.web.bind.annotation.ExceptionHandler;
  13. import org.springframework.web.bind.annotation.RestControllerAdvice;
  14. import javax.validation.UnexpectedTypeException;
  15. import java.util.List;
  16. import java.util.Objects;
  17. /**
  18. * @author 孤巷.
  19. * @description 异常全局处理配置
  20. * @date 2020/11/17 9:44
  21. */
  22. @RestControllerAdvice
  23. @Slf4j
  24. public class ExceptionControllerAdvice {
  25. @ExceptionHandler(MethodArgumentNotValidException.class)
  26. public JSONObject methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
  27. BindingResult result = e.getBindingResult();
  28. List<ObjectError> errorList = result.getAllErrors();
  29. String defaultMessage = errorList.get(0).getDefaultMessage();
  30. log.error(String.format("参数校验异常1 == {}%s", result.getAllErrors()));
  31. return ResultUtil.failIllegalParameter(
  32. "参数【"
  33. + Objects.requireNonNull(e.getFieldError()).getField()
  34. + "】校验异常:"
  35. + defaultMessage);
  36. }
  37. /**
  38. * 参数校验注解错误导致的异常
  39. */
  40. @ExceptionHandler(UnexpectedTypeException.class)
  41. public JSONObject unexpectedTypeException(UnexpectedTypeException e) {
  42. return ResultUtil.error(e.getMessage());
  43. }
  44. @ExceptionHandler(BindException.class)
  45. public JSONObject bindExceptionHandler(BindException e) {
  46. BindingResult result = e.getBindingResult();
  47. List<ObjectError> errorList = result.getAllErrors();
  48. String defaultMessage = errorList.get(0).getDefaultMessage();
  49. log.error(String.format("参数校验异常2 == {}%s", result.getAllErrors()));
  50. return ResultUtil.failIllegalParameter(
  51. "参数【"
  52. + Objects.requireNonNull(e.getFieldError()).getField()
  53. + "】异常:"
  54. + defaultMessage);
  55. }
  56. @ExceptionHandler(InvalidFormatException.class)
  57. public JSONObject handHttpConverterException(InvalidFormatException e) {
  58. StringBuilder errors = new StringBuilder();
  59. List<JsonMappingException.Reference> path = e.getPath();
  60. for (JsonMappingException.Reference reference : path) {
  61. errors.append("参数【")
  62. .append(reference.getFieldName())
  63. .append("】输入不合法,需要的是 ")
  64. .append(e.getTargetType().getName())
  65. .append(" 类型,")
  66. .append("提交的值是:")
  67. .append(e.getValue().toString());
  68. }
  69. log.error("参数校验异常3 == {}", errors);
  70. return ResultUtil.failIllegalParameter(errors.toString());
  71. }
  72. @ExceptionHandler(MissingServletRequestParameterException.class)
  73. public JSONObject handleMissingServletRequestParameterException(
  74. MissingServletRequestParameterException e) {
  75. StringBuilder errors = new StringBuilder();
  76. errors.append("参数【").append(e.getParameterName()).append("】为必传字段:").append(e.getMessage());
  77. log.error("参数校验异常4 == {}", errors);
  78. return ResultUtil.failIllegalParameter(errors.toString());
  79. }
  80. @ExceptionHandler(HttpMessageConversionException.class)
  81. public JSONObject handleConversionException(HttpMessageConversionException e) {
  82. StringBuilder errors = new StringBuilder();
  83. errors.append("参数校验异常5:").append(e.getMessage());
  84. log.error("参数校验异常 == {}", errors);
  85. return ResultUtil.failIllegalParameter(errors.toString());
  86. }
  87. }

总结

以上就是今天要讲的内容,本文仅仅简单介绍了AOP的使用,AOP不止是可以实现日志打印的效果,还能实现很多好用的方法,后续我们都会讲到,请持续关注。谢谢!

发表评论

表情:
评论列表 (有 0 条评论,123人围观)

还没有评论,来说两句吧...

相关阅读