天天看點

SpringBoot2 內建日志,複雜業務下的自定義實作一、日志體系內建二、內建環境三、核心代碼說明四、源代碼位址

本文源碼: GitHub·點這裡 || GitEE·點這裡

一、日志體系內建

1、日志管理

在系統的開發中,最關鍵的一個元件工具就是日志,日志列印友善問題排查,或者生産事故回溯,日志記錄用來監控并分析系統性能點,并以此為依據,不斷對系統進行優化;同時基于使用者的記錄檔,對使用者行為進行分析,開發智能推薦的功能,或者進行營銷投放,這在系統中都是常見且關鍵的業務流程。

2、ELK日志體系

在大型系統架構中,ELK的日志管理系統是系統必備功能,ELK-Stack是Elasticsearch、Logstash、Kiban三個開源軟體的組合,通常用來做日志分析,實時資料檢索。基于Logstash做資料流動通道,使日志資料不斷的流入搜尋元件,基于Elasticsearch做資料實時查詢,基于Kiban的ES可視化界面,以此實作日志資料的搜集、存儲、分析等核心功能,且該體系友善擴充。

ELK相關文章:

  • SpringBoot2整合ElasticSearch架構
  • 搜尋引擎架構,ElasticSearch叢集模式
  • 基于Logstash全量或增量同步資料到ES中間件

基于ELK體系的核心操作,有關于ElasticSearch其他文章可以自行查閱之前的内容,這裡不在陳列,好像很多東西都是這樣一點點積累出來的。

二、內建環境

1、項目結構

SpringBoot2 內建日志,複雜業務下的自定義實作一、日志體系內建二、內建環境三、核心代碼說明四、源代碼位址

defined-log-api:測試工程;

defined-log-config:日志核心子產品,依賴之後使用該子產品下注解即可;

2、資料表結構

