又被逼著最佳化程式碼,這次我幹掉了出入參 Log日誌

程式設計師小富發表於2020-07-20

本文收錄在個人部落格:www.chengxy-nds.top,技術資源共享。

最近技術部突然颳起一陣 review 程式碼的小風,挨個專案組過程式碼,按理說這應該是件挺好的事,讓別人指出自己程式碼中的不足,查缺補漏,對提升自身編碼能力有很大幫助,畢竟自己審查很容易“陶醉”在自己寫的程式碼裡。 在這裡插入圖片描述

不過,程式碼 review 的詳細程度令人髮指,一行一行的分析,簡直就是個培訓班啊。不誇張的說,如果我村裡僅有縣重點小學學歷的四大爺,來聽上一個月後,保證能上手開發,666~

既然組內氣氛到這了,我們也得行動起來,要不哪天評審到我的程式碼,讓人家指指點點的心裡多少有點不舒服,與其被動最佳化程式碼不如主動出擊~

選最佳化程式碼的方向,方法入參和返回結果日誌首當其衝,每個方法都會有這兩個日誌,一大堆冗餘的程式碼,而且什麼樣的列印格式都有,非常的雜亂。

`public OrderDTO getOrder(OrderVO orderVO, String name) {

    log.info("訂單詳情入參:orderVO={},name={}", JSON.toJSONString(orderVO), name);

    OrderDTO orderInfo = orderService.getOrderInfo(orderVO);

    log.info("訂單詳情結果:orderInfo={}", JSON.toJSONString(orderInfo));

    return orderInfo;

}
`

下邊我們利用 AOP 實現請求方法的入參、返回結果日誌統一列印,避免日誌列印格式雜亂,同時減少業務程式碼量。

一、自定義註解

自定義切面註解@PrintlnLog 用來輸出日誌,註解許可權 @Target({ElementType.METHOD}) 限制只在方法上使用,註解中只有一個引數 description ,用來自定義方法輸出日誌的描述。

`@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface PrintlnLog {

/**
 * 自定義日誌描述資訊文案
 *
 * @return
 */
String description() default "";

}
`

二、切面類

接下來編寫@PrintlnLog 註解對應的切面實現,doBefore()中輸出方法的自定義描述、入參、請求方式、請求url、被呼叫方法的位置等資訊,doAround() 中列印方法返回結果。

注意: 如何想指定切面在哪個環境執行,可以用@Profile 註解,只列印某個環境的日誌。

`@Slf4j
@Aspect
@Component
//@Profile({“dev”}) //只對某個環境列印日誌
public class LogAspect {

private static final String LINE_SEPARATOR = System.lineSeparator();

/**
 * 以自定義 @PrintlnLog 註解作為切面入口
 */
@Pointcut("@annotation(com.chengxy.unifiedlog.config.PrintlnLog)")
public void PrintlnLog() {
}

/**
 * @param joinPoint
 * @author fu
 * @description 切面方法入參日誌列印
 * @date 2020/7/15 10:30
 */
@Before("PrintlnLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {

    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    String methodDetailDescription = this.getAspectMethodLogDescJP(joinPoint);

    log.info("------------------------------- start --------------------------");
    /**
     * 列印自定義方法描述
     */
    log.info("Method detail Description: {}", methodDetailDescription);
    /**
     * 列印請求入參
     */
    log.info("Request Args: {}", JSON.toJSONString(joinPoint.getArgs()));
    /**
     * 列印請求方式
     */
    log.info("Request method: {}", request.getMethod());
    /**
     * 列印請求 url
     */
    log.info("Request URL: {}", request.getRequestURL().toString());

    /**
     * 列印呼叫方法全路徑以及執行方法
     */
    log.info("Request Class and Method: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
}

/**
 * @param proceedingJoinPoint
 * @author xiaofu
 * @description 切面方法返回結果日誌列印
 * @date 2020/7/15 10:32
 */
@Around("PrintlnLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

    String aspectMethodLogDescPJ = getAspectMethodLogDescPJ(proceedingJoinPoint);

    long startTime = System.currentTimeMillis();

    Object result = proceedingJoinPoint.proceed();
    /**
     * 輸出結果
     */
    log.info("{},Response result  : {}", aspectMethodLogDescPJ, JSON.toJSONString(result));

    /**
     * 方法執行耗時
     */
    log.info("Time Consuming: {} ms", System.currentTimeMillis() - startTime);

    return result;
}

/**
 * @author xiaofu
 * @description 切面方法執行後執行
 * @date 2020/7/15 10:31
 */
@After("PrintlnLog()")
public void doAfter(JoinPoint joinPoint) throws Throwable {
    log.info("------------------------------- End --------------------------" + LINE_SEPARATOR);
}

/**
 * @param joinPoint
 * @author xiaofu
 * @description @PrintlnLog 註解作用的切面方法詳細細資訊
 * @date 2020/7/15 10:34
 */
public String getAspectMethodLogDescJP(JoinPoint joinPoint) throws Exception {
    String targetName = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    Object[] arguments = joinPoint.getArgs();
    return getAspectMethodLogDesc(targetName, methodName, arguments);
}

/**
 * @param proceedingJoinPoint
 * @author xiaofu
 * @description @PrintlnLog 註解作用的切面方法詳細細資訊
 * @date 2020/7/15 10:34
 */
public String getAspectMethodLogDescPJ(ProceedingJoinPoint proceedingJoinPoint) throws Exception {
    String targetName = proceedingJoinPoint.getTarget().getClass().getName();
    String methodName = proceedingJoinPoint.getSignature().getName();
    Object[] arguments = proceedingJoinPoint.getArgs();
    return getAspectMethodLogDesc(targetName, methodName, arguments);
}

/**
 * @param targetName
 * @param methodName
 * @param arguments
 * @author xiaofu
 * @description 自定義註解引數
 * @date 2020/7/15 11:51
 */
public String getAspectMethodLogDesc(String targetName, String methodName, Object[] arguments) throws Exception {
    Class targetClass = Class.forName(targetName);
    Method[] methods = targetClass.getMethods();
    StringBuilder description = new StringBuilder("");
    for (Method method : methods) {
        if (method.getName().equals(methodName)) {
            Class[] clazzs = method.getParameterTypes();
            if (clazzs.length == arguments.length) {
                description.append(method.getAnnotation(PrintlnLog.class).description());
                break;
            }
        }
    }
    return description.toString();
}

}
`

三、應用

我們在需要列印入參和返回結果日誌的方法,加上@PrintlnLog註解,並新增自定義方法描述。

`@RestController
@RequestMapping
public class OrderController {

@Autowired
private OrderService orderService;

@PrintlnLog(description = "訂單詳情Controller")
@RequestMapping("/order")
public OrderDTO getOrder(OrderVO orderVO, String name) {

    OrderDTO orderInfo = orderService.getOrderInfo(orderVO);

    return orderInfo;
}

}
`

程式碼裡去掉 log.info日誌列印,加上 @PrintlnLog 看一下效果,清晰明瞭。 在這裡插入圖片描述 Demo GitHub地址:github.com/chengxy-nds/Springboot-...

原創不易,燃燒秀髮輸出內容,如果有一丟丟收穫,點個贊鼓勵一下吧!

整理了幾百本各類技術電子書,送給小夥伴們。關注公號回覆【666】自行領取。和一些小夥伴們建了一個技術交流群,一起探討技術、分享技術資料,旨在共同學習進步,如果感興趣就加入我們吧!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章