您的位置:首页 > 财经 > 产业 > 永久免费crm都有什么_苏州网站建设软件_网站网络营销公司_青岛seo整站优化哪家专业

永久免费crm都有什么_苏州网站建设软件_网站网络营销公司_青岛seo整站优化哪家专业

2024/12/23 2:04:41 来源:https://blog.csdn.net/wen262856298/article/details/144281758  浏览:    关键词:永久免费crm都有什么_苏州网站建设软件_网站网络营销公司_青岛seo整站优化哪家专业
永久免费crm都有什么_苏州网站建设软件_网站网络营销公司_青岛seo整站优化哪家专业

业务开发时经常需要打印出入参数据 方便出问题时定位问题,下面我给出两种实现方式,大家评判一下哪一个好

1、基于AOP实现

1.1 定义切面类

package com.cloud.infrastrure.common;import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;@Slf4j
@Aspect
@Component
@Order(Integer.MAX_VALUE)
public class LogAspect {private static final String LINE_SEPARATOR = System.lineSeparator();/*** 接口描述*/private String description = null;@Pointcut("within(com.cloud.interfaces.facade.*)")public void sportPoint() {// Do nothing because of X and Y.}@Pointcut("@annotation(org.springframework.web.bind.annotation.ExceptionHandler)")public void exceptionPointcut() {// Do nothing because of X and Y.}/*** 环绕*/@Around("within(com.cloud.interfaces.facade.*)")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {long startTime = System.currentTimeMillis();// 开始打印请求日志ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();// 获取 @Log 注解的参数信息getAspectLogAttribute(proceedingJoinPoint);// 打印请求相关参数log.info("========================================== Start ==========================================");// 打印请求 urllog.info("URL            : {}", request.getRequestURL().toString());// 打印描述信息log.info("Description    : {}", description);// 打印 Http methodlog.info("HTTP Method    : {}", request.getMethod());// 打印调用 controller 的全路径以及执行方法log.info("Class Method   : {}.{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), proceedingJoinPoint.getSignature().getName());// 打印请求的 IPlog.info("IP             : {}", request.getRemoteAddr());// 打印请求头
//        log.info("Request Headers   : {}", JSON.toJSONString(handlerHeaders(request)));// 打印请求入参log.info("Request Args   : {}", handlerParameter(proceedingJoinPoint));// 检查appId、appKeyObject result = proceedingJoinPoint.proceed();// 打印出参log.info("Response Args  : {}", JSON.toJSONString(result));// 执行耗时log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);// 接口结束后换行,方便分割查看log.info("=========================================== End ===========================================" + LINE_SEPARATOR);return result;}/*** 获取切面注解的描述** @param joinPoint 切点*/@SuppressWarnings("rawtypes")private void getAspectLogAttribute(JoinPoint joinPoint) throws ClassNotFoundException {String targetName = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();Object[] arguments = joinPoint.getArgs();Class targetClass = Class.forName(targetName);Method[] methods = targetClass.getMethods();for (Method method : methods) {if (method.getName().equals(methodName)) {Class[] clazz = method.getParameterTypes();if (clazz.length == arguments.length) {if (method.getAnnotation(ApiOperation.class) != null) {description = method.getAnnotation(ApiOperation.class).value();}break;}}}}private Map<String, String> handlerHeaders(HttpServletRequest request) {Map<String, String> headerMap = new HashMap<>();Enumeration<String> headerNames = request.getHeaderNames();if (Objects.nonNull(headerNames)) {while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = request.getHeader(headerName);headerMap.put(headerName, headerValue);}}return headerMap;}private String handlerParameter(JoinPoint point) {HashMap<String, Object> param = new HashMap<String, Object>(16);MethodSignature methodSignature = (MethodSignature) point.getSignature();String[] parameterNames = methodSignature.getParameterNames();Object[] args = point.getArgs();int i = 0;for (Object pojo : args) {//去掉空参数if (pojo == null || pojo instanceof HttpServletRequest|| pojo instanceof MultipartFile || pojo instanceof HttpServletResponse) {i++;continue;}param.put(parameterNames[i], pojo);i++;}String result = "";try {result = JSON.toJSONString(param);} catch (Exception e) {log.error("请求参数格式异常");}return result;}
}

编写一个Controller 

package com.cloud.interfaces.facade;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; 
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Slf4j
@Validated
@RestController
@RequestMapping("/test")
@Api(tags = "Test API")
public class TestController {@ApiOperation(value = "测试order", notes = "测试order")@PostMapping("/Order")public Result<Order> clearTrade(@RequestBody Order order) {return Result.ofSucceed(order);}

打印的日志信息

2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.haoran.cloud.pay.infrastrure.common.LogAspect  - ========================================== Start ==========================================
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.cloud.infrastrure.common.LogAspect  - URL            : http://localhost:32122/error-card/query
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.cloud.infrastrure.common.LogAspect  - Description    : null
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.cloud.infrastrure.common.LogAspect  - HTTP Method    : POST
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.cloud.infrastrure.common.LogAspect  - Class Method   : com.cloud.interfaces.facade.TradeErrorCardController.query
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.haoran.cloud.infrastrure.common.LogAspect  - IP             : 127.0.0.1
2024-12-06 09:12:48,838--[TID: N/A]-- [http-nio-32122-exec-1] INFO  com.cloud.infrastrure.common.LogAspect  - Request Args   : {"zoneId":"UTC"}

2、使用spring内置工具实现

打印入参使用 org.springframework.web.filter.CommonsRequestLoggingFilter

打印出参使用 OncePerRequestFilter 借助org.springframework.web.util.ContentCachingResponseWrapper;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CommonsRequestLoggingFilter;@Configuration
public class RequestLoggingConfig {@Beanpublic CommonsRequestLoggingFilter logFilter() {CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();filter.setIncludeQueryString(true);filter.setIncludePayload(true);filter.setIncludeHeaders(true);filter.setIncludeClientInfo(true);filter.setAfterMessagePrefix("AfterMessagePrefix DATA ");filter.setBeforeMessagePrefix("BeforeMessagePrefix DATA ");return filter;}
}

当然如果log4j框架 是log42j.xml 文件配置需要增加

