使用dubbo分布式框架進行微服務的開發,一個大系統往往會被拆分成很多不同的子系統,並且子系統還會部署多台機器,當其中一個系統出問題了,查看日志十分麻煩。
所以需要一個固定的流程ID和機器ip地址等來把所有的日志進行染色處理,當然可以通過調用其他接口時參數進行傳遞,但是這樣子對代碼的耦合性太強,對代碼有侵入性。
我們可以通過dubbo的filter 結合slf4j的MDC或者log4j2的ThreadContext的進行參數的注入,可以直接在日志文件中配置被注入的參數,這樣就對系統和日志id打印進行了解耦。
其中當用logback日志的時候是需要調用MDC的方法,而log4j2則需要調用ThreadContext的方法。
下面的例子是使用slf4j的日志模式:
1.上游系統調用下游系統和下游系統接收上游系統定義兩個filter
ProviderRpcTraceFilter(生產者)
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
/**
* 日志染色
* @author phpdragon
*/
@Activate(group = {Constants.PROVIDER},order = 1)
public class ProviderRpcTraceFilter implements Filter {
/**
*
* @param invoker
* @param invocation
* @return
* @throws RpcException
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String traceId = RpcContext.getContext().getAttachment("trace_id");
if (StringUtils.isBlank(traceId)) {
traceId = this.getUUID() ;
}
//設置日志traceId變量
MDC.put("traceId", traceId);
RpcContext.getContext().setAttachment("trace_id", traceId);
try{
return invoker.invoke(invocation);
}finally {
MDC.remove("traceId");
}
}
/**
* 獲取UUID
* @return String UUID
*/
public String getUUID(){
String uuid = UUID.randomUUID().toString();
//替換-字符
return uuid.replaceAll("-", "");
}
}
ConsumerRpcTraceFilter(消費者)
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
/**
* 日志染色ProviderRpcTraceFilter
* @author phpdragon
*/
@Activate(group = {Constants.CONSUMER})
public class ConsumerRpcTraceFilter implements Filter {
/**
*
* @param invoker
* @param invocation
* @return
* @throws RpcException
*/
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
String traceId = MDC.get("traceId");
if (StringUtils.isBlank(traceId)) {
traceId = this.getUUID() ;
}
RpcContext.getContext().setAttachment("trace_id", traceId);
return invoker.invoke(invocation);
}
/**
* 獲取UUID
* @return String UUID
*/
public String getUUID(){
String uuid = UUID.randomUUID().toString();
//替換-字符
return uuid.replaceAll("-", "");
}
}
2.下游系統被調用的時候可以通過dubbo中RpcContext.getAttachment()方法來獲取上游系統傳遞下來的值
String traceId = RpcContext.getContext().getAttachment("trace_id");
3.使用MDC來設置日志變量 %X{traceId}
MDC.put("traceId", traceId);
4.在方法調用完成后移除該ID
try{
return invoker.invoke(invocation);
}finally {
MDC.remove("traceId");
}
5.當上游系統調用下游系統的時候,可以通過dubbo中RpcContext.setAttachment()方法進行參數傳遞
RpcContext.getContext().setAttachment("trace_id", traceId);
6.然后在/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter (或者 com.apache.dubbo.rpc.Filter ) 文件中配置filter
providerRpcTraceFilter=com.xxx.xxx.filter.ProviderRpcTraceFilter consumerRpcTraceFilter=com.xxx.xxx.filter.ConsumerRpcTraceFilter

7.如果要打印服務器ip,使用com.alibaba.dubbo.common.utils.NetUtils工具獲取ip,然后put到MDC里面
String serverIp = NetUtils.getLocalHost()
MDC.put("serverId", serverIp);
8.設置logback.xml 的日志輸出格式
%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{serverId}] [%X{sessionId}] -%5p ${PID:-} [%15.15t] %-40.40logger{39} : %m%n
注意:
1. dubbo應用同時擔任provider、consumer時,RpcTraceFilter 不能合並成一個類,必須分開。
2.多線程的情況下,會取不到這個ID,需要做處理,比如將創建線程的時候將id通過參數傳入(見:https://blog.csdn.net/qq_20641565/article/details/78628115)
