Selenium Gridの可観測性

目次

Selenium Grid

Gridは、さまざまなブラウザとオペレーティングシステムの組み合わせでテストを実行することにより、テストのスケーリングと分散を支援します。

可観測性

可観測性には、トレース、メトリクス、ログの3つの柱があります。Selenium Grid 4は完全に分散されるように設計されているため、可観測性により内部構造の理解とデバッグが容易になります。

分散トレーシング

単一のリクエストまたはトランザクションは、複数のサービスとコンポーネントにまたがります。トレーシングは、各サービスがリクエストを実行する際に、リクエストのライフサイクルを追跡します。これは、エラーシナリオでのデバッグに役立ちます。トレーシングコンテキストで使用される主な用語を以下に示します。

トレース トレーシングを使用すると、リクエストを起点から最終目的地まで、複数のサービスを介して追跡できます。このリクエストの経路は、デバッグ、エンドツーエンドのフローの監視、および障害の特定に役立ちます。トレースは、エンドツーエンドのリクエストフローを示します。各トレースには、識別子として一意のIDがあります。

スパン 各トレースは、スパンと呼ばれる時間計測された操作で構成されています。スパンには開始時刻と終了時刻があり、サービスによって実行される操作を表します。スパンの粒度は、インストルメンテーションの方法によって異なります。各スパンには一意の識別子があります。トレース内のすべてのスパンは、同じトレースIDを持ちます。

スパン属性 スパン属性は、各スパンに関する追加情報を提供するキーと値のペアです。

イベント イベントは、スパン内のタイムスタンプ付きログです。これらは、既存のスパンに追加のコンテキストを提供します。イベントには、イベント属性としてキーと値のペアも含まれています。

イベントロギング

ロギングは、アプリケーションをデバッグするために不可欠です。ロギングは多くの場合、人間が読める形式で行われます。ただし、マシンがログを検索および分析するには、明確に定義された形式にする必要があります。構造化ロギングは、ログを固定形式で一貫して記録する一般的な手法です。通常、次のフィールドが含まれます。

  • タイムスタンプ
  • ログレベル
  • ロガークラス
  • ログメッセージ(これは、ログが記録された操作に関連するフィールドにさらに分解されます)

ログとイベントは密接に関連しています。イベントは、単一の作業単位を実行するために利用可能なすべての情報をカプセル化します。ログは、本質的にイベントのサブセットです。根本的に、両方ともデバッグに役立ちます。詳細な理解については、以下のリソースを参照してください。

  1. https://www.honeycomb.io/blog/how-are-structured-logs-different-from-events/
  2. https://charity.wtf/2019/02/05/logs-vs-structured-events/

Gridの可観測性

Seleniumサーバーは、OpenTelemetryを使用してトレーシングでインストルメントされています。サーバーへのすべてのリクエストは、開始から終了までトレースされます。各トレースは、リクエストがサーバー内で実行されるときの一連のスパンで構成されています。Seleniumサーバーのほとんどのスパンは、次の2つのイベントで構成されています。

  1. 通常イベント - 作業単位に関するすべての情報を記録し、作業の正常な完了をマークします。
  2. エラーイベント - エラーが発生するまでのすべての情報を記録し、エラー情報を記録します。例外イベントをマークします。

Seleniumサーバーの実行

  1. スタンドアロン
  2. ハブとノード
  3. 完全分散
  4. Docker

トレースの可視化

すべてのスパン、イベント、およびそれぞれの属性は、トレースの一部です。トレーシングは、上記のすべてのモードでサーバーを実行しているときに機能します。

デフォルトでは、トレーシングはSeleniumサーバーで有効になっています。Seleniumサーバーは、2つのエクスポーターを介してトレースをエクスポートします。

  1. コンソール - すべてのトレースとそれらに含まれるスパンをFINEレベルでログに記録します。デフォルトでは、SeleniumサーバーはINFOレベル以上でログを出力します。Selenium Grid jar/sの実行中に、任意のログレベルを渡すためにlog-levelフラグを使用できます。
java -jar selenium-server-4.0.0-<selenium-version>.jar standalone --log-level FINE
  1. Jaeger UI - OpenTelemetryは、コードにトレースをインストルメントするためのAPIとSDKを提供します。一方、Jaegerはトレーシングバックエンドであり、トレーシングテレメトリデータの収集、およびデータのクエリ、フィルタリング、視覚化機能を支援します。

