package com.jadyer.seed.comm.annotation.log; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; import com.jadyer.seed.comm.annotation.SeedLog; import com.jadyer.seed.comm.util.JadyerUtil; import com.jadyer.seed.comm.util.LogUtil; import com.jadyer.seed.comm.util.RequestUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; /** * 日志记录切面器 * <ul> * <li> * Around("execution(* com.jadyer.seed..*Controller.*(..))")<br/> * 测试发现Controller中只要有一个方法是public则其所有方法都会被切,当所有方法都不是public时才都不会被切 * </li> * <li> * Around("execution(public * com.jadyer.seed..*Controller.*(..))")<br/> * 此时就只会切public的方法,而不会受其它方法修饰符的影响,推荐使用 * </li> * </ul> * Created by 玄玉<https://jadyer.cn/> on 2015/8/18 9:49. */ //@Aspect //@Component public class LogAspectOld { //@Around("execution(public * com.jadyer.seed..*Controller.*(..)) or execution(public * com.xuanyu..*Controller.*(..))") //@Around("execution(public * com.jadyer.seed..*Controller.*(..))") public Object log(ProceedingJoinPoint joinPoint) throws Throwable { Object respData; long startTime = System.currentTimeMillis(); String className = joinPoint.getTarget().getClass().getSimpleName(); //获取类名(这里只切面了Controller类) String methodName = joinPoint.getSignature().getName(); //获取方法名 String methodInfo = className + "." + methodName; //组织类名.方法名 //Object[] objs = joinPoint.getArgs(); //获取方法参数 //String paramInfo = com.alibaba.fastjson.JSON.toJSONString(args); /* if(RouterController.class.getSimpleName().equals(className)){ return joinPoint.proceed(); } */ /* * 打印Controller入参 * 1.也可以使用@Resource注入private HttpServletRequest request;再加上setRequest()即可 * 当使用@Resource注入HttpServletRequest时,在JUnit中通过new ClassPathXmlApplicationContext("applicationContext.xml")加载Spring时会报告下面的异常 * org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.servlet.http.HttpServletRequest] found for dependency * 所以这里通过RequestContextHolder来获取HttpServletRequest * 2.当上传文件时,由于表单设置了enctype="multipart/form-data",会将表单用其它的文本域与file域一起作为流提交 * 所以此时request.getParameter()是无法获取到表单中的文本域的,这时可以借助文件上传组件来获取比如org.apache.commons.fileupload.FileItem * 3.RabbitMQ订阅过来的消息时,这里得到的servletRequestAttributes==null,所以加了一个判断 */ ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); if(null == attributes){ return joinPoint.proceed(); } HttpServletRequest request = attributes.getRequest(); LogUtil.getLogger().info("{}()-->{}被调用,客户端IP={},入参为[{}]", methodInfo, request.getRequestURI(), RequestUtil.getClientIP(request), JadyerUtil.buildStringFromMap(request.getParameterMap())); /* * 使用自定义注解 */ Method method = ((MethodSignature)joinPoint.getSignature()).getMethod(); if(method.isAnnotationPresent(SeedLog.class)){ SeedLog seedLog = method.getAnnotation(SeedLog.class); String logData = "动作:" + seedLog.action().getCode() + "(" + seedLog.action().getMsg() +")" + ",描述:" + seedLog.value(); LogUtil.getLogger().info("{}()-->{}被调用,客户端IP={},Log注解为[{}]", methodInfo, request.getRequestURI(), RequestUtil.getClientIP(request), logData); } /* * 表单验证 */ //Object[] objs = joinPoint.getArgs(); //for (Object obj : objs) { // if (null != obj && obj.getClass().getName().startsWith("com.jadyer.seed.ucs")) { // LogUtil.getLogger().info("{}()-->{}被调用, 客户端IP={}, 得到的表单参数为{}", methodInfo, request.getRequestURI(), IPUtil.getClientIP(request), ReflectionToStringBuilder.toString(obj, ToStringStyle.MULTI_LINE_STYLE)); // String validateResult = ValidatorUtil.validate(obj); // LogUtil.getLogger().info("{}()-->{}的表单-->{}", methodInfo, request.getRequestURI(), StringUtils.isBlank(validateResult)?"验证通过":"验证未通过"); // if (StringUtils.isNotBlank(validateResult)) { // throw new SeedException(CodeEnum.SYSTEM_BUSY.getCode(), validateResult); // } // } //} /* * 执行Controller的方法 */ respData = joinPoint.proceed(); long endTime = System.currentTimeMillis(); String returnInfo; if(null!=respData && respData.getClass().isAssignableFrom(ResponseEntity.class)){ returnInfo = "ResponseEntity"; }else{ //出参就不再格式化输出了,因为通常接口返回的都是实体类,类属性很多,很占面积,影响查日志 //returnInfo = JSON.toJSONStringWithDateFormat(respData, JSON.DEFFAULT_DATE_FORMAT, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullBooleanAsFalse); returnInfo = JSON.toJSONStringWithDateFormat(respData, JSON.DEFFAULT_DATE_FORMAT, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullBooleanAsFalse); } LogUtil.getLogger().info("{}()-->{}被调用,出参为[{}],Duration[{}]ms", methodInfo, request.getRequestURI(), returnInfo, endTime-startTime); LogUtil.getLogger().info("---------------------------------------------------------------------------------------------"); //注意這里一定要原封不动的返回joinPoint.proceed()结果,若返回JSON.toJSONString(respData)则会报告下面的异常 //java.lang.String cannot be cast to com.jadyer.seed.comm.constant.CommResult //这是由于JSON.toJSONString(respData)得到的是字符串,而实际Controller方法里面返回的是CommResult对象 return respData; } }