网络请求链路:
client->gateway->serviceA->serviceB
实现办法
gateway接收请求初始化这个请求的request_id,转发请求到下游服务时携带request_id的请求头,serviceA接收到请求将request_id注入log MDC,需要调用其他服务时使用feignClient,配置转发上游服务发送过来的请求头。这样同一请求以request_id串联起来。
日志格式:
在各个服务添加aop拦截controller,将每个请求的url header param result记录下来,这样在查询时很容易定位问题。
效果:
相关代码:
@Configuration
public class FeignConfiguration {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
HttpServletRequest request = attrs.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
/**
* 遍历请求头里面的属性字段,将logId和token添加到新的请求头中转发到下游服务
* */
requestTemplate.header(name, value);
}
}
}
};
}
}
@Slf4j
@Aspect //定义一个切面
@Configuration
public class LogRecordAspect {
// 定义切点Pointcut
@Pointcut("execution(* com.xx.controller.*Controller.*(..))")
public void excudeService() {
}
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
long start = System.currentTimeMillis();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
Enumeration<String> headers = request.getHeaderNames();
StringBuffer headerStr = new StringBuffer();
while (headers.hasMoreElements()){
String headerName = headers.nextElement();
if(headerName.equals("uid")||headerName.equals("Authorization")){
headerStr.append(headerName).append(":").append(request.getHeader(headerName)).append(";");
}
}
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
long end = System.currentTimeMillis();
log.info("request url: {}, header : {}, method: {}, uri: {}, params: {},time : {} , result: {}", url,headerStr.toString(), method, uri, queryString,end-start, JSON.toJSONString(result));
return result;
}
}
@Configuration
public class FilterConfig {
@Autowired
private LogMDCFilter logMDCFilter;
@Bean
public FilterRegistrationBean<LogMDCFilter> logFilterRegistration() {
FilterRegistrationBean<LogMDCFilter> registration =
new FilterRegistrationBean<LogMDCFilter>();
// 注入过滤器
registration.setFilter(logMDCFilter);
// 拦截规则
registration.addUrlPatterns("/*");
// 过滤器名称
registration.setName(logMDCFilter.getClass().getName());
// 过滤器顺序
registration.setOrder(0);
return registration;
}
}
@Component
public class LogMDCFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request= (HttpServletRequest)servletRequest;
if(request.getHeader("request_id")!=null){
MDC.put("requestId", request.getHeader("request_id"));
}else{
MDC.put("requestId", UUID.randomUUID().toString().replace("-", ""));
}
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
MDC.remove("requestId");
}
}
@Override
public void destroy() {
}
}