Java 用 Cloud クライアント ライブラリのトラブルシューティング

このドキュメントでは、Java 用 Cloud クライアント ライブラリのロギングと一般的な問題のトラブルシューティングの概要について説明します。

ロギング

クライアント ライブラリのロギングを有効にするには、java.util.logging を使用する方法と、SLF4J でクライアント ライブラリのデバッグ ロギングを使用する方法の 2 つがあります。

java.util.logging を使用する

Java 用 Cloud クライアント ライブラリは、Java Logging API(java.util.logging)パッケージを使用します。ロギングレベルを構成すると、トラブルシューティングに役立つ次の情報が表示されます。

  • 基盤となるクライアント / サーバー通信のタイミング。
  • リクエスト メッセージとレスポンス メッセージのヘッダー。
  • 基盤となる依存関係ライブラリの詳細メッセージ。

Java 用 Cloud クライアント ライブラリの詳細ロギングをすばやく有効にするには、次の内容で logging.properties ファイルを作成します。

# run java program pointing to this properties file with the java arg
#   -Djava.util.logging.config.file=path/to/logging.properties
handlers=java.util.logging.ConsoleHandler
java.util.logging.SimpleFormatter.format=%1$tF %1$tT,%1$tL %4$-8s %3$-50s - %5$s %6$s%n

# --- ConsoleHandler ---
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
.level=INFO

# --- Specify logging level for certain packages ---
# com.google.api is for HTTP 1.1 layer
com.google.api.level=ALL
# io.grpc is for gRPC + Netty layer
io.grpc.level=FINE
# com.google.auth is for authentication
com.google.auth.level=FINE

# Example when you specify the storage library's level. This works when
# the target Cloud library uses the logging API.
com.google.cloud.storage.level=INFO

次に、Program argument ではなく VM argument として -Djava.util.logging.config.file=path/to/logging.properties を指定して、アプリケーションを実行します。

IntelliJ を使用する場合は、[実行/デバッグ構成] で VM 引数を指定します。

VM 引数を指定する場所を示す IntelliJ の実行/デバッグ構成

プログラムの JVM が構成どおりに実行されている場合は、コンソールに FINE レベルのロギングが表示されます。

出力例:

2023-04-05 13:03:01,761 FINE     com.google.auth.oauth2.DefaultCredentialsProvider  - Attempting to load credentials from well known file: /usr/local/google/home/suztomo/.config/gcloud/application_default_credentials.json
2023-04-05 13:03:01,847 FINE     io.grpc.ManagedChannelRegistry                     - Unable to find OkHttpChannelProvider
java.lang.ClassNotFoundException: io.grpc.okhttp.OkHttpChannelProvider
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:315)
...

これは、ロギングレベルを構成する方法の一つです。Java Logging API の使用方法について詳しくは、Java Logging の概要をご覧ください。

クライアント ライブラリのデバッグ ロギング(SLF4J)

Cloud クライアント ライブラリには、アプリケーションと API の統合のトラブルシューティングに役立つオプトインのデバッグ ロギングが用意されています。 ロギングが有効になると、リクエストやレスポンスなどの主要なイベントが、データ ペイロードやヘッダーなどのメタデータとともに、標準エラー ストリームに記録されます。

警告: クライアント ライブラリのデバッグ ロギングには、平文のデータ ペイロードが含まれます。これには、自分や顧客の個人情報(PII)、秘密鍵、漏洩すると侵害される可能性のあるその他のセキュリティ データなどの機密データが含まれる可能性があります。アプリケーション ログでは常に適切なデータの整備を実践し、最小権限の原則に従ってください。また、Google では、クライアント ライブラリのデバッグ ロギングは、アクティブなデバッグ中にのみ一時的に有効にし、本番環境では永続的に使用しないことをおすすめします。

前提条件

このライブラリは、SLF4J インターフェースを使用したデバッグ ロギングをサポートしています。

クライアント ライブラリと ライブラリ BOM には、slf4j-api の依存関係は含まれていません。 クライアント ライブラリのデバッグ ロギングを有効にする前に、SLF4J や対応するロギングの実装と構成など、ロギングの依存関係を設定する必要があります。

