一、日志体系集成
1、日志管理
在系统的开发中,最关键的一个组件工具就是日志,日志打印方便问题排查,或者生产事故回溯,日志记录用来监控并分析系统性能点,并以此为依据,不断对系统进行优化;同时基于用户的操作日志,对用户行为进行分析,开发智能推荐的功能,或者进行营销投放,这在系统中都是常见且关键的业务流程。
2、ELK日志体系
在大型系统架构中,ELK的日志管理系统是系统必备功能,ELK-Stack是Elasticsearch、Logstash、Kiban三个开源软件的组合,通常用来做日志分析,实时数据检索。基于Logstash做数据流动通道,使日志数据不断的流入搜索组件,基于Elasticsearch做数据实时查询,基于Kiban的ES可视化界面,以此实现日志数据的搜集、存储、分析等核心功能,且该体系方便扩展。
基于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、记录参数
这样自定义日志流程就完成了。
如果觉得本文对你有帮助,可以点赞关注支持一下,也可以关注我公众号,上面有更多技术干货文章以及相关资料共享,大家一起学习进步!