CREATE TABLE dt_defined_log (
    id INT ( 11 ) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
    class_name VARCHAR ( 200 ) DEFAULT NULL COMMENT '請求類名',
    method_name VARCHAR ( 100 ) DEFAULT NULL COMMENT '請求方法名',
    method_desc VARCHAR ( 100 ) DEFAULT NULL COMMENT '請求方法描述',
    api_type INT ( 1 ) DEFAULT 0 COMMENT 'API類型',
    biz_nature INT ( 1 ) DEFAULT 0 COMMENT '業務性質類型',
    data_flow_type INT ( 1 ) DEFAULT 0 COMMENT '日志資料流向',
    req_param VARCHAR ( 200 ) DEFAULT NULL COMMENT '請求封包',
    res_param VARCHAR ( 200 ) DEFAULT NULL COMMENT '響應封包',
    PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8 COMMENT = '日志記錄表';           

這裡完全基于業務需求自定義即可。

三、核心代碼說明

1、注解參數

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DefinedLog {

    /**
     * 操作類型
     */
    ApiTypeEnum apiType () ;

    /**
     * 方法描述
     */
    String methodDesc();

    /**
     * 業務性質
     */
    BizNatureEnum bizNature() ;

    /**
     * 資料流向,與業務性質關聯
     */
    DataFlowEnum dataFlow() ;

    /**
     * 存儲入參
     */
    boolean isSaveReqParam () default true ;

    /**
     * 存儲出參
     */
    boolean isSaveResParam() default true ;

    /**
     * 是否需要異步處理
     */
    boolean isAsync () default false ;
}           

這裡描述一下如下幾個參數的意思:

bizNature:業務性質,即該日志是否有分析,或者營銷推廣操作,例如在在電商業務中,浏覽系列商品後是否推送廣告;

dataFlow:資料流向,即資料存儲後是否向其他資料源推送,常見可能推送到MQ或者Redis或者分析引擎中,推薦類系統中對關鍵日志實時性要求極高,可以基于此做使用者行為實時分析;

isAsync:是否異步處理,在一些并發高的接口中,避免日志記錄成為性能問題的一個因素;

其他相關參數都是十分常見,例如接口類型增删改查,入參出參封包存儲,方法子產品的描述等等,這些都可以基于業務的需求自定義,然後做相關業務處理開發,思路開闊即可。

2、切面攔截

基于切面程式設計是方式,做相關日志處理,擷取相應參數,建構日志模型即可。

@Component
@Aspect
public class LogAop {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAop.class);

    @Value("${spring.application.app-id}")
    private String appId ;
    @Resource
    private DefineLogService defineLogService ;

    /**
     * 日志切入點
     */
    @Pointcut("@annotation(com.defined.log.annotation.DefinedLog)")
    public void logPointCut() {

    }

    /**
     * 環繞切入
     */
    @Around("logPointCut()")
    public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = null ;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try{
            // 執行方法
            result = proceedingJoinPoint.proceed();
            stopWatch.stop();
        } catch (Exception e){
            stopWatch.stop();
        } finally {
            // 儲存日志
            LOGGER.info(" execute time: {} ms ", stopWatch.getTotalTimeMillis());
            DefineLogModel defineLogModel = buildLogParam (proceedingJoinPoint);
            defineLogModel.setResParam(JSONObject.toJSONString(result));
            defineLogService.saveLog(defineLogModel) ;
        }
        return result ;
    }

    private DefineLogModel buildLogParam (ProceedingJoinPoint point){

        DefineLogModel defineLogModel  = new DefineLogModel() ;

        MethodSignature signature = (MethodSignature) point.getSignature();
        Method reqMethod = signature.getMethod();
        String className = point.getTarget().getClass().getName();
        Object[] reqParam = point.getArgs();

        LOGGER.info("請求方法:"+reqMethod.getName());
        LOGGER.info("請求類名:"+className);
        LOGGER.info("請求參數:"+ JSONObject.toJSONString(reqParam));
        // 擷取方法上注解
        reqMethod.getAnnotation(DefinedLog.class).getClass();
        DefinedLog definedLog = reqMethod.getAnnotation(DefinedLog.class);

        // 建構參數
        String methodName = reqMethod.getName() ;
        Integer apiType = definedLog.apiType().getApiType();
        String apiTypeDesc = definedLog.apiType().getApiTypeDesc();
        String methodDesc = definedLog.methodDesc() ;
        Integer bizNature = definedLog.bizNature().getBizNature() ;
        Integer dataFlowType = definedLog.dataFlow().getDataFlowType();
        boolean isSaveReqParam = definedLog.isSaveReqParam();
        boolean isSaveResParam = definedLog.isSaveResParam();
        boolean isAsync = definedLog.isAsync() ;

        defineLogModel.setAppId(appId);
        defineLogModel.setClassName(className);
        defineLogModel.setMethodName(methodName);
        defineLogModel.setMethodDesc(methodDesc);
        defineLogModel.setApiType(apiType);
        defineLogModel.setApiTypeDesc(apiTypeDesc);
        defineLogModel.setBizNature(bizNature);
        defineLogModel.setDataFlowType(dataFlowType);
        defineLogModel.setSaveReqParam(isSaveReqParam);
        defineLogModel.setSaveResParam(isSaveResParam);
        defineLogModel.setAsync(isAsync);
        defineLogModel.setReqParam(JSONObject.toJSONString(reqParam));

        return defineLogModel ;
    }
}           

3、使用方式

DefinedLog注解在接口方法上即可。

@RestController
public class LogController {

    @GetMapping("/logApi")
    @DefinedLog(apiType=ApiTypeEnum.COMPOSITE,
                methodDesc="測試日志",
                bizNature= BizNatureEnum.DEFAULT,
                dataFlow= DataFlowEnum.DEFAULT)
    public String logApi (@RequestParam("param") String param){
        return "success-re" ;
    }

}           

4、記錄參數

SpringBoot2 內建日志,複雜業務下的自定義實作一、日志體系內建二、內建環境三、核心代碼說明四、源代碼位址

這樣自定義日志流程就完成了。

四、源代碼位址

GitHub·位址
https://github.com/cicadasmile/middle-ware-parent
GitEE·位址
https://gitee.com/cicadasmile/middle-ware-parent           

繼續閱讀