1. 程式人生 > >全域性異常處理器實現系統異常日誌記錄到資料庫

全域性異常處理器實現系統異常日誌記錄到資料庫

一、需求描述:

每次系統出現異常(有系統異常,也有業務功能的異常)都需要讓運維拉生產上的日誌檔案,檢視哪個地方出問題了,根據列印的log日誌定位問題點以及原因,比較浪費時間。為了解決這個問題,就想到:當系統出現異常時,將異常資訊記錄到資料庫中,然後以簡訊或郵件的形式通知管理員登入到管理系統後臺頁面進行檢視具體異常資訊,從而快速定位和判斷出現異常的位置和原因,直到修復。

二、實現思路:

1、建立一張資料表,專門用於存放異常資訊,例如:遠端訪問IP、異常所在類、異常出現的方法名稱、異常型別、異常發生時間以及異常的詳細內容。

2、自定義一個異常處理器,當系統出現異常時就攔截該異常,並且獲取必要資訊,記錄到資料庫儲存。

3、將異常資訊記錄到資料庫之後,再發送郵件/簡訊通知系統管理員,提醒其登入後臺系統進行檢視。(後臺系統做一個異常資訊的檢視頁面,進行異常日誌的檢視和管理)

三、實現步驟:

1、建立資料表

CREATE TABLE t_exception_log (
  id int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  ip varchar(20) DEFAULT NULL COMMENT '遠端訪問主機IP',
  class_name varchar(255) DEFAULT NULL COMMENT '類名',
  method_name varchar(120) DEFAULT NULL COMMENT '方法名',
  exception_type varchar(255) DEFAULT NULL COMMENT '異常型別',
  exception_msg text COMMENT '異常資訊',
  addtime datetime NOT NULL COMMENT '異常發生時間',
  is_view tinyint(2) DEFAULT '1' COMMENT '是否檢視,1:未檢視、2:已檢視',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='異常資訊日誌表';

2、自定義全域性異常處理器,攔截並處理異常資訊,傳送通知郵件

package com.its.handler;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import com.its.common.utils.IPUtils;
import com.its.common.utils.mail.MailSenderFactory;
import com.its.entity.ExceptionLog;
import com.its.service.system.ExceptionLogService;

/**
 * 全域性異常處理器
 * 
 * @author HY
 * @date 建立時間:2017年2月24日
 * @version
 */
public class GlobalExceptionHandler implements HandlerExceptionResolver {

	private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

	@Autowired
	private ExceptionLogService exceptionLogService;

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("ex", ex);

		if (handler instanceof HandlerMethod) {
			LOGGER.info(">>>>>>系統異常,記錄異常資訊到資料庫------start------");
			// 遠端訪問IP
			String ip = IPUtils.getRemortIP(request);
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			String className = handlerMethod.getBeanType().getName();
			String methodName = handlerMethod.getMethod().getName();
			StringWriter sw = new StringWriter();
			ex.printStackTrace(new PrintWriter(sw, true));

			// 插入異常日誌到資料庫
			ExceptionLog log = new ExceptionLog();
			log.setIp(ip);
			log.setClassName(className);
			log.setMethodName(methodName);
			log.setExceptionType(ex.getClass().getSimpleName());
			log.setExceptionMsg(sw.toString()); // 異常詳細資訊
			log.setIsView((byte) 1); // 預設未讀,1:為檢視、2:已檢視
			log.setAddtime(new Date());
			this.exceptionLogService.insertExceptionLogSelective(log);
			LOGGER.info(">>>>>>系統異常,記錄異常資訊到資料庫------end------");

			// TODO 此處先寫死。後期完善,接收人從資料庫配置中獲取
			try {
				String recipient = "
[email protected]
"; String subject = "【XXXX系統異常通知】"; Object content = "管理員,您好:<br/>   XXXX系統出現異常,請立即登入後臺系統:“系統管理”--“異常日誌管理”進行檢視。<br/>   " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); MailSenderFactory.getSender().send(recipient, subject, content); } catch (AddressException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); } } String viewName = "error/500"; if (ex instanceof UnauthorizedException) { // 後臺系統用的shiro做許可權控制,該異常也是shiro的異常 viewName = "error/403"; } return new ModelAndView(viewName, model); } }

3、在Spring配置檔案中加上如下內容:

<!-- 全域性異常處理器 -->
<bean id="exceptionResolver" class="com.xhh.ddhd.handler.GlobalExceptionHandler" />


其它程式碼:

ExceptionLog.java

package com.its.entity;

import java.util.Date;

/**
 * 異常資訊日誌表
 * 
 */
public class ExceptionLog {
	/**
	 * 主鍵id
	 */
	private Integer id;

	/**
	 * 遠端訪問主機IP
	 */
	private String ip;

	/**
	 * 類名
	 */
	private String className;

	/**
	 * 方法名
	 */
	private String methodName;

	/**
	 * 異常型別
	 */
	private String exceptionType;

	/**
	 * 異常發生時間
	 */
	private Date addtime;

	/**
	 * 是否檢視,1:未檢視、2:已檢視
	 */
	private Byte isView;

	/**
	 * 異常資訊
	 */
	private String exceptionMsg;

       
        // setter、getter方法略......

}

IPUtils.java

package com.its.common.utils;

import javax.servlet.http.HttpServletRequest;

/**
 * IP地址操作工具類
 * 
 * @author HY
 * @date 建立時間:2017年2月24日
 * @version
 */
public class IPUtils {

	/**
	 * 獲取遠端訪問主機ip地址
	 * 
	 * 建立時間:2017年2月24日
	 * 
	 * @author HY
	 * @param request
	 * @return
	 */
	public static String getRemortIP(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if ((ip == null) || (ip.length() == 0) || ("unknown".equalsIgnoreCase(ip))) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

}

MailSenderFactory.java

package com.its.common.utils.mail;

/**
 * 郵件傳送工廠
 * 
 * @author HY
 * @date 建立時間:2017年2月28日
 * @version
 */
public class MailSenderFactory {

	private static SimpleMailSender getInstance = null;

	private MailSenderFactory() {

	}

	public static SimpleMailSender getSender() {
		if (getInstance == null) {
			getInstance = new SimpleMailSender();
		}
		return getInstance;
	}

}

SimpleMailSender.java

package com.its.common.utils.mail;

import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import com.its.common.context.Global;

/**
 * 簡單郵件(不帶附件的郵件)傳送器
 * 
 * @author HY
 * @date 建立時間:2017年2月28日
 * @version
 */
public class SimpleMailSender {

	private final transient Properties props = System.getProperties();

	private transient MailAuthenticator authenticator;

	private transient Session session;
        // 從本地快取中獲取郵件相關配置資訊
	private static final String USERNAME = Global.getSysValue("mail_sender_username");
	private static final String PASSWORD = Global.getSysValue("mail_sender_password");
	private static final String MAIL_SMTP_HOST = Global.getSysValue("mail_smtp_host");
	private static final String MAIL_SMTP_PORT = Global.getSysValue("mail_smtp_port");
	private static final String MAIL_SMTP_AUTH = Global.getSysValue("mail_smtp_auth").equals("1") ? "true" : "false";

	public SimpleMailSender() {
		init();
	}

	/**
	 * 初始化
	 * 
	 * 建立時間:2017年2月28日
	 * 
	 * @author HY
	 * @param smtpHostName
	 *            SMTP伺服器地址
	 * @param username
	 *            傳送郵件的使用者名稱(郵箱地址)
	 * @param password
	 *            密碼
	 */
	private void init() {
		// 初始化Properties
		props.put("mail.smtp.host", MAIL_SMTP_HOST);
		props.put("mail.smtp.port", MAIL_SMTP_PORT);
		props.put("mail.smtp.auth", MAIL_SMTP_AUTH);
		// 驗證傳送者賬戶密碼
		authenticator = new MailAuthenticator(USERNAME, PASSWORD);
		// 建立Session
		session = Session.getInstance(props, authenticator);
	}

	/**
	 * 傳送郵件(單發)
	 * 
	 * 建立時間:2017年2月28日
	 * 
	 * @author HY
	 * @param recipient
	 *            收件人郵箱地址
	 * @param subject
	 *            郵件主題
	 * @param content
	 *            郵箱內容
	 * @throws AddressException
	 * @throws MessagingException
	 */
	public void send(String recipient, String subject, Object content) throws AddressException, MessagingException {
		// 1.建立mime型別郵件
		final Message message = new MimeMessage(session);
		// 2.設定發件人
		message.setFrom(new InternetAddress(authenticator.getUsername()));
		// 3.設定收件人
		message.setRecipient(RecipientType.TO, new InternetAddress(recipient));
		// 4.設定郵件主題
		message.setSubject(subject);
		// 5.設定郵件內容
		message.setContent(content.toString(), "text/html;charset=utf-8");
		// 6.傳送
		Transport.send(message);
	}

	/**
	 * 群發郵件
	 * 
	 * 建立時間:2017年2月28日
	 * 
	 * @author HY
	 * @param recipients
	 *            多個收件人(郵箱地址集合)
	 * @param subject
	 *            郵件主題
	 * @param content
	 *            郵件內容
	 * @throws AddressException
	 * @throws MessagingException
	 */
	public void send(List<String> recipients, String subject, Object content) throws AddressException, MessagingException {
		// 1.建立mime型別郵件
		final Message message = new MimeMessage(session);
		// 2.設定發件人
		message.setFrom(new InternetAddress(authenticator.getUsername()));
		// 3.設定收件人
		final int num = recipients.size();
		InternetAddress[] addresses = new InternetAddress[num];
		for (int i = 0; i < num; i++) {
			addresses[i] = new InternetAddress(recipients.get(i));
		}
		message.setRecipients(RecipientType.TO, addresses);
		// 4.設定郵件主題
		message.setSubject(subject);
		// 5.設定郵件內容
		message.setContent(content.toString(), "text/html;charset=utf-8");
		// 6.傳送
		Transport.send(message);
	}

}

MailAuthenticator.java

package com.its.common.utils.mail;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

/**
 * 郵箱伺服器登入驗證器
 * 
 * @author HY
 * @date 建立時間:2017年2月28日
 * @version
 */
public class MailAuthenticator extends Authenticator {

	/**
	 * 使用者名稱(登入郵箱地址)
	 */
	private String username = null;

	/**
	 * 密碼
	 */
	private String password = null;

	public MailAuthenticator() {

	}

	public MailAuthenticator(String username, String password) {
		this.username = username;
		this.password = password;
	}

	/**
	 * 賬戶認證
	 */
	@Override
	protected PasswordAuthentication getPasswordAuthentication() {
		return new PasswordAuthentication(username, password);
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

4、驗證

隨便在controller中製造一個異常,然後看能否成功即可。此處就略過了...

注意:

我們專案用的MVC框架是SpringMVC,如果在springmvc配置檔案中配置了SimpleMappingExceptionResolver的話,則會導致以上的自定義全域性異常處理器無法正常

工作,若配置了,則將springmvc配置檔案中的相關內容去掉即可。

5、實際效果:

頁面:


郵件:


相關推薦

全域性異常處理器實現系統異常日誌記錄資料庫

一、需求描述: 每次系統出現異常(有系統異常,也有業務功能的異常)都需要讓運維拉生產上的日誌檔案,檢視哪個地方出問題了,根據列印的log日誌定位問題點以及原因,比較浪費時間。為了解決這個問題,就想到:當系統出現異常時,將異常資訊記錄到資料庫中,然後以簡訊或郵件的形式通知管理

springboot—spring aop 實現系統操作日誌記錄存儲到數據庫

work prop 請求 pack spa 成功 方法 代碼 shu 原文:https://www.jianshu.com/p/d0bbdf1974bd 采用方案: 使用spring 的 aop 技術切到自定義註解上,針對不同註解標誌進行參數解析,記錄日誌

spring配置日誌切面,實現系統操作日誌記錄

//做系統是經常會遇到的情況之一,對系統操作日誌存表記錄 下面給出下例子 需要注意的是,日誌通常資料量會很大,建議已每個人月一張表,或者其他方式分表 例如:logs_2012_1             logs_2012_2             logs_2012_

Laravel 5.1 中的異常處理器和HTTP異常處理 abort()

錯誤日誌 exce ant upload 記錄 再次 .org splay don 原文 http://laravelacademy.org/post/1867.html 錯誤和異常是處理程序開發中不可回避的議題,在本地開發中我們往往希望能捕獲程序拋出的異常並將其顯示打印

使用Spring AOP自定義註解方式實現使用者操作日誌記錄

1,開發環境 作業系統:Windows 7 JDK:1.8.0_161 Eclipse:Mars.2 Release (4.5.2) 2,自定義註解類UserLog @Target({ElementType.PARAMETER, ElementType.METHOD}) @R

Springboot 如何使用AOP同時織入多個切面?實現使用者 操作日誌記錄功能

首先匯入AOP的pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-sta

Git設定全域性hooks規範化客戶端提交日誌記錄

背景:         專案由於git進行管控,對commit的日誌需要進行規範化輸出,git的hooks預設管控單倉庫,在.git/hooks裡面,預設關閉,如果啟用的話將字尾.sample去掉。系統提供的commit檢驗是git文字的最後是否有空格,根據規定,需要進行規

spring aop 實現使用者操作日誌記錄功能

首先寫好一個工具類 LogAspect.java package com.yangjf.commons; import java.lang.reflect.Method; import java.util.Date; import org.aspectj.lang.Join

《web工程aop實現前臺操作日誌記錄》初稿

寫日誌功能很繁瑣,博主廢了一些時間寫出來分享,用的ssm框架,後期會設定優先順序,避免所有方法都會被記錄到日誌。開始: 1、首先定義一個自定義註解@controllerLog和@serviceLog package com.hhwy.business.annotation

Spring Aop實現使用者操作日誌記錄

package com.jixi.controller; import com.jixi.pojo.Log; import com.jixi.pojo.User; import com.jixi.service.ILogService; import com.jixi.service.IUserServic

Laravel 中的異常處理器和HTTP異常處理例項教程

Laravel應用中所有的異常都通過 App\Exceptions\Handler 進行處理,下面我們先簡單分析下給異常處理器類的屬性和方法: $dontReport屬性 protected $d

課堂動手動腦驗證以及自定義異常實現異常處理——java異常

異常(exception):發生在程式執行期間,表明出現了一個非法執行的情況。許多JDK中的方法在檢測到非法情況時,都會丟擲一個異常物件。例如:陣列越界和被0除。 程式碼驗證: package test; import javax.swing.*; class AboutException { p

從壹開始前後端分離 [.netCore 不定期更新 ] 三十五║ 完美實現全域性異常日誌記錄

緣起 哈嘍我是不定期更新的日常,昨天群裡小夥伴問到了記錄日誌,當然,以前我也挖過這個坑,後來一直沒有來得及填上,也想著 swagger 一直又有錯誤資訊展示的功能,就遲遲沒有新增這個功能,不過昨天夜裡想了想,還是需要增加上,旨在提高框架的高效性。不定期日常就直接上程式碼了,我有一個小想法,就是希望大家有好的

SpringBoot通過AOP實現系統日誌記錄(三)-Mapper層日誌監控及自定義異常攔截

本文是SpringBoot通過AOP實現系統日誌記錄(三)-Mapper層日誌監控及異常攔截,若要實現Service層監控,請點選傳送門: SpringBoot通過AOP實現系統日誌記錄(二)-Service層日誌監控 由於公司業務上的需求,現在需要對整個系統做日誌效能監控,方便開發人員快速

SpringBoot通過Aspect切面實現系統日誌及Mapper異常攔截(包括日誌表設計)

最近專案中需要記錄服務端介面訪問日誌,所以在開發過程中回顧了一下AOP相關的內容,特此記錄,便於日後查閱。 1、引入依賴 <!-- 引入aop--> <dependency> <groupId>org.springframework.boo

Spring MVC + dubbo分散式系統基於全域性配置的異常處理器

使用@ControllerAdvice/@RestControllerAdvice配合@ExceptionHandler註解配置全域性的異常處理器,處理呼叫dubbo服務時的Exception。 測試程

SpringBoot通過Aspect切面實現系統日誌及Mapper異常攔截(附帶日誌表設計)

最近專案中需要記錄服務端介面訪問日誌,所以在開發過程中回顧了一下AOP相關的內容,特此記錄,便於日後查閱。 1、引入依賴 <!-- 引入aop--> <dependency> <groupId>org.springfra

springmvc在處理請求過程中出現異常資訊交由異常處理器進行處理,自定義異常處理器可以實現一個系統異常處理邏輯。為了區別不同的異常通常根據異常型別自定義異常類,這裡我們建立一個自定義系統異常,如果controller、service、dao丟擲此類異常說明是系統預期處理的異常資訊。

springmvc在處理請求過程中出現異常資訊交由異常處理器進行處理,自定義異常處理器可以實現一個系統的異常處理邏輯。 1.1 異常處理思路 系統中異常包括兩類:預期異常和執行時異常RuntimeException,前者通過捕獲異常從而獲取異常資訊,後者主要通過規範程式碼開發、測試通過手段減少執

springmvc在處理請求過程中出現異常信息交由異常處理器進行處理,自定義異常處理器可以實現一個系統異常處理邏輯。為了區別不同的異常通常根據異常類型自定義異常類,這裏我們創建一個自定義系統異常,如果controller、service、dao拋出此類異常說明是系統預期處理的異常信息。

ansi req -type this spring 進行 name ext code springmvc在處理請求過程中出現異常信息交由異常處理器進行處理,自定義異常處理器可以實現一個系統的異常處理邏輯。 1.1 異常處理思路 系統中異常包括兩類:預期異常和運行時異常Ru

使用@ExceptionHandler實現全域性異常處理器

使用SpringMVC提供的@ControllerAdvice,@ExceptionHandler可以方便的實現全域性異常處理器. 不僅方便,可以更細粒度的控制各種異常. 首先建立一個全域性異常處理類: /** * Created with IntelliJ IDEA.