前言
在之前的一篇文章中,我写了一个aop日志打印的功能,通过注解的实现,可以让接口直接打印出入参和出参,并且在日志上添加uuid。因为当服务器的请求很多的时候,会发现日志都挤在一起,自己都不知道这个日志信息对应的调用链路的情况了。
相关文章:spring cloud脚手架项目(五)日志模块之出入参数和日志链路追踪UUID
2 实现效果
上一篇文章的实现效果
[430c8ee7-6d34-4414-8ccc-9042c4cc340c][2020-10-23 22:14:24.948][http-nio-8761-exec-5][INFO ][com.chen.common.logAop.ParamsLog2Aspect]:ClassMethod:com.chen.core.service.impl.CommonServiceImpl.test15,RequestArgs:["111"]
[430c8ee7-6d34-4414-8ccc-9042c4cc340c][2020-10-23 22:14:24.959][http-nio-8761-exec-5][INFO ][c.c.core.service.impl.CommonServiceImpl]:resultTest:{"code":"200","msg":"成功","data":"hello"}
[430c8ee7-6d34-4414-8ccc-9042c4cc340c][2020-10-23 22:14:24.960][http-nio-8761-exec-5][INFO ][com.chen.common.logAop.ParamsLog2Aspect]:ClassMethod:com.chen.core.service.impl.CommonServiceImpl.test15,ResponseArgs:{"code":"200","msg":"成功","data":"success"},Time-Consuming:12ms
应用A代码
//A应用调用一个B应用远程Feign接口,并且打印出结果值,远程接口很简单,也是一个返回成功的意思
@ParamsLog2(logTrace=true)
public Result<String> test15(String name) {
ResultTest<String> resultTest = chenTestService.test1();
log.info("resultTest:{}",new Gson().toJson(resultTest));
return Result.success("success");
}
被调用的B应用代码
@Override
public ResultTest<String> test1() {
log.info("test1");
return ResultTest.success("hello");
}
优化以及改进
那么现在有一个问题。当我们的整个系统是微服务的情况下。我A应用的日志是找到了。但是调用B应用的时候要怎么这个B应用的这个请求就是我这个A应用请求进来的,那么应该怎么办呢?
那么方案就是使用request的head头。在A应用请求进来的时候,产生一个UUID,然后把这个UUID放入head头中再传给B应用。B应用再获得这个head头中的UUID,以此反复。
至于线程中实现打印UUID,使用的是MCD这个功能
4 实现代码
WebConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor);
}
}
LogInterceptor
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Component
@Slf4j
public class LogInterceptor extends HandlerInterceptorAdapter {
private static final String HEAD_ID = "HEAD_ID";
private static final String REQUEST_HEAD_ID = "head_id";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String headId = request.getHeader(REQUEST_HEAD_ID);
log.info("preHandle before headId:{}",headId);
if (headId == null) {
headId = UUID.randomUUID().toString();
request.setAttribute(REQUEST_HEAD_ID,headId);
}
if (StringUtils.isEmpty(MDC.get(HEAD_ID))) {
MDC.put(HEAD_ID, headId);
}
log.info("preHandle after headId:{}",headId);
return true;
}
}
FeignTrackInterceptor
import com.google.gson.Gson;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Slf4j
public class FeignTrackInterceptor implements RequestInterceptor {
private static final String HEAD_ID = "head_id";
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
log.info("request:{}", new Gson().toJson(request.getHeader(HEAD_ID)));
log.info("feign:{}", new Gson().toJson(requestTemplate.header(HEAD_ID)));
String headId = request.getHeader(HEAD_ID);
if(headId==null){
headId = (String)request.getAttribute(HEAD_ID);
}
requestTemplate.header(HEAD_ID, headId);
}
}
FeignSupportConfig
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
**/
@Configuration
public class FeignSupportConfig {
/**
* feign请求拦截器
*
* @return
*/
@Bean
public RequestInterceptor requestInterceptor() {
return new FeignTrackInterceptor();
}
}
结果
A应用日志
[][][2020-10-23 22:59:11.594][http-nio-8761-exec-8][INFO ][com.chen.core.interceptor.LogInterceptor]:preHandle before headId:null
[5bb3322c-34ff-4dc2-b605-02bd68b36790][][2020-10-23 22:59:11.594][http-nio-8761-exec-8][INFO ][com.chen.core.interceptor.LogInterceptor]:preHandle after headId:5bb3322c-34ff-4dc2-b605-02bd68b36790
[5bb3322c-34ff-4dc2-b605-02bd68b36790][4176a364-2369-4ef9-83c6-42e979d8cdfd][2020-10-23 22:59:11.595][http-nio-8761-exec-8][INFO ][com.chen.common.logAop.ParamsLog2Aspect]:ClassMethod:com.chen.core.service.impl.CommonServiceImpl.test15,RequestArgs:["222"]
[5bb3322c-34ff-4dc2-b605-02bd68b36790][4176a364-2369-4ef9-83c6-42e979d8cdfd][2020-10-23 22:59:11.595][http-nio-8761-exec-8][INFO ][c.c.c.interceptor.FeignTrackInterceptor]:request:null
[5bb3322c-34ff-4dc2-b605-02bd68b36790][4176a364-2369-4ef9-83c6-42e979d8cdfd][2020-10-23 22:59:11.595][http-nio-8761-exec-8][INFO ][c.c.c.interceptor.FeignTrackInterceptor]:feign:{"queries":{},"headers":{"head_id":[]},"method":"GET","url":"/hi/test1","decodeSlash":true}
[5bb3322c-34ff-4dc2-b605-02bd68b36790][4176a364-2369-4ef9-83c6-42e979d8cdfd][2020-10-23 22:59:11.606][http-nio-8761-exec-8][INFO ][c.c.core.service.impl.CommonServiceImpl]:resultTest:{"code":"200","msg":"成功","data":"hello"}
[5bb3322c-34ff-4dc2-b605-02bd68b36790][4176a364-2369-4ef9-83c6-42e979d8cdfd][2020-10-23 22:59:11.606][http-nio-8761-exec-8][INFO ][com.chen.common.logAop.ParamsLog2Aspect]:ClassMethod:com.chen.core.service.impl.CommonServiceImpl.test15,ResponseArgs:{"code":"200","msg":"成功","data":"success"},Time-Consuming:12ms
B应用日志
[][][2020-10-23 22:59:11.603][http-nio-8762-exec-3][INFO ][com.chen.hi.interceptor.LogInterceptor]:preHandle before headId:5bb3322c-34ff-4dc2-b605-02bd68b36790
[5bb3322c-34ff-4dc2-b605-02bd68b36790][][2020-10-23 22:59:11.603][http-nio-8762-exec-3][INFO ][com.chen.hi.interceptor.LogInterceptor]:preHandle after headId:5bb3322c-34ff-4dc2-b605-02bd68b36790
[5bb3322c-34ff-4dc2-b605-02bd68b36790][][2020-10-23 22:59:11.603][http-nio-8762-exec-3][INFO ][c.c.hi.service.impl.ChenTestService2Impl]:test1
至于请求还是上面的实现代码。
可以看到A应用产生了一个UUID为5bb3322c-34ff-4dc2-b605-02bd68b36790的调用链
然后B应用获得到了A应用产生的这个UUID。
logback配置
MCD相关代码
//MCD设置参数HEAD_ID
MDC.put(HEAD_ID, headId);
logback配置
<property name="FILE_LOG_PATTERN" value="[%X{HEAD_ID}][%X{TRACE_ID}][%d{yyyy-MM-dd HH:mm:ss.SSS}][%thread][%-5level][%logger{40}]:%msg%n"/>
%X{HEAD_ID}就是在MCD中传入的参数,然后会在日志格式中打印出来