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