上一篇文章 Spring aop+自定義註解統一記錄用戶行爲日誌 記錄了 web
層中經過自定義註解配合Spring aop
自動記錄用戶行爲日誌的過程。那麼按照分佈式架構中Dubbo
服務層的調用過程是否也能夠實現統一記錄日誌?自定義日誌攔截器能夠實現這個需求。html
在使用Dubbo
搭建的分佈式項目中,服務層代碼調用是這樣的:java
@GetMapping(value = "/info") 2 public BaseResult userInfo() { 3 //rpc遠程調用用戶服務 4 BaseResult result = mUserService.userInfo(); 6 return result; 7 }
這裏的用戶服務位於另一個服務進程,由服務提供者暴露出來,讓web
層遠程調用,須要記錄服務結果的調用過程,便於跟蹤定位bug
.web
翻看下Dubbo
官方文檔,能夠看到以下內容:spring
簡要說明:apache
Dubbo
中全部的攔截器所有繼承自org.apache.dubbo.rpc.Filter
接口,咱們本身也能夠自行擴展,只要繼承該接口便可.filter
默認在內置 filter
以後執行新增 DubboServiceFilter
攔截器以下:架構
public class DubboServiceFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(DubboServiceFilter.class); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { //外部日誌開關默認關閉 String logSwitch = StringUtils.equals(RedisUtil.get(BaseConstants.CACHE_SERVICE_LOG_SWITCH), BaseConstants.YES) ? BaseConstants.YES : BaseConstants.NO; if (StringUtils.equals(BaseConstants.YES, logSwitch)) { //打印入參日誌 DubboServiceRequest serviceRequest = new DubboServiceRequest(); serviceRequest.setInterfaceName(invocation.getInvoker().getInterface().getName()); serviceRequest.setMethodName(invocation.getMethodName()); serviceRequest.setArgs(invocation.getArguments()); LOGGER.info("dubbo服務接口入參: " + JSON.toJSONString(serviceRequest)); } //開始時間 long startTime = System.currentTimeMillis(); //執行接口調用邏輯 Result result = invoker.invoke(invocation); //調用耗時 long elapsed = System.currentTimeMillis() - startTime; //若是發生異常 則打印異常日誌 if (result.hasException() && invoker.getInterface() != GenericService.class) { LOGGER.error("dubbo執行異常: ", result.getException()); } else { if (StringUtils.equals(BaseConstants.YES, logSwitch)) { //打印響應日誌 DubboServiceResponse serviceResponse = new DubboServiceResponse(); serviceResponse.setMethodName(invocation.getMethodName()); serviceResponse.setInterfaceName(invocation.getInvoker().getInterface().getName()); serviceResponse.setArgs(invocation.getArguments()); serviceResponse.setResult(new Object[]{result.getValue()}); serviceResponse.setSpendTime(elapsed); LOGGER.info("dubbo服務響應成功,返回數據: " + JSON.toJSONString(serviceResponse)); } } //返回結果響應結果 return result; } }
代碼中對應的實體bean
以下:app
入參實體:分佈式
/** * @program: easywits * @description:Dubbo服務請求入參實體 * @author: zhangshaolin * @create: 2019-01-08 20:35 **/ @Data public class DubboServiceRequest implements Serializable{ private static final long serialVersionUID = 7127824956842786618L; /** * 接口名 */ private String interfaceName; /** * 方法名 */ private String methodName; /** * 參數 */ private Object[] args; }
響應實體:ide
/** * @program: easywits * @description: Dubbo服務響應結果實體 * @author: zhangshaolin * @create: 2019-01-08 20:36 **/ @Data public class DubboServiceResponse implements Serializable{ private static final long serialVersionUID = -2531169660859647737L; /** * 接口名 */ private String interfaceName; /** * 方法名 */ private String methodName; /** * 參數 */ private Object[] args; /** * 返回結果 */ private Object result; /** * 調用耗時(毫秒) */ private long spendTime; }
在/src/main/resources/META-INF/dubbo
目錄下新增純文本文件org.apache.dubbo.rpc.Filter
內容爲:spa
dubboServiceFilter=com.easywits.common.filter.DubboServiceFilter
DubboServiceFilter
攔截器的完整包名.最後在服務提供者配置文件中添加配置使攔截器生效:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" ...省略部分代碼"> <!--服務提供方應用信息,用於計算依賴關係--> <dubbo:application name="easywits-upms-rpc-service"/> <!--用dubbo協議在20881端口暴露服務--> <dubbo:protocol name="dubbo" port="20881" payload="52428800"/> <!--自定義服務層過濾器,值爲上述步驟文本文件中的鍵--> <dubbo:provider filter="dubboServiceFilter"/> ....省略部分服務配置 </beans>
抓一下咱們業務中的部分日誌信息看下效果,以下圖:
能夠清楚地看到Dubbo
服務接口調用的請求參數信息,以及最終的響應結果信息,便於定位線上問題。
參考文檔:http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html
記錄一個比較簡單的具體實用場景,後續會不按期更新更多的實用場景,歡迎關注公衆號【張少林同窗】!