参考文档
Spring Boot 2.x 教程系列 | AOP 切面统一打印请求日志最佳实践:https://juejin.im/post/5c6cf099f265da2d8c7dc3b0
手把手教你如何优雅的使用Aop记录带参数的复杂Web接口日志:https://www.cnblogs.com/detectiveHLH/p/10361006.html
相关依赖
<!-- aop 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- 用于日志切面中,以 json 格式打印出入参(本来使用阿里的 FASTJSON, 但是对于文件上传的接口,打印参数会报错,换为 Gson) --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.5</version> </dependency>
打印日志
打印日志方案——重写请求和输出类的toString
@Override public String toString() { return JSONObject.toJSONString(this); }
打印日志
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.base.Strings; import com.yixiu.crm.common.util.ResponseCode; import com.yixiu.crm.common.util.ServerResponse; import com.yixiu.crm.constants.EnumSuite; import com.yixiu.crm.dao.mapper.CrmOptionLogMapper; import com.yixiu.crm.dao.po.CrmOptionLog; import com.yixiu.crm.model.OptionParam; import com.yixiu.crm.model.PublicParam; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; 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.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; import java.util.List; /** * @Auther: shiaige.zheng * @Description: 通用的apo打印日志 */ @Aspect @Component @Slf4j public class LogAspect { @Resource private CrmOptionLogMapper crmOptionLogMapper; /** * Excel导出的url */ private final static List<String> URL_EXCEL = Arrays.asList( // 公海导出 "/crm/excelExport/openHotelList", // 私海导出 "/crm/excelExport/selfHotelList", // 公海统计导出 "/crm/excelExport/openHotelStatistics", // 拜访列表导出 "/crm/excelExport/hotelVisitList", // 拜访统计导出 "/crm/excelExport/hotelVisitVisitStatistics", // 陪访统计导出 "/crm/excelExport/hotelVisitAccompanyingStatistics", // 已合作酒店列表导出 "/crm/excelExport/DataBoardCooperativeHotelList", // 已合作酒店统计导出 "/crm/excelExport/DataBoardCooperativeHotelstatistics", // 已合作差评详情导出 "/crm/excelExport/DataBoardAnalysisOfBadCommentsByJoinDate", // 评论分析导出 "/crm/excelExport/DataBoardAnalysisOfCommentsByDate", // 评分分析导出 "/crm/excelExport/DataBoardHotelCommentScoreAnalysis" ); /** * 用来记录请求进入的时间,防止多线程时出错,这里用了ThreadLocal */ private static final ThreadLocal<Long> STAR_TIME = new ThreadLocal<>(); /** * 定义切入点,controller下面的所有类的所有公有方法,这里需要更改成自己项目的 */ @Pointcut("execution(public * com.yixiu.crm.controller..*.*(..))") public void requestLog() { } /** * 方法之前执行,日志打印请求信息 * * @param joinPoint joinPoint */ @Before("requestLog()") public void doBefore(JoinPoint joinPoint) { STAR_TIME.set(System.currentTimeMillis()); ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = servletRequestAttributes.getRequest(); // 打印请求相关参数 log.info("========================================== Start =========================================="); log.info(new Date().toString()); // 打印请求 url log.info("URL : {}", request.getRequestURL().toString()); // 打印 Http method log.info("HTTP Method : {}", request.getMethod()); // 打印调用 controller 的全路径以及执行方法 log.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // 打印请求的 IP log.info("IP : {}", request.getRemoteAddr()); // 打印请求入参 log.info("Request Args : {}", joinPoint.getArgs()); log.info("========================================== Start =========================================="); } /** * 方法返回之前执行,打印才返回值以及方法消耗时间,同时获取入参和返参 * * @param response 返回值 */ @AfterReturning(returning = "response", pointcut = "requestLog()") public void doAfterReturning(JoinPoint point, Object response) { log.info("=========================================== End ==========================================="); log.info(new Date().toString()); // 打印出参 log.info("Response Args : {}", response); //打印请求耗时 log.info("Request spend times : [{}ms]", System.currentTimeMillis() - STAR_TIME.get()); STAR_TIME.remove(); log.info("=========================================== End ==========================================="); } /** * 打印异常 */ @AfterThrowing(throwing = "ex", pointcut = "requestLog()") public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) { log.error("=========================================== End ==========================================="); ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = servletRequestAttributes.getRequest(); log.error(new Date().toString()); // 打印请求 url log.error("URL : {}", request.getRequestURL().toString()); // 打印 Http method log.error("HTTP Method : {}", request.getMethod()); // 打印调用 controller 的全路径以及执行方法 log.error("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); // 打印请求的 IP log.error("IP : {}", request.getRemoteAddr()); // 打印请求入参 log.error("Request Args : {}", joinPoint.getArgs()); log.error("Method Signature: {}", joinPoint.getSignature()); log.error("Throwable:", ex); //打印请求耗时 log.error("Request spend times : [{}ms]", System.currentTimeMillis() - STAR_TIME.get()); STAR_TIME.remove(); log.error("=========================================== End ==========================================="); } }