使用dubbo分佈式框架進行微服務的開發,一個大系統每每會被拆分紅不少不一樣的子系統,而且子系統還會部署多臺機器,當其中一個系統出問題了,查看日誌十分麻煩。php
因此須要一個固定的流程ID和機器ip地址等來把全部的日誌進行染色處理,固然能夠經過調用其餘接口時參數進行傳遞,可是這樣子對代碼的耦合性太強,對代碼有侵入性。apache
咱們能夠經過dubbo的filter 結合slf4j的MDC或者log4j2的ThreadContext的進行參數的注入,能夠直接在日誌文件中配置被注入的參數,這樣就對系統和日誌id打印進行了解耦。服務器
其中當用logback日誌的時候是須要調用MDC的方法,而log4j2則須要調用ThreadContext的方法。session
下面的例子是使用slf4j的日誌模式:多線程
1.上游系統調用下游系統和下游系統接收上游系統定義兩個filter框架
ProviderRpcTraceFilterdom
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 extends 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 extends 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()方法來獲取上游系統傳遞下來的值ide
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
9.最後經過Kibana對日誌進行收集歸總,使用trace_id篩選,快速定位到錯誤位置。
注意:
1. dubbo應用同時擔任provider、consumer時,RpcTraceFilter 不能合併成一個類,必須分開。
2.多線程的狀況下,會取不到這個ID,須要作處理,好比將建立線程的時候將id經過參數傳入(見:Dubbo分佈式日誌追蹤,多線程不能獲取竄ID和IP問題)
PS:
https://blog.csdn.net/qq_20641565/article/details/78627202
https://blog.csdn.net/qq_20641565/article/details/78628115
https://www.jianshu.com/p/3dca4aeb6edd
https://blog.csdn.net/weixin_39178876/article/details/85088410
https://blog.csdn.net/xiaolyuh123/article/details/80560662