不论在神魔类型的项目中,日志系统绝对是一个不可少的存在,那么,怎末用一个最简便的方式来实现日志在数据库中的存储呢??最近在项目中正好负责了日志模块的实现,就简单记录一下。
我在这个项目中使用的是aop自定义注解的方式,大致步骤如下:
1.第一步,首先需要先定义一个注解类,来实现部分方法介绍信息的传递和切入点的切入时机。
package com.cms.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 操作日志注解类
* 请填上日志描述:desc
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerOptLog {
/**
* 日志描述
* @return
*/
String desc() default "";
}
spring自定义注解,不多解释,其中属性desc是为切面传输必要参数的。
2.第二步,需要定义一个切面类,并在切面类中编写aop切入的前后通知等方法。
package com.cms.common.annotation;
import com.cms.common.utils.IPUtil;
import com.cms.common.utils.security.AccountShiroUtil;
import com.cms.common.utils.security.UserShiroUtil;
import com.cms.entity.company.Account;
import com.cms.entity.system.log.ErrorLog;
import com.cms.entity.system.log.OperateLog;
import com.cms.entity.system.users.Users;
import com.cms.service.system.log.ErrorLogService;
import com.cms.service.system.log.OptLogService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
@Aspect
@Component
@Order(1)// Order值越小优先被加载
public class OperateAspect {
private final Log logger = LogFactory.getLog(OperateAspect.class);
private Boolean recordOptLog = true;
private OperateLog operateLog;
@Autowired
OptLogService operateLogService;
@Autowired
ErrorLogService errorLogService;
@Before(value = "@annotation(com.cms.common.annotation.ControllerOptLog)")
public void beforeAdvice(JoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
if (recordOptLog) {
try {
operateLog = new OperateLog();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String ip = IPUtil.getIpAddr(request);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Users currentUser= UserShiroUtil.getCurrentUser();
ControllerOptLog controllerOptLog = method.getAnnotation(ControllerOptLog.class);
String desc = controllerOptLog.desc();
RequestMapping requestMappingAnnotation = joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class);
String url = request.getRequestURI();
//添加记录内容
operateLog.setIp(ip);
operateLog.setUserName(currentUser.getName());
operateLog.setParams(method.getParameters().toString());
operateLog.setUserCode(currentUser.getCode());
operateLog.setUrl(url);
operateLog.setDesc(desc);
operateLog.setBeginTime(new Date().toLocaleString());
} catch (Throwable e) {
logger.error("记录操作日志错误", e);
}
}
}
@AfterReturning(value = "@annotation(com.cms.common.annotation.ControllerOptLog)")
public void afterAdvice(JoinPoint joinPoint) throws Throwable {
if (recordOptLog) {
try {
operateLog.setEndTime(new Date().toLocaleString());
operateLogService.log(operateLog);
} catch (Throwable e) {
logger.error("记录操作日志错误", e);
}
}
}
@AfterThrowing(value = "@annotation(com.cms.common.annotation.ControllerOptLog)",throwing = "e")
public void erroeAdvice(JoinPoint joinPoint,Throwable e){
Object[] args = joinPoint.getArgs();
ErrorLog errorLog = new ErrorLog();
if (recordOptLog) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String ip = IPUtil.getIpAddr(request);
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Users currentUser = UserShiroUtil.getCurrentUser();
ControllerOptLog controllerOptLog = method.getAnnotation(ControllerOptLog.class);
String desc = controllerOptLog.desc();
RequestMapping requestMappingAnnotation = joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class);
String url = request.getRequestURI();
errorLog.setUserCode(currentUser.getCode());
errorLog.setUserName(currentUser.getName());
errorLog.setParams(method.getParameters().toString());
errorLog.setIp(ip);
errorLog.setDesc(desc);
errorLog.setContent(e.toString());
errorLog.setUrl(url);
errorLog.setCurrTime(new Date().toLocaleString());
errorLogService.saveErrorAndOpt(errorLog);
}
}
}
需要注意的是,在这个切面类中所有的通知方法切入的位置都是指定为执行自定义注解时
value = "@annotation(com.cms.common.annotation.ControllerOptLog)"
在执行到切面方法 的时候可以通过 JoinPoint 对象获取一些请求上的参数,比如request,parms等等,同时因为使用的是自定义注解,所以可以通过
method.getAnnotation();
方法获取到标注注解的时候存入的操作介绍,最后把这些需要的参数封装到日志对象中,存入数据库或者指定位置中。
在需要切入的地方需要标上自己定义的注解,我的如下:
/**
* 跳转到发件箱
* @return
*/
@ControllerOptLog(desc = "发件箱跳转")
@RequestMapping("tohairlist")
public String tolist(Model model) {
model.addAttribute("permitBtn", getPermitBtn(Const.RESOURCES_TYPE_BUTTON));
model.addAttribute("tableBtn", getPermitBtn(Const.RESOURCES_TYPE_FUNCTION));
return "/system/message/Hairbox/tolist";
}
第一次记录日志,若有更好的方式可以评论下。