  <Loggers><Logger additivity="true" name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="debug"><AppenderRef ref="Console"/></Logger></Loggers>

如果是logback日志框架

logging:level:org.springframework.web.filter.CommonsRequestLoggingFilter: DEBUG

出参打印实现 基于 OncePerRequestFilter 和 ContentCachingResponseWrapper 结合

package com.cloud.infrastrure.config;import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.IoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;@Component
@Slf4j
public class ResponseWrapperFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);byte[] requestBody = requestWrapper.getContentAsByteArray();Map<String, String[]> parameterMap = requestWrapper.getParameterMap();log.info(JSON.toJSONString(parameterMap));String contentType = request.getContentType();log.info("request contentType " + contentType);
//        ServletInputStream inputStream = requestWrapper.getInputStream();
// 这里不能获取 一旦获取 就不能再次读取了
//        String toString = IoUtils.toString(inputStream, "UTF-8");log.info("requestBody1 " + new String(requestBody));
//        log.info("requestBody2 " + toString);filterChain.doFilter(requestWrapper, responseWrapper);// 可以在这里处理响应数据byte[] responseBody = responseWrapper.getContentAsByteArray();log.info("responseBody " + new String(responseBody));// 处理body,例如添加签名responseWrapper.setHeader("X-Signature", "some-signature");// 必须调用此方法以将响应数据发送到客户端responseWrapper.copyBodyToResponse();}
}

3、回归总结

后续可以使用 springboot starter 来做 


