Merge remote-tracking branch 'origin/test'

This commit is contained in:
周敏 2025-01-09 21:55:33 +08:00
commit f5250beda8
4 changed files with 163 additions and 4 deletions

View File

@ -2,6 +2,7 @@ package cn.axzo.foundation.util;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC; import org.slf4j.MDC;
@UtilityClass @UtilityClass
@ -12,27 +13,35 @@ public class TraceUtils {
*/ */
public static final String TRACE_ID_IN_MDC = "TraceId"; public static final String TRACE_ID_IN_MDC = "TraceId";
/**
* 老服务用的是 ctxLogId这里兼容一下老服务
*/
public static final String TRACE_ID_CTX_LOG_ID = "ctxLogId";
public String getOrCreateTraceId() { public String getOrCreateTraceId() {
String res = MDC.get(TRACE_ID); String res = getTraceId();
if (Strings.isNullOrEmpty(res)) { if (Strings.isNullOrEmpty(res)) {
res = UUIDBuilder.generateShortUuid(); res = UUIDBuilder.generateShortUuid();
MDC.put(TRACE_ID, res); MDC.put(TRACE_ID, res);
MDC.put(TRACE_ID_IN_MDC, res); MDC.put(TRACE_ID_IN_MDC, res);
MDC.put(TRACE_ID_CTX_LOG_ID, res);
} }
return res; return res;
} }
public String getTraceId() { public String getTraceId() {
return MDC.get(TRACE_ID); return StringUtils.firstNonBlank(MDC.get(TRACE_ID_CTX_LOG_ID), MDC.get(TRACE_ID));
} }
public void putTraceId(String traceId) { public void putTraceId(String traceId) {
MDC.put(TRACE_ID, traceId); MDC.put(TRACE_ID, traceId);
MDC.put(TRACE_ID_IN_MDC, traceId); MDC.put(TRACE_ID_IN_MDC, traceId);
MDC.put(TRACE_ID_CTX_LOG_ID, traceId);
} }
public void removeTraceId() { public void removeTraceId() {
MDC.remove(TRACE_ID); MDC.remove(TRACE_ID);
MDC.remove(TRACE_ID_IN_MDC); MDC.remove(TRACE_ID_IN_MDC);
MDC.remove(TRACE_ID_CTX_LOG_ID);
} }
} }

View File

