0731打印异常的堆栈信息

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_30531261/article/details/81436020

让logger语句记录异常的堆栈信息

前言

补个日志。

其实CSND也有类似的文章,但是我也有思考过,所以我也想记录一下。我们直接用logger.info("异常信息为:"+e)或者logger.info(e.getMessage())只能记录到异常的描述信息,却没有其异常具体发生在哪一行代码。这样即使通过日志发现出现了异常,也没法马上定位问题。因此就催生了一个想法,是否能像在idea本地跑程序时出现未捕获的异常时,控制台能打印出完整的错误堆栈信息,把这个信息记录到logger语句中。

描述

写接口时,为了方便后期查看日志定位问题,我对service的logger日志进行了改造,将所有的logger语句拼接成一条记录,然后再打印出来。因此会在service层最外层使用try catch finally。这样一个请求的日志就能统一出现,不会由于多个请求导致的日志穿插记录。日志如:

[2018-06-26 17:09:54.038] -- [http-nio-8079-exec-27] -- [INFO] -- [OrderServiceImpl.java:513 >>>> Method = query] -- [Content = 
********【1121订单查询】标识:useruuid==d86a732d-2da6-11e8-xxx-xxxx,source==1,phone==135705xxxxx,orderNo==201806260000xxxxx
********【1121订单查询】入参:com.uroad.etc.dto.OrderInput@6ab68cb3[useruuid=d86a732d-2da6-11e8-xxx-xxxx,orderNo=2018062600xxxx,source=1]
********【1121订单查询】请求:request decode data:1121110802CCF5B968C014E702xxxxx
********【1121订单查询】响应:response deceode data:90001A4B0D01060F103136303832323xxxxxx
********【1121订单查询】updateTradStateAndPushMsgByCardNo 更新表结果:1,cardNo:1608227140xxx
********【1121订单查询】出参:com.uroad.etc.dto.OrderOutput@42879e4b[tradeText=2018-06-26 17:09:31,tradeDate=2018xxx,tradTime=170xxx,orderNo=201806260000003xxx,tradMoney=1,tradState=3,serialNo=<null>,cardNo=160822714xxx,terminalNo=<null>,billState=<null>,orderType=1,cardType=<null>,plateNo=<null>,payChannel=6]]

但这样做有个不好的地方,就是当出现异常的时候,虽然捕获了,也打印了异常信息,但是异常信息不能准确定位错误信息,会像这样:

[2018-07-31 21:41:04] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content = 
异常信息为:java.lang.NullPointerException]

假如service层代码简单,那么可以很快就定位出出现异常的地方。可是假如service层稍稍复杂些,代码稍稍多一些,就没法迅速的找处是哪个地方出现异常。

像我们在本地测试的时候,当出现了意料之外的运行时异常,控制台是能打印出完整的错误信息,像这样:

public static void main(String[] args) {
    String s = "1";
    if(s.equals("1")){
        s=null;
    }
    s.equals("1");
}

控制台是能打印出完整信息的:

Exception in thread "main" java.lang.NullPointerException
    at TestException.main(TestException.java:14)

这样我们就能一下子就知道错误是在TestException的第14行,即s.equals("1)这里,那我们就能推出是对象s是空对象。

但是由于我在service层的最外层使用了try catch,即我的代码是像这样的:

public static void main(String[] args) {
    try {
        String s = "1";
        if(s.equals("1")){
            s=null;
        }
        s.equals("1");
    }catch (Exception e){
        logger.info("异常信息为:" + e);
    }
}

这样,当出现异常,异常信息为:

[2018-07-31 21:46:04] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content = 
异常信息为:java.lang.NullPointerException]

这样,只能知道是第17行的logger语句打印的信息。但是正如我前面所说我在service层使用了try catch finally来处理日志,所以只有一个logger语句,所以没法知道错误发生在哪里。

解决方法

于是思考,是否有这么一个方法,可以打印出完整的堆栈信息,最终还是在网上找到了:
参考文章:https://blog.csdn.net/wwsscc168/article/details/51837662

public static String toStackTrace(Exception e)
{
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);

    try
    {
        e.printStackTrace(pw);
        return sw.toString();
    }
    catch(Exception e1)
    {
        return "";
    }
}

只是,不知道这样会不会很占内存。很占空间。

解决方法2

这样也能打印出错误位置:logger.info(e.getMessage(),e);

输出如:

[2018-07-31 21:52:57] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content = 
null]
java.lang.NullPointerException: null
    at TestException.main(TestException.java:15)

这样也行:

logger.info("出现异常啦:",e);

但是这样就必须得一个单独的logger语句。

可以通过线程号去追踪。。。

最优解决方法

找到了个新方法,不需要改JVM配置,也不需要用Write流:

public static String logClzInfo(Exception e) {
    StringBuffer sb = new StringBuffer();
    sb.append(e.getClass() + " " + e.getMessage() + "\n");
    StackTraceElement[] stackTraceElement = e.getStackTrace();
    for (StackTraceElement traceElement : stackTraceElement) {
        sb.append("\tat " + traceElement + "\n");
    }
    return sb.toString();
}

这样,就能在catch中通过参数e获取到完整的错误信息了。这样一来,以后出了问题,通过日志,就能一下找到问题所在了。

猜你喜欢

转载自blog.csdn.net/weixin_30531261/article/details/81436020