V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iiicarus
V2EX  ›  Java

求教,如何从 HttpServletRequest 中获取类名和方法名

  •  
  •   iiicarus · 2019-09-09 12:02:00 +08:00 · 4521 次点击
    这是一个创建于 1688 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目是 Springboot,前后不分离。目前使用的 AOP 记录日志,很全面,都可以记下来。 但是据说 Springboot2 的 HttpTraceFilter 效率更高,但是写了之后不知道怎么从 HttpServletRequest 中获取类名和方法名。 求教:如果一定要采用 HttpTraceFilter 方式记录日志,如何从 HttpServletRequest 中获取方法名和类名?

    5 条回复    2019-09-10 11:25:29 +08:00
    iiicarus
        1
    iiicarus  
    OP
       2019-09-09 12:02:14 +08:00
    @Configuration
    @ConditionalOnWebApplication
    public class HttpTraceConfiguration {

    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    static class ServletTraceFilterConfiguration {

    @Bean
    public HttpTraceLogFilter httpTraceLogFilter(MeterRegistry registry) {
    return new HttpTraceLogFilter(registry);
    }
    }
    }
    iiicarus
        2
    iiicarus  
    OP
       2019-09-09 12:02:31 +08:00
    @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;
    }
    }
    iiicarus
        3
    iiicarus  
    OP
       2019-09-09 12:02:50 +08:00
    代码如上
    Aresxue
        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 做匹配,这种违反规范的做法在正式工程请不要采用。
    iiicarus
        5
    iiicarus  
    OP
       2019-09-10 11:25:29 +08:00
    @Aresxue 谢谢!从 request 中获取类名和方法名太困难了,感觉违背了 AOP 存在的意义!我们领导也是非要我这样做,目前的做法是,从路径 path 中分析得到模块。也没有具体的类名和方法名,不过另外做了 AOP,没提交,等后面要的时候再说吧~~~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5771 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 01:54 · PVG 09:54 · LAX 18:54 · JFK 21:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.