几种记录方法执行日志的方式

文章介绍了两种记录方法执行日志的优雅方式:一是通过封装lambda表达式创建Util工具类,自动获取并打印类名和方法名;二是利用SpringAOP的注解实现方法的环绕增强,自定义异常处理并记录日志。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

想要看到某一个方法的执行情况,比如现在有一个testService的类,调用该类中的testRpcService方法,想要打印日志如下,一般情况下会把日志打印的代码每次自己手动拷贝类名、方法名,然后写同样的日志:
在这里插入图片描述
每次都自己拷贝,有时候还会拷错,我想要优雅的写代码,因此记录几种记录方法执行日志的方式

一、封装lambda表达式成util工具

工具代码:

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;

/**
 * @author ZhaoXu
 * @date 2023/4/15 19:22
 */
@Slf4j
public class MethodExecuteUtils {
    @FunctionalInterface
    public interface LogMethodFunction<Q, B> extends Serializable {
        /**
         * 执行方法
         * @param q
         * @return
         */
        B execute(Q q);

        /**
         * 获取lambda方法
         *
         * @return
         * @throws Exception
         */
        default SerializedLambda getSerializedLambda() throws Exception {
            Method write = this.getClass().getDeclaredMethod("writeReplace");
            write.setAccessible(true);
            return (SerializedLambda) write.invoke(this);
        }
    }

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    /**
     * 执行rpc远程调用逻辑
     *
     * @param function
     * @param <Q>      response
     * @param <B>      response
     * @return
     */
    public static <Q, B> B logAround(Q request, LogMethodFunction<Q, B> function) {
        SerializedLambda serializedLambda = null;
        try {
            serializedLambda = function.getSerializedLambda();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 类名
        String className = serializedLambda.getImplClass();
        String[] packageSplit = className.split("/");
        className = packageSplit[packageSplit.length - 1];

        // 方法名
        String methodName = serializedLambda.getImplMethodName();
        methodName = className + "." + methodName + " ";
        log.info(methodName + "execute before, requestBody: {}", OBJECT_MAPPER.valueToTree(request));

        B responseBody = null;
        long startTime = System.currentTimeMillis();
        try {
            responseBody = function.execute(request);
        } catch (Throwable e) {
            log.error(methodName + "execute error, exception: {}", OBJECT_MAPPER.valueToTree(e));
        } finally {
            log.info(methodName + "execute after, responseBody: {}", OBJECT_MAPPER.valueToTree(responseBody));
            log.info(methodName + "execute time: {}", System.currentTimeMillis() - startTime + " millisecond");
        }
        return responseBody;
    }
}

使用方式:

BaseRequest baseRequest = new BaseRequest();
        baseRequest.setPage(1);
        BaseResponse baseResponse = MethodExecuteUtils.logAround(baseRequest, testService::testRpcService);   

二、使用spring aop切面的形式去增强方法

2.1 首先定义注解

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author KuiChi
 * @date 2023/4/15 16:18
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAround {
    /**
     * 执行异常message
     * @return
     */
    String detailMessage() default "业务执行异常";
}

2.2 然后写一个aspect方法去增强

package com.example.springbootdemoz.aspect;

import com.example.springbootdemoz.annotation.LogAround;
import com.example.springbootdemoz.exception.BusinessException;
import com.example.springbootdemoz.exception.ErrorCode;
import com.fasterxml.jackson.databind.ObjectMapper;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
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.CodeSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author KuiChi
 * @date 2023/6/4 16:13
 */
@Component
@Aspect
@Slf4j
public class LogMethodAspect {
    private static final String DEFAULT_SERVICE_STR = "Service";

    private static final ClassPool CLASS_POOL = ClassPool.getDefault();

    private static Field DETAIL_MESSAGE_FIELD;

    private static Field CAUSE_FIELD;

    private static Method FILL_IN_STACK_TRACE_METHOD;

