preface
AOP is used to process the request log, record the request IP address, URL, request parameters, request results and other information, and asynchronously save the log information to facilitate the troubleshooting and analysis of online problems.
Train of thought
-
For @requestMapping, @getMapping and other annotations, @around notifies processing log information
-
Use Spring event publishing subscriptions to save log information with @async
implementation
section
-
Log Definition
@Data public class RequestLog implements Serializable { // id private String id; / / request url private String url; // Request method private String method; // Client IP address private String ip; // User agent private String userAgent; // Request call class (which controller) private String module; // Request to call class methods (@requestMapping, etc.) private String operation; // Request parameters private String args; // Request the result private Object result; // Successful (Exception is false) private boolean success; // Execution time (ms) private long duration; / / founder private String createdBy; // Create time private LocalDateTime createdAt; } Copy the code
-
Section definition
@Slf4j @Aspect @Component public class RequestLogAspect { @Autowired private ApplicationEventPublisher publisher; @Autowired private ObjectMapper objectMapper; // Maximum length of exception information private static final int MAX_EXCEPTION_LEN = 2048; @Pointcut( "@annotation(org.springframework.web.bind.annotation.RequestMapping) ||" + "@annotation(org.springframework.web.bind.annotation.GetMapping) || " + "@annotation(org.springframework.web.bind.annotation.PostMapping) ||" + "@annotation(org.springframework.web.bind.annotation.PutMapping) || " + "@annotation(org.springframework.web.bind.annotation.DeleteMapping) ") public void pointCut(a) {}@Around("pointCut()") public Object logRequest(ProceedingJoinPoint joinPoint) throws Throwable { StopWatch stopWatch = new StopWatch(); stopWatch.start(UUID.randomUUID().toString()); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); RequestLog requestLog = initLog(); requestLog.setModule(method.getDeclaringClass().getName()); requestLog.setOperation(method.getName()); requestLog.setArgs(getArgs(joinPoint)); Object result = null; try { result = joinPoint.proceed(); return result; } catch (Exception exception) { requestLog.setSuccess(false); String exceptionStr = exception.toString(); result = exceptionStr.length() > MAX_EXCEPTION_LEN ? exceptionStr.substring(0, MAX_EXCEPTION_LEN) : exceptionStr; throw exception; } finally { stopWatch.stop(); requestLog.setDuration(stopWatch.getTotalTimeMillis()); requestLog.setResult(result); publisher.publishEvent(newRequestLogEvent(requestLog)); }}private String getArgs(ProceedingJoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); String[] names = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); Map<String, Object> argMap = new HashMap<>(); if (Objects.nonNull(args) && args.length > 0) { for (int i = 0, len = args.length; i < len; i++) { Object arg = args[i]; // Request response parameters are not processed if (arg instanceof ModelAndView || arg instanceof HttpServletRequest || arg instanceof HttpServletResponse || (arg instanceof MultipartFile) || (arg instanceof MultipartFile[])) { continue; } argMap.put(names[i], arg); } } String argStr = ""; try { if (argMap.size() > 0) { argStr = objectMapper.writeValueAsString(argMap); }}catch (Exception ex) { } return argStr; } private RequestLog initLog(a) { RequestLog requestLog = new RequestLog(); requestLog.setId(UUID.randomUUID().toString()); requestLog.setCreatedAt(LocalDateTime.now()); requestLog.setSuccess(true); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); requestLog.setUserAgent(request.getHeader("user-agent")); requestLog.setIp(getIp(request)); requestLog.setUrl(request.getRequestURI()); requestLog.setMethod(request.getMethod()); // log.setCreatedBy(); TODO:Get the user ID from the context return requestLog; } private String getIp(HttpServletRequest request) { String ipAddress = null; try { 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(); if (Objects.equals("127.0.0.1", ipAddress)) { // Obtain the local IP address based on the network adapter InetAddress inet = null; try { inet = InetAddress.getLocalHost(); } catch(UnknownHostException e) { e.printStackTrace(); } ipAddress = inet.getHostAddress(); }}// In the case of multiple proxies, the first IP address is the real IP address of the client, and multiple IP addresses are separated by ',' if(ipAddress ! =null && ipAddress.length() > 15) { / / = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); }}}catch (Exception e) { ipAddress = ""; } returnipAddress; }}Copy the code
Event subscription
-
An event definition
@AllArgsConstructor @Data public class RequestLogEvent implements Serializable { private RequestLog requestLog; } Copy the code
-
Event subscription
@Slf4j @Component public class RequestLogListener { // To use @async, enable the @enableAsync annotation @Async @EventListener(RequestLogEvent.class) public void logRequest(RequestLogEvent event) { RequestLog requestLog = event.getRequestLog(); // TODO:It can be sent to message queues, saved to databases, and so on log.info("requestLog {}", requestLog); }}Copy the code
At the end
If this article is helpful to you, please like 👍🏻 to support it. If there is any mistake or better suggestion, welcome to correct, thank you.