基于注解的系统日志

来源:互联网 发布:全国计算机考试软件 编辑:程序博客网 时间:2024/06/08 10:12

对于一些比较重要的管理系统,操作日志的记录是非常重要,它可以记录下管理人员的任何操作,也便于开发人员在故障中排查问题,可以维护和扩展的日志是尤为重要的。

注解的日志方便和实用,只需要在需要记录的日志方法上加入注解,下面不多说直接上代码

首先创建一个注解和日志实体

@Target({ElementType.METHOD})    @Retention(RetentionPolicy.RUNTIME)     @Documented public @interface SystemLog {/** 日志操作类型  */OperateTypes type();/** 日志操作对象  */OperateObj object();/** 日志信息描述  */String description()  default "";    /** * @Description:日志信息描述(支持实体类字段) * 传值方式:根据实体类的字段名获取参数,(参数名与参数值":"分隔,参数之间","分隔) * 如:用户名:userName,姓名:name,手机:phone * @author XPY * @date 2016年7月28日上午10:42:43 */String descriptionField()  default "";    /** * @Description:日志信息描述(支持表达式) * 传值方式:{0}表示方法参数内的第一个参数,{1}表示方法参数内的第二个参数,以此类推 * 如:科目:{0},课程:{1} * @author XPY * @date 2016年7月28日上午10:42:43 */String descriptionRex()  default "";  

public class LogEntity{/** *  */private static final long serialVersionUID = 1L;private Long id;/** * 操作人ID */private Long userid;/** * 操作人名称 */private String userName;/** * 实体名称 */private String obj;/** * 操作类型 */private String type;/** * 日志字符串 */private String log;/** * 日志中记录的操作结果 */private String result;/** * 操作发生的时间戳 */private Date crtDate;/** * 发起请求的IP地址 */private String requestIP;/** * 异常信息 */private String error;public LogEntity() {super();}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public Long getUserid() {return userid;}public void setUserid(Long userid) {this.userid = userid;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getObj() {return obj;}public void setObj(String obj) {this.obj = obj;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getLog() {return log;}public void setLog(String log) {this.log = log;}public String getResult() {return result;}public void setResult(String result) {this.result = result;}public Date getCrtDate() {return crtDate;}public void setCrtDate(Date crtDate) {this.crtDate = crtDate;}public String getRequestIP() {return requestIP;}public void setRequestIP(String requestIP) {this.requestIP = requestIP;}public String getError() {return error;}public void setError(String error) {this.error = error;}

然后就是日志的切面类,包括日志的记录以及日志的描述

/** *  * @Description 日志切入类 * @author XPY * @date 2016年7月25日下午7:32:25 */@Aspect@Componentpublic class SystemLogAspect {private static String splitParaStr = ",";private static String splitNameValueStr = ":";// 注入Service用于把日志保存数据库@Resourceprivate LogService logService;// 本地异常日志记录对象private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);// 基于注解的切入点,service层及controller层都可使用@Pointcut("@annotation(com.noah.system.log.annotation.SystemLog)")public void serviceAspect() {}/*  @Around("serviceAspect()")   public Object process(ProceedingJoinPoint point) throws Throwable {   System.out.println("@Around:执行目标方法之前...");  //访问目标方法的参数:   Object[] args = point.getArgs(); //用改变后的参数执行目标方法 Object  Object returnValue = point.proceed(args);  returnValue = point.proceed(args);  System.out.println("@Around:执行目标方法之后...");  System.out.println("@Around:被织入的目标对象为:" + point.getTarget());   return "原返回值:" + returnValue + ",这是返回结果的后缀";   }*//** * 正常返回通知 用于拦截Service层记录用户的操作 *  * @param joinPoint *            切点 */@SuppressWarnings({ "rawtypes", "unchecked" })@AfterReturning(pointcut = "serviceAspect()")public void doAfter(JoinPoint joinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 读取session中的用户User user = (User) LiveUtils.getCurrentUser();if (user == null) {logger.info("=====读取用户信息失败=====");return;}// 请求的IPString ip = IpUtil.getIpAddress(request);// 获取用户请求方法的参数并序列化为JSON格式字符串/*String params = "";if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {for (int i = 0; i < joinPoint.getArgs().length; i++) {params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";}}*/try {// *========控制台输出=========*//logger.info("=====正常返回通知开始=====");logger.info("请求方法:"+ (joinPoint.getTarget().getClass().getName() + "."+ joinPoint.getSignature().getName() + "()"));logger.info("方法描述:" + getServiceMethod(joinPoint).getLog());logger.info("请求人:" + user.getName());logger.info("请求IP:" + ip);// *========数据库日志=========*//Object[] param = joinPoint.getArgs();Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();Method method = null;String methodName = joinPoint.getSignature().getName();Class targetClass = joinPoint.getTarget().getClass();method = targetClass.getMethod(methodName, parameterTypes);if (method != null) {SystemLog ann = method.getAnnotation(SystemLog.class);LogEntity log = (LogEntity) SpringBeanUtil.getBean("logEntity");log.setType(getServiceMethod(joinPoint).getType());log.setObj(getServiceMethod(joinPoint).getObj());log.setRequestIP(ip);log.setResult(Constants.SUCCESS);log.setUserid(user.getId());log.setUserName(user.getUserName());log.setCrtDate(new Date());log.setError("");if (getServiceMethod(joinPoint).getLog().isEmpty()) {log.setLog(getParamsRemark(ann, param));}else{log.setLog(getServiceMethod(joinPoint).getLog());}// 保存数据库logService.add(log);}logger.info("=====正常返回通知结束=====");} catch (Exception e) {// 记录本地异常日志logger.error("==正常返回通知异常==");logger.error("异常信息:{}", e.getMessage());}}/** * 异常通知 用于拦截service层记录异常日志 *  * @param joinPoint * @param e * @throws Throwable */@SuppressWarnings({ "unchecked", "rawtypes" })@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Throwable e)throws Throwable {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 读取session中的用户User user = (User) LiveUtils.getCurrentUser();// 请求的IPString ip = IpUtil.getIpAddress(request);// 获取用户请求方法的参数并序列化为JSON格式字符串String params = "";if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {for (int i = 0; i < joinPoint.getArgs().length; i++) {params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";}}try {/* ========控制台输出========= */logger.info("=====异常通知开始=====");logger.info("异常代码:" + e.getClass().getName());logger.info("异常信息:" + e.getMessage());logger.info("异常方法:"+ (joinPoint.getTarget().getClass().getSimpleName() + "."+ joinPoint.getSignature().getName() + "()"));logger.info("方法描述:" + getServiceMethod(joinPoint).getLog());logger.info("请求人:" + user.getName());logger.info("请求IP:" + ip);logger.info("请求参数:" + params);/* ==========数据库日志========= */Object[] param = joinPoint.getArgs();Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();Method method = null;String methodName = joinPoint.getSignature().getName();Class targetClass = joinPoint.getTarget().getClass();method = targetClass.getMethod(methodName, parameterTypes);if (method != null) {SystemLog ann = method.getAnnotation(SystemLog.class);LogEntity log = (LogEntity) SpringBeanUtil.getBean("logEntity");log.setType(getServiceMethod(joinPoint).getType());log.setObj(getServiceMethod(joinPoint).getObj());log.setRequestIP(ip);log.setResult(Constants.FAILTURE);log.setUserid(user.getId());log.setUserName(user.getUserName());log.setCrtDate(new Date());log.setError("异常代码:" + e.getClass().getName()+"异常信息:" + e.getMessage());if (getServiceMethod(joinPoint).getLog().isEmpty()) {log.setLog(getParamsRemark(ann, param));}else{log.setLog(getServiceMethod(joinPoint).getLog());}// 保存数据库logService.add(log);logger.info("=====异常通知结束=====");}} catch (Exception ex) {// 记录本地异常日志logger.error("==异常通知异常==");logger.error("异常信息:{}", ex.getMessage());}/* ==========记录本地异常日志========== */logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName()+ joinPoint.getSignature().getName(), e.getClass().getName(),e.getMessage(), params);}/** * 获取注解中对方法的描述信息 用于service层注解 *  * @param joinPoint *            切点 * @return 方法描述 * @throws Exception */@SuppressWarnings("rawtypes")public static LogEntity getServiceMethod(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();LogEntity logEntity = new LogEntity();for (Method method : methods) {if (method.getName().equals(methodName)) {Class[] clazzs = method.getParameterTypes();if (clazzs.length == arguments.length) {String type = method.getAnnotation(SystemLog.class).type().type;String obj = method.getAnnotation(SystemLog.class).object().type;String description = method.getAnnotation(SystemLog.class).description();logEntity.setType(type);logEntity.setObj(obj);logEntity.setLog(description);break;}}}return logEntity;}/** * 通过java反射来从传入的参数object里取出我们需要记录的字段等属性 * @param obj 参数实体 * @param param 参数字段 * @return 参数字段的值 */private String getID(Object obj, String param) {if (obj instanceof String) {return obj.toString();}PropertyDescriptor pd = null;Method method = null;String v = "";try {pd = new PropertyDescriptor(param, obj.getClass());method = pd.getReadMethod();v = String.valueOf(method.invoke(obj));} catch (Exception e) {e.printStackTrace();}return v;}/** * 通过反射调用bean的get方法或{1}这样的表达式生成描述信息 * {0}为第一位参数的值,{1}为第二位参数的值 * 如果参数为bean的情况可以"用户名:userName"这样的方式获取参数值 * @param param 所有参数 * @return 描述信息 */private String getParamsRemark(SystemLog annotation, Object[] param) {String propertys = annotation.descriptionField();String params = annotation.descriptionRex();StringBuffer sb = new StringBuffer();String desc1 = "";String desc2 = "";if (null != propertys && !"".equals(propertys)) {String[] propertysRemark = annotation.descriptionField().split(splitParaStr);for (String property : propertysRemark) {// 按参数对象属性,取参数值String name = property.split(splitNameValueStr)[0];String value = property.split(splitNameValueStr)[1];sb.append(name + splitNameValueStr + getID(param[0], value)+ splitParaStr).append(",");}desc1 = sb.toString().substring(0, sb.length() - 1);} if(null != params && !"".equals(params)) {List<String> list = StringUtil.getList("\\{(.*?)\\}", params);// 按参数位置取参数值for (int i = 0; i < list.size(); i++) {String str = list.get(i);Object obj = param[Integer.parseInt(str)];params = StringUtil.replease("\\{" + Integer.parseInt(str)+ "\\}", params, obj.toString())+",";}desc2 =  params;}return desc1+desc2;}}


需要通过代理方式来增强AOP
public interface BeanSelfAware {//AOP增强后的代理对象 public void setSelf(Object proxyBean);}
/** * @Description 动态注入代理类 * @author XPY * @date 2016年7月28日下午2:08:25 */public class InjectBeanSelfProcessor implements BeanPostProcessor  {         public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException      {          if(bean instanceof BeanSelfAware)          {              System.out.println("inject proxy:" + bean.getClass());              BeanSelfAware proxyBean = (BeanSelfAware)bean;              proxyBean.setSelf(bean);              return proxyBean;          }          return bean;      }         public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException      {          return bean;      }  }  

最终的使用方式,在方法加入注解即可

@SystemLog(type=OperateTypes.login,object=OperateObj.SysUser,description="登录系统")public ModelAndView login(HttpServletRequest request,HttpSession session,ModelMap model) {}




原创粉丝点击