1. 程式人生 > >Spring實現統一捕獲介面層異常與郵件警報

Spring實現統一捕獲介面層異常與郵件警報

Java中提供的try{}catch{}finally{}來進行異常捕獲,然後當系統業務複雜,程式碼量則多,為了排除系統錯誤,我們一般在介面層進行異常捕獲,捕獲到異常時列印日誌,通過日誌的方式來排除錯誤。系統越複雜,介面數量越多,如果我們對所有介面都try{}catch{}的話,那麼工作量不僅會很大,以後系統維護難度也會變大。然後SpringMvc在框架層提供給了一種介面層統一處理異常的方法。

  • @ExceptionHandler:統一處理某一類異常,從而能夠減少程式碼重複率和複雜度。
public abstract class AbstractController {

    protected
Logger log = LoggerFactory.getLogger(this.getClass()); @ExceptionHandler @ResponseBody public String handleException(HttpServletRequest request, HttpServletResponse response, Exception ex){ if(!(ex instanceof NoNeedHandlerException)){ log.error("{}:\n介面引數:{}\n{}", request.getRequestURI(), JSONObject.toJSONString(request.getParameterMap()),CommonMail.getFromException(ex)); MonitorInfo monitorInfo = new
MonitorInfo(); monitorInfo.setMethodName(request.getRequestURI()); monitorInfo.setParams(request.getParameterMap()); monitorInfo.setException(ex); ExceptionMonitorHelper.monitor(monitorInfo); }else{ log.info("{}:\n介面引數:{}\n{}", request.getRequestURI(), JSONObject.toJSONString(request.getParameterMap()), ex.getMessage()); } if
(ex instanceof HandlerException){ HandlerException exception = (HandlerException)ex; return FmtResult(false, exception.getExceptionCode(), exception.getMessage(), ""); } return FmtResult(false, ResultCode.CODE_ERROR, ResultCode.ERROR_MESSAGE, ""); } }

只需要在handleException對異常進行進行處理即可,這樣介面層的異常全部在這個地方統一處理,開發成本和維護成本大大減低。

  • 郵件監控與警報
    對於一些異常資訊,一般都是通過列印日誌的方式來記錄,通過日誌的方式去追溯錯誤。但是日誌很多(尤其是日誌列印不規範),很難定位,日誌的及時性差,並不能及時通知到開發和運維人員線上錯誤,尤其是一些線上嚴重bug。在這裡可以通過郵件警報的方式來對系統進行監控。
    具體實現如下:
/**
 * ZSQ 監控資訊類
 */
public class MonitorInfo {

    private String methodName;

    private Map<String, ?> params;

    private Exception exception;

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Map<String, ?> getParams() {
        return params;
    }

    public void setParams(Map<String, ?> params) {
        this.params = params;
    }

    public Exception getException() {
        return exception;
    }

    public void setException(Exception exception) {
        this.exception = exception;
    }
}
/**
 * ZSQ 2018年4月8日14:39:51
 * 異常監控類
 */
public class ExceptionMonitorHelper {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionMonitorHelper.class);

    private static final LinkedBlockingQueue<MonitorInfo> queue = new LinkedBlockingQueue<>(0xfff8);

    private Thread monitorThread;

    private volatile boolean isStop = false;

    public static String ENVIROMENT = null;


    public void start(){
        monitorThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!isStop) {
                    try {
                        //可以呼叫queue.drainTo來批量處理異常資訊,提升讀取速率,減少加鎖和釋放鎖的次數
                        MonitorInfo moniterInfo = queue.take();
                        if (!(moniterInfo.getException() instanceof NoNeedHandlerException)) {
                            logger.debug("介面處理錯誤:{},{}", JSONObject.toJSONString(moniterInfo), moniterInfo.getException());
                            if (DEV_EMAILS != null) {
                                CommonMail.sendCodeWarnEmail(ENVIROMENT , moniterInfo, ExceptionLevel.SERIOUS);
                            }
                        }
                    } catch (Exception e) {
                        logger.error("exception monitor error {}", e);
                    }
                }
            }
        });
        monitorThread.setDaemon(true);//設定為守護執行緒 主執行緒退出時即退出
        monitorThread.start();
        logger.info("exception monitor start");
    }

    public void stop(){
        isStop = true;
        monitorThread.interrupt();
        try {
            monitorThread.join();
        } catch (InterruptedException e) {
            logger.error(e.getMessage(), e);
        }
    }

    public static void monitor(MonitorInfo failInfo){
        try {
            queue.put(failInfo);
        } catch (InterruptedException e) {
            logger.error("exception monitor put error{}", e);
        }
    }
}
/***
郵件工具類
**/
public class CommonMail {
    private static final String NAME = "網";
    private static final String USERNAME = "[email protected]";
    private static final String PASSWD = "Qi123456";
    private static String MAILHOST = "smtp.exmail.qq.com";// 傳送郵件的主機
    private static int SMTPPORT = 465;
    private static boolean TLS = true;
    private static boolean DEBUG = true;
    private static boolean SSL = true;

    //開發者郵箱配置
    private static String DEV_USERNAME;
    private static String DEV_PASSWD;
    private static String DEV_NAME;
    public static String []DEV_EMAILS;

    static {
        MAILHOST = ConfigPropertyConfigurer.getContextProperty("mail.mailhost") == null ? "smtp.exmail.qq.com" : ConfigPropertyConfigurer.getContextProperty("mail.mailhost");
        SMTPPORT = ConfigPropertyConfigurer.getContextProperty("mail.smtpport") == null ? 465 : Integer.parseInt( ConfigPropertyConfigurer.getContextProperty("mail.smtpport"));
        TLS = ConfigPropertyConfigurer.getContextProperty("mail.tls") == null ? true : Boolean.parseBoolean(ConfigPropertyConfigurer.getContextProperty("mail.tls"));
        DEBUG = ConfigPropertyConfigurer.getContextProperty("mail.debug") == null ? false : Boolean.parseBoolean(ConfigPropertyConfigurer.getContextProperty("mail.debug"));
        SSL = ConfigPropertyConfigurer.getContextProperty("mail.ssl") == null ? false : Boolean.parseBoolean(ConfigPropertyConfigurer.getContextProperty("mail.ssl"));
        DEV_USERNAME = ConfigPropertyConfigurer.getContextProperty("dev.mail.username");
        DEV_PASSWD = ConfigPropertyConfigurer.getContextProperty("dev.mail.password");
        DEV_NAME = ConfigPropertyConfigurer.getContextProperty("dev.mail.name");
        DEV_EMAILS = ConfigPropertyConfigurer.getContextProperty("dev.mail.receivers").split(",");
    }

    public static void sendDevEmail(String[] receivers, String subject, String msg){
     System.setProperty("java.net.preferIPv4Stack", "true");
        final SimpleEmail email = new SimpleEmail();
        email.setTLS(TLS);
        email.setDebug(DEBUG);
        email.setSSL(SSL);
        email.setHostName(MAILHOST);
        email.setSmtpPort(SMTPPORT);
        email.setAuthenticator(new DefaultAuthenticator(DEV_USERNAME, DEV_PASSWD));
        try {
            email.setFrom(DEV_USERNAME, DEV_NAME);
            for(String receiver:receivers){
             email.addTo(receiver);
            }            
            email.setCharset("GB2312");
            email.setSubject(subject);
            email.setMsg(msg);
            new Thread(){           
                public void run() {
                    try {
                        email.send();
                    } catch (EmailException e) {                        
                        e.printStackTrace();
                    }   
                } 
            }.start();
        } catch (EmailException e) {
            e.printStackTrace();
        }
   } 

    public static void sendCodeWarnEmail(final String subject, final String msg, int type){    
        String sub = "系統警報";
        if(1 == type){
            sub += "嚴重錯誤";
        }else  if(2 == type){
            sub += "普通錯誤";
        }else  if(3 == type){
            sub += "非預期結果";
        }
        sub += subject;

        final String tsub = sub;        

        sendDevEmail(DEV_EMAILS, tsub, msg);         
    }

    public static void sendCodeWarnEmail(String enviroment, MonitorInfo monitorInfo, int type){
        String subject = "環境:"+(StringUtils.isBlank(enviroment)?"正式":enviroment)+"\n"+
                "方法名稱:"+monitorInfo.getMethodName();

        String msg ="方法引數:\n";
        msg += (JSON.toJSON(monitorInfo.getParams())+"\n");
        msg += "異常資訊:\n";
        msg += CommonMail.getFromException(monitorInfo.getException());
        CommonMail.sendCodeWarnEmail(subject, msg, type);
    }

    public static String getFromException(Exception e){
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw, true));
        String str = sw.toString();
        return  str;
    }    
}

筆者只是拋磚引玉,大家有更好的方案或優化方法可以提出來,共同學習進步!!!Thank you!!!

相關推薦

Spring實現統一捕獲介面異常郵件警報

Java中提供的try{}catch{}finally{}來進行異常捕獲,然後當系統業務複雜,程式碼量則多,為了排除系統錯誤,我們一般在介面層進行異常捕獲,捕獲到異常時列印日誌,通過日誌的方式來排除錯誤。系統越複雜,介面數量越多,如果我們對所有介面都try{}c

Spring 實現執行緒介面 Runnable 註解注入失敗 @Resource annotation is not supported on static fields

原因就是spring和多執行緒安全的問題,不讓注入 解決方案有以下幾種 一.變數字首加static,生成set方法並加上@Resource,記住把set方法 static 去掉,否則會丟擲 @Resource annotation is not supported on static f

《TCP/IP詳解卷2:實現》筆記--介面:乙太網和環回

1.乙太網介面 Net/3乙太網裝置驅動程式都遵循同樣的設計。對於大多數Unix裝置驅動程式來說,都是這樣,因為寫一個新介面卡的驅動 程式總是在一個已有的驅動程式的基礎上修改而來的。下面我們簡要地概述一下乙太網的標準和一個乙太網驅動程式的設 計。下圖是一個IP分組的乙太網封

Spring+Hibernate操作持久HibernateTemplateHibernateTransactionManager

Spring對Hibernate有很好的支援    DataSource ->SessionFactory-> HibernateTranscationManagerHibernate中通過SessionFactory建立和維護Ses

springboot全域性異常處理(包含404錯誤處理) 一:解決spring boot中rest介面404,500等錯誤返回統一的json格式(備用地址) 二:SpringBoot入門——區域性全域性的異常處理(備用地址)

個人整理參考文件: 一:解決spring boot中rest介面404,500等錯誤返回統一的json格式(備用地址) 二:SpringBoot入門——區域性與全域性的異常處理(備用地址) 三:SpringBoot全域性異常處理(備用地址) 四:sprin

實現ResponseBodyAdvice介面統一攔截介面返回資料時,controller返回值是String 型別時異常

為實現介面統一返回資料時自定義的攔截響應資料時出現異常情況。 如果controller 方法返回值 是String 型別時丟擲異常。 異常如下: java.lang.ClassCastException: com.lk.face.common.model.ResponseDataVo c

spring Aop實現身份驗證和springboot異常統一處理

文章有不當之處,歡迎指正,如果喜歡微信閱讀,你也可以關注我的微信公眾號:好好學java,獲取優質學習資源。 一、spring Aop身份驗證 一般,如果使用者沒有登入的話,使用者只可以檢視商品,但是其他的,比如支付等是不能夠進行操作的,這個時候,我們

測試開發專題:spring-boot統一異常捕獲

# java異常介紹 異常時相對於return的一種退出機制,可以由系統觸發,也可由程式通過throw語句觸發,異常可以通過try/catch語句進行捕獲並處理,如果沒有捕獲,則會導致程式退出並輸出異常棧資訊,異常有不同的型別,所有異常類都有一個共同的父類Throwable,下面我們先從Throwable開

JS 實現變量在字符串中的多拼接嵌套

code source soft 編輯 str clas strong ros eid 簡單點。。。 ‘<a onclick="editCsRole(roleId)" class="openNewTab">編輯</a>‘; 其中 role

使用Spring MVC統一異常處理實戰

tro 處理機制 tor attr 運行 target icon message 404錯誤 原文地址:http://cgs1999.iteye.com/blog/1547197 1 描述 在J2EE項目的開發中,不管是對底層的數據庫操作過程,還是業務層的處理過程,還是控

Spring Boot? 統一異常處理

xtend import put itl ava advice efault ges spring 效果區: 代碼區: package com.wls.integrateplugs.exception.dto; public class ErrorI

spring boot 統一異常處理

res status fin erro throwable instance 拋出異常 方案 let 需求源自於任何一個業務的編寫總會有各種各樣的條件判斷,需要時時手動拋出異常,又希望讓接口返回友好的錯誤信息。 spring boot提供的幫助是自動將異常重定向到路由為/e

java異常——捕獲異常+再次拋出異常異常

init fileinput 代碼塊 演示 建議 實例 ssa ack 拋出異常 【0】README 0.1) 本文描述+源代碼均 轉自 core java volume 1, 旨在理解 java異常——捕獲異常+再次拋出異常與異常鏈

JAVA面向物件程式設計之購物車介面的設計功能的實現

1、小組成員及分工 小組成員 負責工作 蘭澤祥 負責總體介面、Git程式碼庫 吳修恩 負責MVC模式、類名包名的規範化 2、Git 倉庫地址 倉庫地址:https://gitee

Android系統硬體抽象原理實現之WIFI

http://m.blog.csdn.net/linux_zkf/article/details/7492720 整個WIFIHAL實現都很簡單,都是對wpa_supplicant的操作和使用,如果需要自己實現 WIFI HAL可以參考wifi.c來實現wifi.h中所定義的

Java——異常捕獲

幾乎所有的程式碼裡面都會出現異常,為了保證程式在出現異常之後可以正常執行完畢,就需要進行異常處理。 異常的繼承類結構: 所有的異常都是由Throwable繼承而來。 Error類:描述Java執行時內部錯誤與資源耗盡錯誤。這種內部錯誤一旦出現,除了告知使用者並使程式安全終止之外,再

spring boot 統一返回資料及全域性異常處理

記錄關於spring boot 統一返回資料及全域性異常處理的操作實現。 一、統一返回資料 1、定義一個超類:BaseResponseVo @Data @NoArgsConstructor public class BaseResponseVo{ protected Integer r

WebService—CXF整合Spring實現介面釋出和呼叫過程

CXF整合Spring實現介面釋出 釋出過程如下: 1、引入jar包(基於maven管理) <dependency> <groupId>org.apache.cxf</groupId> <artifactId>

CSharp的介面顯式實現和IDisposable介面using關鍵字的關係

近日,有同事寫的http下載模組出現了一些問題,在Review程式碼的過程中發現一個奇怪的地方: 針對從WebResponse中取出來的Stream,在用完以後,對於Stream手動依次呼叫了Close、Dispose。 if (reader != null) { reader.Clos