在实际的业务系统中,我们通常都希望程序自动的打印方法的入参和返回值,某些特定的方法可能不想打印返回值(返回数据过大,打印日志影响效率),特有了下面的实现。

1、忽略返回值的java注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* 不需要打印返回值的log
* @author yangzhilong
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NotPrintResponseLog {
public boolean value() default false;
}

2、日志记录切面类

 import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; /**
* 记录Rest和service的方法的入参和返回值
   * @author yangzhilong
*/
@Aspect
@Component
@Slf4j
public class CommonLogAspect {
@Pointcut(value = "execution(public * com.tomato..*Impl.*(..))")
private void pointcutService() { }
@Pointcut(value = "execution(public * com.tomato..*Rest*.*(..))")
private void pointcutRest() { }
/*
//拦截restmapping注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.RequestMapping public * com.tomato..*(..))")
private void pointCut() { } //拦截post注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.PostMapping public * com.tomato..*(..))")
private void pointCutPost() { } //拦截get注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.GetMapping public * *com.tomato..*(..))")
private void pointCutGet() { } //拦截Delete注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.DeleteMapping public * com.tomato..*(..))")
private void pointCutDelete() { } //拦截put注解
@Pointcut(value = "execution(@org.springframework.web.bind.annotation.PutMapping public * com.tomato..*(..))")
private void pointCutPut() { }*/ @Pointcut("pointcutService()|| pointcutRest()")
private void pointcut() {
} @Around(value = "pointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
String classAndMethodName = null;
Method currentMethod = null; try {
currentMethod = this.getCurrentMethod(pjp);
classAndMethodName = this.getCurrentCompleteMethodName(pjp);
} catch (Throwable e) {
log.error("初始化日志记录信息时出错", e);
return pjp.proceed();
} // 处理入参
this.processBefore(pjp, classAndMethodName); Object result = null;
try {
// 调用目标方法
result = pjp.proceed();
} catch (Throwable e) {
// 目标方法异常了
log.info("end执行方法:{}发生异常,异常简述:{}", classAndMethodName, e.getMessage());
throw e;
} // 处理返回值
processReturnValue(result, currentMethod, classAndMethodName); return result;
} /**
* 得到当前方法的对象引用
* @param pjp
* @return
* @throws NoSuchMethodException
* @throws SecurityException
*/
private Method getCurrentMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException {
MethodSignature mig = (MethodSignature) pjp.getSignature();
return pjp.getTarget().getClass().getMethod(mig.getName(), mig.getParameterTypes());
} /**
* 得到完整的方法名
* @param pjp
* @return
*/
private String getCurrentCompleteMethodName(ProceedingJoinPoint pjp) {
return pjp.getTarget().getClass() + "的" + pjp.getSignature().getName() + "方法";
} /**
* 处理入参打印
* @param pjp
* @param classAndMethodName
*/
private void processBefore(ProceedingJoinPoint pjp, String classAndMethodName) {
try {
if(null==pjp.getArgs() || pjp.getArgs().length==0) {
log.info("begin执行方法:{},方法无入参", classAndMethodName);
} else {
if(pjp.getArgs().length == 1) {
if (pjp.getArgs()[0] instanceof Serializable) {
if(isFile(pjp.getArgs()[0])) {
log.info("begin执行方法:{},入参为文件类型,文件名为:{}", classAndMethodName, getFileName(pjp.getArgs()[0]));
} else {
log.info("begin执行方法:{},入参为:{}", classAndMethodName, JSONObject.toJSONString(pjp.getArgs()[0]));
}
}
} else {
log.info("begin执行方法:{},有多个入参", classAndMethodName);
List<Object> list = Arrays.asList(pjp.getArgs());
final AtomicInteger index = new AtomicInteger(1);
list.stream().filter(x -> x instanceof Serializable).forEach(x -> {
if(isFile(x)) {
log.info("入参{}:{}", index.get(), getFileName(x));
} else {
log.info("入参{}:{}", index.get(), JSONObject.toJSONString(x));
}
index.incrementAndGet();
});
}
}
} catch (Throwable e) {
log.error("记录入参日志的时候出错:", e);
}
} /**
* 处理返回值
* @param result
* @param currentMethod
* @param classAndMethodName
*/
private void processReturnValue(Object result, Method currentMethod, String classAndMethodName) {
if(null == result) {
return;
}
try {
if(!currentMethod.isAnnotationPresent(NotPrintResponseLog.class) && result instanceof Serializable) {
log.info("end执行方法:{},返回结果:{}", classAndMethodName, JSONObject.toJSONString(result));
}
} catch (Throwable e) {
log.error("记录返回日志的时候出错:", e);
} } /**
* 获取文件上传的文件名
* @param file
* @return
*/
private String getFileName(Object file) {
return null==file ? "空文件" : ((MultipartFile)file).getName();
} /**
* 判断是否是文件类型
* @param obj
* @return
*/
private boolean isFile(Object obj) {
return obj instanceof MultipartFile;
}
}

