Memecahkan masalah Library Klien Cloud untuk Java

Dokumen ini memberikan ringkasan tentang pencatatan log dan pemecahan masalah umum dengan Library Klien Cloud untuk Java.

Logging

Ada dua cara untuk mengaktifkan logging untuk library klien: menggunakan java.util.logging, atau menggunakan Logging Debug Library Klien dengan SLF4J.

Menggunakan java.util.logging

Cloud Client Libraries for Java menggunakan paket Java Logging API (java.util.logging). Mengonfigurasi level logging akan mengungkapkan informasi yang membantu Anda memecahkan masalah, termasuk:

  • Waktu komunikasi klien-server yang mendasarinya.
  • Header pesan permintaan dan respons.
  • Pesan verbose di library dependensi pokok.

Untuk mengaktifkan logging verbose dengan cepat untuk Library Klien Cloud untuk Java, buat file logging.properties dengan konten berikut:

# 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

Selanjutnya, jalankan aplikasi Anda dengan -Djava.util.logging.config.file=path/to/logging.properties sebagai VM argument, bukan Program argument.

Jika menggunakan IntelliJ, Anda menentukan argumen VM di Run/Debug Configuration:

Konfigurasi Run/Debug IntelliJ yang menunjukkan tempat untuk menentukan argumen VM

Jika JVM program Anda berjalan dengan konfigurasi yang benar, Anda akan melihat pencatatan aktivitas tingkat FINE di konsol.

Contoh output:

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)
...

Ini adalah salah satu cara untuk mengonfigurasi tingkat logging. Untuk mengetahui informasi selengkapnya tentang penggunaan Java Logging API, lihat Ringkasan Logging Java.

Logging Debug Library Klien (SLF4J)

Library Klien Cloud dilengkapi dengan Logging Debug yang bersifat keikutsertaan yang dapat membantu Anda memecahkan masalah integrasi aplikasi dengan API. Saat logging diaktifkan, peristiwa utama seperti permintaan dan respons, beserta payload dan metadata data, seperti header, dicatat ke aliran error standar.

PERINGATAN: Logging Debug Library Klien menyertakan payload data Anda dalam teks biasa, yang dapat mencakup data sensitif seperti PII untuk diri Anda atau pelanggan Anda, kunci pribadi, atau data keamanan lainnya yang dapat disusupi jika bocor. Selalu terapkan kebersihan data yang baik dengan log aplikasi Anda, dan ikuti prinsip akses paling sedikit. Google juga merekomendasikan agar logging debug library klien diaktifkan hanya untuk sementara selama proses debug aktif, dan tidak digunakan secara permanen dalam produksi.

Prasyarat

Library kami mendukung logging debug menggunakan antarmuka SLF4J.

Library klien dan BOM Library tidak menyertakan dependensi slf4j-api. Anda harus menyiapkan dependensi logging, termasuk SLF4J dan implementasi serta konfigurasi logging yang sesuai sebelum mengaktifkan logging debug library klien.

NOTE: Anda harus memiliki SLF4J dan penyedia logging yang sesuai, misalnya, logback, log4j2, dll., di classpath untuk menggunakan fitur logging debug library klien; jika tidak, tidak ada logging debug meskipun Anda mengaktifkannya menggunakan variabel lingkungan.

Mengaktifkan Logging dengan variabel lingkungan

Logging debug dinonaktifkan secara default. Logging debug tidak diaktifkan kecuali secara eksplisit meskipun prasyaratnya terpenuhi.

Anda dapat mengaktifkan logging debug library klien dengan menetapkan variabel lingkungan GOOGLE_SDK_JAVA_LOGGING ke true, tidak peka huruf besar/kecil. Jika tidak disetel atau nilai lainnya, logging debug library klien akan dinonaktifkan.

Contoh penyiapan logging

Dokumentasi memberikan contoh logging debug menggunakan Logback.

Tambahkan dependensi Logback ke aplikasi Anda. Tindakan ini akan membawa dependensi slf4j secara transitif. Pengguna harus melihat penyiapan Logback untuk versi terbaru. Anda dapat melewati langkah ini jika aplikasi Anda sudah memiliki dependensi logback di classpath.

Jika Anda menggunakan Maven:

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

Tambahkan konten ke file konfigurasi logback jika Anda ingin melihat log tingkat DEBUG. Anda dapat melewati langkah ini jika hanya memerlukan log tingkat INFO. Lihat konfigurasi Logback untuk mengetahui informasi selengkapnya.

<!-- 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 tidak dikonfigurasi dengan benar

Jika Anda melihat pengecualian terkait ALPN is not configured properly, seperti:

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.

Gunakan pemeriksa kompatibilitas untuk melihat apakah lingkungan Anda kompatibel dengan klien berbasis gRPC.

Ketidakcocokan mungkin berarti salah satu dari hal berikut:

Memecahkan masalah ClassNotFoundException, NoSuchMethodError, dan NoClassDefFoundError

Error ini sering kali disebabkan oleh adanya beberapa versi atau versi yang bertentangan dari dependensi yang sama di classpath. Konflik dependensi ini sering terjadi pada guava atau protobuf-java.