NOTE: クライアント ライブラリの デバッグ ロギング機能を使用するには、SLF4J と対応するロギング プロバイダ( logback、log4j2 など)がクラスパスに必要です。そうしないと、 環境変数を使用して有効にしてもデバッグ ロギングは行われません。

環境変数を使用してロギングを有効にする

デフォルトでは、デバッグ ロギングはオフになっています。 前提条件を満たしていても、明示的に有効にしない限り、デバッグ ロギングは有効になりません。

クライアント ライブラリのデバッグ ロギングを有効にするには、環境変数 GOOGLE_SDK_JAVA_LOGGINGtrue(大文字と小文字を区別しない)に設定します。 設定されていない場合や他の値の場合、クライアント ライブラリのデバッグ ロギングは無効になります。

ロギングの設定例

ドキュメントでは、Logback を使用したデバッグ ロギングの例を示しています。

Logback の依存関係をアプリケーションに追加すると、slf4j の依存関係が推移的に取り込まれます。 最新バージョンについては、Logback の設定 をご覧ください。 アプリケーションのクラスパスに logback の依存関係がすでにある場合は、この手順を省略できます。

Maven を使用している場合:

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>LATEST_VERSION</version>
</dependency>

DEBUG レベルのログを表示する場合は、logback 構成ファイルにコンテンツを追加します。 INFO レベルのログのみが必要な場合は、この手順をスキップできます。詳細については、Logback の構成 をご覧ください。

<!-- set Client Library log level to DEBUG -->
<logger name="com.google.api"  level="DEBUG"/>
<!-- set Auth Library log level to DEBUG -->
<logger name="com.google.auth" level="DEBUG"/>

ALPN が正しく構成されていない

ALPN is not configured properly に関連する例外が表示される場合は、次のようにします。

Caused by: java.lang.IllegalArgumentException: ALPN is not configured properly. See https://github.com/grpc/grpc-java/blob/master/SECURITY.md#troubleshooting for more information.

互換性チェッカーを使用して、環境が gRPC ベースのクライアントと互換性があるかどうかを確認します。

非互換性とは、次のいずれかを意味します。

ClassNotFoundExceptionNoSuchMethodErrorNoClassDefFoundError のトラブルシューティング

これらのエラーは、クラスパスに同じ依存関係の複数のバージョンまたは競合するバージョンが存在することが原因で発生することがよくあります。これらの依存関係の競合は、guava または protobuf-java で発生することがよくあります。

クラスパスの競合は、次の複数のソースが原因で発生する可能性があります。

  • 依存関係ツリーに同じ推移的依存関係の複数のバージョンがある。
  • ランタイム クラスパスの依存関係のバージョンが、ビルドで指定したバージョンと異なる。

たとえば、Guava バージョン 19.0 に直接的または推移的な依存関係があり、google-cloud-java が Guava バージョン 30.0 を使用している場合、google-cloud-java は Guava 19.0 に存在しない Guava メソッドを使用し、NoSuchMethodError が発生する可能性があります。

同様に、クラスパスに古いバージョンの protobuf-java があり、google-cloud-java で新しいバージョンが必要な場合は、google-cloud-java クラスの初期化に失敗する NoClassDefFoundError が表示されることがあります。

次に例を示します。

java.lang.NoClassDefFoundError: Could not initialize class com.google.pubsub.v1.PubsubMessage$AttributesDefaultEntryHolder

競合を検証する

依存関係ツリーを確認して、同じ依存関係の複数のバージョンがあるかどうかを確認します。

$ mvn dependency:tree

guavaprotobuf-java など、競合する可能性のある依存関係のバージョンを探します。

ランタイムでのみエラーが発生する場合は、ランタイム環境で競合する JAR がランタイム クラスパスに導入されている可能性があります。一般的なケースとして、アプリケーションが実行される Hadoop、Spark、その他のサーバー ソフトウェアのクラスパスに、競合するバージョンの netty