项目是 Springboot,前后不分离。目前使用的 AOP 记录日志,很全面,都可以记下来。 但是据说 Springboot2 的 HttpTraceFilter 效率更高,但是写了之后不知道怎么从 HttpServletRequest 中获取类名和方法名。 求教:如果一定要采用 HttpTraceFilter 方式记录日志,如何从 HttpServletRequest 中获取方法名和类名?
1
iiicarus OP @Configuration
@ConditionalOnWebApplication public class HttpTraceConfiguration { @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) static class ServletTraceFilterConfiguration { @Bean public HttpTraceLogFilter httpTraceLogFilter(MeterRegistry registry) { return new HttpTraceLogFilter(registry); } } } |
2
iiicarus OP @Slf4j
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered { private static final String NEED_TRACE_PATH_PREFIX_BUSINESS = "/business"; private static final String NEED_TRACE_PATH_PREFIX_NPI = "/npi"; private static final String NEED_TRACE_PATH_PREFIX_SYSTEM = "/system"; private static final String IGNORE_CONTENT_TYPE = "multipart/form-data"; private final MeterRegistry registry; public HttpTraceLogFilter(MeterRegistry registry) { this.registry = registry; } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 10; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (!isRequestValid(request)) { filterChain.doFilter(request, response); return; } if (!(request instanceof ContentCachingRequestWrapper)) { request = new ContentCachingRequestWrapper(request); } if (!(response instanceof ContentCachingResponseWrapper)) { response = new ContentCachingResponseWrapper(response); } int status = HttpStatus.INTERNAL_SERVER_ERROR.value(); long startTime = System.currentTimeMillis(); try { filterChain.doFilter(request, response); status = response.getStatus(); } finally { String path = request.getRequestURI(); boolean pathFlag = path.startsWith(NEED_TRACE_PATH_PREFIX_BUSINESS) || path.startsWith(NEED_TRACE_PATH_PREFIX_NPI) || path.startsWith(NEED_TRACE_PATH_PREFIX_SYSTEM); if (pathFlag && !Objects.equals(IGNORE_CONTENT_TYPE, request.getContentType())) { String requestBody = IOUtils.toString(request.getInputStream(), Charsets.UTF_8); log.info(requestBody); //1. 记录日志 HttpTraceLog traceLog = new HttpTraceLog(); traceLog.setPath(path); log.info("path ===================>: {}", path); traceLog.setMethod(request.getMethod()); log.info("method ===================>: {}", request.getMethod()); long timeConsuming = System.currentTimeMillis() - startTime; traceLog.setTimeTaken(timeConsuming); log.info("timeConsuming ===================>: {} ms", timeConsuming); //traceLog.setTime(LocalDateTime.now().toString()); //traceLog.setParameterMap(JsonMapper.INSTANCE.toJson(request.getParameterMap())); traceLog.setStatus(status); log.info("status ===================>: {}", status); //traceLog.setRequestBody(getRequestBody(request)); //traceLog.setResponseBody(getResponseBody(response)); //log.info("Http trace log: {}", JsonMapper.INSTANCE.toJson(traceLog)); //log.info("Http trace log: {}===============================================>", traceLog); } updateResponse(response); } } private boolean isRequestValid(HttpServletRequest request) { try { new URI(request.getRequestURL().toString()); return true; } catch (URISyntaxException ex) { return false; } } private String getRequestBody(HttpServletRequest request) { String requestBody = ""; ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); if (wrapper != null) { try { requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding()); } catch (IOException e) { // NOOP } } return requestBody; } private String getResponseBody(HttpServletResponse response) { String responseBody = ""; ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); if (wrapper != null) { try { responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding()); } catch (IOException e) { // NOOP } } return responseBody; } private void updateResponse(HttpServletResponse response) throws IOException { ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class); Objects.requireNonNull(responseWrapper).copyBodyToResponse(); } @Data private static class HttpTraceLog { private String path; private String parameterMap; private String method; private Long timeTaken; private String time; private Integer status; private String requestBody; private String responseBody; } } |
3
iiicarus OP 代码如上
|
4
Aresxue 2019-09-09 19:46:47 +08:00
假设你想要的是当前请求对象所对应的服务的类名和方法名,是不太好获取的,因为过滤器作用于请求进入 Servlet 之前(对于 Spring MVC 来说是 DispatcherServlet),而在常用的 Spring MVC 中请求路径转发到服务(比如说 Controller)是在 HandlerMapping 和 HandlerAdapter 中做的,在过滤器中的时候你还不知道这个请求会映射到哪个服务。
建议使用 aop 在请求收发层或业务层做日志记录。 就是想在过滤器中处理的话给个思路,可以自定义服务将请求地址和服务做一一映射,比如初始化一个 HashMap, 请求路径为 key, 服务全名为 value(只是举例,请求路径的匹配是复杂多样的,自定义匹配规则很难做到全面) 最后还有一种骚套路是使用 Spring 的 HandlerMapping 和 HandlerAdapter 做匹配,这种违反规范的做法在正式工程请不要采用。 |