github版本號 b3a44f8a7a4452fd28bf2c4562a3e2a6aa7221dchtml
package com.ssm.annotation; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 操做事件,idea test */ String value(); /** * 字段組裝描述內容, * 如{"name=名稱","status=狀態,1=成功;2=失敗"}, * 表單參數爲:name=張三&status=1這樣生成的描述信息爲: * 名稱=張三,狀態=成功 */ String[] entry() default {}; }
package com.ssm.model; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; import java.io.Serializable; import java.util.Date; /*** * @description 對應t_log表 * */ @Data public class OperLog implements Serializable { private static final long serialVersionUID = -8690056878905494181L; private Long id; private String userId;// '操做用戶ID', private String userName;// '操做人名稱', @JSONField (format="yyyy-MM-dd HH:mm:ss") private Date operTime;// '操做時間(yyyy-MM-dd HH:mm:ss)', private String clientIp;// '客戶端IP', private String reqUrl;// 訪問url private String method;// 請求方法 private String operEvent;// 操做事件(刪除,新增,修改,查詢,登陸,退出)', private int operStatus;// '操做狀態(1:成功,2:失敗)', private String logDesc;// 描述信息', }
Pointcut爲匹配含有Log註解的類和方法,同時將某些重要參數,好比操做用戶ID、操做人名稱(若是已登陸的話)、操做時間(yyyy-MM-dd HH:mm:ss)、客戶端IP、訪問url、請求方法、操做事件(刪除,新增,修改,查詢,登陸,退出等,即Log註解中的value的值)、操做狀態、操做狀態等存到數據庫中。java
package com.ssm.annotation; import com.ssm.model.OperLog; import com.ssm.model.User; import com.ssm.service.LogService; import com.ssm.service.UserService; import com.ssm.utils.ConstantVar; import com.ssm.utils.IPAddressUtil; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; /** * 日誌切面 */ @Aspect @Component public class LogAspect { @Autowired private HttpServletRequest request; @Autowired private UserService userService; // 注入Service用於把日誌保存數據庫 @Autowired private LogService logService; private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); // Controller層切點 @Pointcut("@annotation(com.ssm.annotation.Log)") //@annotation用於匹配當前執行方法持有指定註解的方法; public void logAspect() { } /** * 後置通知 用於攔截Controller層記錄用戶的操做 * * @param joinPoint * 切點 * @param rvt * 指定一個 returning 屬性,該屬性值爲 rvt , 表示 容許在 加強處理方法中使用名爲rvt的形參,該形參表明目標方法的返回值。 */ @AfterReturning(returning = "rvt", pointcut = "logAspect()") public void after(JoinPoint joinPoint, Object rvt) { try { String targetName = joinPoint.getTarget().getClass().getName(); // 請求類名稱 String methodName = joinPoint.getSignature().getName(); // 請求方法 Object[] arguments = joinPoint.getArgs(); Class<?> targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String value = ""; StringBuffer descr = new StringBuffer(); for (Method method : methods) { if (method.getName().equals(methodName)) { @SuppressWarnings("rawtypes") Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { if(method.getAnnotation(Log.class) != null){ // 若是包含註解@log() value = method.getAnnotation(Log.class).value(); String[] anEntry = method.getAnnotation(Log.class).entry(); for (String en : anEntry) { String[] entry = en.split(","); String[] nameArray = entry[0].split("="); String val = StringUtils.defaultString(request.getParameter(nameArray[0]), ""); if (!StringUtils.isBlank(val)) { if (entry.length == 2) { String[] valueEntry = entry[1].split(";"); for (String valueArray : valueEntry) { String[] vals = valueArray.split("="); if (vals[0].equalsIgnoreCase(val)) { val = vals[1]; break; } } } descr.append(','); descr.append(nameArray[1]); descr.append('='); descr.append(val); } } if (descr.length() > 0) { descr.deleteCharAt(0); } break; } } } } OperLog operLog = new OperLog(); if (request.getRequestURI().contains("/login") && "loginPost".equalsIgnoreCase(joinPoint.getSignature().getName())) { // 用戶登陸日誌記錄 operLog.setUserId(request.getParameter("username")); Subject curUser = SecurityUtils.getSubject(); User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER); if (loginUser != null) { operLog.setUserId(loginUser.getId()); operLog.setUserName(loginUser.getUserName()); operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_SUCCESS); } else { operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_FAIL); } }else if (request.getRequestURI().contains("/logout") && "logout".equalsIgnoreCase(joinPoint.getSignature().getName())) { // 退出日誌 String userId = (String) arguments[0]; operLog.setUserId(userId); User loginUser = userService.findUserByUserId(userId); operLog.setUserName(loginUser.getUserName()); } else { Subject curUser = SecurityUtils.getSubject(); if(curUser.getPrincipal()!=null){ //從session中獲取當前登陸用戶的User對象 User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER); operLog.setUserName(loginUser.getUserName()); operLog.setUserId(loginUser.getId()); } } if(new Integer(operLog.getOperStatus())!=null){ operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_SUCCESS); } operLog.setClientIp(IPAddressUtil.getIpAddress(request)); operLog.setReqUrl(request.getRequestURI()); joinPoint.getSignature(); operLog.setMethod(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName()); operLog.setOperEvent(value); operLog.setLogDesc("該方法實際入參爲:"+descr.toString()); // 描述信息 // 保存數據庫 logService.insertLog(operLog); } catch (Exception e) { // 記錄本地異常日誌 logger.error("後置通知異常:異常信息:", e.getMessage()); e.printStackTrace(); } } }
<!-- 啓動對@AspectJ註解的支持 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <!-- 日誌註解 --> <bean id="LogAspect" class="com.ssm.annotation.LogAspect"/>
@Controller public class IndexController { @Log(value = "進入guest", entry = { "parameter1=參數1","parameter2=參數2", }) @RequestMapping(value = "/guest", method = RequestMethod.GET) public String guest(Model model,String parameter1,Integer parameter2) { return "guest/guestIndex"; } }
異常處理,除常規日誌字段外,還將 具體錯誤信息 ,Exception類型 ,該方法實際入參都保存到數據庫中git
package com.ssm.aop; import com.ssm.annotation.Log; import com.ssm.model.OperLog; import com.ssm.model.User; import com.ssm.service.LogService; import com.ssm.utils.IPAddressUtil; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.springframework.aop.ThrowsAdvice; import com.ssm.utils.ConstantVar; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.lang.reflect.Method; /** * aop:異常處理 */ public class ExceptionHandler implements ThrowsAdvice { private static final Logger LOG = Logger.getLogger(ExceptionHandler.class); @Autowired private HttpServletRequest request; @Autowired private LogService logService; public void afterThrowing(JoinPoint joinPoint, Exception e) { LOG.error("出現Exception:url爲" + request.getRequestURI() + ";錯誤類型爲"+e.getStackTrace()[0]+""); OperLog operLog = new OperLog(); StringBuffer operEvent = new StringBuffer(); String descr4Exception = ""; // 具體錯誤信息 try { String targetName = joinPoint.getTarget().getClass().getName(); // 請求類名稱 String methodName = joinPoint.getSignature().getName(); // 請求方法 Object[] arguments = joinPoint.getArgs(); Class<?> targetClass = null; targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { if(method.getAnnotation(Log.class) != null){ // 若是包含註解@log() operEvent.append(method.getAnnotation(Log.class).value()); operEvent.append("。"); break; } } } } operEvent.append("該方法實際入參爲:"); for (int i = 0; i < joinPoint.getArgs().length; i++) { operEvent.append(joinPoint.getArgs()[i]); operEvent.append(","); } operEvent.deleteCharAt(operEvent.length()-1); //刪除最後一個 "," operEvent.append("。Exception類型爲:"); operEvent.append(e.getClass()); descr4Exception = createExceptionDetail(e); Subject curUser = SecurityUtils.getSubject(); if (request.getRequestURI().contains("/logout") && "logout".equalsIgnoreCase(joinPoint.getSignature().getName())) { // 退出日誌 String userId = (String) arguments[0]; operLog.setUserId(userId); } if(curUser.getPrincipal()!=null){ //從session中獲取當前登陸用戶的User對象 User loginUser = (User) curUser.getSession().getAttribute(ConstantVar.LOGIN_USER); operLog.setUserName(loginUser.getUserName()); operLog.setUserId(loginUser.getId()); } operLog.setClientIp(IPAddressUtil.getIpAddress(request)); }catch (ClassNotFoundException e1) { e1.printStackTrace(); LOG.error("實例化失敗:ClassNotFoundException"); }catch (IOException e2) { e2.printStackTrace(); operLog.setClientIp("未知IP:IOException"); } operLog.setReqUrl(request.getRequestURI()); operLog.setMethod(joinPoint.getSignature().getDeclaringTypeName()+","+joinPoint.getSignature().getName()); operLog.setOperEvent((operEvent.toString()).length()>255?(operEvent.toString()).substring(0,255):operEvent.toString()); operLog.setOperStatus(ConstantVar.OPER_LOG_STATUS_FAIL); operLog.setLogDesc("具體Exception信息爲:"+ descr4Exception); try{ // 保存到數據庫 logService.insertLog(operLog); }catch (Exception ex){ ex.printStackTrace(); LOG.error("log保存數據庫失敗"); } } /** * 異常數組轉成字符串 * * @param e * @return * @author * @2016-8-18 下午5:43:20 */ private String createExceptionDetail(Exception e) { StackTraceElement[] stackTraceArray = e.getStackTrace(); StringBuilder detail = new StringBuilder(); for (int i = 0; i < stackTraceArray.length; i++) { //255位,此處是考慮數據庫相應字段的大小限制 if((detail.toString()+stackTraceArray[i]).length() > 255){ return detail.toString(); } detail.append(stackTraceArray[i] + "\r\n"); } return detail.toString(); } }
<!-- 日誌註解 --> <bean id="LogAspect" class="com.ssm.annotation.LogAspect"/> <!-- 異常捕獲aop --> <bean id="exceptionHandler" class="com.ssm.aop.ExceptionHandler" /> <aop:config> <aop:aspect ref="exceptionHandler"> <aop:pointcut id="exceptionService" expression="execution(* com.ssm.*.*.*(..))" /> <aop:after-throwing pointcut-ref="exceptionService" method="afterThrowing" throwing="e"/> </aop:aspect> </aop:config>
@Log(value = "進入guest,此處模擬拋出異常") @RequestMapping(value = "/guestError", method = RequestMethod.GET) public String guestError(Model model) { LOG.info("進入guest的index"); if(true) { throw new RuntimeException(); } return "guest/guestIndex"; }
Spring aop 實現異常攔截 - 塗墨留香 - 博客園github