SpringBoot获取完整的堆栈异常及异常堆栈日志简化

    在SpringBoot项目的Controller类里,在微信支付成功后,客户端向服务端查询订单结果的方法里面,希望将订单业务逻辑过程中的异常捕获,然后构建自己的异常类(MessageException),然后将异常写入到服务器端的日志(slf4j.Logger)里。

    @ResponseBody
	@RequestMapping(value = "query", method = RequestMethod.POST)
	public ApiResult orderPayQuery(@RequestBody QueryRequest request) throws Exception {
		
		String errorMsg = null;
		String outTradeNo = request.getOutTradeNo();
		try {

            // ......
			// 订单业务逻辑处理
            orderService.updateOrder(order, queryTradeStatus);
            // ......

		} catch (Exception e) {
			
			errorMsg =  "订单查询异常:" + e.toString();
		}
		
		if(errorMsg != null) {
			throw new MessageException(errorMsg);
		}
		return ApiResult.ok(orderService.getOrderResult(outTradeNo));
		
	}

    但是有天在修改支付业务sevice类 orderService.updateOrder(order, queryTradeStatus) 方法后,运行时抛出一个空指针异常,但是在服务端的error.log日志里却没有找到service类的抛出异常的具体代码行的信息。

    异常日志如下:显示最抛出异常是line 437,也就是“throw new MessageException(errorMsg);” 所在的代码行,具体的异常信息:订单查询异常:java.lang.NullPointerException。这个看不到Service类的业务逻辑方法抛出异常的具体是哪一行代码。

     分析下来,是因为抛出的空指针异常被try-catch后,获取到的是 e.toString()。源代码如下:

    /**
     * Returns a short description of this throwable.
     * The result is the concatenation of:
     * <ul>
     * <li> the {@linkplain Class#getName() name} of the class of this object
     * <li> ": " (a colon and a space)
     * <li> the result of invoking this object's {@link #getLocalizedMessage}
     *      method
     * </ul>
     * If {@code getLocalizedMessage} returns {@code null}, then just
     * the class name is returned.
     *
     * @return a string representation of this throwable.
     */
    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }

    这里 toString(),因为 getLocalizedMessage() 为 null, 所以返回的内容是异常类名:java.lang.NullPointerException, 所以要想获取到异常的完整的堆栈信息,应该采用下面的方法:

errorMsg =  "订单查询异常:" + stackTrace(e);

    /***
     * 获取栈信息
     * @param exception 异常对象
     * @return 栈追踪的完整信息
     */
    public static String stackTrace(Exception exception) {
        StringWriter stringWriter = new StringWriter();
        exception.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }

    所以,要想获取完整的堆栈信息,不要使用 toString() 方法 ,另外 getMessage() 方法可能获取的也是 null。

    另外,spring相关项目大部分情况下都会通过全局捕获异常,并将其包装为更人性化地提示给前端。并且在捕获异常后一般也都会记录到日志里(异常的调用堆栈信息),方便开发排查问题。这里有个比较明显的问题,如果记录全部异常堆栈信息,不但浪费磁盘空间,而且底层框架的堆栈信息对排查问题没有多大指导性意义。如果想继续优化,建议参考下文:

springboot之业务异常堆栈信息优化

猜你喜欢

转载自blog.csdn.net/crazestone0614/article/details/128667347