1前書き

このクイックチュートリアルでは、Springのログフィルタを使用して着信要求をログに記録する基本を説明します。ロギングを始めたばかりの場合は、/java-logging-intro[ロギングイントロ記事]および/slf4j-with-log4j2-logback[SLF4J記事]のリンクを確認してください。


2 Mavenの依存関係

ロギングの依存関係は、イントロ記事のものと単純に同じになります。ここに単純にSpringを追加しましょう。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

最新バージョンはhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework%22%20AND%20a%3A%22spring-core%22[springのためにここに見つけることができます-コア]。


3基本Webコントローラ

まず最初に、この例で使用するコントローラを定義しましょう。

@RestController
public class TaxiFareController {

    @GetMapping("/taxifare/get/")
    public RateCard getTaxiFare() {
        return new RateCard();
    }

    @PostMapping("/taxifare/calculate/")
    public String calculateTaxiFare(
      @RequestBody @Valid TaxiRide taxiRide) {

       //return the calculated fare
    }
}


4カスタム要求ログ

Springは、Webリクエストの前後にアクションを実行するようにユーザー定義のインターセプターを設定するためのメカニズムを提供します。

Springリクエストインターセプターの中で注目に値するインターフェースの1つは

HandlerInterceptor

です。

  1. __preHandle() – このメソッドは実際のコントローラの前に実行されます

サービス方法
。 __afterCompletion() – コントローラが起動した後にこのメソッドが実行されます。

応答を送信する準備ができました

さらに、Springは

HandlerInterceptorAdaptorクラスのデフォルト実装を

HandlerInterceptorAdaptor__クラスの形式で提供します。これはユーザーが拡張できます。


__HandlerInterceptorAdaptor

__asを拡張して、独自のインターセプターを作成しましょう。

@Component
public class TaxiFareRequestInterceptor
  extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(
      HttpServletRequest request,
      HttpServletResponse response,
      Object handler) {
        return true;
    }

    @Override
    public void afterCompletion(
      HttpServletRequest request,
      HttpServletResponse response,
      Object handler,
      Exception ex) {
       //    }
}

最後に、

TaxiFareController

クラスで定義されたパス

/taxifare

にマッピングされたコントローラーメソッド呼び出しの前後処理をキャプチャするように、MVCライフサイクル内で

TaxiRideRequestInterceptor

を設定します。

@Configuration
public class TaxiFareMVCConfig implements WebMvcConfigurer {

    @Autowired
    private TaxiFareRequestInterceptor taxiFareRequestInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(taxiFareRequestInterceptor)
          .addPathPatterns("/** ** /taxifare/** ** /");
    }
}

結論として、

WebMvcConfigurer

は、

addInterceptors()

メソッドを呼び出すことによって、Spring MVCのライフサイクル内に

TaxiFareRequestInterceptor

を追加します。

最大の課題は、ロギングのためにリクエストとレスポンスのペイロードのコピーを取得し、それでもサーブレットが処理するためにリクエストされたペイロードを残すことです。

読み取り要求に関する主な問題は、入力ストリームが最初に読み取られるとすぐに、それが消費されたとマークされ、再度読み取ることができないということです。

アプリケーションはリクエストストリームを読み込んだ後に例外をスローします。

{
  "timestamp": 1500645243383,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.http.converter
    .HttpMessageNotReadableException",
  "message": "Could not read document: Stream closed;
    nested exception is java.io.IOException: Stream closed",
  "path": "/rest-log/taxifare/calculate/"
}

  • この問題を克服するために、キャッシュを利用してリクエストストリームを保存し、それをロギングに使用できます。

Springはhttp://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/ContentCachingRequestWrapper.html[ContentCachingRequestWrapper]やhttp://docs.springなどの便利なクラスをいくつか提供しています。ロギング目的で要求データをキャッシュするために使用できるio/spring/docs/current/javadoc-api/org/springframework/web/util/ContentCachingResponseWrapper.html[ContentCachingResponseWrapper]。


ContentCachingRequestWrapper

クラスを使用してリクエストオブジェクトをキャッシュするように

preHandle()

of

TaxiRideRequestInterceptor

クラスを調整しましょう。

@Override
public boolean preHandle(HttpServletRequest request,
  HttpServletResponse response, Object handler) {

    HttpServletRequest requestCacheWrapperObject
      = new ContentCachingRequestWrapper(request);
    requestCacheWrapperObject.getParameterMap();
   //Read inputStream from requestCacheWrapperObject and log it
    return true;
}

ご覧のとおり、実際のリクエストオブジェクトを乱すことなく、ペイロードデータをログに記録するために使用できる

ContentCachingRequestWrapper

