前置条件
本项目使用springboot 2.5 logback日志打印,已引入slf4j包 dubbo版本是2.7.3
1、增加MVC AOP 设置MDC
package test;
import org.apache.dubbo.rpc.RpcContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
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 javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
@Aspect
@Component
@Order(-5)
public class ControllerInterceptor {
@Value("${server.port}")
private String port;
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义一个切入点.
* 解释下:
* ~ 第一个 * 代表任意修饰符及任意返回值.
* ~ 第二个 * 任意包名
* ~ 第三个 * 代表任意方法.
* ~ 第四个 * 定义在web包或者子包
* ~ 第五个 * 任意方法
* ~ .. 匹配任意数量的参数.
*/
@Pointcut("execution(public * com.main.handler..*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String traceId = String.valueOf(request.getSession().getId()).split("-")[0]+":"+Thread.currentThread().getId();
MDC.put("traceId",traceId);
RpcContext.getContext().setAttachment("traceId", traceId);
// 记录下请求内容
logger.info("**************%CONTROLLER%**************");
logger.info("访问地址 : " + request.getRequestURL().toString());
logger.info("调用类型 : " + request.getMethod());
logger.info("客户地址 : " + request.getRemoteAddr() + ":" + port);
logger.info("服务方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//获取所有参数方法一:
Enumeration<String> enu = request.getParameterNames();
StringBuilder params = new StringBuilder();
params.append("[");
while (enu.hasMoreElements()) {
String paraName = (String) enu.nextElement();
params.append(paraName + ": " + request.getParameter(paraName)).append(",");
}
params.append("]");
logger.info("参数列表 :" + params.toString());
logger.info("***************************************");
}
@AfterReturning("webLog()")
public void doAfterReturning(JoinPoint joinPoint) {
// 处理完请求,返回内容
}
}
关键作用是生成traceId并放入到MDC中,并且放入到dubbo拦截器上下文中。
String traceId = creatUUID(); //改为自己的规则
MDC.put(“traceId”,traceId);
RpcContext.getContext().setAttachment(“traceId”, traceId);
2、修改日志打印增加打印MDC
使用 %X{traceId} 获取刚刚设置的traceId值
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>MDC[%X{traceId}] | %d{yyyy-MM-dd HH:mm:ss.SSS} | %thread | %logger :%-3L| %msg%n</pattern>
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>-->
<charset>utf-8</charset>
</encoder>
3、下游dubbo项目增加拦截器获取传递的MDC值
1、项目的resources/增加 /META-INF/dubbo/internal 新增文件 org.apache.dubbo.rpc.Filter
文件内容写上拦截器所在的包名和类,根据实际情况修改
traceIdFilter=com.common.TraceIdFilter
2、新建一个类 TraceIdFilter
package com.common;
import ecan.netapp.util.CommonUtil;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.ListenableFilter;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.slf4j.MDC;
@Activate(group = {CommonConstants.PROVIDER,CommonConstants.CONSUMER})
public class TraceIdFilter extends ListenableFilter {
@Override
public Result invoke(org.apache.dubbo.rpc.Invoker<?> invoker, org.apache.dubbo.rpc.Invocation invocation) throws org.apache.dubbo.rpc.RpcException {
String traceId = RpcContext.getContext().getAttachment("traceId");
if ( !CommonUtil.isEmpty(traceId) ) {
// *) 从RpcContext里获取traceId并保存
TraceIdUtils.setTraceId(traceId);
MDC.put("traceId", traceId);
} else {
// *) 交互前重新设置traceId, 避免信息丢失
RpcContext.getContext().setAttachment("traceId", TraceIdUtils.getTraceId());
MDC.put("traceId", TraceIdUtils.getTraceId());
}
// *) 实际的rpc调用
return invoker.invoke(invocation);
}
}
附带工具类 TraceIdUtils
package com.common;
public class TraceIdUtils {
private static final ThreadLocal<String> traceIdCache
= new ThreadLocal<String>();
public static String getTraceId() {
return traceIdCache.get();
}
public static void setTraceId(String traceId) {
traceIdCache.set(traceId);
}
public static void clear() {
traceIdCache.remove();
}
}
最后同样修改一下本项目的logback配置文件增加mdc打印
4、效果
消费者项目日志,前面有MDC信息
服务提供者日志,也能获取传递过来的MDC信息