복붙노트

[SPRING] 스프링 MVC로 http 요청을 제대로 로깅하는 법

SPRING

스프링 MVC로 http 요청을 제대로 로깅하는 법

안녕하세요, 내 응용 프로그램에서 HTTP 요청을 로깅하는 일반적인 방법을 알아 내려고 노력했습니다. 지금까지 로깅을 처리하는 방법은 다음과 같습니다.

@RequestMapping(value="register", method = RequestMethod.POST)
    @ResponseBody
    public String register(@RequestParam(value="param1",required=false) String param1, @RequestParam("param2") String param2, @RequestParam("param3") String param3, HttpServletRequest request){
        long start = System.currentTimeMillis();
        logger.info("!--REQUEST START--!");

        logger.info("Request URL: " + request.getRequestURL().toString());

        List<String> requestParameterNames = Collections.list((Enumeration<String>)request.getParameterNames());
        logger.info("Parameter number: " + requestParameterNames.size()); 

 for (String parameterName : requestParameterNames){
           logger.info("Parameter name: " + parameterName + " - Parameter value: " + request.getParameter(parameterName));
        }
                  //Some processing logic, call to the various services/methods with different parameters, response is always String(Json)
        String response = service.callSomeServiceMethods(param1,param2,param3);

logger.info("Response is: " + response);

        long end = System.currentTimeMillis();
        logger.info("Requested completed in: " + (end-start) + "ms");
        logger.info("!--REQUEST END--!");   

        return response;
    }

그래서 지금 내가 다른 컨트롤러 / 메소드에 대해하는 일은 메소드 내부에서 시작부터 메소드와 다른 처리 로직까지 모든 것을 복사 한 다음 위의 템플릿에 표시된 것처럼 아래에서부터 모든 것을 복사합니다.

그것은 좀 더러스럽고 많은 코드 반복이 있습니다 (나는 그것을 좋아하지 않습니다). 하지만 모든 것을 기록해야합니다.

누구나 이런 종류의 로깅에 대해 더 많은 경험을 가지고 있습니까?

