为了方便项目部署在服务器之后,当出现BUG以及某些特殊需求时,会因为无法看到日志或者出现异常无法快速及时处理的情况的时候,那么AOP日志拦截打印的作用就体现出来了。同时加上作者日志生成到服务器的工具,将会更加方便处理问题;
提示:以下是本篇文章正文内容,下面案例可供参考
代码如下
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
代码如下:
import lombok.Data;
/**
* @author 孤巷.
* @description 日志打印model
* @date 2021/3/13 11:44
*/
@Data
public class SystemRequestLogModel {
private String ip;
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private Object result;
private Long timeCost;
}
代码如下:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* @author 孤巷.
* @description 方法级日志切面
* @date 2021/3/22 11:36
*/
@Slf4j
@Component
public class RequestUtil {
@Autowired(required = false)
protected HttpServletRequest request; // 自动注入request
/**
* reqContext对象中的key: 转换好的json对象
*/
private static final String REQ_CONTEXT_KEY_PARAMJSON = "REQ_CONTEXT_KEY_PARAMJSON";
/**
* 获取客户端ip地址 *
*/
public String getClientIp() {
String ipAddress = null;
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
if ("0:0:0:0:0:0:0:1".equals(ipAddress)) {
ipAddress = "127.0.0.1";
}
return ipAddress;
}
public String getRequestParams(JoinPoint proceedingJoinPoint) {
// 参数名
String[] paramNames =
((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
// 参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private String buildRequestParam(String[] paramNames, Object[] paramValues) {
Map<String, Object> requestParams = new HashMap<>();
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
if (value instanceof HttpServletRequest || value instanceof HttpServletResponse) {
continue;
}
// 如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
value = file.getOriginalFilename(); // 获取文件名
}
requestParams.put(paramNames[i], value);
}
JSONObject json = new JSONObject(requestParams);
return json.toJSONString();
}
}
代码如下:
/**
* @author 孤巷.
* @description 常量
* @date 2021/3/13 11:44
*/
public class Constant {
public static final String NULL_POINTER_EXCEPTION = "空指针异常";
public static final String PARSE_EXCEPTION = "格式解析异常";
public static final String ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION = "超出索引异常";
public static final String SQL_EXCEPTION = "SQL异常";
public static final String INTERNAL_SERVER_ERROR = "内部服务器异常";
public static final String NETWORK_BUSY = "网络繁忙,请稍后重试";
public static final String SQL_EXCEPTION_MESSAGE = "数据库执行异常:";
public static final String DATE_FORMAT_ERROR = "时间格式异常";
public static final String DATABASE_CONNECT_OUT_TIME = "数据库连接超时";
}
重点来了哈!代码如下:
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.crypto.CryptoException;
import cn.hutool.http.HttpException;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.whxx.common.constant.Constant;
import com.whxx.common.exception.IllegalTokenException;
import com.whxx.common.exception.NetworkBusyException;
import com.whxx.common.exception.PayException;
import com.whxx.common.exception.SysConfigException;
import com.whxx.common.model.SystemRequestLogModel;
import com.whxx.common.util.RequestUtil;
import com.whxx.common.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
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.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.text.ParseException;
import java.time.format.DateTimeParseException;
/**
* @author 孤巷.
* @description 接口请求切面
* @date 2020/11/17 9:44
*/
@Aspect
@Component
@Slf4j
public class ControllerRequestAspect {
@Resource
private RequestUtil requestUtil;
/**
* 扫描所有Controller
*/
@Pointcut("execution(public * com.whxx.exchange.controller.*.*(..))")
public void request() {
}
@Around("request()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
throw new Exception("ServletRequestAttributes is null");
}
HttpServletRequest request = attributes.getRequest();
SystemRequestLogModel systemRequestLogModel = new SystemRequestLogModel();
systemRequestLogModel.setIp(request.getRemoteAddr());
systemRequestLogModel.setUrl(request.getRequestURL().toString());
systemRequestLogModel.setHttpMethod(request.getMethod());
if ("get".equalsIgnoreCase(request.getMethod())) {
// 处理GET请求中文参数乱码
String decodeParams = "";
if (request.getQueryString() != null) {
try {
decodeParams = URLDecoder.decode(request.getQueryString(), "utf-8"); // 将中文转码
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
systemRequestLogModel.setRequestParams(decodeParams);
} else {
systemRequestLogModel.setRequestParams(
requestUtil.getRequestParams(proceedingJoinPoint));
}
systemRequestLogModel.setClassMethod(
String.format(
"%s.%s",
proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
Object result;
try {
result = proceedingJoinPoint.proceed();
systemRequestLogModel.setResult(result);
systemRequestLogModel.setTimeCost(System.currentTimeMillis() - start);
log.info("请求信息 == {}", systemRequestLogModel);
} catch (Exception e) {
result = exceptionGet(e, systemRequestLogModel);
}
return result;
}
private JSONObject exceptionGet(Exception e, SystemRequestLogModel systemRequestLogModel) {
systemRequestLogModel.setResult(e);
log.error(e.getMessage(), e);
log.error("异常信息 == {}", systemRequestLogModel);
if (e instanceof NetworkBusyException) { // 自定义异常:对外接口调用异常,失败/或对外接口自身报错
return ResultUtil.error(Constant.NETWORK_BUSY);
} else if (e instanceof PayException) { //自定义异常:支付异常
return ResultUtil.fail(e.getMessage());
} else if (e instanceof SysConfigException) { //自定义异常:系统配置异常
return ResultUtil.fail(e.getMessage());
} else if (e instanceof IllegalTokenException) {//自定义异常:非法token
return ResultUtil.fail(e.getMessage());
} else if (e instanceof NullPointerException) { // 空指针异常
return ResultUtil.error(Constant.NULL_POINTER_EXCEPTION);
} else if (e instanceof DuplicateKeyException) { // 唯一键冲突
return ResultUtil.error(Constant.DUPLICATE_KEY);
} else if (e instanceof ParseException) { // 时间格式解析异常
return ResultUtil.error(Constant.PARSE_EXCEPTION);
} else if (e instanceof ArrayIndexOutOfBoundsException) { // 超出索引异常
return ResultUtil.error(Constant.ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION);
} else if (e instanceof SQLException) { // sql异常
return ResultUtil.error(Constant.SQL_EXCEPTION);
} else if (e instanceof DateTimeParseException) { // 时间格式解析异常
return ResultUtil.error(Constant.DATE_FORMAT_ERROR);
} else if (e instanceof CannotGetJdbcConnectionException) { // 数据库连接超时
return ResultUtil.error(Constant.DATABASE_CONNECT_OUT_TIME);
} else if (e instanceof CryptoException) { // 二维码无效
return ResultUtil.fail(Constant.CRYPTO_ERROR);
} else if (e instanceof IORuntimeException) { //请求被拒绝,服务器未开启
return ResultUtil.fail(e.getMessage());
} else if (e instanceof HttpException) { //hutool http请求超时
return ResultUtil.fail(e.getMessage());
} else if (e instanceof BadSqlGrammarException) {//数据库执行异常
return ResultUtil.error(Constant.SQL_EXCEPTION_MESSAGE + ((BadSqlGrammarException) e).getSQLException().getMessage());
} else if (e instanceof JSONException) { //JSON相关异常
return ResultUtil.fail(e.getMessage());
} else if (e instanceof RuntimeException) {
return ResultUtil.error(e.getMessage());
} else { // 其他未知异常
return ResultUtil.error(Constant.INTERNAL_SERVER_ERROR);
}
}
}
最后只需要把下方的controller路径修改为实际的项目路径即可
@Pointcut(“execution(public * com.whxx.exchange.controller..(…))”)
代码如下:
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.whxx.common.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.UnexpectedTypeException;
import java.util.List;
import java.util.Objects;
/**
* @author 孤巷.
* @description 异常全局处理配置
* @date 2020/11/17 9:44
*/
@RestControllerAdvice
@Slf4j
public class ExceptionControllerAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public JSONObject methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
List<ObjectError> errorList = result.getAllErrors();
String defaultMessage = errorList.get(0).getDefaultMessage();
log.error(String.format("参数校验异常1 == {}%s", result.getAllErrors()));
return ResultUtil.failIllegalParameter(
"参数【"
+ Objects.requireNonNull(e.getFieldError()).getField()
+ "】校验异常:"
+ defaultMessage);
}
/**
* 参数校验注解错误导致的异常
*/
@ExceptionHandler(UnexpectedTypeException.class)
public JSONObject unexpectedTypeException(UnexpectedTypeException e) {
return ResultUtil.error(e.getMessage());
}
@ExceptionHandler(BindException.class)
public JSONObject bindExceptionHandler(BindException e) {
BindingResult result = e.getBindingResult();
List<ObjectError> errorList = result.getAllErrors();
String defaultMessage = errorList.get(0).getDefaultMessage();
log.error(String.format("参数校验异常2 == {}%s", result.getAllErrors()));
return ResultUtil.failIllegalParameter(
"参数【"
+ Objects.requireNonNull(e.getFieldError()).getField()
+ "】异常:"
+ defaultMessage);
}
@ExceptionHandler(InvalidFormatException.class)
public JSONObject handHttpConverterException(InvalidFormatException e) {
StringBuilder errors = new StringBuilder();
List<JsonMappingException.Reference> path = e.getPath();
for (JsonMappingException.Reference reference : path) {
errors.append("参数【")
.append(reference.getFieldName())
.append("】输入不合法,需要的是 ")
.append(e.getTargetType().getName())
.append(" 类型,")
.append("提交的值是:")
.append(e.getValue().toString());
}
log.error("参数校验异常3 == {}", errors);
return ResultUtil.failIllegalParameter(errors.toString());
}
@ExceptionHandler(MissingServletRequestParameterException.class)
public JSONObject handleMissingServletRequestParameterException(
MissingServletRequestParameterException e) {
StringBuilder errors = new StringBuilder();
errors.append("参数【").append(e.getParameterName()).append("】为必传字段:").append(e.getMessage());
log.error("参数校验异常4 == {}", errors);
return ResultUtil.failIllegalParameter(errors.toString());
}
@ExceptionHandler(HttpMessageConversionException.class)
public JSONObject handleConversionException(HttpMessageConversionException e) {
StringBuilder errors = new StringBuilder();
errors.append("参数校验异常5:").append(e.getMessage());
log.error("参数校验异常 == {}", errors);
return ResultUtil.failIllegalParameter(errors.toString());
}
}
以上就是今天要讲的内容,本文仅仅简单介绍了AOP的使用,AOP不止是可以实现日志打印的效果,还能实现很多好用的方法,后续我们都会讲到,请持续关注。谢谢!