Java 使用Spring AOP进行全局日志记录

前言

通常情况下,我们将项目部署到服务器后,如果接口出现了bug,那么我们就只有干着急(时间一长,我们就不知道接口的请求参数,也不知道接口返给用户的信息)。这时,如果我们通过AOP将接口的请求参数和返回对象保存到日志文件中,那么我们就可以继续调试。

代码

这里我们使用了以下的第三方包:

  1. fastjson: https://mvnrepository.com/artifact/com.alibaba/fastjson
  2. AOP: https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop
  3. lombok: https://mvnrepository.com/artifact/org.projectlombok/lombok
  4. slf4j:https://mvnrepository.com/artifact/org.slf4j/slf4j-api
  5. Log4j2:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2
package com.cfl.jd.aop;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 类描述:
 *  日志切面:记录请求的参数,返回的结果
 * @ClassName LogAOP
 * @Author msi
 * @Date 2020/11/4 16:13
 * @Version 1.0
 */
@Slf4j
@Aspect
@Component
public class LogAOP {
    /**
     * 手动创建的对象所在包
     */
    public static final String PACKAGE_NAME = "com.cfl.jd";

    /**
     * 全局日志输出
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "execution(* com.cfl.jd.controller.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 参数值
        Object[] args = pjp.getArgs();
        Signature signature = pjp.getSignature();
        // 完全方法名(完全类名+方法名)
        String entirelyMethodName = signature.getDeclaringTypeName() + "." + signature.getName();
        // 参数名
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] parameterNames = methodSignature.getParameterNames();
        Class[] parameterTypes = methodSignature.getParameterTypes();
        // 方法返回值
//        String returnTypeName = methodSignature.getReturnType().getName();
        StringBuilder sb = new StringBuilder();
        for (int i = 0, length = args.length; i < length; i++) {
            sb.append("参数:").append(parameterNames[i]).append("(").append(parameterTypes[i].getName()).append(")")
                    .append(" ————> ");
            if (Objects.nonNull(args[i])) {
                if (parameterTypes[i].getName().indexOf(LogAOP.PACKAGE_NAME) != -1) {
                    sb.append(JSON.toJSONString(args[i]));
                } else{
                    sb.append(args[i].toString());
                }
            } else {
                sb.append("null");
            }

            if (i < length - 1) {
                sb.append("\n");
            }

        }
        if (args.length > 0) {
            log.info("方法:{} 开始执行,请求参数如下:\n{}",entirelyMethodName, sb.toString());
        }

        // 继续执行方法
        Object obj = pjp.proceed();

        if (Objects.nonNull(obj)) {
            String name = obj.getClass().getName();
            if (name.indexOf(LogAOP.PACKAGE_NAME) != -1) {
                log.info("方法:{} 结束执行,返回值:\n{}", entirelyMethodName, JSON.toJSONString(obj));
            } else {
                log.info("方法:{} 结束执行,返回值:\n{}", entirelyMethodName, obj.toString());
            }
        }

        return obj;
    }

}

输出测试

[11-05 21:24:46] [INFO] - com.cfl.jd.aop.LogAOP.around(LogAOP.java:70) - 方法:com.cfl.jd.controller.UserController.login 开始执行,请求参数如下:
参数:loginUsername(java.lang.String) ————> 603088455@qq.com
参数:loginPassword(java.lang.String) ————> 123456
参数:rememberPassword(boolean) ————> false
[11-05 21:24:46] [INFO] - com.cfl.jd.aop.LogAOP.around(LogAOP.java:79) - 方法:com.cfl.jd.controller.UserController.login 结束执行,返回值:
{"code":"1","data":{"admin":false,"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiMTIiLCI2MDMwODg0NTVAcXEuY29tIiwiNjU4NDRiOTE2MjA5NTdjZDJiYWVjNzViMzQyMGIxYzRlMzg2YjUzZDhjZjI5ZWUzZGI0Y2YxYTE0NDQ3ODI0M2E3OTMxMGEyNDY2OWIwMTMwODQ2ZjFkYzA4ZWY0N2ExMjJmYzQ4ZDMzMzAwNDkwOWQyY2QwYTUwMTgwYTY2NDciLCJmYWxzZSJdLCJpc3MiOiJtc2kiLCJleHAiOjE2MDU4Nzg2ODYsImlhdCI6MTYwNDU4MjY4Nn0.p5fMVH1LGt5T84IfBlNom_-qj-Ha01oNwEa7BZUomMo"},"message":"成功"}

辅助工具

因为本人使用的idea开发工具,所以这里介绍两款比较好用的插件:

  1. Grep Console ,输出控制台自定义输出样式插件
  2. All Format, 格式化字符串

写在最后

注意:因为JSON.toJSONString(obj) 这个方法在遇到 obj是一些比较大的对象(类似于HttpServletRequest, MultipartFile…),就会报错。所以,我们在记录日志时,如果参数是我们自己可控的代码,那么就使用json字符串,但是如果是框架不可控的参数对象,就是用toString方法。

log4j 的配置详情,请看我之前的博客: Java 配置日志系统

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值