您的位置:首页 > 运维架构

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

2017-05-19 00:00 363 查看
摘要: Maven+SpringMVC+Spring+MyBatis+shiro+log日志管理(aop)+Exception异常统一处理(aop) ,将陆续集成新功能(github地址:https://github.com/jiangcaijun/ssm)

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,数据库插入数据为







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





4、相关参考链接

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

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

基于spring注解AOP的异常处理 - 小眼儿 - 博客园
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring aop log java