Beberapa sumber dapat menyebabkan konflik classpath:

  • Beberapa versi dependensi transitif yang sama di pohon dependensi.
  • Classpath runtime Anda memiliki versi dependensi yang berbeda dengan yang Anda tentukan dalam build.

Misalnya, jika Anda memiliki dependensi langsung atau transitif pada Guava versi 19.0, dan google-cloud-java menggunakan Guava versi 30.0, maka google-cloud-java mungkin menggunakan metode Guava yang tidak ada di Guava 19.0, dan menyebabkan NoSuchMethodError.

Demikian pula, jika classpath Anda memiliki protobuf-java versi lama, tetapi google-cloud-java memerlukan versi yang lebih baru, Anda mungkin akan melihat NoClassDefFoundError yang gagal menginisialisasi class google-cloud-java.

Contoh:

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

Memvalidasi konflik

Periksa hierarki dependensi untuk melihat apakah Anda memiliki beberapa versi dependensi yang sama:

$ mvn dependency:tree

Cari versi dependensi yang berpotensi menimbulkan konflik seperti guava atau protobuf-java.

Jika Anda mengalami error hanya selama runtime, lingkungan runtime Anda mungkin memperkenalkan JAR yang bertentangan ke classpath runtime Anda. Kasus umum adalah Hadoop, Spark, atau software server lain yang dijalankan aplikasi Anda memiliki versi JAR netty, guava, atau protobuf-java yang bertentangan di classpath.

Mendeteksi konflik selama build

Untuk mendeteksi kesalahan penautan dependensi pada waktu kompilasi, tambahkan Aturan Penerapan Pemeriksa Penautan di pom.xml Anda:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>3.0.0-M3</version>
        <dependencies>
          <dependency>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>linkage-checker-enforcer-rules</artifactId>
            <version>1.5.7</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>enforce-linkage-checker</id>
            <!-- Important! Should run after compile -->
            <phase>verify</phase>
            <goals>
              <goal>enforce</goal>
            </goals>
            <configuration>
              <rules>
                <LinkageCheckerRule
                    implementation="com.google.cloud.tools.dependencies.enforcer.LinkageCheckerRule"/>
              </rules>
            </configuration>
          </execution>
        </executions>
      </plugin>

Namun, tidak ada cara untuk mendeteksi konflik classpath runtime. Anda harus sepenuhnya mengetahui JAR/class mana yang disertakan dalam classpath runtime karena setiap lingkungan server berbeda.

Menyelesaikan konflik

Ada berbagai strategi untuk menyelesaikan konflik, tetapi Anda harus memahami akar penyebab konflik:

  • Jika Anda mengontrol hierarki dependensi, upgrade dependensi yang bertentangan (misalnya, mengupgrade Guava). Pendekatan ini adalah yang paling andal, tetapi dapat memerlukan upaya yang signifikan dan beberapa rilis library untuk memastikan kompatibilitas.
  • Jika Anda tidak dapat mengubah dan mengirim versi baru dependensi, impor com.google.cloud:libraries-bom:25.1.0 (atau versi yang lebih baru) untuk memilih versi dependensi yang konsisten. Pendekatan ini menyederhanakan pengelolaan dependensi. Misalnya, berikut cara Anda dapat bergantung pada versi Guava dan com.google.cloud:google-cloud-storage yang konsisten tanpa menetapkan versi salah satunya secara eksplisit:
  ...
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>libraries-bom</artifactId>
        <version>25.1.0</version>
        <type>pom</type>
        <scope>import</scope>
       </dependency>
     </dependencies>
  </dependencyManagement>
  ...
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-storage</artifactId>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
    </dependency>
    ...
  </dependencies>
  ...
  • Catatan rilis libraries-bom menampilkan library dependensi yang kompatibel. Misalnya, https://github.com/googleapis/java-cloud-bom/releases/tag/v26.31.0 menampilkan:

    These client libraries are built with the following Java libraries:
    
    - Guava: 32.1.3-jre
    - Protobuf Java: 3.25.2
    - Google Auth Library: 1.22.0
    - Google API Client: 2.2.0
    - gRPC: 1.61.0
    - GAX: 2.41.0
    - Google Cloud Core: 2.31.0
    

    Dengan memeriksa grafik dependensi project Anda (mvn dependency:tree -Dverbose, gradle dependencies, atau sbt dependencyTree), Anda mungkin menemukan beberapa dependensi memiliki versi yang tidak terduga, yang dapat menyebabkan konflik dependensi.

  • Jika perubahan versi dependensi menyebabkan kegagalan lain, pertimbangkan untuk menggabungkan dependensi yang berkonflik dengan library Java. Google Cloud

    Misalnya, untuk memberi bayangan pada guava dan protobuf-java:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>...</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope>
              <relocations>
                <!-- move protobuf to a shaded package -->
                <relocation>
                  <pattern>com.google.protobuf</pattern>
                  <shadedPattern>myapp.shaded.com.google.protobuf</shadedPattern>
                </relocation>
                <!-- move Guava to a shaded package -->
                <relocation>
                  <pattern>com.google.common</pattern>
                  <shadedPattern>myapp.shaded.com.google.common</shadedPattern>
                </relocation>
              </relocations>
            </configuration>
          </execution>
        </executions>
      </plugin>