首页 > 代码库 > springboot-23-aspectj日志记录及threadlocal内存泄漏
springboot-23-aspectj日志记录及threadlocal内存泄漏
对于请求参数的处理和响应, 如果在代码中体现日志会显得很繁琐, 普遍的解决方案是使用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, 然后还有一个异常处理的方法
偶然间看到这个博客
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.aspectj.lang.ProceedingJoinPoint; 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:21:59 */ @Aspect //定义一个切面 @Configuration public class LogAspect { private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class); // private static final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd‘T‘HH:mm:ss.SSS"); // 定义切点 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(); LOGGER.info("请求开始, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString); // result的值就是被拦截方法的返回值 Object result = pjp.proceed(); LOGGER.info("请求结束,url: {}, response: {}, time: {}s", url, JSON.toJSONString(result), (System.currentTimeMillis() - startTime) / 1000); return result; } }
然后还需要加入一个全局异常处理框架:
http://www.cnblogs.com/wenbronk/p/6850785.html
具体效果等待进一步测试
springboot-23-aspectj日志记录及threadlocal内存泄漏
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。