本文原始碼:GitHub·點這裡 || GitEE·點這裡
一、日誌體系整合
1、日誌管理
在系統的開發中,最關鍵的一個元件工具就是日誌,日誌列印方便問題排查,或者生產事故回溯,日誌記錄用來監控並分析系統效能點,並以此為依據,不斷對系統進行優化;同時基於使用者的操作日誌,對使用者行為進行分析,開發智慧推薦的功能,或者進行營銷投放,這在系統中都是常見且關鍵的業務流程。
2、ELK日誌體系
在大型系統架構中,ELK的日誌管理系統是系統必備功能,ELK-Stack是Elasticsearch、Logstash、Kiban三個開源軟體的組合,通常用來做日誌分析,實時資料檢索。基於Logstash做資料流動通道,使日誌資料不斷的流入搜尋元件,基於Elasticsearch做資料實時查詢,基於Kiban的ES視覺化介面,以此實現日誌資料的蒐集、儲存、分析等核心功能,且該體系方便擴充套件。
ELK相關文章:
基於ELK體系的核心操作,有關於ElasticSearch其他文章可以自行查閱之前的內容,這裡不在陳列,好像很多東西都是這樣一點點積累出來的。
二、整合環境
1、專案結構
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、記錄引數
這樣自定義日誌流程就完成了。
四、原始碼地址
GitHub·地址
https://github.com/cicadasmile/middle-ware-parent
GitEE·地址
https://gitee.com/cicadasmile/middle-ware-parent
推薦閱讀:微服務架構系列