1. 序章

マイクロサービスやWebサービス全般を扱う場合、ユーザーがサービスをどのように操作しているかを知ることは非常に役立ちます。 これは、当社のサービスにヒットしたすべてのリクエストを追跡し、この情報を収集して後で分析することで実現できます。

これを支援し、ZipkinのようにSpringと簡単に統合できるシステムがいくつかあります。 ただし、 Spring Boot Actuatorにはこの機能が組み込まれており、すべてのHTTPリクエストをトレースするhttpTraceエンドポイントを介して使用できます。 このチュートリアルでは、それを使用する方法と、要件に合うようにカスタマイズする方法を示します。

2. HttpTraceエンドポイントのセットアップ

このチュートリアルでは、 aMavenSpring Bootプロジェクトを使用します。

最初に行う必要があるのは、 SpringBootActuator依存関係をプロジェクトに追加することです。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

その後、アプリケーションでhttpTraceエンドポイントを有効にする必要があります。

そのためには、application.propertiesファイルを変更してhttpTraceエンドポイントを含める必要があります。

management.endpoints.web.exposure.include=httptrace

より多くのエンドポイントが必要な場合は、コンマで区切って連結するか、ワイルドカード*を使用してすべてを含めることができます。

これで、httpTraceエンドポイントがアプリケーションのアクチュエータエンドポイントリストに表示されます。

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/actuator",
      "templated": false
    },
    "httptrace": {
      "href": "http://localhost:8080/actuator/httptrace",
      "templated": false
    }
  }
}

Webサービスの/actuator エンドポイントに移動すると、有効なすべてのアクチュエータエンドポイントを一覧表示できることに注意してください。

3. トレースの分析

httpTraceアクチュエータエンドポイントが返すトレースを分析してみましょう。

私たちのサービスにいくつかのリクエストを行い、/アクチュエータ/httptrace エンドポイントを呼び出して、返されたトレースの1つを取得しましょう。

{
  "traces": [
    {
      "timestamp": "2019-08-05T19:28:36.353Z",
      "principal": null,
      "session": null,
      "request": {
        "method": "GET",
        "uri": "http://localhost:8080/echo?msg=test",
        "headers": {
          "accept-language": [
            "en-GB,en-US;q=0.9,en;q=0.8"
          ],
          "upgrade-insecure-requests": [
            "1"
          ],
          "host": [
            "localhost:8080"
          ],
          "connection": [
            "keep-alive"
          ],
          "accept-encoding": [
            "gzip, deflate, br"
          ],
          "accept": [
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
          ],
          "user-agent": [
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 OPR/62.0.3331.66"
          ]
        },
        "remoteAddress": null
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Length": [
            "12"
          ],
          "Date": [
            "Mon, 05 Aug 2019 19:28:36 GMT"
          ],
          "Content-Type": [
            "text/html;charset=UTF-8"
          ]
        }
      },
      "timeTaken": 82
    }
  ]
}

ご覧のとおり、応答はいくつかのノードに分割されています。

  • タイムスタンプ:リクエストが受信された時刻
  • principal :該当する場合、リクエストを行った認証済みユーザー
  • セッション:リクエストに関連付けられている任意のセッション
  • request :メソッド、完全なURI、ヘッダーなどのリクエストに関する情報
  • response :ステータスやヘッダーなどの応答に関する情報
  • timeTaken :リクエストの処理にかかる時間

冗長すぎると感じた場合は、この応答をニーズに適合させることができます。 application.properties のmanagement.trace.http.includeプロパティを指定することにより、どのフィールドを返すかをSpringに伝えることができます。

management.trace.http.include=RESPONSE_HEADERS

この場合、応答ヘッダーのみが必要であることを指定しました。 したがって、リクエストヘッダーや所要時間など、以前に含まれていたフィールドが現在のレスポンスに存在しないことがわかります。

{
  "traces": [
    {
      "timestamp": "2019-08-05T20:23:01.397Z",
      "principal": null,
      "session": null,
      "request": {
        "method": "GET",
        "uri": "http://localhost:8080/echo?msg=test",
        "headers": {},
        "remoteAddress": null
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Length": [
            "12"
          ],
          "Date": [
            "Mon, 05 Aug 2019 20:23:01 GMT"
          ],
          "Content-Type": [
            "text/html;charset=UTF-8"
          ]
        }
      },
      "timeTaken": null
    }
  ]
}

含めることができるすべての可能な値は、ソースコードとデフォルトの値にあります。

4. HttpTraceRepositoryのカスタマイズ

デフォルトでは、httpTraceエンドポイントは最後の100リクエストのみを返し、それらをメモリに保存します。 幸いなことに、独自の HttpTraceRepository を作成することで、これをカスタマイズすることもできます。

それでは、リポジトリを作成しましょう。 HttpTraceRepository インターフェイスは非常にシンプルで、2つのメソッドを実装するだけで済みます。 findAll()すべてのトレースを取得します。 およびadd()を使用して、リポジトリにトレースを追加します。

簡単にするために、リポジトリはトレースもメモリに保存し、サービスにヒットした最後のGETリクエストのみを保存します。

@Repository
public class CustomTraceRepository implements HttpTraceRepository {

    AtomicReference<HttpTrace> lastTrace = new AtomicReference<>();

    @Override
    public List<HttpTrace> findAll() {
        return Collections.singletonList(lastTrace.get());
    }

    @Override
    public void add(HttpTrace trace) {
        if ("GET".equals(trace.getRequest().getMethod())) {
            lastTrace.set(trace);
        }
    }

}

この単純な例はあまり役に立たないように見えるかもしれませんが、これがどれほど強力であり、ログをどこにでも保存できるかを確認できます。

5. トレースするパスのフィルタリング

最後に説明するのは、トレースするパスをフィルタリングする方法です。これにより、関心のないいくつかのリクエストを無視できます。

サービスにいくつかのリクエストを行った後、 httpTrace エンドポイントを少し試してみると、アクチュエータリクエストのトレースも取得されていることがわかります。

{
  "traces": [
    {
      "timestamp": "2019-07-28T13:56:36.998Z",
      "principal": null,
      "session": null,
      "request": {
        "method": "GET",
        "uri": "http://localhost:8080/actuator/",
         // ...
}

これらのトレースは役に立たない可能性があるため、除外することをお勧めします。 その場合は、独自のHttpTraceFilter を作成し、shouldNotFilterメソッドで無視するパスを指定する必要があります:

@Component
public class TraceRequestFilter extends HttpTraceFilter {

  public TraceRequestFilter(HttpTraceRepository repository, HttpExchangeTracer tracer) {
      super(repository, tracer);
  }

  @Override
  protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
      return request.getServletPath().contains("actuator");
  }
}

HttpTraceFilter は単なる通常のSpringフィルターですが、トレース固有の機能がいくつかあることに注意してください。

6. 結論

このチュートリアルでは、 httpTrace Spring Bootアクチュエータエンドポイントを紹介し、その主な機能を示しました。 また、もう少し深く掘り下げて、特定のニーズに合うようにデフォルトの動作を変更する方法を説明しました。

いつものように、例の完全なソースコードは、GitHubから入手できます。