概述
组件Feign自身的日志是debug级别并且日志行数太多,不符合我们精简日志的要求,故自己实现日志切面。
import com.alibaba.fastjson.JSON;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
@Aspect
@Slf4j
public class LogAspect {
private final String PARENT_HTTP_REQUEST_TRACKING_ID = "B2cParentHttpRequestTrackingId";
//private final String CURRENT_REQUEST_TRACKING_ID = "B2cCurrentRequestTrackingId";
private final Integer MAX_LENGTH = 1500;
@Pointcut("execution(* com.sf.pharma..start..*Controller.*(..))")
public void controllerPointCut() {
}
@Around("controllerPointCut()")
public Object controllerAround(ProceedingJoinPoint pjp) throws Throwable {
long startTimeMillis = System.currentTimeMillis();
//返回切入点的签名
Signature sig = pjp.getSignature();
if (!(sig instanceof MethodSignature)) {
log.error("该日志切面只能用于方法!");
return pjp.proceed();
}
//获取全类名和方法名
String className = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
//
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest httpRequest = servletRequestAttributes.getRequest();
if (this.isBlackListUrl(httpRequest.getRequestURI())) {
return pjp.proceed();
}
//Controller类的方法请求参数全部打印,不截取。
Object resultObj = null;
String errorMsg = null;
String parentRequestId = UUID.randomUUID().toString();
String currentRequestId = UUID.randomUUID().toString();
try {
//简单请求链路跟踪
httpRequest.setAttribute(PARENT_HTTP_REQUEST_TRACKING_ID, parentRequestId);
StringBuilder reqContentSb = new StringBuilder();
reqContentSb.append("RequestId:").append(currentRequestId)
.append(", ").append("ParentRequestId:").append(parentRequestId)
.append(", Request-").append(httpRequest.getMethod()).append("-Url:").append(httpRequest.getRequestURI())
.append(", ").append("RequestClass:").append(className)
.append(", ").append("RequestMethod:").append(methodName)
.append(", ").append("RequestHeadParams:").append(StringUtil.subStr(buildReqHeaderLog(httpRequest), MAX_LENGTH))
.append(", ").append("RequestParams:").append(buildReqParamsLog(pjp));
log.info(reqContentSb.toString());
resultObj = pjp.proceed();
} catch (Throwable t) {
errorMsg = t.toString();
throw t;
} finally {
StringBuilder respContentSb = new StringBuilder();
respContentSb.append("ResponseId:").append(currentRequestId)
.append(", ").append("ParentResponseId:").append(parentRequestId)
.append(", ").append("ResponseTime:").append(System.currentTimeMillis() - startTimeMillis).append("ms")
.append(", ").append("ResponseResult:").append(StringUtil.subStr(JSON.toJSONString(resultObj), MAX_LENGTH));
if (StringUtils.isNotBlank(errorMsg)) {
respContentSb.append(", ").append("ResponseErrorMsg:").append(errorMsg);
}
log.info(respContentSb.toString());
}
return resultObj;
}
/**
* 这个切面粒度太粗,会把调用下游接口的日志也打印出来。
*/
@Pointcut("execution(* com.sf.pharma..api.facade..*Facade.*(..))")
public void feignPointCut() {
}
@Around("feignPointCut()")
public Object feignAround(ProceedingJoinPoint pjp) throws Throwable {
long startTimeMillis = System.currentTimeMillis();
//返回切入点的签名
Signature sig = pjp.getSignature();
if (!(sig instanceof MethodSignature)) {
log.error("该日志切面只能用于方法!");
return pjp.proceed();
}
//获取全类名和方法名
String className = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
//
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
HttpServletRequest httpRequest = servletRequestAttributes.getRequest();
if (this.isBlackListUrl(httpRequest.getRequestURI())) {
return pjp.proceed();
}
//Feign类的方法请求参数太长时截取。
Object resultObj = null;
String errorMsg = null;
String parentRequestId = null;
String currentRequestId = UUID.randomUUID().toString();
try {
//简单请求链路跟踪
parentRequestId = (String) httpRequest.getAttribute(PARENT_HTTP_REQUEST_TRACKING_ID);
if (StringUtil.isEmpty(parentRequestId)) {
parentRequestId = UUID.randomUUID().toString();
httpRequest.setAttribute(PARENT_HTTP_REQUEST_TRACKING_ID, parentRequestId);
}
StringBuilder reqContentSb = new StringBuilder();
reqContentSb.append("RequestId:").append(currentRequestId)
.append(", ").append("ParentRequestId:").append(parentRequestId)
.append(", ").append("RequestClass:").append(className)
.append(", ").append("RequestMethod:").append(methodName)
.append(", ").append("RequestParams:").append(StringUtil.subStr(buildReqParamsLog(pjp), MAX_LENGTH));
log.info(reqContentSb.toString());
resultObj = pjp.proceed();
} catch (Throwable t) {
errorMsg = t.toString();
throw t;
} finally {
StringBuilder respContentSb = new StringBuilder();
respContentSb.append("ResponseId:").append(currentRequestId)
.append(", ").append("ParentResponseId:").append(parentRequestId)
.append(", ").append("ResponseTime:").append(System.currentTimeMillis() - startTimeMillis).append("ms")
.append(", ").append("ResponseResult:").append(StringUtil.subStr(JSON.toJSONString(resultObj), MAX_LENGTH));
if (StringUtils.isNotBlank(errorMsg)) {
respContentSb.append(", ").append("ResponseErrorMsg:").append(errorMsg);
}
log.info(respContentSb.toString());
}
return resultObj;
}
private boolean isBlackListUrl(String requestURI) {
//一些url无需打印日志,可以排除一部分。
String[] blackAry = new String[]{"/glhealthcheck/status/1", "/glhealthcheck/status/0"};
for (int i = 0; i < blackAry.length; i++) {
if (requestURI.endsWith(blackAry[i])) {
return true;
}
}
return false;
}
private String buildReqParamsLog(ProceedingJoinPoint pjp) {
//返回被通知方法参数列表
Object[] args = pjp.getArgs();
Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.asList(args).stream();
//序列化时过滤掉request和response
List<Object> reqArgs = stream
.filter(arg -> (!(arg instanceof HttpServletRequest)
&& !(arg instanceof HttpServletResponse)
&& !(arg instanceof MultipartFile)))
.collect(Collectors.toList());
return JSON.toJSONString(reqArgs);
}
private String buildReqHeaderLog(HttpServletRequest httpRequest) {
HashMap headerMap = new HashMap(16);
Enumeration<String> headerNames = httpRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerKey = headerNames.nextElement();
String headerValue = httpRequest.getHeader(headerKey);
//一些head参数对我们无用,可以排除一部分。
//保留"clientId"、"Content-Length"、"Content-Type"、"Cookie"、"User-Agent"、"Host", "origin", "referer"、
//保留"x-real-ip"、"x-forwarded-for"、
String[] blackAry = new String[]{"x-cat-trace", "x-rontgen", "x-service-chain", "postman-token",
"sec-ch-ua", "sec-ch-ua-mobile", "sec-ch-ua-platform", "sec-fetch-dest", "sec-fetch-mode", "sec-fetch-site",
"accept", "accept-encoding", "accept-language", "connection", "cache-control", "scope"};
List<String> blackList = Arrays.asList(blackAry);
if (blackList.contains(headerKey)) {
continue;
}
headerMap.put(headerKey, headerValue);
}
return JSON.toJSONString(headerMap);
}
}
最后
以上就是要减肥蜗牛为你收集整理的Springcloud中Controller和Feign的日志切面实现的全部内容,希望文章能够帮你解决Springcloud中Controller和Feign的日志切面实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复