前言

上一篇文章 Spring aop+自定义注解统一记录用户行为日志 记录了 web层中通过自定义注解配合Spring aop自动记录用户行为日志的过程。那么按照分布式架构中Dubbo服务层的调用过程是否也可以实现统一记录日志?自定义日志拦截器可以实现这个需求。

需求场景

在使用Dubbo搭建的分布式项目中,服务层代码调用是这样的:

1     @GetMapping(value = "/info")
22    public BaseResult userInfo() {
33        //rpc远程调用用户服务
44        BaseResult result = mUserService.userInfo();
56        return result;
67    }

这里的用户服务位于另外一个服务进程,由服务提供者暴露出来,让web层远程调用,需要记录服务结果的调用过程,便于跟踪定位bug.

自定义日志拦截器

翻看下Dubbo官方文档,可以看到如下内容:

简要说明:

  • Dubbo 中所有的拦截器全部继承自org.apache.dubbo.rpc.Filter接口,我们自己也可以自行扩展,只要继承该接口即可.
  • 用户自定义 filter 默认在内置 filter 之后执行

新增 DubboServiceFilter 拦截器如下:

 1public class DubboServiceFilter implements Filter {
2
3    private static final Logger LOGGER = LoggerFactory.getLogger(DubboServiceFilter.class);
4
5    @Override
6    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
7        //外部日志开关默认关闭
8        String logSwitch = StringUtils.equals(RedisUtil.get(BaseConstants.CACHE_SERVICE_LOG_SWITCH), BaseConstants.YES) ? BaseConstants.YES : BaseConstants.NO;
9        if (StringUtils.equals(BaseConstants.YES, logSwitch)) {
10            //打印入参日志
11            DubboServiceRequest serviceRequest = new DubboServiceRequest();
12            serviceRequest.setInterfaceName(invocation.getInvoker().getInterface().getName());
13            serviceRequest.setMethodName(invocation.getMethodName());
14            serviceRequest.setArgs(invocation.getArguments());
15            LOGGER.info("dubbo服务接口入参: " + JSON.toJSONString(serviceRequest));
16        }
17        //开始时间
18        long startTime = System.currentTimeMillis();
19        //执行接口调用逻辑
20        Result result = invoker.invoke(invocation);
21        //调用耗时
22        long elapsed = System.currentTimeMillis() - startTime;
23        //如果发生异常 则打印异常日志
24        if (result.hasException() && invoker.getInterface() != GenericService.class) {
25            LOGGER.error("dubbo执行异常: ", result.getException());
26        } else {
27            if (StringUtils.equals(BaseConstants.YES, logSwitch)) {
28                //打印响应日志
29                DubboServiceResponse serviceResponse = new DubboServiceResponse();
30                serviceResponse.setMethodName(invocation.getMethodName());
31                serviceResponse.setInterfaceName(invocation.getInvoker().getInterface().getName());
32                serviceResponse.setArgs(invocation.getArguments());
33                serviceResponse.setResult(new Object[]{result.getValue()});
34                serviceResponse.setSpendTime(elapsed);
35                LOGGER.info("dubbo服务响应成功,返回数据: " + JSON.toJSONString(serviceResponse));
36            }
37        }
38        //返回结果响应结果
39        return result;
40    }
41}

代码中对应的实体bean如下:

入参实体:

 1/**
2 * @program: easywits
3 * @description:Dubbo服务请求入参实体
4 * @author: zhangshaolin
5 * @create: 2019-01-08 20:35
6 **/
7@Data
8public class DubboServiceRequest implements Serializable{
9    private static final long serialVersionUID = 7127824956842786618L;
10
11    /**
12     * 接口名
13     */
14    private String interfaceName;
15
16    /**
17     * 方法名
18     */
19    private String methodName;
20
21    /**
22     * 参数
23     */
24    private Object[] args;
25}

响应实体:

 1/**
2 * @program: easywits
3 * @description: Dubbo服务响应结果实体
4 * @author: zhangshaolin
5 * @create: 2019-01-08 20:36
6 **/
7@Data
8public class DubboServiceResponse implements Serializable{
9    private static final long serialVersionUID = -2531169660859647737L;
10
11    /**
12     * 接口名
13     */
14    private String interfaceName;
15
16    /**
17     * 方法名
18     */
19    private String methodName;
20
21    /**
22     * 参数
23     */
24    private Object[] args;
25
26    /**
27     * 返回结果
28     */
29    private Object result;
30
31    /**
32     * 调用耗时(毫秒)
33     */
34    private long spendTime;
35}

/src/main/resources/META-INF/dubbo目录下新增纯文本文件org.apache.dubbo.rpc.Filter 内容为:

1dubboServiceFilter=com.easywits.common.filter.DubboServiceFilter
  • 键值对形式,键随便起个名字
  • 值为DubboServiceFilter拦截器的完整包名.

最后在服务提供者配置文件中添加配置使拦截器生效:

 1<?xml version="1.0" encoding="UTF-8"?>
2<beans xmlns="http://www.springframework.org/schema/beans"
3      ...省略部分代码">
4
5    <!--服务提供方应用信息,用于计算依赖关系-->
6    <dubbo:application name="easywits-upms-rpc-service"/>
7
8    <!--用dubbo协议在20881端口暴露服务-->
9    <dubbo:protocol name="dubbo" port="20881" payload="52428800"/>
10
11    <!--自定义服务层过滤器,值为上述步骤文本文件中的键-->
12    <dubbo:provider filter="dubboServiceFilter"/>
13
14    ....省略部分服务配置
15</beans>
16

验证结果

抓一下我们业务中的部分日志信息看下效果,如下图:

可以清楚地看到Dubbo服务接口调用的请求参数信息,以及最终的响应结果信息,便于定位线上问题。

参考文档:http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html

最后

记录一个比较简单的具体实用场景,后续会不定期更新更多的实用场景,欢迎关注公众号【张少林同学】!

Dubbo自定义日志拦截器的更多相关文章

  1. SpringMVC 自定义一个拦截器

    自定义一个拦截器方法,实现HandlerInterceptor方法 public class FirstInterceptor implements HandlerInterceptor{ /** * ...

  2. springmvc自定义的拦截器以及拦截器的配置

    一.自定义拦截器 Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口. 二.HandlerIn ...

  3. Spring自定义一个拦截器类SomeInterceptor,实现HandlerInterceptor接口及其方法的实例

    利用Spring的拦截器可以在处理器Controller方法执行前和后增加逻辑代码,了解拦截器中preHandle.postHandle和afterCompletion方法执行时机. 自定义一个拦截器 ...

  4. struts2-权限拦截器、日志拦截器、execAndWait(进度条)拦截器配置

    1.权限拦截器 package login; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.Serv ...

  5. Mybatis自定义SQL拦截器

    本博客介绍的是继承Mybatis提供的Interface接口,自定义拦截器,然后将项目中的sql拦截一下,打印到控制台. 先自定义一个拦截器 package com.muses.taoshop.com ...

  6. .net core Blazor+自定义日志提供器实现实时日志查看器

    场景 我们经常远程连接服务器去查看日志,比较麻烦,如果直接访问项目的某个页面就能实时查看日志就比较奈斯了,花了1天研究了下.net core 日志的原理,结合blazor实现了基本效果. 实现原理 自 ...

  7. .net core系列之《对AOP思想的理解及使用AspectCore实现自定义日志拦截》

    对于AOP这个名词,相信对于搞过MVC开发的人来说,都很熟悉,里面各种各样的Filter简直是将AOP体现到了极致. 那么什么是AOP呢? AOP(Aspect Oriented Programmin ...

  8. spring自定义注解拦截器的配置

    1.创建注解文件 (文件格式为注解) 这里面什么都不需要写 文件名就是注解名称 如下 是@anno package com.ABC123.anno; import java.lang.annotati ...

  9. struts2自定义Interceptor拦截器

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

随机推荐

  1. 2015年传智播客JavaEE 第168期就业班视频教程day45-ERP项目-0107-其他子系统

    一套ERP系统中一定会有CRM,不可能说我所有数据都是散着放的,你想用就随便写一个.你出去和人聊,一定得说我这里有什么有什么,然后你就可以和人说你做的是进销存.人家要问CRM或者说财务系统你就说那不是 ...

  2. redis的连接方法|连接池|操作

    1.先看下redis的连接 import redis # 连接服务端 r = redis.Redis(host="127.0.0.1",port=6379) #获取所有的key值 ...

  3. php模拟http请求

    在http简析中,我们提到了浏览器请求资源的一个流程,那么这个流程能不能用php来模拟呢?答案是肯定的. php模拟http请求需要实现以下步骤: 1.连接apache服务器 使用fsockopen: ...

  4. 线程并发线程安全介绍及java.util.concurrent包下类介绍

    线程Thread,在Java开发中多线程是必不可少的,但是真正能用好的并不多! 首先开启一个线程三种方式 ①new Thread(Runnable).start() ②thread.start(); ...

  5. 对于一个web工程,如果我们复制一个已有的工程粘贴到同一个workspace下,我们除了需要更改工程的名字还需要更改这个新工程的content root,否则会报错。

    对于一个web工程,如果我们复制一个已有的工程粘贴到同一个workspace下,我们除了需要更改工程的名字还需要更改这个新工程的content root,否则会报错.步骤如下: 右键新的工程---&g ...

  6. Linux enca命令

    一.简介 enca是Linux下的文件编码转换工具. 二.安装 http://dl.cihar.com/enca/   http://www.2cto.com/os/201404/295528.htm ...

  7. ubuntu 卡在登陆界面无法进入桌面,但是可以进入命令行界面

    ubuntu 卡在登陆界面无法进入桌面,但是可以进入命令行界面(初步断定是Xwindows界面软件出问题了,所以重装即可!)Solve: 1.Ctrl+Alt+F1进入命令行界面,root账户登陆2. ...

  8. python pip 代理设置

    pip install --proxy="user:password@server:port" packagename origin url: http://xiuxixiuxi. ...

  9. ResourceUtils读取properties文件

    注意: properties文件要放在classPath下面,也就是与src下. path.properties(在properties文件中\代表着没有完,下行同本行是一个内容) perfectMa ...

  10. 数据结构(c语言版)文摘

    第一章  绪论 数据结构:是一门研究非数值计算的程序设计问题中计算机的操作对象以及它们之间的关系和操作等的学科. 数据:是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并被计算机程序处理 ...