@ -2,6 +2,7 @@ package cn.axzo.foundation.web.support.interceptors;
import cn.axzo.foundation.util.TraceUtils; import cn.axzo.foundation.util.TraceUtils;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -14,7 +15,7 @@ public class TraceInterceptor implements HandlerInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String upstreamTraceId = request.getHeader(TraceUtils.TRACE_ID); String upstreamTraceId = StringUtils.firstNonBlank(request.getHeader(TraceUtils.TRACE_ID_CTX_LOG_ID), request.getHeader(TraceUtils.TRACE_ID));
if (!Strings.isNullOrEmpty(upstreamTraceId)) { if (!Strings.isNullOrEmpty(upstreamTraceId)) {
// 优先使用上游的 traceId // 优先使用上游的 traceId
TraceUtils.putTraceId(upstreamTraceId); TraceUtils.putTraceId(upstreamTraceId);
@ -22,6 +23,7 @@ public class TraceInterceptor implements HandlerInterceptor {
String traceId = TraceUtils.getOrCreateTraceId(); String traceId = TraceUtils.getOrCreateTraceId();
request.setAttribute(TraceUtils.TRACE_ID_IN_MDC, traceId); request.setAttribute(TraceUtils.TRACE_ID_IN_MDC, traceId);
request.setAttribute(TraceUtils.TRACE_ID_CTX_LOG_ID, traceId);
return true; return true;
} }

View File

@ -27,10 +27,14 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 统一打印日志(支持忽略此aop, 采用@IgnoreRequestLog注解) * 统一打印日志(支持忽略此aop, 采用@IgnoreRequestLog注解)<br>
* @Deprecated 改类不会处理 @RequestMapping没有在controller上定义的情况<br>
* 建议使用{@link RequestLogV2Aspect}
* @see RequestLogV2Aspect
**/ **/
@Aspect @Aspect
@Slf4j @Slf4j
@Deprecated
public class RequestLogAspect { public class RequestLogAspect {
private final static Integer DEFAULT_LOG_SIZE = 2048; private final static Integer DEFAULT_LOG_SIZE = 2048;
private Integer logSize; private Integer logSize;

View File

@ -0,0 +1,144 @@
package cn.axzo.foundation.web.support.log;
import cn.axzo.foundation.exception.BusinessException;
import cn.axzo.foundation.result.ApiResult;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableSet;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.springframework.core.annotation.AnnotationUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 统一打印日志(支持忽略此aop, 采用@IgnoreRequestLog注解)
**/
@Aspect
@Slf4j
public class RequestLogV2Aspect {
private final static Integer DEFAULT_LOG_SIZE = 2048;
private Integer logSize;
@Builder
public RequestLogV2Aspect(Integer logSize) {
this.logSize = Optional.ofNullable(logSize).orElse(DEFAULT_LOG_SIZE);
}
private static Set<Class<?>> EXCLUDE_CLASSES = ImmutableSet.of(ServletRequest.class, ServletResponse.class,
MultipartFile.class, BindingResult.class);
@Around("@within(restController)||@annotation(restController)")
public Object doAround(ProceedingJoinPoint joinPoint, RestController restController) throws Throwable {
//避免拦截非http请求. 如feign
if (RequestContextHolder.getRequestAttributes() == null) {
return joinPoint.proceed();
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (request != null) {
//用于在AbstractExceptionHandler中为dumpRequest, AbstractExceptionHandler无法直接获取body
request.setAttribute("params", this.getUserArgs(joinPoint.getArgs()));
}
IgnoreRequestLog annotation = AnnotationUtils.findAnnotation(((MethodSignature) joinPoint.getSignature()).getMethod(),
IgnoreRequestLog.class);
// 获取注解中的ignoreTypes
ImmutableSet<IgnoreRequestLog.IgnoreType> ignoreTypes = Optional.ofNullable(annotation)
.map(a -> ImmutableSet.copyOf(a.types())).orElse(ImmutableSet.of());
Stopwatch stopwatch = Stopwatch.createStarted();
String requestLog = buildRequestLog(joinPoint, ignoreTypes);
String userSession = StringUtils.EMPTY;
String employeeSession = StringUtils.EMPTY;
String appClient = StringUtils.EMPTY;
Object proceed;
try {
proceed = joinPoint.proceed(joinPoint.getArgs());
} catch (BusinessException e) {
//stein会收集error级别的异常并告警. 对BizException. 不期望收到告警邮件. 因此将BizException对应的日志级别调整为warn
log.warn("api log, process error, caught BizException, url = {}, params = {}, userSession = {}, employeeSession = {}, appClient = {}, time cost = {} ms, BizException:",
request.getRequestURI(), requestLog, userSession, employeeSession, appClient, stopwatch.elapsed(TimeUnit.MILLISECONDS), e);
throw e;
} catch (Exception e) {
log.error("api log, process error, uncaught exception, url = {}, params = {}, userSession = {}, employeeSession = {}, appClient = {}, time cost = {} ms, exception:",
request.getRequestURI(), requestLog, userSession, employeeSession, appClient, stopwatch.elapsed(TimeUnit.MILLISECONDS), e);
throw e;
} catch (Error error) {
log.error("api log, process error, uncaught error, url = {}, params = {}, userSession = {}, employeeSession = {}, appClient = {}, time cost = {} ms, error:",
request.getRequestURI(), requestLog, userSession, employeeSession, appClient, stopwatch.elapsed(TimeUnit.MILLISECONDS), error);
throw error;
}
// 如果该接口定义的类型不是SILENT的, 则打印日志
if (!ignoreTypes.contains(IgnoreRequestLog.IgnoreType.ALL)) {
String responseLog;
try {
responseLog = buildResponseLog(proceed, ignoreTypes);
} catch (Exception ex) {
log.error("api log, process error, resp serialize error, url = {}, params = {}, userSession = {}, employeeSession = {}, appClient = {}, time cost = {} ms, error:",
request.getRequestURI(), requestLog, userSession, employeeSession, appClient, stopwatch.elapsed(TimeUnit.MILLISECONDS), ex);
throw ex;
}
log.info("api log, url = {}, params = {}, userSession = {}, employeeSession = {}, appClient = {}, result = {}, time cost = {} ms",
request.getRequestURI(),
StringUtils.left(requestLog, logSize),
userSession,
employeeSession,
appClient,
responseLog,
stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
return proceed;
}
private String buildRequestLog(ProceedingJoinPoint joinPoint, Set<IgnoreRequestLog.IgnoreType> ignoreTypes) {
if (ignoreTypes.contains(IgnoreRequestLog.IgnoreType.REQUEST)) {
return "IGNORED";
}
return JSONObject.toJSONString(this.getUserArgs(joinPoint.getArgs()));
}
private String buildResponseLog(Object proceed, Set<IgnoreRequestLog.IgnoreType> ignoreTypes) {
if (proceed == null || !(ApiResult.class.isAssignableFrom(proceed.getClass()))) {
return StringUtils.EMPTY;
}
if (ignoreTypes.contains(IgnoreRequestLog.IgnoreType.RESPONSE)) {
return "IGNORED";
}
return StringUtils.left(JSONObject.toJSONString(proceed), logSize);
}
protected List<Object> getUserArgs(Object[] args) {
return Arrays.stream(args)
.filter((p) ->
Objects.nonNull(p) && EXCLUDE_CLASSES.stream().noneMatch(clz -> clz.isAssignableFrom(p.getClass()))
).collect(Collectors.toList());
}
}