大公司都在用的-日誌組件| Java Debug 筆記

**本文正在參加「Java主題月 - Java Debug筆記活動」,詳情查看 活動連接 **web

前言

項目初期,爲了快速迭代上線,不少功能都是項目搭建者從別的地方Copy過去,而後根據項目實際狀況進行相應的修改。spring

後期伴隨着業務的迭代,對於中心化組件的需求也漸漸產生。好比個人老東家,在K8S上差很少部署了200個項目,瓜熟蒂落的誕生了受權組件,日誌組件,註解組件,長鏈接服務等。安全

固然啦,咱們今天的主角是日誌組件。markdown

要素

格式

日誌的格式通常包含幾種類型:入參仍是出參,類,方法,內容,開始時間,描述,仍是耗時。有了固定的格式後,日誌的採集會變得相對容易點。app

若是是對安全有要求的,可能還會帶上加密信息和簽名。post

可配置

不能說你用了日誌組件,而後全部的方法中都打印日誌,大大浪費了硬盤空間,暴殄天物。測試

  1. 有些須要按項目層面打日誌,例如controller層面和service層面。
  2. 有些service的方法我不須要打印日誌,那我須要加個註解用來表示不用打印日誌。
  3. 有些方法不須要打印入參,只須要打印出參,這時候我須要支持出入參是否打印配置。
  4. 有些方法的入參或者出參數據量較大,但數據又不關鍵。同時方法的開始時間和耗時又必需要保留。

看到這裏確定有人要說了,哪有這麼複雜。但相信我,在金融級別的系統裏就是有這種需求。ui

侵入性

SpringBoot誕生初期爲何會快速流行,就是源於它配置簡單。 若是一個組件的代碼侵入性很強,用的人就會大打折扣甚至無人問津。this

在老東家搞的Dubbo組件就由於須要在apollo加一行配置,推了很久才被同事漸漸接受。太難了~~加密

實現

格式

這是車轍本身在用的日誌格式

[類型:'入參'] | [類:'TestLogService'] | [方法:'annotest'] | [入參:'[{"yuan":"123456.789"}]'] | [開始時間:'2021-05-13 17:49:46'] | [描述:'測試']。
[類型:'出參'] | [類:'TestLogService'] | [方法:'annotest'] | [出參:'null'] | [結束時間:'2021-05-13 17:49:46'] | [耗時:31] | [描述:'測試']
複製代碼

可配置

  1. 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(EnableLogRegister.class)
public @interface EnableApplicationLog {
    /**
     * 是否開啓controller註解
     * @return
     */
    boolean enableController() default true;
    /**
     * 是否開啓service註解
     * @return
     */
    boolean enableService() default true;
}
複製代碼
  1. 經過實現ImportBeanDefinitionRegistrar,將本身須要的類交給Spring管理。這樣就根據EnableApplicationLog 註解上的配置判斷是否讓切面類被Spring管理。所以組件沒有采用SpringBoot Starter的形式,相對靈活。能夠參考Mybatis的MapperScan是怎麼作的。
class EnableLogRegister implements ImportBeanDefinitionRegistrar{
    boolean enableController = annoAttrs.getBoolean("enableController");
    if(enableController){
        basePackages.add(BASE_HANDLER_CONTROLLER);
    }
    boolean enableService = annoAttrs.getBoolean("enableService");
    if(enableService){
        basePackages.add(BASE_HANDLER_SERVICE);
    }
}
複製代碼
  1. 定義切點
@Aspect
@Slf4j
public class EnableLogControllerLogHandler extends BaseLogHandler {
    @Pointcut("within(@(org.springframework.stereotype.Controller || org.springframework.web.bind.annotation.RestController) *)")
    private void allMethod() {
    }

    @Around("allMethod()")
    public Object doAround(ProceedingJoinPoint call) throws Throwable {
        return handle(call);
    }
}
// service層面切點與controller一致
複製代碼
  1. 定義可配置功能註解。
// 不須要打印日誌註解
public @interface NoNeedLog {
}
// 須要打印日誌的配置註解
public @interface LogParam {

    String desc() default "";

    boolean isPrintIn() default true;

    boolean isPrintOut() default true;
}
複製代碼
  1. 頂層handler處理
// 判斷是否須要打印日誌
        if(!needLog(call)){
            Object result = call.proceed();
            return result;
        }

        Boolean isPrintIn = true, isPrintOut = true;
        String desc = null;
        // 獲取是否須要打印入參和出參
        LogParam logParam = this.getLogParam(call);
        if(logParam != null){
            isPrintIn = logParam.isPrintIn();
            isPrintOut = logParam.isPrintOut();
            desc = logParam.desc();
        }

        // 入參參數打印
        Long startStamp = System.currentTimeMillis();
        if(isPrintIn){
            doBeforeParam(call, desc);
        }

        // invoker調用
        Object result = call.proceed();

        // 返回參數打印
        if(isPrintOut){
            doAfterParam(result, startStamp, call, desc);
        }
        return result;
複製代碼

上面的代碼基本上包含了主要的實現,相信小夥伴們應該對此有了必定的認識,能夠在下方評論中留言喲!

使用

SpringBoot啓動類上添加註解開啓日誌打印

@EnableApplicationLog(enableController = false)
public class TrackAdapterApplication {}
複製代碼

方法不須要日誌

@NoNeedLog
public void annotest(AnnoBean annoBean)
複製代碼

日誌配置不須要入參,添加描述

@LogParam(desc = "測試", isPrintIn = false)
public void annotest(AnnoBean annoBean)
複製代碼

結尾

其實日誌組件裏還有很多東西能夠寫,好比說拋出的異常我怎麼處理。捕獲打印異常並繼續拋出仍是直接作處理並返回,亦或是作一個異常處理組件用來記錄並告警。這邊也不能給出一個完美的答案,畢竟適合的纔是最好的。

這是時隔多月的第一篇博文,接下來但願出一些更高質量的文章,我們下期見啦!

相關文章
相關標籤/搜索