    static {
        try {
            DETAIL_MESSAGE_FIELD = Throwable.class.getDeclaredField("detailMessage");
            DETAIL_MESSAGE_FIELD.setAccessible(Boolean.TRUE);
            CAUSE_FIELD = Throwable.class.getDeclaredField("cause");
            CAUSE_FIELD.setAccessible(Boolean.TRUE);
            FILL_IN_STACK_TRACE_METHOD = Throwable.class.getDeclaredMethod("fillInStackTrace");
            FILL_IN_STACK_TRACE_METHOD.setAccessible(Boolean.TRUE);
        } catch (NoSuchFieldException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @Autowired
    private ObjectMapper objectMapper;

    @Around("@annotation(logAround)")
    public Object doAround(ProceedingJoinPoint joinPoint, LogAround logAround) {
        Signature signature = joinPoint.getSignature();
        Map<String, Object> paramNameAndValue = getParamNameAndValue(joinPoint);

        // 类名
        String fullClassName = signature.getDeclaringTypeName();
        int lastPointIndex = fullClassName.lastIndexOf(".");
        String className = fullClassName.substring(lastPointIndex + 1);

        // 方法名
        String methodName = signature.getName();
        methodName = className + "#" + methodName + " ";

        Object proceed = null;
        long startTime = System.currentTimeMillis();
        try {
            proceed = joinPoint.proceed();
            // 方法所在类.方法名
            // TestServiceImpl#testService execute after, params:{}, response:{}
            log.info(methodName + "execute success, params:{}, response:{}", objectMapper.valueToTree(paramNameAndValue),
                    objectMapper.valueToTree(proceed));
            log.info(methodName + "execute time:{}", (System.currentTimeMillis() - startTime) + " millisecond");
        } catch (Throwable throwable) {
            // TestServiceImpl#testService execute error, params:{}
            log.error(methodName + "execute error, params:{}", objectMapper.valueToTree(paramNameAndValue), throwable);
            throw getException(fullClassName, logAround, throwable);
        }
        return proceed;
    }

    private BusinessException getException(String fullClassName, LogAround logAround, Throwable throwable) {
        BusinessException businessException;
        try {
            String subClassName = "";
            if (fullClassName.contains(DEFAULT_SERVICE_STR)) {
                int service = fullClassName.toLowerCase().lastIndexOf("service");
                subClassName = fullClassName.substring(0, service) + "Exception";
            } else {
                subClassName = fullClassName + "BaseException";
            }
            Class<?> subClass;
            synchronized (CLASS_POOL) {
                try {
                    subClass = Class.forName(subClassName, true, CLASS_POOL.getClassLoader());
                } catch (ClassNotFoundException e) {
                    // 需要生成类
                    CtClass subCtClass = CLASS_POOL.makeClass(subClassName);
                    CtClass superClass = CLASS_POOL.get(BusinessException.class.getName());
                    subCtClass.setSuperclass(superClass);
                    //构造函数
                    CtClass stringClass = CLASS_POOL.get(String.class.getName());
                    CtClass[] constructorParamClassArr = new CtClass[]{stringClass, CtClass.intType};
                    CtConstructor constructor = CtNewConstructor.make(constructorParamClassArr, null, subCtClass);
                    subCtClass.addConstructor(constructor);
                    subCtClass.toClass();
                    subCtClass.detach();
                    subClass = Class.forName(subClassName, true, CLASS_POOL.getClassLoader());
                }
            }
            businessException = (BusinessException) subClass.getConstructor(String.class, int.class).newInstance(logAround.detailMessage(), ErrorCode.BUSINESS_EXCEPTION.getCode());
            DETAIL_MESSAGE_FIELD.set(businessException, logAround.detailMessage());
            CAUSE_FIELD.set(businessException, throwable);
            FILL_IN_STACK_TRACE_METHOD.invoke(businessException);
        } catch (Exception ex) {
            log.error("getException error", ex);
            businessException = new BusinessException(ErrorCode.BUSINESS_EXCEPTION.getDesc(), ErrorCode.BUSINESS_EXCEPTION.getCode());
        }
        return businessException;
    }

    /**
     * 获取参数Map集合
     *
     * @param joinPoint
     * @return
     */
    private Map<String, Object> getParamNameAndValue(ProceedingJoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>(8);
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();

        for (int i = 0; i < paramNames.length; i++) {
            param.put(paramNames[i], paramValues[i]);
        }
        return param;
    }

}

使用方法,直接注解在方法上就可以了,会自动进行环绕。

@LogAround(detailMessage = "查询数据异常")
public BaseResponse testRpcService(BaseRequest request) {
    BaseResponse baseResponse = new BaseResponse();
    baseResponse.setName("zx");
    return baseResponse;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值