import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.CommonsRequestLoggingFilter;import javax.annotation.PostConstruct;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;@Component
@Slf4j
@Order(2)
public class RequestLoggingFilter extends CommonsRequestLoggingFilter {@PostConstructpublic void init() {setIncludeQueryString(true);setIncludePayload(true);
//        setIncludeHeaders(true);
//        setIncludeClientInfo(true);setMaxPayloadLength(1024);}@Overrideprotected int getMaxPayloadLength() {return super.getMaxPayloadLength();}@Overrideprotected void afterRequest(HttpServletRequest request, String message) {if (request.getContentType().contains("application/json")) {log.info("client: {} url: {} requestBody {}", request.getRemoteAddr(), request.getRequestURL(), message);}}@Overrideprotected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {if (request.getRequestURI().startsWith("/actuator/health")) {return true;}if (request.getRequestURI().startsWith("/actuator/prometheus")) {return true;}return super.shouldNotFilter(request);}}

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
@Slf4j
@Order(1)
public class ResponseLoggingFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);filterChain.doFilter(request, responseWrapper);byte[] responseBody = responseWrapper.getContentAsByteArray();int contentSize = responseWrapper.getContentSize();if (responseWrapper.getContentType().contains("application/json")) {log.info("client: {} url: {} contentSize: {} responseBody {}", request.getRemoteAddr(), request.getRequestURL(), contentSize, new String(responseBody));}responseWrapper.copyBodyToResponse();}@Overrideprotected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {if (request.getRequestURI().startsWith("/actuator/health")) {return true;}if (request.getRequestURI().startsWith("/actuator/prometheus")) {return true;}return super.shouldNotFilter(request);}
}

4、日志内容压缩优化

对于那些出入参 contentSize 比较大的 比如一个列表查询结果 可能 有上万个字节

就需要对这些字节压缩 然后base64 encode  输出到日志 当我们遇到这类日志 就需要base64解码

具体优化后的类是


import com.common.CompressionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;@Component
@Slf4j
@Order(1)
public class ResponseLoggingFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);filterChain.doFilter(request, responseWrapper);byte[] responseBody = responseWrapper.getContentAsByteArray();int contentSize = responseWrapper.getContentSize();if (responseWrapper.getContentType().contains("application/json")) {if (contentSize > 1024) {byte[] compress = CompressionUtils.compress(responseBody);String message = Base64.getEncoder().encodeToString(compress);log.info("{} {} contentSize: {} compress {}", request.getMethod(), request.getRequestURI(), contentSize, message);} else {log.info("{} {} contentSize: {} {}", request.getMethod(), request.getRequestURI(), contentSize, new String(responseBody));}}responseWrapper.copyBodyToResponse();}@Overrideprotected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {if (request.getRequestURI().startsWith("/actuator/health")) {return true;}if (request.getRequestURI().startsWith("/actuator/prometheus")) {return true;}return super.shouldNotFilter(request);}
}

压缩工具是


import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.Inflater;@Slf4j
public class CompressionUtils {public static void main(String[] args) {compress("base64编码");}public static String compress(String encodeData) {String x = new String(decompress(Base64.getDecoder().decode(encodeData)));System.out.println(x);return x;}public static byte[] compress(byte[] data) {Deflater deflater = new Deflater();deflater.setInput(data);deflater.finish();byte[] buffer = new byte[1024];int compressedDataLength;try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(data.length)) {while (!deflater.finished()) {compressedDataLength = deflater.deflate(buffer);byteArrayOutputStream.write(buffer, 0, compressedDataLength);}return byteArrayOutputStream.toByteArray();} catch (IOException e) {log.error("compress failed", e);}return new byte[0];}public static byte[] decompress(byte[] data) {Inflater inflater = new Inflater();inflater.setInput(data);byte[] buffer = new byte[1024];int decompressedDataLength;try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(data.length)) {while (!inflater.finished()) {decompressedDataLength = inflater.inflate(buffer);byteArrayOutputStream.write(buffer, 0, decompressedDataLength);}return byteArrayOutputStream.toByteArray();} catch (IOException | java.util.zip.DataFormatException e) {log.error("decompress failed", e);}return new byte[0];}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com