해결법

  1. ==============================

    1.인터셉터를 사용하십시오.

    인터셉터를 사용하십시오.

    모든 요청에 ​​대해 실행됩니다.

  2. ==============================

    2.편집 : 또한,이 답변을 향상시키는이 대답에 @ membersound의 의견을 참조하십시오.

    편집 : 또한,이 답변을 향상시키는이 대답에 @ membersound의 의견을 참조하십시오.

    Spring은이를 지원합니다. CommonsRequestLoggingFilter를 참조하십시오. Spring Boot를 사용한다면, 그 타입의 bean을 등록하면 Boot가 그것을 필터 체인에 적용 할 것입니다. 처럼:

    @Bean
    public Filter logFilter() {
        CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(5120);
        return filter;
    }
    

    또한이 로깅 필터는 로그 수준을 DEBUG로 설정해야합니다. 예 : 다음을 사용하여 logback.xml에서이 작업을 수행하십시오.

    <logger name="org.springframework.web.filter.CommonsRequestLoggingFilter" level="DEBUG"/>
    
  3. ==============================

    3.읽기 요청의 주된 문제는 입력 스트림이 사라지 자마자 그 사람이 사라지고 다시 읽을 수 없다는 것입니다. 따라서 입력 스트림을 캐싱해야합니다. Spring은 웹상의 여러 장소에서 캐싱을위한 클래스를 직접 작성하는 대신 ContentCachingRequestWrapper와 ContentCachingResponseWrapper라는 유용한 클래스를 제공합니다. 이러한 클래스는 매우 효과적으로 활용할 수 있습니다 (예 : 로깅 용 필터).

    읽기 요청의 주된 문제는 입력 스트림이 사라지 자마자 그 사람이 사라지고 다시 읽을 수 없다는 것입니다. 따라서 입력 스트림을 캐싱해야합니다. Spring은 웹상의 여러 장소에서 캐싱을위한 클래스를 직접 작성하는 대신 ContentCachingRequestWrapper와 ContentCachingResponseWrapper라는 유용한 클래스를 제공합니다. 이러한 클래스는 매우 효과적으로 활용할 수 있습니다 (예 : 로깅 용 필터).

    web.xml에 필터를 정의하십시오.

    <filter>
        <filter-name>loggingFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>loggingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    필터는 DelegatingFilterProxy로 선언되었으므로 @Component 또는 @Bean 주석을 사용하여 Bean으로 선언 할 수 있습니다. loggingFilter의 doFilter 메소드에서 필터 체인에 전달하기 전에 스프링에서 제공 한 클래스로 요청 및 응답을 래핑하십시오.

    HttpServletRequest requestToCache = new ContentCachingRequestWrapper(request);
    HttpServletResponse responseToCache = new ContentCachingResponseWrapper(response);
    chain.doFilter(requestToCache, responseToCache);
    String requestData = getRequestData(requestToCache);
    String responseData = getResponseData(responseToCache);
    

    입력 스트림은 chain.doFilter () 후에 입력 스트림이 사용되는 즉시 래핑 된 요청에 캐시됩니다. 그런 다음 아래와 같이 액세스 할 수 있습니다.

    public static String getRequestData(final HttpServletRequest request) throws UnsupportedEncodingException {
        String payload = null;
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
            }
        }
        return payload;
    }
    

    그러나 사물은 응답에 대해 조금 다릅니다. 응답은 필터 체인에 전달되기 전에 래핑되었으므로 반환되는 즉시 응답이 출력 스트림에 캐시됩니다. 그러나 출력 스트림도 소비되므로 wrapper.copyBodyToResponse ()를 사용하여 응답을 출력 스트림에 다시 복사해야합니다. 아래 참조 :

    public static String getResponseData(final HttpServletResponse response) throws IOException {
        String payload = null;
        ContentCachingResponseWrapper wrapper =
            WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            byte[] buf = wrapper.getContentAsByteArray();
            if (buf.length > 0) {
                payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                wrapper.copyBodyToResponse();
            }
        }
        return payload;
    }
    

    희망이 도움이됩니다!

  4. ==============================

    4.다음은 제가 사용할 수있는 작은 라이브러리입니다 : spring-mvc-logger

    다음은 제가 사용할 수있는 작은 라이브러리입니다 : spring-mvc-logger

    나는 그것을 maven central을 통해 사용할 수있게했다.

    <dependency>
        <groupId>com.github.isrsal</groupId>
        <artifactId>spring-mvc-logger</artifactId>
        <version>0.2</version>
    </dependency>
    
  5. ==============================

    5.@ B.Ali가 응답 한 것에 추가. 이것을 스프링 비동기 요청 (serlvet 3.0 이상) 처리 시나리오에서 사용하는 경우 다음 코드가 나에게 효과적입니다.

    @ B.Ali가 응답 한 것에 추가. 이것을 스프링 비동기 요청 (serlvet 3.0 이상) 처리 시나리오에서 사용하는 경우 다음 코드가 나에게 효과적입니다.

    public class OncePerRequestLoggingFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        boolean isFirstRequest = !isAsyncDispatch(request);
        HttpServletRequest requestToUse = request;
        HttpServletResponse responseToUse = response;
    
        // The below check is critical and if not there, then the request/response gets corrupted.
        // Probably because in async case the filter is invoked multiple times.
        if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) {
            requestToUse = new ContentCachingRequestWrapper(request);
        }
    
        if (isFirstRequest && !(response instanceof ContentCachingResponseWrapper)) {
            responseToUse = new ContentCachingResponseWrapper(response);
        }
    
        filterChain.doFilter(requestToUse, responseToUse);
    
        if (!isAsyncStarted(request)) {
            ContentCachingResponseWrapper responseWrapper =
                    WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
            responseWrapper.copyBodyToResponse(); // IMPORTANT to copy it back to response
        }
    }
    
    @Override
    protected boolean shouldNotFilterAsyncDispatch() {
        return false; // IMPORTANT this is true by default and wont work in async scenario.
    }
    

    }

  6. ==============================

    6.어떤 기술 답변으로 ... 그것은 다릅니다 .. 당신이 사용하고있는 기술 스택과 귀하의 요구 사항은 무엇입니까?

    어떤 기술 답변으로 ... 그것은 다릅니다 .. 당신이 사용하고있는 기술 스택과 귀하의 요구 사항은 무엇입니까?

    예를 들어,보다 일반적으로 로깅을 만들고 싶다면 더 많은 것을 할 수 있습니다. 귀하의 경우에는 로깅되고 봄 컨텍스트에서 처리되는 요청 만 로깅합니다. 따라서 다른 요청을 "누락"시킬 수 있습니다.

    앱을 실행하는 데 사용중인 컨테이너 또는 웹 서버를 살펴 보겠습니다. 그러면 Spring에 대한 이러한 종속성이 제거됩니다. 플러스 컨테이너는 로깅 공급자를 연결하고 코드 외부의 로그 형식을 구성 할 수있는 유연성을 제공합니다. 예를 들어 Apache 웹 서버를 사용하는 경우 Apache 웹 서버 로깅을 사용하여 모든 HTTP 요청을 액세스 로깅 레이어에 기록합니다. 그러나 로깅 옵션 중 일부는 성능 저하를 초래할 수 있으므로주의하십시오. 액세스 패턴 모니터링 관점에서 진지하게 필요한 것만 기록하십시오.

    당신이 바람둥이를 사용하고 있다면, 바람둥이는 또한 당신이 물건을 기록하도록 허락 할 것입니다. 사용중인 바람둥이에 대한 톰캣 문서에서 액세스 밸브를 검색하십시오. 그것은 가능성의 세계를 열어 줄 것입니다.

    보다 광범위한 로깅은 예외 전략의 영역, 즉 시스템에서 문제가 발생할 때보고 싶은 세부 사항의 종류 여야합니다.

  7. from https://stackoverflow.com/questions/6631257/how-to-log-properly-http-requests-with-spring-mvc by cc-by-sa and MIT license