22

SpringBoot2 集成日志,复杂业务下的自定义实现

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU4Njg0MzYwNw%3D%3D&%3Bmid=2247484826&%3Bidx=1&%3Bsn=b2f6b76a196233f7ba0dc6cf92824e09
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、日志体系集成

1、日志管理

在系统的开发中,最关键的一个组件工具就是日志,日志打印方便问题排查,或者生产事故回溯,日志记录用来监控并分析系统性能点,并以此为依据,不断对系统进行优化;同时基于用户的操作日志,对用户行为进行分析,开发智能推荐的功能,或者进行营销投放,这在系统中都是常见且关键的业务流程。

2、ELK日志体系

在大型系统架构中,ELK的日志管理系统是系统必备功能,ELK-Stack是Elasticsearch、Logstash、Kiban三个开源软件的组合,通常用来做日志分析,实时数据检索。基于Logstash做数据流动通道,使日志数据不断的流入搜索组件,基于Elasticsearch做数据实时查询,基于Kiban的ES可视化界面,以此实现日志数据的搜集、存储、分析等核心功能,且该体系方便扩展。

ELK相关文章:

基于ELK体系的核心操作,有关于ElasticSearch其他文章可以自行查阅之前的内容,这里不在陈列,好像很多东西都是这样一点点积累出来的。

二、集成环境

1、项目结构

qEfiier.png!mobile

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、记录参数

JfUrIf.png!mobile

这样自定义日志流程就完成了。

四、源代码地址

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

uaE36bV.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK