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操作持久層HibernateTemplate與HibernateTransactionManager
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異常——捕獲異常+再次拋出異常與異常鏈
Spring MVC - MultipartFile實現檔案上傳(單檔案與多檔案上傳)
前提:引入jar包。 <dependency>
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