Dubbo 2.7 分布式日志 traceId MDC传递

前置条件

本项目使用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%npattern>
            
            <charset>utf-8charset>
        encoder>

3、下游dubbo项目增加拦截器获取传递的MDC值

1、项目的resources/增加 /META-INF/dubbo/internal 新增文件 org.apache.dubbo.rpc.Filter
Dubbo 2.7 分布式日志 traceId MDC传递_第1张图片
文件内容写上拦截器所在的包名和类,根据实际情况修改

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信息
Dubbo 2.7 分布式日志 traceId MDC传递_第2张图片
服务提供者日志,也能获取传递过来的MDC信息
Dubbo 2.7 分布式日志 traceId MDC传递_第3张图片

你可能感兴趣的:(技术分享,dubbo,分布式,java)