クラスを使用してリクエストオブジェクトをキャッシュしました。

requestCacheWrapperObject.getContentAsByteArray();

  • 制限**


  • ContentCachingRequestWrapper

    クラスは次のもののみをサポートします。

Content-Type:application/x-www-form-urlencoded
Method-Type:POST

  • リクエストデータが確実に

使用する前に

ContentCachingRequestWrapper

にキャッシュされています。

requestCacheWrapperObject.getParameterMap();

===

5 Springの組み込みリクエストロギング

Springはペイロードを記録するための組み込みソリューションを提供します。設定を使用してSpringアプリケーションにプラグインすることで既製のフィルタを使用できます。


AbstractRequestLoggingFilter

はロギングの基本機能を提供するフィルタです。サブクラスは

beforeRequest()

メソッドと

afterRequest()

メソッドをオーバーライドして、リクエストの周囲で実際のロギングを実行する必要があります。

Springフレームワークは、入ってくるリクエストをログに記録するために使用できる3つの具体的な実装クラスを提供します。これら3つのクラスは以下のとおりです。


  • CommonsRequestLoggingFilter


  • Log4jNestedDiagnosticContextFilter

    (廃止予定)


  • ServletContextRequestLoggingFilter

それでは、

CommonsRequestLoggingFilter

に移動して、着信要求をログに記録するように設定しましょう。

====

5.1. Spring Bootアプリケーションを設定する

Spring Bootアプリケーションはリクエストロギングを有効にするためにbean定義を追加することで設定できます

@Configuration
public class RequestLoggingFilterConfig {

    @Bean
    public CommonsRequestLoggingFilter logFilter() {
        CommonsRequestLoggingFilter filter
          = new CommonsRequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(10000);
        filter.setIncludeHeaders(false);
        filter.setAfterMessagePrefix("REQUEST DATA : ");
        return filter;
    }
}

また、このロギングフィルタでは、ログレベルをDEBUGに設定する必要があります。

logback.xml

に下記の要素を追加することで、DEBUGモードを有効にすることができます。

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

DEBUGレベルのログを有効にするもう1つの方法は、

application.properties

に以下を追加することです。

logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=
  DEBUG

====

5.2. 従来のWebアプリケーションを設定する

標準のSpring Webアプリケーションでは、

Filter

はXML構成またはJava構成のいずれかを介して設定できます。従来のJavaベースの設定を使用して

CommonsRequestLoggingFilter

を設定しましょう。

ご存知のとおり、

CommonsRequestLoggingFilter



includePayload

属性はデフォルトでfalseに設定されています。 Java設定を使用してコンテナにインジェクトする前に、

includePayload

を有効にするために属性の値をオーバーライドするカスタムクラスが必要です。

public class CustomeRequestLoggingFilter
  extends CommonsRequestLoggingFilter {

    public CustomeRequestLoggingFilter() {
        super.setIncludeQueryString(true);
        super.setIncludePayload(true);
        super.setMaxPayloadLength(10000);
    }
}

それでは、link:/spring-xml-vs-java-configを使って

CustomeRequestLoggingFilter

を注入する必要があります。

public class CustomWebAppInitializer implements
  WebApplicationInitializer {
    public void onStartup(ServletContext container) {

        AnnotationConfigWebApplicationContext context
          = new AnnotationConfigWebApplicationContext();
    context.setConfigLocation("com.baeldung");
    container.addListener(new ContextLoaderListener(context));

    ServletRegistration.Dynamic dispatcher
          = container.addServlet("dispatcher",
          new DispatcherServlet(context));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/");

    container.addFilter("customRequestLoggingFilter",
          CustomeRequestLoggingFilter.class)
          .addMappingForServletNames(null, false, "dispatcher");
    }
}

===

6. 動作例

これで、Spring Bootとコンテキストを結び付けて、着信要求のロギングが期待どおりに機能することを確認できます。

@Test
public void givenRequest__whenFetchTaxiFareRateCard__thanOK() {
    TestRestTemplate testRestTemplate = new TestRestTemplate();
    TaxiRide taxiRide = new TaxiRide(true, 10l);
    String fare = testRestTemplate.postForObject(
      URL + "calculate/",
      taxiRide, String.class);

    assertThat(fare, equalTo("200"));
}

===

7. 結論

この記事では、インターセプターを使用して基本的なWeb要求ロギングを実装する方法を説明しました。また、このソリューションの限界と課題も示しました。

それから、私たちはすぐに使える簡単なロギングメカニズムを提供する組み込みのフィルタクラスを示しました。

いつものように、例とコードスニペットの実装は次のとおりです。
利用可能

over on
GitHub。