Jaeger UIを使用してトレースを視覚化する詳細な手順は、次のコマンドを実行することで取得できます。

java -jar selenium-server-4.0.0-<selenium-version>.jar info tracing

サーバーを実行してトレースをJaegerに送信するための非常に良い例とスクリプト

イベントログの活用

トレースを視覚化するためにエクスポートしたくない場合でも、イベントロギングを有効にするにはトレーシングを有効にする必要があります。
デフォルトでは、トレーシングは有効になっています。コンソールにログを表示するために追加のパラメータを渡す必要はありません。 スパン内のすべてのイベントは、FINEレベルでログに記録されます。エラーイベントは、WARNレベルでログに記録されます。

すべてのイベントログには、次のフィールドがあります。

フィールドフィールド値説明
イベント時刻eventIdエポックナノ秒単位でのイベントレコードのタイムスタンプ。
トレースIDtracedId各トレースは、トレースIDによって一意に識別されます。
スパンIDspanIdトレース内の各スパンは、スパンIDによって一意に識別されます。
スパン種別spanKindスパン種別は、スパンのタイプを示すスパンのプロパティです。これは、スパンによって実行される作業単位の性質を理解するのに役立ちます。
イベント名eventNameこれは、ログメッセージにマッピングされます。
イベント属性eventAttributesこれはイベントログの核心を形成し、実行された操作に基づいて、JSON形式のキーと値のペアが含まれています。これには、ロガークラスを示すハンドラークラス属性も含まれています。

ログのサンプル

FINE [LoggingOptions$1.lambda$export$1] - {
  "traceId": "fc8aef1d44b3cc8bc09eb8e581c4a8eb",
  "spanId": "b7d3b9865d3ddd45",
  "spanKind": "INTERNAL",
  "eventTime": 1597819675128886121,
  "eventName": "Session request execution complete",
  "attributes": {
    "http.status_code": 200,
    "http.handler_class": "org.openqa.selenium.grid.router.HandleSession",
    "http.url": "\u002fsession\u002fdd35257f104bb43fdfb06242953f4c85",
    "http.method": "DELETE",
    "session.id": "dd35257f104bb43fdfb06242953f4c85"
  }
}

上記のフィールドに加えて、OpenTelemetry仕様に基づいて、エラーログは以下で構成されています。

フィールドフィールド値説明
例外タイプexception.type例外のクラス名。
例外メッセージexception.message例外の理由。
例外スタックトレースexception.stacktrace例外がスローされた時点のコールスタックを出力します。例外の発生源を理解するのに役立ちます。

エラーログのサンプル

WARN [LoggingOptions$1.lambda$export$1] - {
  "traceId": "7efa5ea57e02f89cdf8de586fe09f564",
  "spanId": "914df6bc9a1f6e2b",
  "spanKind": "INTERNAL",
  "eventTime": 1597820253450580272,
  "eventName": "exception",
  "attributes": {
    "exception.type": "org.openqa.selenium.ScriptTimeoutException",
    "exception.message": "Unable to execute request: java.sql.SQLSyntaxErrorException: Table 'mysql.sessions_mappa' doesn't exist ..." (full message will be printed),
    "exception.stacktrace": "org.openqa.selenium.ScriptTimeoutException: java.sql.SQLSyntaxErrorException: Table 'mysql.sessions_mappa' doesn't exist\nBuild info: version: '4.0.0-alpha-7', revision: 'Unknown'\nSystem info: host: 'XYZ-MacBook-Pro.local', ip: 'fe80:0:0:0:10d5:b63a:bdc6:1aff%en0', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.13.6', java.version: '11.0.7'\nDriver info: driver.version: unknown ...." (full stack will be printed),
    "http.handler_class": "org.openqa.selenium.grid.distributor.remote.RemoteDistributor",
    "http.url": "\u002fsession",
    "http.method": "POST"
  }
}

注:ログは読みやすくするために上記で整形されています。ログの整形はSeleniumサーバーではオフになっています。

上記の手順で、トレースとログを表示する準備が整うはずです。

参考文献

  1. トレーシングの理解
  2. OpenTelemetryトレーシングAPI仕様
  3. Selenium Wiki
  4. 構造化ログ vs イベント
  5. Jaegerフレームワーク