往往一个系统的日志部分也是非常重要的,日志可以用来分析系统出现的异常信息,下面就分享一篇使用spring aop切面来异步添加日志的操作,其中用到了 队列和多线程。
第一步:创建日志对象
import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="t_xt_audit_log") public class AuditLog { @Id @GenericGenerator(name = "paymentableGenerator", strategy = "assigned") private String id; private String description; private String method; private Integer type; @Column(name="request_ip") private String requestIp; @Column(name="exception_code") private String exceptionCode; @Column(name="exception_detail") private String exceptionDetail; private String params; @Column(name="create_by") private String createBy; @Column(name="create_date") private Date createDate; @Column(name="app_id") private String appId; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getRequestIp() { return requestIp; } public void setRequestIp(String requestIp) { this.requestIp = requestIp; } public String getExceptionCode() { return exceptionCode; } public void setExceptionCode(String exceptionCode) { this.exceptionCode = exceptionCode; } public String getExceptionDetail() { return exceptionDetail; } public void setExceptionDetail(String exceptionDetail) { this.exceptionDetail = exceptionDetail; } public String getParams() { return params; } public void setParams(String params) { this.params = params; } public String getCreateBy() { return createBy; } public void setCreateBy(String createBy) { this.createBy = createBy; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } }
第二步:创建存放日志的队列
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.springframework.stereotype.Component; import com.izhbg.typz.sso.audit.dto.AuditLog; @Component public class AuditLogQueue { private BlockingQueue<AuditLog> blockingQueue = new LinkedBlockingQueue<AuditLog>(); public void add(AuditLog auditLog) { blockingQueue.add(auditLog); } public AuditLog poll() throws InterruptedException { return blockingQueue.poll(1, TimeUnit.SECONDS); } }
第三步:保存日志的线程,从队列中读取日志
import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.izhbg.typz.sso.audit.dto.AuditLog; import com.izhbg.typz.sso.audit.service.AuditLogService; /** * * @ClassName: AuditLogConsumer * @Description: 日志的保存线程 * @author caixl * @date 2016-5-11 上午11:23:12 * */ @Component public class AuditLogConsumer implements Runnable{ private static Logger logger = LoggerFactory.getLogger(AuditLogConsumer.class); public static final int DEFAULT_BATCH_SIZE = 64; private AuditLogQueue auditLogQueue; private AuditLogService auditLogService; private int batchSize = DEFAULT_BATCH_SIZE; private boolean active = true; private Thread thread; @PostConstruct public void init() { thread = new Thread(this); thread.start(); } @PreDestroy public void close() { active = false; } public void run() { while (active) { execute(); } } public void execute() { List<AuditLog> auditDtos = new ArrayList<AuditLog>(); try { int size = 0; while (size < batchSize) { AuditLog auditLog = auditLogQueue.poll(); if (auditLog == null) { break; } auditDtos.add(auditLog); size++; } } catch (Exception ex) { logger.info(ex.getMessage(), ex); } if (!auditDtos.isEmpty()) { auditLogService.batchLog(auditDtos);//持久化日志到数据库 } } @Resource public void setAuditLogQueue(AuditLogQueue auditLogQueue) { this.auditLogQueue = auditLogQueue; } @Resource public void setAuditLogService(AuditLogService auditLogService) { this.auditLogService = auditLogService; } public void setBatchSize(int batchSize) { this.batchSize = batchSize; } }
第四步:创建 controller层 自定义注解
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; /** * * @ClassName: SystemControllerLog * @Description: 自定义注解 拦截Controller * @author caixl * @date 2016-5-10 下午2:16:12 * */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SystemControllerLog { String description() default ""; }
第五步:创建service层自定义注解
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; /** * * @ClassName: SystemServiceLog * @Description: 自定义注解 拦截service * @author caixl * @date 2016-5-10 下午2:17:12 * */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SystemServiceLog { String description() default ""; }
第六步:创建切点
import java.lang.reflect.Method; import java.util.Date; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import net.sf.json.util.JSONUtils; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.izhbg.typz.base.util.CommonUtil; import com.izhbg.typz.base.util.IdGenerator; import com.izhbg.typz.sso.audit.component.AuditLogQueue; import com.izhbg.typz.sso.audit.dto.AuditLog; import com.izhbg.typz.sso.audit.manager.AuditLogManager; import com.izhbg.typz.sso.util.SpringSecurityUtils; /** * * @ClassName: AuditLogAspect * @Description: 切点类 * @author caixl * @date 2016-5-10 下午2:50:34 * */ @Aspect @Component public class AuditLogAspect { private AuditLogQueue auditLogQueue; //本地异常日志记录对象 private static final Logger logger = Logger.getLogger(AuditLogManager.class); //Service层切点 @Pointcut("@annotation(com.izhbg.typz.sso.annotation.SystemServiceLog)") public void serviceAspect() { } //Controller层切点 @Pointcut("@annotation(com.izhbg.typz.sso.annotation.SystemControllerLog)") public void controllerAspect() { } @Before("controllerAspect()") public void doBefore(JoinPoint joinPoint) { //@ TODO 异步线程处理 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession session = request.getSession(); //读取session中的用户 //请求的IP String ip = CommonUtil.getIpAddress(request); //获取用户请求方法的参数并序列化为JSON格式字符串 String params = ""; if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { for ( int i = 0; i < joinPoint.getArgs().length; i++) { params += JSONUtils.valueToString(joinPoint.getArgs()[i]) + ";"; } } try { //*========控制台输出=========*// System.out.println("=====前置通知开始====="); System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); System.out.println("方法描述:" + getControllerMethodDescription(joinPoint)); System.out.println("请求人:" + SpringSecurityUtils.getCurrentUsername()); System.out.println("请求IP:" + ip); //*========数据库日志=========*// AuditLog log = new AuditLog(); log.setId(IdGenerator.getInstance().getUniqTime()+""); log.setDescription(getControllerMethodDescription(joinPoint)); log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); log.setType(0); log.setRequestIp(ip); log.setExceptionCode( null); log.setExceptionDetail( null); log.setParams( params); log.setCreateBy(SpringSecurityUtils.getCurrentUsername()+"("+SpringSecurityUtils.getCurrentUserId()+")"); log.setCreateDate(new Date()); log.setAppId(SpringSecurityUtils.getCurrentUserAppId()); //保存数据库 auditLogQueue.add(log); System.out.println("=====前置通知结束====="); } catch (Exception e) { //记录本地异常日志 logger.error("==前置通知异常=="); } } /** * 获取注解中对方法的描述信息 用于service层注解 * * @param joinPoint * 切点 * @return 方法描述 * @throws Exception */ public static String getServiceMthodDescription(JoinPoint joinPoint) throws Exception { 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 description = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { description = method.getAnnotation(SystemServiceLog.class).description(); break; } } } return description; } /** * 获取注解中对方法的描述信息 用于Controller层注解 * * @param joinPoint * 切点 * @return 方法描述 * @throws Exception */ public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception { 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 description = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { description = method.getAnnotation(SystemControllerLog.class).description(); break; } } } return description; } @Resource public void setAuditLogQueue(AuditLogQueue auditLogQueue) { this.auditLogQueue = auditLogQueue; } }
使用方式:
@RedisCache(type=TXtYh.class,fieldKey="parameterMap.yhId") @RequestMapping("user-edit") @SystemControllerLog(description = "编辑用户") public String userEdit(@RequestParam Map<String, Object> parameterMap, Model model) throws Exception{ String yhId= StringUtils.getString(parameterMap.get("yhId")); String currentAppId= StringUtils.getString(parameterMap.get("currentAppId")); TXtYh user = null; if(StringHelper.isNotEmpty(yhId)) user = tXtYhService.findByYhId(yhId); String result = tXtJgService.getJgsJSON(currentAppId); model.addAttribute("user", user); model.addAttribute("result", result); model.addAttribute("currentAppId", currentAppId); List<TXtYy> tXtYyList = tXtYyService.queryAll(); model.addAttribute("txtYy", tXtYyList); return "admin/guser/getguser"; }