SSH異常和日誌處理方案

 

1         異常和日誌的作用

1.1.    異常的作用

Java異常機制是爲了對程序中可能出現的已知錯誤進行捕獲,並進行相應處理。從是否反饋給用戶來看,存在兩類異常:

系統異常:這類異常由系統本身的低級異常引起,例如數據庫連接失敗、內存溢出、空指針異常等等,這類異常不需要出現在前臺,因爲用戶看不懂也沒有必要看到這些異常信息。這類異常需要在日誌中進行完整記錄以供日後開發人員進行查看分析。

應用異常:即自定義異常,這類異常需要通過前臺反饋給用戶,友好提示用戶當前操作異常。應用異常通過系統異常轉換而來,例如新建用戶時,發生“主鍵衝突異常”,則需要在UserinfoDao中將“主鍵衝突異常”捕獲,並轉換爲應用異常,異常提示信息設爲“該用戶名XXX已存在,請使用其它用戶名”,並將該異常信息反饋給前臺。只要系統異常影響到的用戶的當前操作,就必須給用戶提示信息,比如“系統後臺發生錯誤,請稍後再試”等。應用異常應按照提示方式的異常進行分類,對應不同的提示頁面。

1.2.    日誌的作用

系統運行日誌:記錄系統的運行情況,跟蹤代碼運行時軌跡;

異常和錯誤日誌:記錄異常堆棧信息,以供開發人員查看分析;

業務日誌:記錄業務信息和用戶操作,例如用戶登錄、刪除數據、更新數據等。

2.      異常的處理原則

1、避免過大的try塊,不要把不會出現異常的代碼放到try塊裏面,儘量保持一個try塊對應一個或多個異常。

2、細化異常的類型,不要不管什麼類型的異常都寫成Excetpioncatch語句表示我們預期會出現某種異常,而且希望能夠處理該異常。異常類的作用就是告訴Java編譯器我們想要處理的是哪一種異常,然後針對具體的異常類進行不同的處理。例如在DAO層中我們應該只捕獲SQLExceptionDataAccessException(Spring自定義的數據訪問異常類)這些數據庫異常類,其他的異常NullPointExceptionNumberFormatException等應通過檢測代碼增加其健壯性來解決,而不應該通過try..catch(Exception e)…語句捕獲所有的異常。

3、不要把自己能處理的異常拋給別人,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新拋出新類型的異常。。處理方式包括:

Ø  處理異常。針對該異常採取一些行動,例如修正問題、提醒某個人或進行其他一些處理,要根據具體的情形確定應該採取的動作。再次說明,調用printStackTrace算不上已經處理好了異常

Ø  重新拋出異常。處理異常的代碼在分析異常之後,認爲自己不能處理它,重新拋出異常也不失爲一種選擇。

Ø  把該異常轉換成另一種異常。大多數情況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被用戶瞭解的異常)。

4、如果對catch塊儘量保持一個塊捕獲一類異常,在catch語句中儘可能指定具體的異常類型,必要時使用多個catch

例:

      try {

      catch (Exception e) {

           e.printStackTrace();

           log.error("UserinfoDao execute() failed");

}

這段代碼捕獲了異常,但實際上對異常並沒有進行處理,可以算得上Java編程中的殺手。按照這個方式,在DAO層發生異常被忽略,Service層就認爲DAO層運行正確,就不會回滾,事務控制就沒有任何作用!!!

Ø  printStackTrace對調試程序有幫助,但程序調試階段結束之後,printStackTrace就不應再在異常處理模塊中擔負主要責任。

Ø  日誌儘量在系統中的各個出口,例如Action、調度方法等處統一記錄,可減少工作量,況且日誌"UserinfoDao execute() failed”並沒有說明異常的詳細信息,沒有必要向日志輸出這句話。

Ø  即使catch中加入throw new RuntimeException(e);語句。如果捕獲的異常是RuntimeException,更沒有必要將異常再次轉化爲RuntimeException,這樣不僅異常的本身的類型丟失,重新定義異常也造成一定消耗。

所以該處的處理原則應是:如果該處異常需要能轉化爲業務異常反饋給用戶,則需要捕捉低級異常並轉換成業務異常上拋,否則不做任何處理!!!

6、不要用try...catch參與控制程序流程,異常控制的根本目的是處理程序的非正常情況。

3.      開發中異常的處理方式

本系統使用SSH框架,DAO+Service+Action三層架構,捕獲原則是隻有將低級系統異常轉化爲應用異常的需要才進行捕捉。

各層的處理方式如下:

DAO層:引發DAO異常的問題往往是不可恢復的,如數據連接失敗,SQL語句存在語法錯誤,強制捕捉的檢查型異常除了限制開發人員的自由度以外,並沒有提供什麼有意義的作用。因此,Spring的異常體系都是建立在運行期異常的基礎上,這些異常都繼承於DataAccessExceptionRuntimeException異常子類),所以,除了出於將低級系統異常轉化爲應用異常的需要,沒有必要捕獲異常,讓DAO類自動上拋異常即可。

Service層:只捕獲自定義應用異常,其他異常上拋。

Action:只捕獲自定義應用異常,其他異常上拋。Struts2提供了異常攔截器,攔截器會將定義的異常捕獲,記錄日誌,然後根據配置的異常的類型順序跳轉到相應的頁面。配置如下:

struts.xml配置文件

<interceptor-ref name="defaultStack">

       <param name="exception.logEnabled">true</param><!--開啓日誌記錄 -->

       <param name="exception.logLevel">error</param><!-- 日誌級別爲ERROR -->

</interceptor-ref>

<global-exception-mappings><!—異常類和跳轉頁面配置 -->

       <exception-mapping result="basicerror" exception="com.***.***.exception.BasicException"/>

       <exception-mapping result="error" exception="java.lang.Exception"/>

</global-exception-mappings>

com.***.***.exception.BasicException爲自定義應用異常,如果客戶端的請求執行過過程中產生com.***.***.exception.BasicException異常,則會自動轉到basicerror頁面,從而給用戶相應的提示。

basicerror頁面

<%@ page language="java" contentType="text/xml; charset=UTF-8"       pageEncoding="UTF-8"%>${exception.message }

4.      自定義應用異常

 

異常名稱

說明

com.***.***.exception.BasicException

基礎異常類,本系統中所有的自定義異常類均需繼承本類

com.***.***.exception. DuplicateKeyException

主鍵衝突異常,繼承BasicException

 

Userinfo save方法異常處理

@Override

       public void save(Userinfo entity) {

              try {

                     super.save(entity);

              catch (DataIntegrityViolationException e) {

                     if(e.getCause().getCause() instanceof SQLException){

                            SQLException sqlE = (SQLException)e.getCause().getCause();

                            if(sqlE.getErrorCode()==1){//ORACLE主鍵衝突異常代碼

                                   throw new DuplicateKeyException("用戶名:"+entity.getUserid()+"已存在,請使用其他用戶名");

                            }

                     }

              }

       }

5.      日誌配置

系統中目前配置了三個日誌記錄器,一個爲異常記錄器,專門記錄異常信息,日誌文件到達一定大小後將產生新的日誌文件,文件名稱爲exception.log,另一個爲系統運行記錄器,按照日期記錄所有的日誌信息。

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