写在前面
经历了近两个月的时间,我终于完成了一个项目点十多个接口的开发到现场SIT。在这个过程中我深刻的意识到了添加日志的重要性(以前的自己从来不会想着添加日志,这次是真的给我上了一课)。
举一个授信占用接口的例子。在我们的系统中,前台对一笔交易进行提交审批的时候,最终会通过一个接口发送对应的数据给与之对应的信贷系统(两个系统的交互,其实还有涉及中转平台)。在调用授信接口前,我统会对那一笔业务进行相应的授信数据处理(业务流程也十分复杂),最终会把接口需要的参数(对方信贷系统需要的参数)添加到指定的对象中,然后接口再组装成指定格式的报文数据,发送给对方。
在这个过程中有一个问题,我如何能够确保我接受到的参数是正确的?即当我按照对方信贷系统需要的参数去对象中get值得时候,拿到了null,我如何去定位是因为前台授信处理后给我传的对象中的值为null(授信后没有传),还是我拿到值以后,由于我进行了相关的逻辑操作导致变成了null?
授信开发人员往往会在调用相关接口的地方,将发送给的对象打印到日志中,以便于日中定位是他们传值的问题还是接口这边的逻辑问题。
只能说这个解决办法是,授信功能的开发人员定位问题的方法,但现场出现了生产问题,第一个找的人却往往是接口的开发人员(我就被找了几次),此时我由于不了解授信相关的业务逻辑,更不可能知道他们会在什么地方打印传给接口的对象,所以我需要自己去对接受到的参数对象进行打印。
可这人呀,总是会忘记,只有每次出现了问题,我才会想到,为什么我最开始没有在这个位置打印一下结果呢?
总结一句话:我要打印方法传入的参数的内容
我能想到的打印方式有三种,有其他更好的方法,欢迎评论给出
在方法开始的位置,直接调用相关的log.info(xxx)方法
1、定义一个logger变量
private static Logger logger = LoggerFactory.getLogger(xxx.class);
2、指定位置打印参数
logger.info("xxxx");
直接抽取为一个公共方法
1、将传入当前类这个步骤放到工具类中
public static void logInfo(Class<?> clazz,Throwable e){ LoggerFactory.getLogger(clazz).info(e.getMessage(),e); }
2、每个方法内直接调用
LogUtils.logInfo(this.getClass(),"xxxx");
有感于前面的方式太麻烦,且不好看,我想到的一个解决方法。使用时仅需要在类上添加一个@Log注解即可,但是该注解仅能打印接受和返回的对象,对于业务中的日志打印目前是没有办法完成,解析对象是利用的Gson(我甚至想过使用一个模板方法,对应的解析过程给一个扩展接口,自己想修改就重写对应的方法,但想想算了,后期有需要再说)
目前该注解的功能为:
- 打印方法接受到的对象的值,使用时只需要添加对应的前缀字符串即可定位日志记录
- 可以选择是否打印返回对象
我使用了一个try catch包裹我的代码,并且catch中并未抛出异常,仅仅是添加了一个error的日志记录。原因是因为我怕接受到的参数解析会出现异常,以至于影响了原本正常的业务流程
1、自定义一个Log注解
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { // 日志前的标记 String[] logPreArr(); // 是否需要打印返回对象 boolean isNeedReturn() default false; }
2、利用Spring的AOP,编写相关的切入方法
@Aspect @Component public class LogAspect { // 配置织入点 @Pointcut("@annotation(pers.mobian.logannotation.annotation.Log)") public void logPointCut() { } @Before("logPointCut() ") public void beforeLog(JoinPoint point) throws Exception { // 1、目标类 Class<?> aClass = point.getTarget().getClass(); Logger log = LoggerFactory.getLogger(aClass); try { // 2、目标方法 Log logAnno = getAnnotationLogByPoint(point); if (logAnno != null) { // 3、打印目标参数 Object[] args = point.getArgs(); Gson gson = new Gson(); String[] value = logAnno.logPreArr(); if (value.length == 1) { for (Object arg : args) { log.info(point.getSignature().getName() + "方法:" + value[0] + ":" + gson.toJson(arg)); } } else if (value.length == args.length) { for (int i = 0; i < args.length; i++) { log.info(point.getSignature().getName() + "方法:" + value[i] + ":" + gson.toJson(args[i])); } } else { log.info(point.getSignature().getName() + "方法:" + "参数类型与Log注解指定个数不匹配" + gson.toJson(args)); } } } catch (Exception e) { log.error(point.getSignature().getName() + "方法:Log注解解析出现异常:" + e.getMessage()); } } @AfterReturning(pointcut = "logPointCut() ", returning = "jsonResult") public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) throws Exception { Class<?> aClass = joinPoint.getTarget().getClass(); Logger log = LoggerFactory.getLogger(aClass); try { Log logAnno = getAnnotationLogByPoint(joinPoint); if (logAnno != null) { boolean needReturn = logAnno.isNeedReturn(); if (needReturn) { log.info(joinPoint.getSignature().getName() + "方法:" + "返回结果:" + jsonResult); } } } catch (Exception e) { log.error(joinPoint.getSignature().getName() + "方法:Log注解解析出现异常:" + e.getMessage()); } } private Log getAnnotationLogByPoint(JoinPoint joinPoint) throws Exception { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(Log.class); } return null; } }
3、编写的测试方法
@Log(logPreArr = {"测试方法4"},isNeedReturn = true) public Map<String, Object> show4(Teacher student) { Map<String, Object> dataMap = new HashMap<>(); dataMap.put("第一个",1); dataMap.put("第二个","2"); dataMap.put("第三个",true); dataMap.put("第四个",student); Map<String, Object> returnMap = new HashMap<>(); returnMap.put("第一个",1); returnMap.put("第二个","2"); returnMap.put("第三个",true); returnMap.put("第四个",student); returnMap.put("第五个",dataMap); return returnMap; } @Log(logPreArr = {"接受到的老师实体类", "接收到的学生实体类"}) public String show5(Teacher teacher, Student student) { return student.toString(); }
打印结果: