SSM框架_4(添加log日志管理(aop)+Exception异常统一处理(aop))

2017-05-15(添加log日志管理(aop)+Exception异常统一处理(aop))

github版本号 b3a44f8a7a4452fd28bf2c4562a3e2a6aa7221dc

1、添加log日志管理(aop)

1.1 Log.java:定义Log注解,Target为类和方法上
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 {};
}
1.2 OperLog.java:model类,即日志
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;// 描述信息',
}

1.3 LogAspect.java:日志切面

Pointcut为匹配含有Log注解的类和方法,同时将某些重要参数,比如操作用户ID、操作人名称(如果已登录的话)、操作时间(yyyy-MM-dd HH:mm:ss)、客户端IP、访问url、请求方法、操作事件(删除,新增,修改,查询,登录,退出等,即Log注解中的value的值)、操作状态、操作状态等存到数据库中。

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();
		}
	}
}
1.4 spring-mvc.xml:添加aop相关配置
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 日志注解 -->
<bean id="LogAspect" class="com.ssm.annotation.LogAspect"/>

1.5 IndexController.IndexController:Log注解的实际应用

@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";
	}
	
}

2、Exception异常统一处理(aop)

2.1ExceptionHandler.java:aop

异常处理,除常规日志字段外,还将 具体错误信息Exception类型该方法实际入参都保存到数据库中

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();
    }
}

2.2 spring-mvc.xml:添加aop相关配置
<!-- 日志注解 -->
<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>

3 相关日志

3.1 为方便测试,利用guestError方法来抛出异常
@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";
}
3.2 当url为 http://127.0.0.1:8080/项目名/guestError,数据库插入数据为

数据库异常日志_1

数据库异常日志_2

数据库异常日志_3

3.3 未登录前访问,与登录后访问所留下的日志记录

控制台日志

数据库日志

4、相关参考链接

Spring aop 实现异常拦截 - 涂墨留香 - 博客园

利用spring aop统一处理异常和打日志 - Ray的专栏 - 博客频道 - CSDN.NET

基于spring注解AOP的异常处理 - 小眼儿 - 博客园

转载于:https://my.oschina.net/u/3136014/blog/904643

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值