前言
在代码中编写日志是日常开发中的基本要求,记录日志一方面是方便日后通过日志能够查找问题原因,另一方面也可以用于后续的审计工作。我以前对团队编写日志的要求是,通过打印的日志就可以看出整个代码的业务逻辑,所以我们团队的代码可以做到没有注释,通过代码中的日志就可以了解到整个代码的业务逻辑。这样要求的好处是看日志查问题的时候,通过看日志就知道代码的处理逻辑,而不用再翻出代码来看业务处理逻辑了。我相信很多同学都有类似的感受,就是在查日志的时候发现日志的信息都非常的混乱,查起问题来别提多别扭了,如果日志都按业务逻辑记录好了那就不会存在这样的问题。
1. 日志要求
我们在代码中记录日志从大类上区分,一般有两种,一种是所有交易都记录的系统基础日志,日志中会包含系统时间、请求返回报文、用户名等等信息;还有一种是业务相关的逻辑日志。
1.1 日志分级
我们先来说下大家记录的最多的业务逻辑日志,记录逻辑日志我们一般都会对日志进行分级error、info、debug这些级别,因为日志记录的太详细就会影响到服务器性能,所以一般我们在生产上只会打开info级别的,有的交易量比较大的系统甚至只打开error级别的日志。
1.2 逻辑日志记录
开头说了对于info或者debug级别的日志,需要能够在代码中明确写出,在每个代码分支的地方都记录分支条件的日志,这样当出现问题后通过查看日志就能够知道为什么走到的错误的分支,直接看日志就知道出异常时候每一步关键参数的值,如果逻辑日志记录的完整的话,查问题甚至都不需要在ide中进行debug。
例:在每个分支都记录逻辑日志,这样方便在日志中查找到问题根因
private boolean initEngine(){
logger.info("init all engines begin: ");
Map<String, IEngine> engines = commonEngineFactory.getAllEngines();
Map<String, IEngine> customEngines = customEngineFactory.getAllEngines();
if(customEngines == null && engines == null){
logger.info("customEngines == null && engines == null.");
return false;
}else if(customEngines != null) {
engines.putAll(customEngines);
}
CloudAppContext.getInstance().setEngineMap(engines);
logger.info("init engines = " + engines);
return true;
}
1.3 切面纪录系统日志
对于系统日志我们一般采取的是通过切面进行日志记录,这样可以最大限度的减少日志对系统逻辑的影响,再通过自定义注解可以在需要加系统日志的逻辑类方法前面加上系统切面注解就可以。下面的章节我们详细讲解下如何实现切面日志。
2. 切面日志实现
为了实现上面说到的切面日志,需要实现下面几个东西,首先要定义一个自定义环绕注解,第二要定义一个Aspect类来重写环绕注解方法,最后还要自定义一个写日志接口,并实现这个接口。下面分别介绍下具体实现方式。
实现完注解切面日志相关类后,可以在相关业务类的逻辑方法前加上方法环绕注解@SysLog,这样就实现了环绕注解,对业务逻辑代码完全没有侵入性。
/**
* 保存配置
*/
@SysLog("保存配置")
@RequestMapping("/save")
public ResponseBean save(@RequestBody SysConfigEntity config){
ValidatorUtils.validateEntity(config);
sysConfigService.save(config);
return ResponseBean.ok();
}
2.1 环绕注解
自定义一个SysLog的注解类,方便在其他逻辑类里使用,这样逻辑代码就不用在逻辑代码中加入这种通用系统日志,在逻辑代码中将只存在业务日志。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
在切面类中重写环绕注解,around方法的point入参就是需要进行代理的业务类的切入点,调用point.proceed()就会代理调用加了@SysLog注解的业务方法,在这个业务方法前后我们可以加上我们要记录的任何系统日志信息。
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long