【Java】異常 打印

如何打印一個異常?

分兩種case吧。

1.不使用日誌框架,即簡單的systemout方式。

    public static void main(String args[]) {
        try {
            new Main().g();
        } catch (Exception e) {
            System.out.println(e);
            System.out.println(e.getMessage());
        }
    }

    public void g() {
        throw new RuntimeException("testEx");
    }

上面是一個簡單的例子,運行之後,輸出如下:

java.lang.RuntimeException: testEx
testEx

可以看到,似乎並不是我們想要的樣子,因爲沒有堆棧信息,所以直接打印e或者e.getMessage都不是正確姿勢。

爲什麼?

直接打印e,相當於直接調用Exception的toString方法,讓我們看看其實現,是繼承自其父類中的:

    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }

可以看到,僅僅是打印了異常的類名,確實沒有調用棧信息。這裏的message是一些額外的信息,是父類Throwable裏的一個成員變量,如果在構建一個Throwable實例(或子類)傳入一個string類型的message時,這個變量就會被賦值。

所以,異常類的toString方法僅僅會打印出異常的類名外加額外指定的message(如果有指定的話)。並且,e.getMessage()方法正是返回了額外的message信息。

那麼該如何打印堆棧信息呢?

繼續查看Throwable類,發現其有一個stacktrace變量以及相關的方法:

    /**
     * Native code saves some indication of the stack backtrace in this slot.
     */
    private transient Object backtrace;


    private synchronized StackTraceElement[] getOurStackTrace() {
        // Initialize stack trace field with information from
        // backtrace if this is the first call to this method
        if (stackTrace == UNASSIGNED_STACK ||
            (stackTrace == null && backtrace != null) /* Out of protocol state */) {
            int depth = getStackTraceDepth();
            stackTrace = new StackTraceElement[depth];
            for (int i=0; i < depth; i++)
                stackTrace[i] = getStackTraceElement(i);
        } else if (stackTrace == null) {
            return UNASSIGNED_STACK;
        }
        return stackTrace;
    }

    native StackTraceElement getStackTraceElement(int index);

看來,如果想打印異常棧,必須調用這些方法纔行。

我們可以看一下apache common庫中的工具類:

ExceptionUtils.getStackTrace(e)

結果如下:

java.lang.RuntimeException: testEx
    at com.liyao.s.Main.g(Main.java:114)
    at com.liyao.s.Main.main(Main.java:105)
可以看到,這次是成功打印了。

該方法的實現:

    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        throwable.printStackTrace(pw);
        return sw.getBuffer().toString();
    }

使用一個printwriter實例,將throwable實例的異常棧打印出來,這纔是正確打印異常的方法。

 

2.使用日誌框架,比如logback。

我們可以直接傳入一個throwable實例即可,不需要手動處理其調用棧,logback內部已經封裝了相關邏輯。

            logger.error("ex: ", e);

17:38:23.665 [main] ERROR com.liyao.s.Main - ex: 
java.lang.RuntimeException: testEx
    at com.liyao.s.Main.g(Main.java:113) ~[classes/:na]
    at com.liyao.s.Main.main(Main.java:105) ~[classes/:na]

日誌框架中都提供了包含Throwable類型參數的日誌打印接口:

    void xxx(String var1, Throwable var2);

內部會處理異常棧,我們不必再額外處理異常棧信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章