¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * Copyright (c) 2018-2028, DreamLu All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * Neither the name of the dreamlu.net developer nor the names of its |
| | | * contributors may be used to endorse or promote products derived from |
| | | * this software without specific prior written permission. |
| | | * Author: DreamLu 墿¥æ¢¦ (596392912@qq.com) |
| | | */ |
| | | package org.springblade.core.log.aspect; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | 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.reflect.MethodSignature; |
| | | import org.springblade.core.launch.log.BladeLogLevel; |
| | | import org.springblade.core.log.props.BladeRequestLogProperties; |
| | | import org.springblade.core.tool.jackson.JsonUtil; |
| | | import org.springblade.core.tool.utils.ClassUtil; |
| | | import org.springblade.core.tool.utils.StringPool; |
| | | import org.springblade.core.tool.utils.StringUtil; |
| | | import org.springblade.core.tool.utils.WebUtil; |
| | | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; |
| | | import org.springframework.core.MethodParameter; |
| | | import org.springframework.core.io.InputStreamSource; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.context.request.WebRequest; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Method; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | /** |
| | | * Spring boot æ§å¶å¨ è¯·æ±æ¥å¿ï¼æ¹ä¾¿ä»£ç è°è¯ |
| | | * |
| | | * @author L.cm |
| | | */ |
| | | @Slf4j |
| | | @Aspect |
| | | @AutoConfiguration |
| | | @AllArgsConstructor |
| | | @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) |
| | | @ConditionalOnProperty(value = BladeLogLevel.REQ_LOG_PROPS_PREFIX + ".enabled", havingValue = "true", matchIfMissing = true) |
| | | public class RequestLogAspect { |
| | | |
| | | private final BladeRequestLogProperties properties; |
| | | |
| | | /** |
| | | * AOP ç¯å æ§å¶å¨ R è¿åå¼ |
| | | * |
| | | * @param point JoinPoint |
| | | * @return Object |
| | | * @throws Throwable å¼å¸¸ |
| | | */ |
| | | @Around( |
| | | "execution(!static org.springblade.core.tool.api.R *(..)) && " + |
| | | "(@within(org.springframework.stereotype.Controller) || " + |
| | | "@within(org.springframework.web.bind.annotation.RestController))" |
| | | ) |
| | | public Object aroundApi(ProceedingJoinPoint point) throws Throwable { |
| | | BladeLogLevel level = properties.getLevel(); |
| | | // 䏿尿¥å¿ï¼ç´æ¥è¿å |
| | | if (BladeLogLevel.NONE == level) { |
| | | return point.proceed(); |
| | | } |
| | | HttpServletRequest request = WebUtil.getRequest(); |
| | | String requestUrl = Objects.requireNonNull(request).getRequestURI(); |
| | | String requestMethod = request.getMethod(); |
| | | |
| | | // æå»ºæä¸æ¡é¿ æ¥å¿ï¼é¿å
å¹¶å䏿¥å¿éä¹± |
| | | StringBuilder beforeReqLog = new StringBuilder(300); |
| | | // æ¥å¿åæ° |
| | | List<Object> beforeReqArgs = new ArrayList<>(); |
| | | beforeReqLog.append("\n\n================ Request Start ================\n"); |
| | | // æå°è·¯ç± |
| | | beforeReqLog.append("===> {}: {}"); |
| | | beforeReqArgs.add(requestMethod); |
| | | beforeReqArgs.add(requestUrl); |
| | | // æå°è¯·æ±åæ° |
| | | logIngArgs(point, beforeReqLog, beforeReqArgs); |
| | | // æå°è¯·æ± headers |
| | | logIngHeaders(request, level, beforeReqLog, beforeReqArgs); |
| | | beforeReqLog.append("================ Request End ================\n"); |
| | | |
| | | // æå°æ§è¡æ¶é´ |
| | | long startNs = System.nanoTime(); |
| | | log.info(beforeReqLog.toString(), beforeReqArgs.toArray()); |
| | | // aop æ§è¡åçæ¥å¿ |
| | | StringBuilder afterReqLog = new StringBuilder(200); |
| | | // æ¥å¿åæ° |
| | | List<Object> afterReqArgs = new ArrayList<>(); |
| | | afterReqLog.append("\n\n=============== Response Start ================\n"); |
| | | try { |
| | | Object result = point.proceed(); |
| | | // æå°è¿åç»æä½ |
| | | if (BladeLogLevel.BODY.lte(level)) { |
| | | afterReqLog.append("===Result=== {}\n"); |
| | | afterReqArgs.add(JsonUtil.toJson(result)); |
| | | } |
| | | return result; |
| | | } finally { |
| | | long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); |
| | | afterReqLog.append("<=== {}: {} ({} ms)\n"); |
| | | afterReqArgs.add(requestMethod); |
| | | afterReqArgs.add(requestUrl); |
| | | afterReqArgs.add(tookMs); |
| | | afterReqLog.append("=============== Response End ================\n"); |
| | | log.info(afterReqLog.toString(), afterReqArgs.toArray()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¿å±è¯·æ±åæ° |
| | | * |
| | | * @param point ProceedingJoinPoint |
| | | * @param beforeReqLog StringBuilder |
| | | * @param beforeReqArgs beforeReqArgs |
| | | */ |
| | | public void logIngArgs(ProceedingJoinPoint point, StringBuilder beforeReqLog, List<Object> beforeReqArgs) { |
| | | MethodSignature ms = (MethodSignature) point.getSignature(); |
| | | Method method = ms.getMethod(); |
| | | Object[] args = point.getArgs(); |
| | | // 请æ±åæ°å¤ç |
| | | final Map<String, Object> paraMap = new HashMap<>(16); |
| | | // 䏿¬¡è¯·æ±åªè½æä¸ä¸ª request body |
| | | Object requestBodyValue = null; |
| | | for (int i = 0; i < args.length; i++) { |
| | | // è¯»åæ¹æ³åæ° |
| | | MethodParameter methodParam = ClassUtil.getMethodParameter(method, i); |
| | | // PathVariable åæ°è·³è¿ |
| | | PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class); |
| | | if (pathVariable != null) { |
| | | continue; |
| | | } |
| | | RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class); |
| | | String parameterName = methodParam.getParameterName(); |
| | | Object value = args[i]; |
| | | // 妿æ¯bodyçjsonåæ¯å¯¹è±¡ |
| | | if (requestBody != null) { |
| | | requestBodyValue = value; |
| | | continue; |
| | | } |
| | | // å¤ç åæ° |
| | | if (value instanceof HttpServletRequest) { |
| | | paraMap.putAll(((HttpServletRequest) value).getParameterMap()); |
| | | continue; |
| | | } else if (value instanceof WebRequest) { |
| | | paraMap.putAll(((WebRequest) value).getParameterMap()); |
| | | continue; |
| | | } else if (value instanceof HttpServletResponse) { |
| | | continue; |
| | | } else if (value instanceof MultipartFile) { |
| | | MultipartFile multipartFile = (MultipartFile) value; |
| | | String name = multipartFile.getName(); |
| | | String fileName = multipartFile.getOriginalFilename(); |
| | | paraMap.put(name, fileName); |
| | | continue; |
| | | } else if (value instanceof MultipartFile[]) { |
| | | MultipartFile[] arr = (MultipartFile[]) value; |
| | | if (arr.length == 0) { |
| | | continue; |
| | | } |
| | | String name = arr[0].getName(); |
| | | StringBuilder sb = new StringBuilder(arr.length); |
| | | for (MultipartFile multipartFile : arr) { |
| | | sb.append(multipartFile.getOriginalFilename()); |
| | | sb.append(StringPool.COMMA); |
| | | } |
| | | paraMap.put(name, StringUtil.removeSuffix(sb.toString(), StringPool.COMMA)); |
| | | continue; |
| | | } else if (value instanceof List) { |
| | | List<?> list = (List<?>) value; |
| | | AtomicBoolean isSkip = new AtomicBoolean(false); |
| | | for (Object o : list) { |
| | | if ("StandardMultipartFile".equalsIgnoreCase(o.getClass().getSimpleName())) { |
| | | isSkip.set(true); |
| | | break; |
| | | } |
| | | } |
| | | if (isSkip.get()) { |
| | | paraMap.put(parameterName, "æ¤åæ°ä¸è½åºåå为json"); |
| | | continue; |
| | | } |
| | | } |
| | | // åæ°å |
| | | RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class); |
| | | String paraName = parameterName; |
| | | if (requestParam != null && StringUtil.isNotBlank(requestParam.value())) { |
| | | paraName = requestParam.value(); |
| | | } |
| | | if (value == null) { |
| | | paraMap.put(paraName, null); |
| | | } else if (ClassUtil.isPrimitiveOrWrapper(value.getClass())) { |
| | | paraMap.put(paraName, value); |
| | | } else if (value instanceof InputStream) { |
| | | paraMap.put(paraName, "InputStream"); |
| | | } else if (value instanceof InputStreamSource) { |
| | | paraMap.put(paraName, "InputStreamSource"); |
| | | } else if (JsonUtil.canSerialize(value)) { |
| | | // å¤ææ¨¡åè½è¢« json åºååï¼åæ·»å |
| | | paraMap.put(paraName, value); |
| | | } else { |
| | | paraMap.put(paraName, "æ¤åæ°ä¸è½åºåå为json"); |
| | | } |
| | | } |
| | | // 请æ±åæ° |
| | | if (paraMap.isEmpty()) { |
| | | beforeReqLog.append("\n"); |
| | | } else { |
| | | beforeReqLog.append(" Parameters: {}\n"); |
| | | beforeReqArgs.add(JsonUtil.toJson(paraMap)); |
| | | } |
| | | if (requestBodyValue != null) { |
| | | beforeReqLog.append("====Body===== {}\n"); |
| | | beforeReqArgs.add(JsonUtil.toJson(requestBodyValue)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è®°å½è¯·æ±å¤´ |
| | | * |
| | | * @param request HttpServletRequest |
| | | * @param level æ¥å¿çº§å« |
| | | * @param beforeReqLog StringBuilder |
| | | * @param beforeReqArgs beforeReqArgs |
| | | */ |
| | | public void logIngHeaders(HttpServletRequest request, BladeLogLevel level, |
| | | StringBuilder beforeReqLog, List<Object> beforeReqArgs) { |
| | | // æå°è¯·æ±å¤´ |
| | | if (BladeLogLevel.HEADERS.lte(level)) { |
| | | Enumeration<String> headers = request.getHeaderNames(); |
| | | while (headers.hasMoreElements()) { |
| | | String headerName = headers.nextElement(); |
| | | String headerValue = request.getHeader(headerName); |
| | | beforeReqLog.append("===Headers=== {}: {}\n"); |
| | | beforeReqArgs.add(headerName); |
| | | beforeReqArgs.add(headerValue); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |