java異常及日志注意事項


一、異常注意事項

簡單整理了下關於異常的規范:

1)      在異常處理模塊中應提供精確、易讀的錯誤原因信息。

2)      不要處理能夠避免的異常。

3)      一個方法不應該拋出太多類型的異常,最好不超過三個。

4)      不要在try以及finally字段內部使用return語句。

5)      數據庫、io操作等涉及資源池泄漏的操作一定要在finally中來釋放資源。

6)      將try/catch區段置於循環之外。

7)      不要將異常用於程序流程控制,異常處理效率低於條件分支,且跳轉流程難以預測。

8)      在程序中使用異常處理還是使用錯誤返回碼處理,根據是否有利於程序結構來確定,並且異常和錯誤碼不應該混合使用,推薦使用異常。

9)      異常捕獲盡量不要直接catch(Exception),應該把異常細分處理。

10)   對於異常應分類處理,不要只捕捉Exception。

11)   對於異常不能忽略,並且應該記入日志。

二、異常優化

2.1、不要忽略checked Exception

  1)      處理異常,進行修復以讓程序繼續執行。例如在進行數據庫查詢時,數據庫連接斷鏈后重新鏈接成功。

  2)      在對異常進行分析后發現這里不能處理它,那么重新拋出異常,讓調用者處理。異常依次向上拋出,如果所有方法都不能恰當地處理異常,最終會在用戶以恰當的方式提示用戶,由用戶來判斷下一步處理措施。例如在進行數據庫查詢時,斷鏈后重試幾次依舊失敗的情況。

  3)      將異常轉換為其他異常再拋出,這時應該注意不要丟失原始異常信息。這種情況一般用於將底層異常封裝為應用層異常。

  4)      不要捕獲異常,應在函數定義中使用throws聲明將拋出該異常,讓調用者去處理該異常。

  5)      因此,當捕獲一個checked Exception的時候,必須對異常進行處理;如果認為不必要在這里作處理,就不要捕獲該異常,在方法體中聲明方法拋出異常,由上層調用者來處理該異常。

2.2、不要捕獲unchecked Exception

  有兩種checked Exception:

  1)  Error:這種情況屬於JVM發生了不可恢復的故障,例如內存溢出,無法處理。

  2)   RuntimeException:這種情況屬於錯誤的編碼導致的,出現異常后需要修改代碼才能修復,一般來說catch后沒有恰當的處理方式,因此不應該捕獲。(該規則不適用於守護線程中處理catch runtime exception)

2.3、不要一次捕獲所有的異常

  1)      針對try塊中拋出的每種Exception,很可能需要不同的處理和恢復措施,而由於這里只有一個catch塊,分別處理就不能實現。

  2)      Try塊中還可能拋出RuntimeException,代碼中捕獲了所有拋出的RuntimeException而沒有做任何處理,掩蓋了編程的錯誤,會導致程序難以調試。

2.4、使用finally塊釋放資源

  什么是資源:程序中使用的數量有限的對象,或者只能獨占式訪問的對象。例如:線程、線程池、數據庫連接、ftp連接,因為資源是“有限的“,因此資源使用后必須釋放,以避免程序中的資源被耗盡,影響程序運行。某些資源,使用完畢后會自動釋放,如線程,某些資源則需要顯示釋放,如數據庫連接。

    Finally關鍵字保證無論程序使用任何方式離開try塊,finally中的語句都會執行。

    因此,當你需要一個地方來執行在任何情況下都必須執行的代碼時,就可以將這些代碼放入到finally塊中。當你的程序中使用了資源,如數據庫連接,文件,Ftp連接,線程等,必須將示范給這些資源的代碼寫入finally塊中。

2.5、finally塊中不能拋出異常

  JAVA異常處理機制保證無論在任何情況下都先執行finally快的代碼,然后再離開整個try,catch,finally塊。在try,catch塊中向外拋出異常的時候,JAVA虛擬機先轉到finally塊執行finally中的代碼,然后將異常拋出。但如果在finally塊中拋出異常,try,catch塊的異常就不能拋出,外部捕捉到的異常就是finally塊中的異常信息,而try,catch塊中發生的真正的異常堆棧信息則丟失了。

2.6、拋出自定義異常時帶上原始異常信息

  Try塊捕獲拋出的異常后,拋出了新的自定義異常需要將原來異常的堆棧信息帶上,利於排錯。

2.7、不要同時使用異常機制和返回值來進行異常處理

  例如:

  try{

    ......

  }catch(Exception e){

    throw new Exception(msg,e);

    return " ";

   }

  混合使用JAVA異常處理機制和返回值使程序的異常處理部分變得“丑陋不堪”,並難以理解。如果有多種不同的異常情況,就定義多種不同的異常,而不要像上面代碼那樣綜合使用Exception和返回值。

三、日志優化及注意事項

3.1、Log上下文

  在Log中必須盡量帶入上下文的信息,對比以下兩個Log信息,后者比前者更有作用。

3.2、Error或者Warn級別中碰到Exception的情況盡量log完整的異常信息

  Error和Warn級別是比較嚴重的情況,意味着系統出錯或者危險,我們需要更多的信息來幫助分析原因,這個時候越多的信息越有幫助。包含以下內容:

  1)      你是在做什么事情的時候出錯了

  2)      你是在用什么數據做這個事情的時候出錯了

  3)      出錯的信息是什么

  對比下面三個Log語句,第一個提供了詳盡的信息,第二個只提供了部分信息,Exception的Message不一定包含有用的信息,第三個只告訴你出錯了,其他的你一無所知:

  1)      Log.error(“獲取用戶[{}]的用戶信息時出錯”,username,ex);

  2)      Log.error(“獲取用戶[{}]的用戶信息時出錯”,username,ex.getMessage());

  3)     Log.error(“獲取用戶[{}]的用戶信息時出錯”);

3.3、基本的Logger編碼

  1)      在一個對象中通常只使用一個Logger對象,Logger應該是static final的,只有在少數需要在構造函數中傳遞logger的情況下才使用private fianl。

  Static final Logger logger = LoggerFactory.getLogger(Main.class);

  2)      輸出Exceptions的全部Throwable信息,因為logger.error(msg)和logger.error(msg,e.getMessage())這樣的日志輸出方法會丟失掉最重要的StackTrace信息。

  Void foo(){

         Try{

      ......

         }catch(Exception e){

                Logger.error(e.getMessage());//錯誤

                Logger.error(“msg”,e.getMessage());//錯誤

      Logger.error(“msg”,e);//正確

         }

  }

  3)      對與不是特別重要的異常,不允許記錄日志后又拋出異常,因為這樣會多次記錄日志,只允許記錄一次

  Try{

    ......

  }catch(Exception ex){

    Logger.error(errorMessage,ex);

         Throw new UserSercviceException(errorMessage,ex);

  }

  4)      不允許出現System print(包括System.out.println和System.error.println)語句。

  5)      不允許出現e.printStackTrace。

  6)      日志性能的考慮,如果代碼為核心代碼,執行頻率非常高,則輸出日志建議增加判斷,尤其是低級別的輸出<debug、info、warn>。

  7)      有意義的日志

    通常情況下在程序日志里記錄一些比較有意義的狀態數據:程序啟動,退出的時間點:

    程序運行消耗時間;耗時程序的執行進度;重要變量的狀態變化。

    除此之外,在公共的日志里規避打印程序的調試或者提示信息。

  說明:大量地輸出無效日志,不利於系統性能提升,也不利於快速定位錯誤點。記錄日志請思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM