對於請求參數的處理和響應, 如果在代碼中體現日志會顯得很繁瑣, 普遍的解決方案是使用spring的切面方案去解決.
這兒使用的是springboot的切面: http://www.cnblogs.com/wenbronk/p/6848984.html
最開始的aspectj切面解決:
package com.iwhere.easy.travel.aspect; import java.sql.Date; import java.text.SimpleDateFormat; import java.util.Enumeration; import java.util.UUID; import javax.servlet.http.HttpServletRequest; 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.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSONObject; @Aspect @Component public class ControllerAspect { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private String name = "easy-travel-server"; @Pointcut("execution(public * com.wenbronk.controller.*.*(..))") public void controllerLog(){} @Pointcut("execution(public * com.wenbronk.service.*.*(..))") public void serviceLog(){} private ThreadLocal<Long> startTime = new ThreadLocal<>(); private ThreadLocal<String> requestId = new ThreadLocal<>(); private ThreadLocal<String> interfaceName = new ThreadLocal<>(); private ThreadLocal<String> param = new ThreadLocal<>(); private SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); @Before("controllerLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 設置請求開始時間 startTime.set(System.currentTimeMillis()); Date stTimeDate = new Date(startTime.get()); String dateStr = dataFormat.format(stTimeDate); // 設置請求標識 String requestIdStr = UUID.randomUUID().toString(); requestId.set(requestIdStr); // 提取全部參數 paramJson Enumeration<String> paramNames = request.getParameterNames(); JSONObject paramJson = new JSONObject(); while(paramNames.hasMoreElements()){ String paramName = paramNames.nextElement(); paramJson.put(paramName, request.getParameter(paramName)); } // 提取接口標識(url中截取) String requestUrl = request.getRequestURL().toString(); int start = requestUrl.lastIndexOf("/")+1; String interfaceNameStr = null; if (requestUrl.contains("?")){ interfaceNameStr = requestUrl.substring(start, requestUrl.indexOf("?")); } else { interfaceNameStr = requestUrl.substring(start); } param.set(paramJson.toJSONString()); interfaceName.set(interfaceNameStr); // 將requst的唯一標識放置在request中,在其他環節可以穿起來 request.setAttribute("requestId", requestId.get()); } @AfterReturning(returning="rvt",pointcut="controllerLog()") public void doAfterReturning(JoinPoint joinPoint,Object rvt) throws Throwable { // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("finished" + " " + name + " " + interfaceName.get() + " " + requestId.get() + " " + request.getRequestURL().toString() + " " + param.get() + (System.currentTimeMillis() - startTime.get()) + " " + rvt.toString()); } @AfterThrowing(throwing="ex", pointcut="controllerLog()") public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) throws Throwable { // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 發生地點 int lineNum = 0; String className = null; String methodName = null; StackTraceElement[] st = ex.getStackTrace(); for (StackTraceElement stackTraceElement : st) { lineNum = stackTraceElement.getLineNumber(); className = stackTraceElement.getClassName(); methodName = stackTraceElement.getMethodName(); System.out.println("[類:" + className + "]調用" + methodName + "時在第" + lineNum + "行代碼處發生異常!異常類型:" + ex.getClass().getName()); break; } String exceptionMessage = "[類:" + className + "]調用"+ methodName + "時在第" + lineNum + "行代碼處發生異常!異常類型:" + ex.getClass().getName(); logger.info("exception" + " " + name + " " + interfaceName.get() + " " + requestId.get() + " " + request.getRequestURL().toString() + " " + param.get() + " " + exceptionMessage); } }
可見這個里面有一個before和after, 然后還有一個異常處理的方法
附: joinpoint的簡要api
AspectJ使用org.aspectj.lang.JoinPoint接口表示目標類連接點對象,如果是環繞增強時,使用org.aspectj.lang.ProceedingJoinPoint表示連接點對象,該類是JoinPoint的子接口。任何一個增強方法都可以通過將第一個入參聲明為JoinPoint訪問到連接點上下文的信息。我們先來了解一下這兩個接口的主要方法: 1)JoinPoint java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表; Signature getSignature() :獲取連接點的方法簽名對象; java.lang.Object getTarget() :獲取連接點所在的目標對象; java.lang.Object getThis() :獲取代理對象本身; 2)ProceedingJoinPoint ProceedingJoinPoint繼承JoinPoint子接口,它新增了兩個用於執行連接點方法的方法: java.lang.Object proceed() throws java.lang.Throwable:通過反射執行目標對象的連接點處的方法; java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通過反射執行目標對象連接點處的方法,不過使用新的入參替換原來的入參。
偶然間看到這個博客
http://blog.csdn.net/lhqj1992/article/details/52451136 https://my.oschina.net/xpbug/blog/113444 https://segmentfault.com/a/1190000000537475
由於此項目采用的是線程池, 所以可能存在內存一直上漲, 一直到線程池max之后達到一個穩定態, 也就發生了我們認為的內存泄漏
之后改成這個方法:
package com.iwhere.scrapy.aspect; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.ArrayUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.alibaba.fastjson.JSON; /** * 日志記錄 * @author wenbronk * @Date 上午9:33:47 */ @Aspect @Configuration public class LogAspect { private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class); // 定義切點 Pointcut @Pointcut("execution(* com.iwhere.scrapy.controller.*Controller.*(..))") public void excudeService() {} @Around("excudeService()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { Long startTime = System.currentTimeMillis(); RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); String url = request.getRequestURL().toString(); String method = request.getMethod(); String uri = request.getRequestURI(); String queryString = request.getQueryString(); // Object target = pjp.getTarget(); // String name = target.getClass().getName(); Signature signature = pjp.getSignature(); String className = signature.getDeclaringTypeName(); String methodName = signature.getName(); LOGGER.info("請求開始, {}#{}() URI: {}, method: {}, URL: {}, params: {}",className, methodName, uri, method, url, queryString); // result的值就是被攔截方法的返回值 Object result = pjp.proceed(); Long endTime = System.currentTimeMillis(); LOGGER.info("請求結束, {}#{}(), URI: {}, method: {}, URL: {}, time: {}, result: {} ", className, methodName, uri, method, url, (endTime - startTime), JSON.toJSONString(result)); return result; } // @AfterThrowing(throwing="ex", pointcut="excudeService()") // public String doAfterThrowing(JoinPoint joinPoint, Throwable ex) throws Throwable { // // 接收到請求,記錄請求內容 // ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); // HttpServletRequest request = attributes.getRequest(); // // 發生地點 // int lineNum = 0; // String className = null; // String methodName = null; // StackTraceElement[] st = ex.getStackTrace(); // if (ArrayUtils.isNotEmpty(st)) { // lineNum = st[0].getLineNumber(); // className = st[0].getClassName(); // methodName = st[0].getMethodName(); // } // LOGGER.info("Exception: {}#{}() 在第{}行發生{}異常!!!", className, methodName, lineNum, ex.getClass().getName()); // return "exception"; // } }
在里面處理異常, 還是會拋出, 所以單獨出一個異常處理
然后還需要加入一個全局異常處理框架:
http://www.cnblogs.com/wenbronk/p/6850785.html
具體效果等待進一步測試
推薦一個好的博客, 關於aspect的 : http://blog.csdn.net/lemon1003657090/article/details/52431584