20180530补充:

在aop的逻辑内,先走@Around注解的方法。然后是@Before注解的方法,然后这两个都通过了,走核心代码,核心代码走完,无论核心有没有返回值,都会走@After方法。然后如果程序无异常,正常返回就走@AfterReturn,有异常就走@AfterThrowing。

Aspect实现对方法日志的拦截记录的更多相关文章

  1. Apache 日志设置不记录指定文件类型的方法和日志轮

    Apache日志精准的记录了Web访问的记录,但对于访问量很大的站来说,日志文件过大对于分析和保存很不方便.可以在http.conf(或虚拟主机设置文件httpd-vhosts.conf)中进行设置, ...

  2. Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP

    回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...

  3. Spring Boot 使用 Aop 实现日志全局拦截

    前面的章节我们学习到 Spring Boot Log 日志使用教程 和 Spring Boot 异常处理与全局异常处理,本章我们结合 Aop 面向切面编程来实现全局拦截异常并记录日志. 在 Sprin ...

  4. Springboot 三种拦截Rest API的方法-过滤器、拦截器、切片

    过滤器方式实现拦截(Filter) 通过继承Servlet的Filter类来实现拦截: @Component public class TimeFilter implements Filter { @ ...

  5. tail -fn 1000 test.log | grep '关键字' 按照时间段 sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log /var/log/wtmp 该日志文件永久记录每个用户登录、注销及系统的启动、停机的事件

    Linux 6种日志查看方法,不会看日志会被鄙视的 2020-02-11阅读 7.3K0   作为一名后端程序员,和Linux打交道的地方很多,不会看Linux日志,非常容易受到来自同事和面试官的嘲讽 ...

  6. 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决(转载)

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  7. SQLServer 2008以上误操作数据库恢复方法——日志尾部备份(转)

    问题: 经常看到有人误删数据,或者误操作,特别是update和delete的时候没有加where,然后就喊爹喊娘了.人非圣贤孰能无过,做错可以理解,但不能纵容,这个以后再说,现在先来解决问题. 遇到这 ...

  8. [转]SQLServer 2008以上误操作数据库恢复方法——日志尾部备份

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/8491327 问题: 经常看到有人误删数据,或者误操作,特别是update和delete的 ...

  9. SQL Server 2008以上误操作数据库恢复方法——日志尾部备份

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/8491327 问题: 经常看到有人误删数据,或者误操作,特别是update和delete的 ...

随机推荐

  1. git别名;git配置使用shell函数;git别名使用shell函数;git获取当前分支;git alias

    获取当前分支 git symbolic-ref -q --short HEAD 2. 在git别名里使用shell函数,$1获取第一个参数的值,$2……$n依次类推,根据自己习惯需要定制 3. 提交r ...

  2. Understanding Linux CPU stats

    Your Linux server is running slow, so you follow standard procedure and run top. You see the CPU met ...

  3. jstorm简介

    最近在研究jstorm,看了很多资料,所以也想分享出来一些. 安装部署 zeromq 简单快速的传输层框架,安装如下: wget http://download.zeromq.org/zeromq-2 ...

  4. Cesium中导入三维模型方法(dae到glft/bgltf) 【转】

    http://blog.csdn.net/l491453302/article/details/46766909 目录(?)[+] Cesium中目前支持gltf和bgltf两种格式.“gltf是kh ...

  5. Deformable Convolutional Networks-v1-v2(可变形卷积网络)

    如何评价 MSRA 视觉组最新提出的 Deformable ConvNets V2? <Deformable Convolutional Networks>是一篇2017年Microsof ...

  6. listview加载数据

    首先我们需要理清思路:使用ListView显示数据是很方便的,ListVIew的数据之间通过适配器adapter去作为桥梁连接起来.当我们需要使用listview显示大量数据的时候,我们需要使用到分页 ...

  7. windows vs2017环境下编译webkit

    源码地址:https://github.com/BlzFans/wke 先看官方的说明: Web和Flash的嵌入式3D游戏,基于WebKit 建筑工作单元 VS2005: 1安装Visual Stu ...

  8. 15款不容错过的前端开发Javascript和css类库 - 2017版本~

    前端的JS和CSS类库日新月异, 在今天这篇介绍中,我们将精挑细选15款超棒的JS/CSS类库, 希望大家在开发过程中会觉得有帮助~ Core UI 基于Bootstrap4的一套UI类库, Core ...

  9. Spring Boot中Starter是什么

    比如我们要在Spring Boot中引入Web MVC的支持时,我们通常会引入这个模块spring-boot-starter-web,而这个模块如果解压包出来会发现里面什么都没有,只定义了一些POM依 ...

  10. android中RecyclerView控件的列表项横向排列

    本文是在上一篇文章的基础上做的修改:android中RecyclerView控件的使用 1.修改列表项news_item.xml:我这里是把新闻标题挪到了新闻图片的下面显示 <?xml vers ...