背景

某客户审批系统报错,界面上提示审批失败

接口没有其他错误信息,后台也没有报错,找到接口代码,代码如下:

public Map executeTaskAction(.....) throws TaskActionException {
        //....省略不关键代码
        try {
            this.activitiService.executeTaskAction(iRequest, taskId, actionRequest, false);
            stringStringHashMap.put("success", true);
            return stringStringHashMap;
        } catch (Exception e) {
            stringStringHashMap.put("success", false);
            stringStringHashMap.put("message", "发放失败");
            return stringStringHashMap;
        }
    }

从提示信息看,代码应该是抛了Exception,Exception被catch了,但是却未返回Exception的信息,也未打印Exception栈,简单说,就是Exception被吃掉了,以前碰到这种代码除了骂写代码的人傻逼外毫无办法,只能默默改了代码重新部署查看异常信息,有了Arthas后就可以在不改代码情况下查看异常信息。

排查

这次我们还是需要借助watch命令,watch命令有个参数-e可以在方法抛出异常后进行观察,并且可以打印出异常信息,命令如下

watch com.xxx.controllers.ActivitiController executeTaskAction "{throwExp}" -e -x 2
  • throwExp:异常信息
  • -e:如果方法异常进行观察
  • -x:打印对象深度

我们观察executeTaskAction这个方法,但并没有打印异常信息,为什么呢?因为该方法已经把异常catch住了,所以不会有异常抛出,该方法如下

public Map executeTaskAction(.....) throws TaskActionException {
        //....省略不关键代码
        try {
            this.activitiService.executeTaskAction(iRequest, taskId, actionRequest, false);
            stringStringHashMap.put("success", true);
            return stringStringHashMap;
        } catch (Exception e) {
            stringStringHashMap.put("success", false);
            stringStringHashMap.put("message", "发放失败");
            return stringStringHashMap;
        }
    }

我们如果需要捕获异常,需要观察this.activitiService.executeTaskAction方法,修改命令如下

$ watch com.xx.activiti.service.IActivitiService executeTaskAction "{throwExp}" -e -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 3 , method count: 2) cost in 740 ms, listenerId: 6
method=com.xx.activiti.service.impl.ActivitiServiceImpl.executeTaskAction location=AtExceptionExit
ts=2021-08-04 14:33:37; [cost=155.974219ms] result=@ArrayList[
    java.lang.NullPointerException
        at com.xx.account.service.impl.UserServiceImpl.selectDirectLeader(UserServiceImpl.java:671)
        at com.xx.account.service.impl.UserServiceImpl$$FastClassBySpringCGLIB$$b54795f.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)

我们拿到异常信息是一个NullPointerException,并且出错的代码在

com.xx.account.service.impl.UserServiceImpl.selectDirectLeader(UserServiceImpl.java:671)

该方法如下

@Override
    public String selectDirectLeader(String jobCode) {
        OrgPost orgPost = orgPostMapper.selectByJobCode(jobCode);
        String s = userMapper.selectByJobCode(orgPost.getFatherJobCode());
        if(StringUtils.isNotBlank(s)){
            return s;
        }else {
          return this.selectDirectLeader(orgPost.getFatherJobCode());
        }
    }

我们需要拿到参数去数据库中查询,才能找到空指针的具体原因,以下命令可以拿到参数值

$ watch com.hand.hap.account.service.impl.UserServiceImpl selectDirectLeader "{params[0]}"  -b -x 2
ts=2021-08-04 14:39:24; [cost=0.03817ms] result=@ArrayList[
    @String[00006098],
]
method=com.hand.hap.account.service.impl.UserServiceImpl.selectDirectLeader location=AtEnter
ts=2021-08-04 14:39:24; [cost=0.00804ms] result=@ArrayList[
    @String[00006098],
]

这样拿到参数后去数据库中查询,发现有个字段维护为空,修复下数据审批恢复正常。

Trackback

no comment untill now

Add your comment now