开发者学堂课程【全面讲解开源数据库中间件 MyCat 使用及原理(四):MyCat - 日志模块 - 微服务通过 AOP 记录日志】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/758/detail/13324
MyCat - 日志模块 - 微服务通过 AOP 记录日志
内容介绍:
一、AOP 记录日志
二、测试
上节开发完成了记录日志的接口,要去记录各个微服务中的信息,还需要各个微服务中通过 AOP 拦截用户的请求然后记录(组装)日志,然后通过 Feign 调用微服务中的接口来记录日志。接下来我们将完成这一步的操作。
一、AOP 记录日志
1.自定义注解
通过 AOP 来记录日志,对于该部分的操作,先要自定义一个注解,需要在微服务中执行,这里以 goods 为例。
比如要记录 goods 商品该日志服务中的信息,则需要在 cn.itcast.goods 中增加 AOP 日志的代码。
声明一个包 aop,然后在该包中要引入一个自定义注解。
代码如下:
@Inherited
@Documented
@Tar get (ElementType .METHOD)
@Retention(RetentionPolicy. RUNTIME)
public interface operatelog {
}
该自定义注解的作用是标识作用方法,如果被该注解标识的方法代表当前该方法需要记录日志。哪些方法需要记录日志则只需要在该方法上加 OperateLog。
比如,在之前的 DemoController 中,要想记录该日志只需在该类的方法前加注解 @ OperateLog,如果不想记录该方法的日志不加该注解即可。(哪个方法加了该注解哪个方法的所有操作日志都会被记录)
2.AOP 通知类
有了自定义注解后,还需有一个解析该注解的地方,解析注解会通过 AOP 对其进行解析。
这里需要定义一个 AOP 的新类(对于当前项目来说,目的是使用 MyCat 进行数据库的分库分表),这里不再写(已写好),部分代码如下:
根据提示导入需要导入的包。
但是还有 OperateLogFeign 客户端,在之前的操作流程中提到在各个流程中组装日志数据,最终要通过 Feign 调用日志微服务中的接口(记录日志属于日志微服务),既然要用到 Feign ,则我们要声明一个 Feign 远程调用的接口,所以接下来声明该远程接口。
该接口应该在 v_feign_api (管理调用的远程接口)中,先创建一个包 cn.itcast.feign.client,在该包中声明一个接口,叫 OperateLogFeign,然后在该接口中声明一个注解 FeignClient,通过该 FeignClient 指定微服务名称 log,再声明一个接口方法(这里参考 OperateLogController 中的方法),但是还需要改路径,在其前面添加 /operateLog(因为要通过 Feign 来指定哪个微服务中的哪个方法,调用该接口就知道要找的是哪个微服务,这里是 log 微服务,log 是微服务的名字,然后调用微服务中的哪个接口是通过路径得知的,这里的路径是 /operateLog/add)
代码如下:
import org.springframework.cloud.openfeign.Feignclient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@Feignclient ( "log")
public interface operateLogFeign {
@PostMapping (“/operateLog/add")
public Result add (@RequestBody Tboperatelog operatelog);
}
到此,将 Feign 接口声明完成,然后在 OperateAdvice.java 中引入即可。
引入后,对于当前的通知类就编写完成了。
3.简单解读该通知类:
@Around(“execution(*cn.itcast.goods.controller.*.*(..))&&@annotation(operateLog)”)
指定要拦截的是哪一个包,通过该切入点表达式进行指定(这里要拦截的是 cn.itcast.goods.controller),*.*代表拦截该包下所有类的所有方法,并且该方法上的注解 operateLog(如果该方法上没有该注解则不会拦截)。
方法内部逻辑:
拦截到该方法后,在该方法中组装日志信息,首先组装当前操作时间及哪个用户操作(由于没有做认证环节,这里直接写 10000),当前操作的是哪个类(类名通过 ProceedingJoinPoint 得到),指定操作的是哪个方法。然后通过 pjp.getArgs 可以得到该参数。
// 表示放行,通过 pjp 的proceed 方法(放行)
Object object =pjp.proceed();
例如,用户需要根据 id 查询 sku 的信息,通过拦截后可以得到参数然后放行(放行指执行原始方法),原始方法执行完成后,可以得到返回值,然后记录结束时间(部分代码如下图)
得到开始时间和结束时间就可以得到原始方法执行的耗时,然后将耗时也记录下来,得到 Object 意味得到了返回值(返回值类型,返回值),然后通过 Feign 去调用远程的微服务中的接口。
4.完整代码为:
@Around("execution ( cn.itcast.goods . controller.*.*(..)〉 && @annotation (operateLiog)")
public Object insertLogAround(ProceedingToinPoint pjp ,OperateLog operatsLog) throws Throwable {
System.out.println (“***********************************记录日志[start]*************************************”)
Tboperatelog op = new Tboperatelog();
DateFormatsdf=new SimpleDateFormat( pattern:"yyy-MM-dd HH : mm : ss"〕;
op.setOperateTire ( sdf.format ( new Date ()));
op-setOperateUser ( "10000") ;
op.setOperateclass(pjp.getTarget().getClass() .getName());
op.setOperateMethod(pjp-getSignature ().getName ( ) ;
string paramAndValue = "";
object [] args = pip.getArgs( );
if(aras != nul1){
for(Object arg:args){
if(arg instanceof String ||arg instanceof Integr=er||arg instanceof Long){
paramAndValue+=arg+”,”;
}else{
paramAndValue+= JSON.toJSONString(arg)+”,”;
}
}
op.setParamAndValue(paramAndValue);
}
long start_time=System.currentTimeMillis();
//旅行
Object object =pjp.proceed();
long end_time=System.currentTimeMillis();
op.setCostTime(end_time-start_time);
if(object!=null){
op.setReturnClass(object.getClass().getName());
op.setReturnValue(object.toString());
}else{
op.setReturnClass(“java.lang.Object”);
op.setReturnValue(“void”);
}
operatLogFeign.add(op);
//插入日志
system. out.println ( "******************************************
记录日志[end]****************************************”)
return object;
System.out.println(“”)
}
}
二、测试
在测试之前,因为用到了 Feign 的远程调用,需要在当前微服务中开启 Feign 这一块的支持,所以需要在 GoodsApplication.java 中加注解 ,如下:
@EnableFeignClients(basePackages=”cn.itcase.feign.client”)
接下来即可进行测试:
1.先启动 Eureka ,然后启动 log,最后启动 goods
log 和 goods 启动完成后,刷新 Eureka:
2.然后对日志记录流程进行测试:
(1)(绕过网关,网关没有启动)访问 DemoController
DemoController 代码:
@RestController
@RequestMapping ( " / demo")
public class DemoController {
@RequestMapping ( " / show")
@OperateLog
public string show ( ) {
return "OK";
}
}
由于在其中加了 OperateLog 注解,所以可以直接访问
(2)访问 goods 微服务,端口号为 9001
有断点出现,先放行,查看是否可以记录对应的日志信息
刷新数据库:
结果说明可以记录对应的日志信息。
(3)通过 Debugger 的形式跟踪具体流程:
在 Tboperatelog op = new Tboperatelog();
和 operatLogFeign.add(op); 处设置断点。
访问根据 id 查询 sku
找到根据 id 查询 sku 的链接然后执行
一访问就进入 op 的断点中:
向下运行,可以得到操作时间、操作类、操作方法以及操作的请求参
数。
Operatelog 目前参数的请求信息为:
当执行 ProceedingJoinPoint 方法时,会请求原始方法,放行让原始方法执行完。
原始方法执行完后,在 object 中封装了结果:
上面的结果就是原始方法返回的结果,得到该结果后就能得到运行耗时的时间以及返回值的类型、返回值,然后通过 Feign 去调用远程接口插入日志。
那么就得到了该日志:
至此,完成了插入日志的操作流程。
3.再测试一个:
之前得知上面的记录日志需要在方法上加 @OperateLog,只有加了才可以记录日志,但是方法 @PostMapping(" / search/ {pakge} / {size}")
没有加 @OperateLog,接下来测试该方法能否被拦截。
找到链接:
然后请求:
查看日志:
没有新增,说明被拦截,并没有记录日志。
所以想要记录日志只需要在方法上加注解 @OperateLog。
以同样的方式可以对 order 中的记录进行改造,如果 order 这个微服务想要记录日志。第一步是将 aop 的包直接拷贝,将包名改为 order。然后想要在引导类上加注解,该注解是:
@Mapperscan (basePackages = "cn . itcast.goods.mapper")
@EnableFeignclients(basePackages = "cn.itcast.feign.client")
开启 Feign 远程的调用,并且指定包扫描。
指定完后,我们的基本操作完成,如果想要去记录某一块日志,则只需要在方法前加 @OperateLog 这个注解即可正常的记录日志。